Skip to content

Restore full-length YouTube playback via WebPO token minting; add SPICE terminal CLI - #96

Merged
OttoApocalypse69 merged 5 commits into
mainfrom
TeRiRi/local-runtime-invariant-tests
Aug 26, 2026
Merged

Restore full-length YouTube playback via WebPO token minting; add SPICE terminal CLI#96
OttoApocalypse69 merged 5 commits into
mainfrom
TeRiRi/local-runtime-invariant-tests

Conversation

@OttoApocalypse69

Copy link
Copy Markdown
Member

Summary

Two independent changes, one playback-critical:

1. fix(cloud): restore full-length YouTube playback via WebPO token minting (v1.0.172)

YouTube's gVis/PO-token enforcement now limits stream URLs resolved without a Proof-of-Origin token to roughly their first megabyte — every byte request past the window returns HTTP 403. In SPICE this surfaced as endless "Retrying this track…" loops on any song longer than ~65 seconds.

The local runtime now runs the same attestation flow as the YouTube web player, fully in-process:

  • lib/po-token.ts fetches the BotGuard challenge from the youtube.com homepage (window.ytAtN paired with that page's ytcfg so the interpreter VM sees EVENT_ID), evaluates it under a jsdom-backed globalThis, snapshots BotGuard, exchanges the response for an integrity token (~12h TTL), and caches the resulting WebPo minter
  • Tokens are minted per video ID and appended as &pot= to stream URLs; minting failures degrade gracefully to token-less URLs instead of breaking resolution
  • Stream resolution prefers YTMUSIC — web-family URLs honor WebPO tokens; iOS-family URLs reject them
  • The signed stream proxy now picks its upstream user-agent by URL shape (pot-tagged web URLs require a browser UA) and emits Access-Control-Allow-Origin: * on media responses, because the shell origin (127.0.0.1) and signed-URL origin (localhost) are cross-origin for <audio> — without this Chromium silently fails with MEDIA_ELEMENT_ERROR: code 4
  • Keeps v1.0.171's embed last-resort for tracks that still fail direct streaming, minus a retry-budget reset that could loop transports

2. feat(cli): SPICE terminal CLI + wrapper boost-mode fix

  • New apps/cli workspace (@spice/cli) with search/stream/download/lyrics/play/status/config commands against a running runtime
  • Wrapper volume injection gains an independent-slider boost mode (window.spiceOwnsMediaVolume) so the desktop slider and YouTube Music's own slider stop overwriting each other; the boost graph gate includes previously rerouted elements
  • Ops: .vercelignore for source-only deploys, apps/cli/dist registered as build output and excluded

Verification

Playback fix verified end to end on this machine:

  • Full-file ranged fetches through the signed proxy: 206 across the 1MB and 2MB boundaries that previously 403'd
  • A real browser <audio> element streams the previously dead track start-to-finish in the app UI (2:18 track observed playing well past the old ~65s kill point)
  • Backend suite 418/418, typecheck clean, lint clean
  • Source-level regression tests pin the attestation flow, URL signing, proxy UA selection, and no-double-append behavior

CLI changes are authored by the parallel CLI workstream; root suite is green except the pre-existing native-release-config workspace alignment check, which passes once both workspaces land together (this PR contains both).

- New apps/cli workspace (@spice/cli): search, stream, download, lyrics,
  play, status, and config commands against a running SPICE runtime
- Wrapper: independent-slider boost mode routes the full multiplier
  through Web Audio (window.spiceOwnsMediaVolume) so the desktop slider
  and YouTube Music's own slider stop overwriting each other; boost graph
  gate now includes previously rerouted elements (wantsWebAudio)
- Ops: .vercelignore for source-only CLI deploys, .vercel ignored,
  apps/cli/dist registered as build output and excluded
YouTube's gVis/PO-token enforcement now limits stream URLs resolved
without a Proof-of-Origin token to roughly their first megabyte; every
request past the window returns HTTP 403, which surfaced as endless
retry loops on any song longer than ~65 seconds at 128kbps.

The local runtime now runs the same attestation flow as the YouTube web
player, in-process:

- lib/po-token.ts: fetches the BotGuard challenge from the youtube.com
  homepage (window.ytAtN paired with that page's ytcfg so the VM sees
  EVENT_ID), evaluates the interpreter under a jsdom-backed globalThis,
  snapshots BotGuard, exchanges the response for an integrity token at
  GenerateIT (~12h TTL), and caches the resulting WebPoMinter. Tokens
  are minted per video ID; failures degrade gracefully to token-less
  URLs instead of breaking resolution.
- lib/youtube.ts: YTMUSIC leads the client order (web-family URLs honor
  WebPO tokens; iOS-family URLs reject them), every stream URL carries
  its &pot= token exactly once, variants expose a capped flag.
- stream proxy: user-agent is chosen by URL shape - pot-tagged web URLs
  require a browser UA, everything else keeps the historical iOS app
  identity; media responses send Access-Control-Allow-Origin:* because
  the shell origin (127.0.0.1) and signed-URL origin (localhost) are
  cross-origin for <audio>, whose silent failure otherwise surfaces as
  MEDIA_ELEMENT_ERROR code 4.

Embed last-resort handling from v1.0.171 remains for tracks that fail
direct streaming, minus a retry-budget reset that could loop transports.

Verified end to end: full-file ranged fetches through the signed proxy
(206 across 1MB and 2MB boundaries), a real browser audio element
streaming the previously dead track start-to-finish, backend suite 418/
418, typecheck clean, lint clean.

SPICE_MEDIA_CORE_VERSION -> 1.0.172.
Adding the @spice/cli workspace tripped native-release-config's exact
equality pin on package.json workspaces, turning Desktop, RPM, and macOS
universal package jobs red before any build step ran.

- Relax that assertion to what it actually protects: apps/backend must
  stay registered so packaged builds keep installing through it.
- New test/workspace-alignment.test.js covers the whole failure class:
  declared-vs-disk consistency (both directions), lockfile records and
  dependency capture per workspace, unique workspace names, npm-level
  acceptance of the workspace set, and a scanner proving no CI workflow
  scripts commands against unregistered @spice/* workspaces.

The drift scenarios are fault-injection verified locally: deleting
apps/cli from workspaces or from package-lock.json both fail loudly
with remediation messages, as does referencing an unregistered
workspace from a workflow.
…ckaging

Next's standalone tracer emits "<pkg>-<hash>" placeholder entries under
.next/node_modules as junctions/symlinks back into the monorepo's hoisted
node_modules whenever a packaged route pulls in a dependency installed
above apps/backend (jsdom arrived with the WebPO minting work). Windows
and macOS Native packaging jobs failed on them in two different layers:
recreating such links needs privileges (EPERM locally) and copying them
ships outbound links that the anti-leak guard rightly refuses.

The packager now resolves those placeholders to their real hoisted
packages and replaces them with plain directories inside the standalone
tree, before any staging copy happens; unknown external links still fail
loudly so leaks stay impossible. Logic lives in a shared helper
(scripts/lib/traced-symlinks.mjs) consumed by the packager.

Verified locally: package:local:windows completes end to end, the
artifact contains a real jsdom directory and zero symlinks.

Tests for this failure class (test/traced-symlinks.test.mjs): name
resolution for plain/scoped/unhashed placeholders, junction replaced by
a real content copy, internal layout links preserved while unresolvable
external links are refused, and the packager's
materialize-before-copy ordering.
@OttoApocalypse69
OttoApocalypse69 merged commit b1fd745 into main Aug 26, 2026
12 checks passed
@OttoApocalypse69
OttoApocalypse69 deleted the TeRiRi/local-runtime-invariant-tests branch August 26, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant