# Findings on the media roadmap, revision 4

Reviewed by **GPT-6 (Codex), 2026-09-07**, fourth pass, against revision 4 at `0ae7da6`, with Paul's rulings treated as fixed. Unedited apart from this note. Revision 5 records which findings were accepted and how.

| Revision-3 finding | Disposition | One-line reason |
|---|---|---|
| R3-1 — Public cache retirement | Closed | All doors now require fresh serving authority; retirement covers every relevant box through acknowledgement or lease expiry. |
| R3-2 — Publication fence | Partial | The console can reject expired commits, but synchronous transforms may expose output before that check succeeds. |
| R3-3 — Relay incarnation | Partial | Username, authority record and incarnation retention are defined; stale authority closes new allocations only. |
| R3-4 — Hostname reservation | Partial | The separate claim fixes length and historical parsing, but misses instance-host acquisition and trailing-hyphen names. |
| R3-5 — SFU signalling | Closed | A dedicated SFU hostname carries LiveKit WebSockets directly, including for tunnel-backed apps. |
| R3-6 — SFU tenant authority and revocation | Partial | Namespacing and signing custody are specified; webhook-based rejection and the refresh mechanism do not enforce revocation. |
| R3-7 — Positive authorization cache | Closed | The exception is removed for every tenant; changing it requires a new ruling. |
| R3-8 — Recording prerequisites | Closed | Redis, separate recording workers and Chrome sandbox requirements are named and remain deferred. |
| R3-9 — Sequential gate heading | Closed | Acceptance gates now follow the dependency graph. |

Reviewed commit `0ae7da6fdebd198ecddfad419d2d05049dcf73f1`. Paul’s eleven rulings are treated as fixed. No files were changed. “Closed” means resolved at roadmap level, not implementation-proven.

**Severity:** P1 breaks a trust guarantee or makes a stage unbuildable as written; P2 is a contract or claim defect to fix before the stage spec; P3 is advisory.

## R4-1 — P1 — Stage 4b: the rejoin deny list is checked after access is restored

**Problem and evidence:** The [tenant-boundary mechanism](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:675) checks removed identities on token refresh and on LiveKit’s participant-joined webhook, claiming an old token cannot restore access.

LiveKit emits `participant_joined` after the participant becomes active and establishes its media connection. Webhook delivery is also not guaranteed: retries can end in abandonment. This is notification after admission, not an admission veto. [LiveKit webhook contract](https://docs.livekit.io/intro/basics/rooms-participants-tracks/webhooks-events/).

A removed participant can therefore present an unexpired token, reconnect and receive room media before being removed again. If webhook delivery fails, the proposed check never runs. The stage-4b gate’s “unable to rejoin” guarantee does not follow.

**Correction:** Enforce the deny list before admitting a join or reconnect, through an unavoidable SFU admission gate. The platform WebSocket endpoint is a possible enforcement point, provided direct signalling cannot bypass it and admission is serialized against removal. Keep webhooks for observation and reconciliation. Test repeated rejoin attempts with delayed and lost webhooks, asserting that no room media becomes accessible.

## R4-2 — P2 — Stage 4b: platform token refresh does not govern established sessions

**Problem and evidence:** [Lines 681–685](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:681) say participants refresh through the platform door and describe removal as declining refresh plus calling remove-participant.

Stock LiveKit refreshes connected clients’ tokens itself. Its server starts a refresh loop and sends newly signed tokens without consulting the platform door. Token expiry does not terminate an established session. [LiveKit token lifecycle](https://docs.livekit.io/frontends/reference/tokens-grants/), [server refresh implementation](https://github.com/livekit/livekit/blob/master/pkg/service/roommanager.go#L702).

A client need not request platform refresh. Refusing that request therefore cannot be the mechanism that discovers or enforces loss of authorization. This remains a defect even after fixing admission under R4-1.

**Correction:** Define an explicit app-to-platform removal operation that records revocation and reliably terminates the active participant, independently of client refresh. If continued participation requires periodic app authorization, specify a server-enforced session lease and its failure behavior. Account for LiveKit-issued refreshed tokens in reconnect enforcement. Browser requests for platform tokens must go through the app’s authenticated backend; the route credential remains server-side.

## R4-3 — P2 — Stages 1–2: synchronous transforms bypass the publication decision

**Problem and evidence:** The [publication fence](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:410) gives the console exclusive publication authority, then expressly allows the computing edge to serve a synchronous transform while publication is still in flight.

A transform can read its source, stall until its attempt lease expires or the source receives an infected verdict, and then finish. The console correctly refuses its commit, but the exception permits the initiating response to receive those same rejected bytes. Manifest-only serving protects subsequent readers; it does not protect this response.

**Correction:** Require successful publication before releasing transformed bytes, or define an equally authoritative response-admission decision covering the attempt lease, source generation and incarnation. Merely retaining a fresh edge serving lease does not establish that this attempt remains valid. Add tests where the initiating request receives no transformed bytes after publication is refused, including expiry with no successor and an infected-source purge during computation.

## R4-4 — P2 — Stage 1 and §11: app-name claims do not cover instance-host claims

**Problem and evidence:** The [media-host claim](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:310) is checked by new app names and renames. Enable checks whether the generated label is an existing app name.

There is another acquisition door. The [instances API](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/console/apiinstances.go:571) permits an app to create a hostname using another, unclaimed label in the account’s platform namespace. Its ownership check calls `HasAppIn`; an [existing test expressly permits unclaimed labels](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/console/apiinstancesenable_test.go:210).

Thus app `widget` can create `media-docs.abc.oncarlos.com` without any app named `media-docs`. The roadmap’s enable-time check misses that existing host. Conversely, checking app creation alone does not prevent a later instance request from claiming a media-owned label.

**Correction:** Make instance-host acquisition consult the media-host claim, and make media enable check existing instance and route ownership. Serialize these competing acquisitions against the same hostname claim. Test both acquisition orders and concurrent requests; preserve existing owners.

## R4-5 — P2 — Stage 1: some valid app names still produce invalid media hostnames

**Problem and evidence:** [Lines 312–318](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:312) promise that the claim accepts `media-` plus any valid app name.

[`release.ValidApp`](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/release/release.go:239) permits trailing hyphens. App `docs-` therefore passes, but its generated label `media-docs-` fails the platform’s [hostname grammar](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/provision/provision.go:223).

The length fix is sound: a 38-character media label can be a hostname without being an app name. However, the accompanying assertions in §1 and stage 1 that every generated label is also a valid app name remain false for source names longer than 26 characters.

**Correction:** Define a collision-safe hostname mapping that covers trailing-hyphen names, or explicitly refuse media enable for them before writing any claim or grant. Preserve historical app parsing. Qualify the app-name collision explanation to the subset of generated labels that satisfy `ValidApp`.

## R4-6 — P2 — Stage 4a: stale relay authority does not retire existing allocations

**Problem and evidence:** The [relay authority rule](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:605) refuses **new allocations** after fifteen minutes, calling this the same lease rule as media serving. The [allocation policy](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:627) ends established allocations on credential expiry or observed disable.

A relay can establish an allocation, lose authority updates, and miss a subsequent disable. At fifteen minutes it stops accepting new allocations but continues carrying the existing one, potentially until the original one-hour credential expires. That is a different stale-authority contract from closing every media door.

**Correction:** Apply authority expiry to existing allocations and their refreshes, terminating them at the deadline. If the intended contract deliberately permits established traffic until credential expiry, state that longer disable bound explicitly and remove the “same lease rule” claim. Test disable during an authority outage with an allocation already carrying traffic.

## R4-7 — P2 — Stage 4b: self-hosting LiveKit does not remove its external STUN defaults

**Problem and evidence:** [Stage 4b](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:648) specifies the hosted server and network ports but does not configure its STUN dependencies.

Current upstream LiveKit supplies default STUN servers to participants when no configured STUN source applies. Those defaults include Twilio and Google. External-IP discovery also uses those defaults when enabled without an explicit server list. [LiveKit ICE-server selection](https://github.com/livekit/livekit/blob/master/pkg/service/roommanager.go#L1061), [default server list](https://github.com/livekit/mediatransportutil/blob/master/pkg/rtcconfig/config.go#L36), [external-IP discovery](https://github.com/livekit/mediatransportutil/blob/master/pkg/rtcconfig/ip.go#L32).

Consequently, hosting the SFU does not by itself satisfy the roadmap’s no-third-party path or the brief’s requirement that STUN be platform-operated. This concerns connection metadata, not a claim that STUN receives call media.

**Correction:** Require explicit platform-owned STUN configuration for both server discovery and client ICE configuration, with no external fallback. Verify the chosen LiveKit version’s effective configuration and test a call with third-party network access blocked. This implements the existing doctrine without changing ruling 9.

## R4-8 — P3 — §1 and stage 1: two descriptions of existing code remain inaccurate

**Problem and evidence:**

- [§1’s scanning description](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:55) presents the infected-object read deny as an unconditional consequence of scanning. [`ScanBlock.Enforce` is optional](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/objstore/objstore.go:82), and [policy construction adds the deny conditionally](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/objstore/aws.go:211).
- [“A fourth route kind”](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md:302) overlooks the existing `status` serving class in the [edge dispatch](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/media-roadmap-spec/internal/edge/edge.go:533). It is fourth only in the app-mintable vocabulary.

**Correction:** Qualify the existing scanner claim as enforcement mode, while preserving ruling 2’s requirement that the new media grant carry the infected deny. Call media a new serving class or the fourth app-mintable route kind.

**Verdict: request changes.** One P1 and six P2 findings remain. The public serving lease and per-box retirement condition close the previous public-cache defect, but SFU revocation still permits access after removal. Publication, hostname ownership, relay staleness and LiveKit’s default external dependencies need the corrections above before their stage specs proceed. None requires reopening Paul’s eleven rulings.