docs(product): record Browser v9 source checkpoint
This commit is contained in:
1 parent
3536671016
commit
d74d9443dd
17 files changed
+261
-239
No files matched your search
+7
-7
@@ -4,28 +4,28 @@
|
||||
- [方案设计](./方案设计.md): accepted implementation design; unfinished release and validation work is tracked in the current TODO ledger.
|
||||
- [Project memory](./project-memory.md): durable current product and source snapshot to read at the start of future work.
|
||||
- [Current status](./status.md): bounded current/index snapshot of production, active milestones, decisions, blockers, and links to detail.
|
||||
- [Verification status](./verification-status.md): demand-loaded ledger of current cross-cutting evidence, open proof boundaries, and expensive-test applicability.
|
||||
- [Verification status](./verification-status.md): exact v9 source gates, current v8 production evidence, open physical proof boundaries, and expensive-test applicability.
|
||||
- [Session handoff](./session-handoff.md): temporary bootstrap prompt that points a new session back to the canonical truth owners.
|
||||
- [Current TODO](./todo.md): unresolved work, active truth holds, and decisions that still require user confirmation.
|
||||
- [Deployment](./deployment.md): exact current/rollback production configuration, release operations, and transport evidence boundaries.
|
||||
- [Deployment](./deployment.md): exact v8 current/rollback production configuration, the not-yet-deployed v9 source contract, release operations, and transport evidence boundaries.
|
||||
- [Maintenance guide](./maintenance.md): Git workflow, context hygiene, document lifecycle, research policy, and automation.
|
||||
- [P2P WebRTC research](./research/webrtc-p2p-screen-sharing.md): evidence, bandwidth model, browser constraints, reference implementations, and feasibility assessment.
|
||||
- [Peer-assisted media research](./research/peer-assisted-media.md): browser shared-encode limits, standard relay re-encoding, deterministic sticky topology, runtime relay-capacity extension, and abandon gates.
|
||||
- [Low-server-cost media routes](./research/low-server-media-routes.md): Host-publication/SFU distribution, direct-to-SFU conclusion, hop-level accounting, privacy-safe ICE evidence, and bounded transport gates.
|
||||
- [Peer-assisted media research](./research/peer-assisted-media.md): browser shared-encode limits, standard relay re-encoding, deterministic sticky topology, v9 exact-route implementation evidence, and remaining physical gates.
|
||||
- [Low-server-cost media routes](./research/low-server-media-routes.md): Host-publication/SFU distribution, v9 route diagnostics, direct-to-SFU conclusion, hop-level accounting, privacy-safe ICE evidence, and bounded transport gates.
|
||||
- [Built-in peer ICE TURN candidate](./research/built-in-peer-ice-turn.md): historical evidence for the rejected participant-wide and selected-edge TURN candidates.
|
||||
- [Advanced peer distribution](./research/advanced-peer-distribution.md): generic local-reconcile evidence plus multi-tree/SVC, encoded-object relay, native RTP forwarding, FEC/network-coding, and MoQ gates.
|
||||
- [Native shared-encode sender](./research/native-shared-encode-sender.md): consolidated Draft #16/#18/#22/#23/#25/#28 ladder, unclassified product-gate failure, staged revalidation, and stop line.
|
||||
- [Native H.264 hardware decision](./research/native-h264-hardware-decision.md): bounded WebCodecs no-go, proved Media Foundation/NVIDIA hardware encode, the default-fmtp `42c01f` stop, and the retained interop boundary.
|
||||
- [Native H.264 opt-in path](./research/native-h264-opt-in-path.md): explicit H.264 sender mode, exact Pion profile registration, and one bounded Host/Viewer loopback with hardware caveats.
|
||||
- [Realtime quality adaptation](./research/realtime-quality-adaptation.md): diagnostic-only capture/send/receive correlation, production generation-rebuild evidence, path-isolated dual representations, and bounded simulcast/Dynacast/SVC checks.
|
||||
- [Realtime quality adaptation](./research/realtime-quality-adaptation.md): current v9 manual profile/codec boundaries, diagnostic-only capture/send/receive correlation, path-isolated dual representations, and bounded simulcast/Dynacast/SVC checks.
|
||||
- [Browser background capture diagnostics](./research/browser-background-capture.md): browser lifecycle evidence, a privacy-bounded capture/send/receive experiment, interpretation gates, and non-goal keepalive mechanisms.
|
||||
- [Browser screen-audio quality](./research/browser-screen-audio-quality.md): capture/source compatibility, standard readback limits, Opus negotiation boundaries, and the retained no-control decision.
|
||||
- [Browser screen-audio quality](./research/browser-screen-audio-quality.md): capture/source compatibility, v9 live sender-ceiling readback, Opus negotiation boundaries, and remaining audible/device evidence.
|
||||
- [Display name and presence boundaries](./research/display-name-presence.md): browser-local storage failure behavior, Unicode normalization, control-character policy, and the Web-only presence capability boundary.
|
||||
- [Agent context governance research](./research/agent-context-governance.md): official Codex, Claude Code, Hermes Agent, and GitHub practices adopted by this repository.
|
||||
- [ADR-0001](./adr/0001-p2p-first-media-topology.md): historical P2P-first baseline; later access and automatic-routing decisions supersede its stale current-state details.
|
||||
- [ADR-0002](./adr/0002-memory-resident-protected-rooms.md): four-digit leased in-memory rooms, local Host defaults, scoped Viewer grants, and orthogonal code entry.
|
||||
- [ADR-0004](./adr/0004-peer-assisted-media-experiment.md): historical standard-WebRTC peer-assisted experiment and reusable evidence.
|
||||
- [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md): accepted, implemented, and deployed automatic P2P-first single-reconcile route model, capacity/accounting, fallback roles, and exact first-frame transaction; remaining real-network validation is tracked in verification status.
|
||||
- [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md): accepted P2P-first single-reconcile route model; its v8 baseline is deployed, while exact v9 first-frame/resource/lifecycle corrections are implemented but not yet deployed and remaining real-network validation is tracked in verification status.
|
||||
- [ADR-0006](./adr/0006-fixed-high-native-sender-canary.md): proposed fixed-`HIGH` native canary, current no-go result, and staged revalidation boundary.
|
||||
- [ADR-0007](./adr/0007-demand-driven-dual-representation-quality.md): accepted path-isolated `HIGH + at most one LOW` policy, optional idle-layer stop, evidence contract, and SVC boundary.
|
||||
- [ADR-0008](./adr/0008-window-scoped-audio-capture.md): browser window-audio hint and the Windows WASAPI process-loopback boundary.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-0002: Memory-Resident Rooms And Scoped Viewer Access
|
||||
|
||||
- Status: Accepted; v8 room model implemented and deployed; v9 neutral code-only denial pending
|
||||
- Status: Accepted; v9 source implemented at `d543f38aacad3df5ef65fde1055cc8e733972afe`; production remains v8
|
||||
- Date: 2026-08-23
|
||||
|
||||
## Context
|
||||
@@ -101,7 +101,7 @@ all code-only Viewer attempts. A valid room grant may bypass that site gate only
|
||||
for the exact Viewer role and room. Neither a code, room password, nor Viewer
|
||||
grant can create a room or become Host authority.
|
||||
|
||||
The accepted `screener-v9` room-entry boundary adds exactly one
|
||||
The current `screener-v9` room-entry boundary uses exactly one
|
||||
`ROOM_ACCESS_DENIED` result. It applies only after site access to a well-formed
|
||||
code-only Viewer attempt whose expected admission rejects an unknown or expired
|
||||
room, disabled code entry, an absent or incorrect room password, a full room, or
|
||||
@@ -160,16 +160,14 @@ the generic `SERVER_ERROR`. None of those paths is folded into
|
||||
|
||||
## Implementation Status
|
||||
|
||||
Current source implements the v8 room, storage, and authorization core of this
|
||||
decision and has removed the old room modes, 12-digit codes, SQLite persistence,
|
||||
`ROOM_DATABASE_PATH`, `ROOM_TTL_SECONDS`, and migration surface atomically on
|
||||
the single `screener-v8` wire. Focused source coverage spans allocation, leases,
|
||||
orthogonal grant/code admission, rotate/revoke, password policy, local profile
|
||||
replay, restart loss, HTTP, signaling, and browser storage privacy.
|
||||
Production release `8f5b3f1` runs this `screener-v8` model. The atomic cutover,
|
||||
restart-loss smoke, orthogonal grant/code admission, stale-v7 rejection,
|
||||
no-SQLite runtime, and read-only rollback restoration checks passed; exact
|
||||
operational evidence is owned by deployment and verification status.
|
||||
The `ROOM_ACCESS_DENIED` boundary above is accepted for the atomic
|
||||
`screener-v9` checkpoint but is not implemented in current source or deployed
|
||||
in production.
|
||||
Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` runs the single
|
||||
`screener-v9` wire and implements this complete room boundary, including
|
||||
allocation, leases, orthogonal grant/code admission, rotate/revoke, password
|
||||
policy, local profile replay, restart loss, browser storage privacy, and the
|
||||
neutral `ROOM_ACCESS_DENIED` result. All expected code-only denials use that
|
||||
same public result followed by the shared authentication-failed close code.
|
||||
|
||||
Production exact `8f5b3f192ddd010ca01c969008e512191312736a`, release `8f5b3f1`,
|
||||
still runs `screener-v8`. It implements the memory-resident room core, but not
|
||||
the v9 neutral code-only denial. No v9 room-entry behavior is deployed yet;
|
||||
exact operational evidence remains owned by deployment and verification status.
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-0005: Automatic Hybrid Media Routing
|
||||
|
||||
- Status: Accepted; route core implemented and deployed; v9 diagnostic delta not implemented or deployed
|
||||
- Status: Accepted; route core deployed in v8; v9 route/diagnostic source implemented at `d543f38aacad3df5ef65fde1055cc8e733972afe`, not deployed
|
||||
- Date: 2026-08-20
|
||||
- Last updated: 2026-08-24
|
||||
|
||||
@@ -273,7 +273,7 @@ one waiter set. An actual SFU usage decrease drains that set once, advances
|
||||
each waiting controller's external fact, and schedules normal reconciliation;
|
||||
there is no periodic capacity poll or resource-specific route controller.
|
||||
|
||||
The accepted v9 Viewer wire adds one strict, revision-fenced `route-status`
|
||||
The current v9 Viewer wire includes one strict, revision-fenced `route-status`
|
||||
union only for states that the existing prepare/active `route-update` cannot
|
||||
express: `{ state: "waiting", reason: "sfu-admission" }` while this Viewer is
|
||||
waiting on central admission, or `{ state: "failed", reason:
|
||||
@@ -283,9 +283,8 @@ candidate, retry hint, or other topology data. Access, Host presence, signaling,
|
||||
media, and autoplay remain independent presentation inputs.
|
||||
|
||||
The room-serial operation does not divide its per-child deadline by Viewer
|
||||
count, but simultaneous joins may form a linear queue. The deployed v8
|
||||
controller does not include a route-timing snapshot. The accepted v9 diagnostic
|
||||
extension observes existing route events without changing the controller: for
|
||||
count, but simultaneous joins may form a linear queue. The current v9 diagnostic
|
||||
surface observes existing route events without changing the controller: for
|
||||
each current child it keeps only the latest route-demand, operation-start,
|
||||
current-candidate-start, first-decoded-frame, and final-outcome timing. A new
|
||||
demand overwrites that child's record; authoritative share stop/replacement,
|
||||
@@ -354,10 +353,15 @@ deployment-wide.
|
||||
|
||||
## Current Deployment Boundary
|
||||
|
||||
Production release `8f5b3f1` runs the `screener-v8` Browser runtime with only
|
||||
direct/STUN peer and LiveKit SFU/UDP routes. Its exact configuration, rollback
|
||||
artifacts, and postflight evidence are owned by the deployment document; real
|
||||
heterogeneous-network and SFU media validation remains open.
|
||||
Production exact `8f5b3f192ddd010ca01c969008e512191312736a`, release `8f5b3f1`,
|
||||
runs the `screener-v8` Browser runtime with only direct/STUN peer and LiveKit
|
||||
SFU/UDP routes. Its exact configuration, rollback artifacts, and postflight
|
||||
evidence are owned by the deployment document; real heterogeneous-network and
|
||||
SFU media validation remains open.
|
||||
|
||||
Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` implements the single
|
||||
`screener-v9` route, first-frame, typed-status, and Host-only diagnostic
|
||||
contract described above. Production has not deployed that source contract.
|
||||
|
||||
## Acceptance Boundary
|
||||
|
||||
@@ -433,9 +437,9 @@ Negative:
|
||||
burst;
|
||||
- make-before-break consumes explicit endpoint and server reservations and may
|
||||
require a bounded-gap cutover when no overlap slot exists; and
|
||||
- real SFU and target-network evidence is still required. The accepted v9
|
||||
diagnostic extension is not part of deployed v8 and must ship atomically with
|
||||
its single current protocol.
|
||||
- real SFU and target-network evidence is still required. The v9 diagnostic
|
||||
source is not part of deployed v8 and must ship atomically with its single
|
||||
current protocol.
|
||||
|
||||
## Relationship to other ADRs
|
||||
|
||||
|
||||
+29
-26
@@ -2,9 +2,11 @@
|
||||
|
||||
Last verified against upstream documentation: 2026-08-24.
|
||||
|
||||
This page records exact release `8f5b3f1` production facts and the current source
|
||||
deployment contract. Product direction and pending migrations are owned by
|
||||
[project memory](./project-memory.md) and [the TODO ledger](./todo.md).
|
||||
This page records exact release `8f5b3f1` production facts and the deployment
|
||||
contract for exact Browser v9 source
|
||||
`d543f38aacad3df5ef65fde1055cc8e733972afe`. V9 is implemented in source but is
|
||||
not yet deployed. Product direction and pending
|
||||
work are owned by [project memory](./project-memory.md) and [the TODO ledger](./todo.md).
|
||||
|
||||
This section documents the repository's UDP-only deployment candidate: one
|
||||
Node.js process provides the built Web client, room API, and WebSocket signaling
|
||||
@@ -34,8 +36,8 @@ The release deploys the single Browser `screener-v8` wire, random four-digit
|
||||
memory rooms with a 24-hour dormant lease, orthogonal grant/code admission,
|
||||
20-Viewer room admission, the uniform endpoint media-copy cap `2`, the
|
||||
one-controller exact-candidate route runtime, and stale-v7 rejection before
|
||||
room authority. The service unit has no writable room StateDirectory and the
|
||||
old live SQLite path is absent. LiveKit is dedicated, has `room.auto_create: false` and
|
||||
room authority. The service unit has no writable room StateDirectory; all room
|
||||
authority is process-memory-only. LiveKit is dedicated, has `room.auto_create: false` and
|
||||
`max_participants: 21`, and is admitted to one global publication ingress plus
|
||||
twenty subscription egress handles. Coturn listens only on UDP 3478 for STUN;
|
||||
LiveKit media listens on UDP 7882.
|
||||
@@ -75,9 +77,10 @@ Do not run the repository full check, typecheck, or build on the current 960 MiB
|
||||
no-swap production host. An inactive-release check before the `261e980c` cutover
|
||||
temporarily starved HTTP and SSH; exact `691863e` remained current and all four
|
||||
services stayed at `NRestarts=0`. The retained production path uses a locally
|
||||
verified dist artifact plus one `npm ci --omit=dev` in a transient unit bounded
|
||||
to 30% CPU, 160 MiB memory high, 256 MiB memory max, and 120 seconds. It then
|
||||
runs only runtime imports, `node:sqlite`, and artifact/inode/permission gates.
|
||||
verified dist artifact plus one `npm ci --omit=dev --ignore-scripts --no-audit --no-fund`
|
||||
in a transient unit bounded to 30% CPU, 160 MiB memory high, 256 MiB memory max,
|
||||
no swap, and 120 seconds. It then runs only runtime imports and the
|
||||
artifact/inode/permission gates.
|
||||
|
||||
Each immutable release must own an independent dependency tree. Never hard-link
|
||||
`node_modules` or another file that deployment may `chown`, `chmod`, replace, or
|
||||
@@ -86,11 +89,12 @@ copy-on-write reflink is acceptable only after an inode audit confirms that the
|
||||
old and new regular-file sets have zero shared inodes. Do not recursively change
|
||||
permissions until that check passes.
|
||||
|
||||
Transport archives must preserve UTF-8 entry names. Prefer the POSIX release
|
||||
archive path and verify the extracted manifest before switching. A Windows ZIP
|
||||
used for the 2026-08-20 route-rescue release altered two non-runtime Chinese
|
||||
documentation names on Linux. Runtime artifacts were unaffected and the
|
||||
immutable upload remains evidence, but do not reuse that packaging method.
|
||||
Either a POSIX archive or ZIP is acceptable transport. Build an explicit runtime
|
||||
manifest before upload, normalize every relative entry path, and reject absolute
|
||||
paths, `..` traversal, unexpected top-level entries, and unapproved links. Verify
|
||||
the uploaded archive hash, extract only into a new release directory, then compare
|
||||
the exact path set, file types, sizes, and per-file hashes with the manifest before
|
||||
switching. Archive format alone is neither integrity nor rollback evidence.
|
||||
|
||||
In a strict-shell deployment, expected service states are data, not command
|
||||
failures. Do not call `systemctl is-active` bare under `set -e`/`ERR`: an
|
||||
@@ -98,9 +102,8 @@ intentionally stopped service returns a nonzero status and can trigger a false r
|
||||
`ActiveState` with `systemctl show`, compare the returned string explicitly, and
|
||||
keep stop, symlink switch, start, health check, and rollback as separate steps.
|
||||
Preflight every inspection dependency before taking a backup or changing a
|
||||
service. If a host lacks the `sqlite3` CLI, use the pinned Node runtime's
|
||||
`node:sqlite` API or stop before the first write. After a start, poll health for
|
||||
a short bounded window instead of treating one request during startup as a
|
||||
service. After a start, poll health for a short bounded window instead of treating
|
||||
one request during startup as a
|
||||
failed release; retain the last failure while still enforcing the deadline.
|
||||
After starting coturn, likewise wait boundedly for its UDP 3478 socket before
|
||||
asserting STUN readiness; `ActiveState=active` can precede socket readiness.
|
||||
@@ -152,14 +155,13 @@ instance/hostname and keep
|
||||
the old release unchanged for rollback. If the candidate fails, roll back the
|
||||
release or instance; do not add a permanent dual-transport branch.
|
||||
|
||||
A memory-room release changes the application environment atomically: remove
|
||||
`ROOM_DATABASE_PATH` and `ROOM_TTL_SECONDS`, add `ROOM_LEASE_SECONDS`, and do
|
||||
not migrate or import SQLite rooms. Starting the candidate invalidates every
|
||||
current production room, Host token, invitation, password verifier, and route;
|
||||
open pages must refresh or explicitly create a replacement room. Keep the
|
||||
recorded old release plus its SQLite environment/config backup unchanged for
|
||||
rollback. Rolling back restores that exact old environment and database with the
|
||||
old binary; it does not attempt to carry candidate rooms into SQLite.
|
||||
A memory-room release starts with empty process authority. Starting the candidate
|
||||
invalidates every current production room, Host token, invitation, password
|
||||
verifier, and route; open pages must refresh or explicitly create a replacement
|
||||
room. No room state crosses a release boundary in either direction. Preserve the
|
||||
immutable current release and the recorded environment, unit, LiveKit, coturn,
|
||||
firewall, and reverse-proxy rollback artifact unchanged, and restore that exact
|
||||
boundary if postflight fails.
|
||||
|
||||
A shared-public-IP instance can test candidate behavior, but it cannot prove the
|
||||
clean-port boundary or approve broad migration. The room-1 smoke uses this
|
||||
@@ -409,8 +411,8 @@ Keep the proxy's access-log retention bounded and access controlled. Requests to
|
||||
`/r/{code}` put the room code in the path, so access logs can contain room codes
|
||||
as well as network metadata. They must not be treated as public artifacts. For
|
||||
a private room, the grant remains in the fragment and is not part of that
|
||||
request target. A code-only entry still requires prior site access unless its
|
||||
policy is disabled; a valid grant remains the direct Viewer path.
|
||||
request target. Every code-only attempt requires prior site access; a disabled
|
||||
policy then rejects that path, while a valid grant remains the direct Viewer path.
|
||||
|
||||
For a process-level liveness probe, send `GET /healthz`. A running process
|
||||
returns HTTP 200 with `{"status":"ok"}` and `Cache-Control: no-store`; other
|
||||
@@ -487,6 +489,7 @@ Open only these public listeners:
|
||||
|
||||
| Destination | Protocol | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `share.example.com:80` | TCP | HTTP redirect and ACME challenge |
|
||||
| `share.example.com:443` | TCP | HTTPS and WSS |
|
||||
| `stun.example.com:3478` | UDP | STUN binding only |
|
||||
| host public address `:7882` | UDP | LiveKit WebRTC ICE/UDP mux |
|
||||
|
||||
+12
-10
@@ -13,34 +13,36 @@ Last updated: 2026-08-24
|
||||
- Keep routing automatic and media distributed. Direct/peer UDP is preferred; the dedicated LiveKit SFU/UDP path is the only application fallback and is not the default topology. Screener configures no TURN, ICE/TCP, media TCP, or TLS-relayed media path.
|
||||
- Every non-server endpoint uses one server-authoritative steady outbound media-copy capacity: default `2`, statically configurable as `1`, `2`, or `3`. A peer child or the Host publication consumes one slot; upstream receive is free, and SFU subscriber egress is accounted at the server. Browser role/UA/visibility does not create a separate tier, and clients cannot raise the deployment value.
|
||||
- The stable routing invariants are one authoritative upstream per Viewer, an acyclic source-reachable active graph, exact authenticated route attempts, endpoint and server admission, one room-serial child operation, and bounded success, wait, or failure.
|
||||
- One room controller reconciles allocation, child reparenting, and relay drain through the same operation. It finds one waiting Viewer or invalid edge and builds one deterministic tuple list. The operation owns its cursor, current candidate and reservations, fact version, plus one total deadline; only its current candidate may produce media. A `screener-v8` prepare names that exact child, selected transport, and server-issued candidate connection ID. A usable old edge remains until the exact candidate child decodes its first new video frame. `settleReady` synchronously commits admission before graph promotion; success commits prepare revision `P`, while failure releases resources, broadcasts rollback `R > P` from one monotonic allocator, and advances the cursor without resetting the deadline. Exhaustion retires hard-invalid, departed-parent, or overflow edges into explicit waiting but retains a healthy bootstrap edge. Bounded-gap preflights server resources, replans the untried suffix after retirement, and appends one ordinary restore candidate when it cut healthy media. A relay with a bad ingress reparents itself as a child and retains its subtree. A disconnected endpoint, or one whose effective downstream capacity falls below its current child count, accepts no excess children; its direct or overflow children enter the same operation and a disconnected endpoint is removed when childless. Authoritative pause aborts the operation, suppresses decoded-progress stall decisions, and resumes from a fresh reconciliation.
|
||||
- One room controller reconciles allocation, child reparenting, and relay drain through the same operation. It finds one waiting Viewer or invalid edge and builds one deterministic tuple list. The operation owns its cursor, current candidate and reservations, fact version, plus one total deadline; only its current candidate may produce media. A `screener-v9` prepare names that exact child, selected transport, and server-issued candidate connection ID. A usable old edge remains until the exact candidate child decodes its first new video frame. `settleReady` synchronously commits admission before graph promotion; success commits prepare revision `P`, while failure releases resources, broadcasts rollback `R > P` from one monotonic allocator, and advances the cursor without resetting the deadline. Exhaustion retires hard-invalid, departed-parent, or overflow edges into explicit waiting but retains a healthy bootstrap edge. Bounded-gap preflights server resources, replans the untried suffix after retirement, and appends one ordinary restore candidate when it cut healthy media. A relay with a bad ingress reparents itself as a child and retains its subtree. A disconnected endpoint, or one whose effective downstream capacity falls below its current child count, accepts no excess children; its direct or overflow children enter the same operation and a disconnected endpoint is removed when childless. Authoritative pause aborts the operation, suppresses decoded-progress stall decisions, and resumes from a fresh reconciliation.
|
||||
- One Host publication may serve all SFU subscriptions. Its exact generation owns one ingress handle and exact Viewer subscription handles own egress; later SFU children reuse the publication, while reserved, committed, and draining handles remain charged until the managed room is deleted and proven absent. Actual SFU usage release wakes only rooms that previously lost a candidate to global admission, and SFU-fed and peer-fed endpoints use the same provisional-child transaction. Parent choice hard-filters authority, reachability, cycles, effective capacity, and producer transition, then orders by shallowest result, remaining steady sender capacity, stable join order, and peer identity; server admission is checked at the current cursor. Acyclicity and room admission bound maximum depth; depth is observed but has no separate hard cap. Healthy committed edges remain sticky: the controller repairs waiting, invalid, departed-parent, and overflow children but does not periodically rebalance the graph. Raw IP, claimed NAT type, UA, geography, all-pairs probes, weighted scores, and periodic room rebalancing are not route inputs.
|
||||
- Ordinary peer ICE is STUN-only, and current LiveKit media is UDP-only. Credentials, room secrets, candidate details, and diagnostic data remain private and narrowly scoped. A future strict-firewall transport requires separate evidence and must remain inside LiveKit rather than becoming another Screener route candidate.
|
||||
- Browser peers use existing periodic WebRTC/LiveKit stats to maintain one exact-route decoded-progress timestamp. New frames or route/connection changes reset it; a non-paused deadline reports the edge once. Track availability, bitrate, FPS, resolution, freeze, RTT, jitter, loss, sender stats, and SFU layer describe only the current path: they remain framework or diagnostic inputs and cannot prove another parent would be better.
|
||||
- Browser capture already requests available share audio by default on initial capture and source switch, with an explicit video-only warning when no audio track is returned. The accepted v9 screen-audio control offers live-switchable 64/128/256 kbps sender ceilings and defaults to 128. The existing last-wins quality wire owns desired state; each endpoint serially applies and reads back current Host/relay/SFU audio senders, exposes local failures, and gives future senders the latest desired value. Opus remains fixed, and without an applied-ack wire the Host does not claim room-wide atomic convergence. These are configuration ceilings, not fidelity claims.
|
||||
- Video codec selection remains `automatic | H.264 | VP8`. The accepted first switching boundary is a generation-fenced renegotiation/republish transaction while sharing is explicitly paused. Preparation never resumes a user-paused share: it stops its deadline but retains the single room-serial codec owner as one bounded prepared record with no timer. The user's normal Resume atomically revalidates those exact bindings, allocates the next server-monotonic resume attempt, and starts one bounded resume/proof deadline. The server keeps authoritative pause until the Host enables its source and returns the exact attempt acknowledgement; only then does it broadcast Resume and issue proof requests. Authorization, source acknowledgement, proof request, and proof response echo that attempt, and Pause invalidates it before another Resume can allocate the next one. Proof failure first restores authoritative pause and immediately runs bounded rollback preparation; the resulting prepared rollback has no timer and waits for the user's next normal Resume before revalidation, a fresh resume attempt, proof, and source enable. Unpaused hot switching is later reuse of that transaction, not an independent mechanism.
|
||||
- Browser capture requests available share audio by default on initial capture and source switch, with an explicit video-only warning when no audio track is returned. Its screen-audio control offers live-switchable 64/128/256 kbps sender ceilings and defaults to 128. The last-wins quality wire owns desired state; each endpoint serially applies and reads back current Host/relay/SFU audio senders, exposes local failures, and gives future senders the latest desired value. Opus remains fixed, and without an applied-ack wire the Host does not claim room-wide atomic convergence. These are configuration ceilings, not fidelity claims.
|
||||
- Video codec selection remains `automatic | H.264 | VP8`. Its first switching boundary is a generation-fenced renegotiation/republish transaction while sharing is explicitly paused. Preparation never resumes a user-paused share: it stops its deadline but retains the single room-serial codec owner as one bounded prepared record with no timer. The user's normal Resume atomically revalidates those exact bindings, allocates the next server-monotonic resume attempt, and starts one bounded resume/proof deadline. The server keeps authoritative pause until the Host enables its source and returns the exact attempt acknowledgement; only then does it broadcast Resume and issue proof requests. Authorization, source acknowledgement, proof request, and proof response echo that attempt, and Pause invalidates it before another Resume can allocate the next one. Proof failure first restores authoritative pause and immediately runs bounded rollback preparation; the resulting prepared rollback has no timer and waits for the user's next normal Resume before revalidation, a fresh resume attempt, proof, and source enable. Unpaused hot switching is later reuse of that transaction, not an independent mechanism.
|
||||
- Recommended quality remains exactly `1080p60`, `1080p30`, and `720p30`, with the middle `1080p30` as the accepted default. `480p` means only an advanced `854x480` resolution choice whose FPS and bitrate remain independently selected; it is not a fourth recommended profile.
|
||||
- Two distribution artifacts are accepted only after the current feature, NAT, and real-network ledger is complete. A public-server one-click deployment package installs the current application plus STUN/SFU and proxy components on a user-owned server. A Windows/macOS/Linux local package runs the Host, application server, and local state on the broadcaster's machine without source, Node, or any external Screener/network service; it keeps workable direct P2P but reports the reachability loss imposed by public ingress, TLS, gateway, NAT, or firewall limits rather than promising universal connectivity.
|
||||
- Current UI, presence, route labels, and diagnostics describe observed state only; they do not create route authority.
|
||||
- Before an explicit public-release decision, the private deployment and repository keep only the current product contract. Incompatible wire, configuration, parser, and API changes ship atomically; stale clients fail before authority, replaced compatibility surfaces are deleted, and Git history owns rollback.
|
||||
- The accepted v9 Viewer presentation remains on the stage until current-generation frame presentation is proven, distinguishes access/Host/queue, peer, SFU, media, autoplay, recovery, and typed failure phases, and keeps `Play` specific to autoplay rejection. A failed Viewer can export its privacy-safe stage/revision/code even before connection metrics exist; an authenticated Host may request one current route/timing snapshot only when downloading diagnostics. Route decisions, queue timing, and current-path quality are diagnostic only. Browser ICE decides direct reachability; Screener does not predict ports, classify NATs, probe TCP, synthesize remote candidates, or reparent a healthy edge for speculative quality gain.
|
||||
- Viewer presentation remains on the stage until current-generation frame presentation is proven, distinguishes access/Host/queue, peer, SFU, media, autoplay, recovery, and typed failure phases, and keeps `Play` specific to autoplay rejection. A failed Viewer can export its privacy-safe stage/revision/code even before connection metrics exist; an authenticated Host may request one current route/timing snapshot only when downloading diagnostics. Route decisions, queue timing, and current-path quality are diagnostic only. Browser ICE decides direct reachability; Screener does not predict ports, classify NATs, probe TCP, synthesize remote candidates, or reparent a healthy edge for speculative quality gain.
|
||||
|
||||
## Current Source And Production
|
||||
|
||||
- Canonical source is the root `main` branch. Audit/integration boundaries require it to be clean, and new branches/worktrees start from its exact commit only after accepted truth is merged.
|
||||
- Current source admits up to `20` Viewers and implements the uniform `1/2/3` endpoint cap, single Browser `screener-v8` wire, exact prepare candidate, first-decoded-frame commit, strict rollback revision, decoded-progress stall, a two-stage direct/SFU operation deadline, and one event-driven room controller. Ordinary peer ICE is STUN-only and the sole application fallback is SFU/UDP; stale TURN environment keys fail startup. SFU admission uses exact publication and Viewer-subscription handles with explicit no-default ingress/egress capacities and a listener-fenced dedicated LiveKit owner.
|
||||
- Current source and production implement the memory-resident room model: random free four-digit codes, the configurable dormant lease, exact-Host resume, restart loss, local creation-preference replay, orthogonal Viewer grants and `open | password | disabled` code entry, and direct invitation/policy controls. SQLite room persistence, `ROOM_DATABASE_PATH`, `ROOM_TTL_SECONDS`, and the writable room StateDirectory are removed from the v8 runtime; the stopped v7 SQLite snapshot exists only in the verified rollback backup.
|
||||
- Canonical integration truth remains the root `main` branch. The current Browser v9 source checkpoint is exact `d543f38aacad3df5ef65fde1055cc8e733972afe`; it must be integrated atomically before deployment, and auxiliary branches or older worktrees do not supersede it.
|
||||
- Current source admits up to `20` Viewers and implements the uniform `1/2/3` endpoint cap, single Browser `screener-v9` wire, exact prepare candidate, first-decoded-frame commit, strict rollback revision, decoded-progress stall, a two-stage direct/SFU operation deadline, and one event-driven room controller. A short-lived exact-generation decoded-frame observer proves only the pending candidate inside the existing total deadline; the periodic stats sampler remains responsible for active-path diagnostics and stalls. Ordinary peer ICE is STUN-only and the sole application fallback is SFU/UDP; stale TURN environment keys fail startup. SFU admission uses exact publication and Viewer-subscription handles with explicit no-default ingress/egress capacities and a listener-fenced dedicated LiveKit owner.
|
||||
- Current source and production implement the memory-resident room model: random free four-digit codes, the configurable dormant lease, exact-Host resume, restart loss, local creation-preference replay, orthogonal Viewer grants and `open | password | disabled` code entry, and direct invitation/policy controls. SQLite room persistence, `ROOM_DATABASE_PATH`, `ROOM_TTL_SECONDS`, and the writable room StateDirectory are absent from both current runtimes; the stopped v7 SQLite snapshot exists only in the verified rollback backup.
|
||||
- Production runs exact `8f5b3f192ddd010ca01c969008e512191312736a`, release `8f5b3f1`, wire `screener-v8`; `/opt/screener/backups/8f5b3f1-precutover-20260823T182438Z` is its verified rollback boundary.
|
||||
- Production implements the endpoint-cap and two-stage direct-to-SFU controller contract, admits 20 Viewers, and uses LiveKit admission of one publication ingress plus 20 subscription egress handles on a dedicated non-auto-create instance. Coturn provides STUN-only UDP 3478 with TCP/TLS and relay ranges disabled; LiveKit media uses UDP 7882.
|
||||
- Browser SFU subscribers reconcile eligible Host screen publications across connect/activate event ordering, participant/track arrival, and reconnect. Production LiveKit logs only warn/error, and the application service has a bounded same-host LiveKit readiness preflight.
|
||||
- Production retains access controls, self-check, diagnostic export, signaling watchdog behavior, and 64/128/256 audio choices. Current source still defaults to `1080p60`, has no advanced 480p resolution, disables live audio mutation, exposes scattered Viewer status/raw route errors, and carries an unused parent-edge quality-proof message. The accepted v9 cleanup and media mutations are not implemented or deployed. Real heterogeneous-network and SFU media evidence remains open.
|
||||
- Current source implements live 64/128/256 kbps audio-ceiling mutation; generation-fenced paused codec prepare, Resume source acknowledgement, decoded-progress proof, and rollback; typed first-frame Viewer presentation and privacy-safe on-demand route diagnostics; neutral room-code denial and responsive entry controls; exactly three recommended profiles with `1080p30` as default and advanced-only `854x480`; and removal of the unused parent-edge quality-proof wire. Production retains the v8 behavior and has none of this v9 batch yet.
|
||||
- Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` passed 670 Web tests in 48 files, TypeScript typecheck, client/server production builds, the access/privacy gate, production-dependency audit, and repository hygiene. Its one-Host/20-Viewer Chrome loopback decoded every Viewer and kept endpoint fanout within cap `2`, but used direct local peer routes and observed no SFU publication; it is not public-network, SFU, mobile, audio-quality, codec-switch, H.264 performance, or background-lifecycle evidence.
|
||||
- `screener`, LiveKit, coturn, and nginx are healthy; local and public health return 200 and `screener` has `NRestarts=0`. Deployment detail is owned by [deployment](./deployment.md).
|
||||
- Chrome loopback, cap2/cap3, short resource, recovery, MBB, and A/V fixtures are bounded evidence only. They do not establish quality, resource limits, or product policy.
|
||||
|
||||
## Current Priority
|
||||
|
||||
1. Checkpoint this accepted Browser work against current source and production without mixing their states.
|
||||
2. Implement the executable [Now ledger](./todo.md) as one atomic Browser v9 release: live audio, paused codec, unused quality-proof removal, Viewer/route diagnostics, room-entry/UI repair, and the corrected quality selection. Diagnose H.264/blur and Host background behavior from controlled evidence only. Distribution packaging starts after current functional and real-network work is settled.
|
||||
1. Integrate and atomically deploy the exact Browser v9 source checkpoint without mixing it with the still-live v8 production state.
|
||||
2. Close the [current evidence ledger](./todo.md) with physical codec/audio, H.264, Host-background, and representative direct/SFU network runs. Browser loopback and state-machine tests do not substitute for those paths.
|
||||
3. Run the mobile lifecycle matrix and later mechanism-simplification audit only at their stated boundaries; Native/executable and distribution work remain later product phases.
|
||||
|
||||
## Working Rules
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
interactive media, endpoint downstream cap `1..3`, and minimal central-server
|
||||
media egress; retained advanced-media measurements may cover smaller cohorts
|
||||
- Status: deterministic peer distribution and provisional make-before-break are
|
||||
retained evidence; current routing converges through ADR-0005's single
|
||||
child-reparent reconciliation, while advanced encoded-media routes remain
|
||||
unimplemented candidates
|
||||
retained evidence. Exact Browser v9 source
|
||||
`d543f38aacad3df5ef65fde1055cc8e733972afe` implements ADR-0005's single
|
||||
child-reparent reconciliation and exact first-frame transaction; production
|
||||
remains v8, and advanced encoded-media routes remain unimplemented candidates
|
||||
|
||||
This document is research evidence, not current architecture or a backlog. See
|
||||
[ADR-0005](../adr/0005-automatic-hybrid-media-routing.md) for accepted routing
|
||||
@@ -260,8 +261,9 @@ restarted. This proves LiveKit participant entry. The timing is consistent with
|
||||
the then-current one-shot grant refresh and recovery state machine, but logs do
|
||||
not prove those transitions and cannot distinguish
|
||||
connect, source, video publish, sender configuration, optional audio publish, or
|
||||
transport failure. The accepted v9 diagnostic, which is not part of deployed
|
||||
v8, exposes only a closed local stage/outcome enum to an on-demand Host snapshot
|
||||
transport failure. Exact v9 source implements the accepted diagnostic, which is
|
||||
not part of deployed v8: it exposes only a closed local stage/outcome enum to an
|
||||
on-demand Host snapshot
|
||||
and deliberately keeps raw errors, URLs, tokens, candidates, and addresses out
|
||||
of wire and logs.
|
||||
|
||||
@@ -311,7 +313,7 @@ continues.
|
||||
|
||||
## Sources And License Boundary
|
||||
|
||||
Sources checked on 2026-08-20 through 2026-08-23:
|
||||
Sources checked on 2026-08-20 through 2026-08-24:
|
||||
|
||||
- [WebRTC SVC](https://www.w3.org/TR/webrtc-svc/),
|
||||
[Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/),
|
||||
|
||||
@@ -43,20 +43,21 @@ stable baseline exists; none is a page-keepalive mechanism.
|
||||
|
||||
## Current Source Boundary
|
||||
|
||||
The source review used `main` commit
|
||||
`64755924c893b4f09b24f81c9a81891cc050ed44`. The relevant capture, preview,
|
||||
sender, and stats source is unchanged from production release
|
||||
`8f5b3f192ddd010ca01c969008e512191312736a`.
|
||||
The source review covers exact Browser v9 source
|
||||
`d543f38aacad3df5ef65fde1055cc8e733972afe`. Production remains exact v8
|
||||
`8f5b3f192ddd010ca01c969008e512191312736a`; none of the v9 changes claims or
|
||||
implements a page-keepalive mechanism.
|
||||
|
||||
- `src/client/media/quality.ts` obtains one `getDisplayMedia()` stream, applies
|
||||
ideal/max capture constraints, marks video as `motion`, and applies sender
|
||||
bitrate, frame-rate, and degradation ceilings. Only an explicit sharing
|
||||
pause changes capture tracks' `enabled` state.
|
||||
bitrate, frame-rate, and degradation ceilings. Explicit sharing pause and
|
||||
authoritative reconnect re-pause change capture tracks' `enabled` state;
|
||||
neither manufactures foreground activity.
|
||||
- `src/client/pages/HostPage.tsx` pauses the existing local preview video when
|
||||
the Host document is hidden or unfocused and resumes that preview when it is
|
||||
visible and focused. This does not stop, mute, disable, or replace the capture
|
||||
track or any sender.
|
||||
- No `requestAnimationFrame()` loop drives production capture or sending.
|
||||
- No `requestAnimationFrame()` loop drives source or production capture or sending.
|
||||
WebRTC capture, encoding, congestion control, and transport remain
|
||||
browser-owned.
|
||||
- `src/client/webrtc/stats.ts` already distinguishes capture settings,
|
||||
@@ -66,6 +67,9 @@ sender, and stats source is unchanged from production release
|
||||
be inferred as an equal media-rate drop.
|
||||
- `src/client/lib/diagnostic-export.ts` exports only the latest bounded sample.
|
||||
It is a useful cross-check, not a historical trace.
|
||||
- V9's short-lived pending-candidate decoded-frame observer reads cumulative RTP
|
||||
progress only for the exact pending route. It neither drives capture nor proves
|
||||
document activity, and it stops when that operation settles.
|
||||
- `scripts/peer-assisted-benchmark.ts` is not evidence for this issue: its
|
||||
synthetic canvas source is timer-driven and its browser launch explicitly
|
||||
disables background timer, occluded-window, and renderer backgrounding.
|
||||
|
||||
@@ -6,11 +6,11 @@ Status: peer and SFU routes already use stereo and a 128 kbps default, but users
|
||||
still report speech-gated movie/game audio, including on a phone connected
|
||||
directly through the SFU. Current Chromium web `getDisplayMedia()` defaults to
|
||||
local speech processing unless the request disables it. The source request is
|
||||
explicit. Current v8 exposes bounded 64/128/256 kbps choices before sharing and
|
||||
applies the selected ceiling when each P2P, browser-relay, or SFU sender is
|
||||
created; it does not mutate an already-active sender. Accepted v9 live mutation
|
||||
and applied readback are not implemented or deployed. Target-device audible
|
||||
proof remains open.
|
||||
explicit. Exact Browser v9 source
|
||||
`d543f38aacad3df5ef65fde1055cc8e733972afe` exposes bounded 64/128/256 kbps
|
||||
choices, applies them to new P2P, browser-relay, and SFU senders, and implements
|
||||
serialized live mutation with applied readback. Production remains v8 and does
|
||||
not yet deploy live mutation. Target-device audible proof remains open.
|
||||
|
||||
## Scope And Decision
|
||||
|
||||
@@ -48,12 +48,11 @@ stereo music in a 64--128 kbps sweet spot, and LiveKit 2.22.0 names 128 kbps
|
||||
`musicHighQualityStereo`. Requested, applied, negotiated, and observed states
|
||||
remain separate.
|
||||
|
||||
The advanced panel is named Share advanced settings. Current v8 stores the
|
||||
The advanced panel is named Share advanced settings. Exact v9 source stores the
|
||||
64/128/256 choice and applies that sender `maxBitrate` ceiling on the existing
|
||||
Opus path when it creates a sender; changing the choice during an active share
|
||||
does not update an existing sender. Accepted v9, which is not implemented or
|
||||
deployed, serializes `getParameters()`/`setParameters()` updates and readback,
|
||||
keeps media and the old applied ceiling on failure, and does not claim room-wide
|
||||
Opus path when it creates a sender. A change during an active share serializes
|
||||
`getParameters()`/`setParameters()` updates and readback, keeps media and the old
|
||||
applied ceiling on failure, and does not claim room-wide
|
||||
convergence without a remote applied acknowledgement. This does not require
|
||||
audio codec renegotiation. On the declared Chrome/Edge screen-audio Host
|
||||
baseline, pinned LiveKit 2.22.0 can update an existing audio sender without
|
||||
@@ -464,9 +463,9 @@ A/V playout timing using a distinguishable stereo fixture plus game/film audio.
|
||||
## UI And Voice Boundary
|
||||
|
||||
Share advanced settings offers exactly 64/128/256 kbps and defaults to 128.
|
||||
Current v8 applies the selected choice before sharing and to any newly created
|
||||
P2P, browser-relay, or SFU sender; it does not live-update an active sender.
|
||||
Accepted v9 live mutation is not implemented or deployed. That extension reads
|
||||
Exact v9 source applies the selected choice before sharing, to every newly
|
||||
created P2P, browser-relay, or SFU sender, and through a serialized live
|
||||
mutation. Production v8 does not live-update an active sender. The v9 path reads
|
||||
the latest desired profile into each sender and keeps endpoint-local applied
|
||||
readback rather than claiming one room-wide applied commit. The setting remains
|
||||
a sender ceiling, not a guaranteed or constant bitrate. Do not expose sample
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
- Scope: one broadcaster, explicit admission up to twenty trusted viewers,
|
||||
low latency, and bounded host media fanout
|
||||
- Status: research and dated route evidence. Current invariants and assisted
|
||||
transport roles are in [ADR-0005](../adr/0005-automatic-hybrid-media-routing.md);
|
||||
the Browser source migration is deployed, while real-network validation
|
||||
remains in [verification status](../verification-status.md).
|
||||
transport roles are in [ADR-0005](../adr/0005-automatic-hybrid-media-routing.md).
|
||||
Exact Browser v9 source `d543f38aacad3df5ef65fde1055cc8e733972afe`
|
||||
implements the current route transaction and diagnostics; production remains
|
||||
v8, while real-network validation remains in [verification status](../verification-status.md).
|
||||
|
||||
## Current Transport Conclusion
|
||||
|
||||
@@ -299,8 +300,9 @@ ingress/egress caps.
|
||||
## Privacy-Safe Route Diagnostic Boundary
|
||||
|
||||
Start with locally retained, redacted `webrtc-internals` as manual ground truth.
|
||||
The deployed v8 product already has a user-initiated local stats export; it does
|
||||
not have the accepted v9 route-timing snapshot. V9 may observe only existing
|
||||
The deployed v8 product already has a user-initiated local stats export. Exact
|
||||
v9 source also implements the route-timing snapshot, but it is not deployed. It
|
||||
may observe only existing
|
||||
join, route-demand, operation-start, candidate-start, first-decoded-frame,
|
||||
settle, share-stop, departure, and room-delete events. It keeps one latest
|
||||
timing/outcome record per current child; a new demand overwrites that record,
|
||||
@@ -555,10 +557,10 @@ counters would not be deployment-wide admission.
|
||||
| Route | Where copies are emitted | Endpoint cost | Evidence status |
|
||||
| --- | --- | --- | --- |
|
||||
| Direct host P2P | Host emits one copy per Viewer | Host upload and sender pipelines grow with Viewers | Baseline evidence; current capacity is owned by the product contract |
|
||||
| Bounded browser relay DAG | Host and each Web relay emit at most the configured endpoint cap | Ordinary browser, but every relay decodes and re-encodes and adds a hop | Source and production admit up to 20; the local 20-Viewer smoke passed, while heterogeneous networks remain open |
|
||||
| Bounded browser relay DAG | Host and each Web relay emit at most the configured endpoint cap | Ordinary browser, but every relay decodes and re-encodes and adds a hop | V9 source and v8 production admit up to 20; the exact v9 local direct loopback gate passed, while heterogeneous networks remain open |
|
||||
| Native shared-encode host | Host targets one encode for standard WebRTC edges | libwebrtc public-API proxy risk spike, with Pion as fallback | Research evidence; still pays per-edge upload |
|
||||
| Native volunteer encoded-RTP relay | Each volunteer forwards one encoded copy | Native install, RTP/RTCP forwarding, packaging, and opt-in relay policy | Research only; no current product authorization |
|
||||
| SFU service | SFU emits authorized subscription copies | Service pays bounded egress; an authoritative Host publisher supplies media | Source topology/admission are deployed and a controlled production canary decoded media; heterogeneous-network and resource validation remains open |
|
||||
| SFU service | SFU emits authorized subscription copies | Service pays bounded egress; an authoritative Host publisher supplies media | V9 source implements the current topology/admission and a v8 production canary decoded media; v9 deployment plus heterogeneous-network/resource validation remain open |
|
||||
| SFU fallback | SFU emits authorized copies | Additional central ingress and egress cost | Deployed bounded path; real heterogeneous-network and resource acceptance remains open |
|
||||
| SVC plus multiple trees | Peers emit striped layer copies across several trees | Layer scheduling, reassembly, redundancy, and more churn state | Separate conditional spike; target endpoint upload near `B` |
|
||||
| Network coding | Peers or servers emit coded blocks | Generations, buffering, decoding, integrity, and a custom media plane | Trace/FEC spike only; optimize loss recovery, not clean bandwidth |
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
- Research date: 2026-08-22
|
||||
- Scope: one game-screen broadcaster and an accepted admission target of twenty
|
||||
trusted viewers; retained executable evidence currently reaches sixteen and
|
||||
the earlier resource/quality gate remains eight
|
||||
- Status: historical evidence plus the deployed bounded controller;
|
||||
accepted ADR-0005 owns automatic peer/SFU routing. Production later removed
|
||||
the room-`1` rollout boundary and a controlled canary decoded SFU media;
|
||||
heterogeneous-network, mobile, resource, and endurance evidence remains open.
|
||||
trusted viewers; exact v9 direct-loopback evidence reaches twenty and the
|
||||
earlier resource/quality gate remains eight
|
||||
- Status: historical evidence plus the bounded controller. Exact Browser v9
|
||||
source `d543f38aacad3df5ef65fde1055cc8e733972afe` implements the current
|
||||
ADR-0005 route transaction; production remains v8 and a controlled canary
|
||||
decoded SFU media. Heterogeneous-network, mobile, resource, and endurance
|
||||
evidence remains open.
|
||||
|
||||
This document owns dated measurements and implementation evidence. Current
|
||||
capacity and routing authority live in [ADR-0005](../adr/0005-automatic-hybrid-media-routing.md)
|
||||
@@ -230,17 +231,18 @@ networks.
|
||||
|
||||
## Implemented Bounded Quality Coordination
|
||||
|
||||
The current implementation coordinates one strict `QualitySettings` object across the
|
||||
peer-assisted tree without adaptation logic. It accepts only 720p/1080p/1440p,
|
||||
integer 15-60 fps, integer 2-12 Mbps, and the three standard degradation
|
||||
preferences; missing, extra, or out-of-range fields fail schema validation.
|
||||
The three visible presets are recommendations rather than wire IDs. The server
|
||||
stores the latest complete object in a room-count-bounded in-memory map,
|
||||
currently defaults to 1080p60 at 8 Mbps with balanced priority, includes it in
|
||||
peer-assisted authenticated snapshots, and broadcasts host changes to online
|
||||
viewers. The value survives a stopped share, is removed with the room, and is
|
||||
not persisted. The accepted next default is the existing 1080p30 recommendation;
|
||||
advanced `480p` adds only an 854x480 resolution value, not a fourth preset.
|
||||
The implementation coordinates one strict `QualitySettings` object across the
|
||||
peer-assisted tree without adaptation logic. Exact v9 source accepts advanced
|
||||
480p/720p/1080p/1440p resolution values, integer 15-60 fps, integer 2-12 Mbps,
|
||||
and the three standard degradation preferences; missing, extra, or out-of-range
|
||||
fields fail schema validation. The three visible presets are recommendations
|
||||
rather than wire IDs, and `480p` is only the advanced 854x480 resolution. The
|
||||
server stores the latest complete object in a room-count-bounded in-memory map,
|
||||
defaults to 1080p30 at 5 Mbps with balanced priority in v9 source, includes it in
|
||||
peer-assisted authenticated snapshots, and broadcasts Host changes to online
|
||||
Viewers. Production v8 retains its 1080p60 default and lacks advanced 480p until
|
||||
the atomic v9 deployment. The value survives a stopped share, is removed with
|
||||
the room, and is not persisted.
|
||||
Ordinary P2P authentication remains unchanged and
|
||||
setting-control messages are forbidden in that mode.
|
||||
|
||||
@@ -527,7 +529,7 @@ event arrives later. The application therefore needs a small text-message
|
||||
challenge rather than waiting for either protocol Ping/Pong or the old socket's
|
||||
close event.
|
||||
|
||||
Under the current `screener-v8` Browser wire, the Web client sends
|
||||
Exact v9 source and deployed v8 both send
|
||||
`signaling-challenge { sequence }`, and the server sends the exact
|
||||
`signaling-challenge-response { sequence }` only to that requesting socket.
|
||||
Server and Browser assets deploy atomically; stale Browser and executable-sender
|
||||
@@ -545,8 +547,8 @@ socket generation are ignored, and the server revalidates the current
|
||||
authenticated session before replying. A hidden document, a visibility
|
||||
restore, or an obviously late timer callback clears pending evidence and starts
|
||||
a fresh five-second baseline instead of declaring failure.
|
||||
This response-only watchdog is deployed in release `21d5cd9f7139`; real silent
|
||||
partition timing remains a production/browser evidence boundary.
|
||||
This response-only watchdog remains deployed in v8 and retained in v9 source;
|
||||
real silent-partition timing remains a production/browser evidence boundary.
|
||||
|
||||
The response is socket-local, contains no room state, secret, candidate, or raw
|
||||
statistics, is not logged, and is limited by the server to at most one response
|
||||
|
||||
@@ -11,7 +11,8 @@ others. `maintain-framerate` may preserve motion by reducing resolution until
|
||||
game UI, maps, subtitles, and text become unreadable; `maintain-resolution`
|
||||
may instead lower frame rate. Neither preference overrides congestion control.
|
||||
|
||||
Current source uses `balanced` as the recommended profile and advanced default;
|
||||
Exact Browser v9 source `d543f38aacad3df5ef65fde1055cc8e733972afe`
|
||||
uses `balanced` as the recommended profile and advanced default;
|
||||
`maintain-resolution` and `maintain-framerate` remain explicit choices. These
|
||||
preferences leave actual degradation to the browser, so Screener observes
|
||||
readback and stats rather than claiming a fixed quality outcome.
|
||||
@@ -47,8 +48,8 @@ Screener therefore does not compute a weighted route score, probe alternative
|
||||
parents, or reparent a currently decoding edge for bitrate, resolution, FPS,
|
||||
freeze ratio, RTT, jitter, loss, or limitation evidence. Only a hard
|
||||
`failed/closed` connection or the existing non-paused decoded-frame stall makes
|
||||
the exact edge invalid. The existing `parent-edge-quality-evidence` message has
|
||||
no route consumer and should be deleted rather than given a threshold-based
|
||||
the exact edge invalid. Exact v9 source deletes the unused
|
||||
`parent-edge-quality-evidence` message rather than giving it a threshold-based
|
||||
meaning. Primary sources checked 2026-08-24: [RFC
|
||||
8836](https://www.rfc-editor.org/rfc/rfc8836.html),
|
||||
[Overcast](https://www.usenix.org/legacy/publications/library/proceedings/osdi2000/full_papers/jannotti/jannotti_html/index.html),
|
||||
@@ -211,8 +212,8 @@ retain the result as current publisher state. This changes no capture
|
||||
constraint, codec, representation, subscriber layer or route policy and does
|
||||
not explain an immediate same-publication report.
|
||||
|
||||
Host SFU publisher A+B remains a separate observability slice. The smallest
|
||||
design is one two-second, publication-generation-bound local sampler owned by
|
||||
Host SFU publisher A+B remains a separate observability slice. Exact v9 source
|
||||
implements one two-second, publication-generation-bound local sampler owned by
|
||||
`SfuPublisher`: merge its video/audio `LocalTrack` reports, reuse the existing
|
||||
strict stats parser and accumulator, correlate capture settings from the owned
|
||||
video track, and emit only while the same publication is active. Activation,
|
||||
@@ -220,9 +221,8 @@ replacement, deactivation and disconnect reset its identity and interval
|
||||
baseline. `HostSfuRoute` may expose that local snapshot to one Host-only
|
||||
publisher row; it must not duplicate the shared Host-to-SFU ingress inside each
|
||||
SFU Viewer card. Viewer inbound remains the per-Viewer C signal. This needs no
|
||||
wire, server telemetry, global score, selector or new UI framework, but crosses
|
||||
publisher sampling, route ownership and Host rendering and therefore is not
|
||||
bundled into the lifecycle repair.
|
||||
wire, server telemetry, global score, selector or new UI framework. Production
|
||||
v8 does not yet deploy this v9 publisher view.
|
||||
|
||||
## Codec Preference And Evidence Boundary
|
||||
|
||||
@@ -707,7 +707,7 @@ latency, CPU, and memory conflict and must be balanced. Its native encoder
|
||||
tuning and hardware integration are not available to a browser-only sender and
|
||||
must not be presented as settings this project already has.
|
||||
|
||||
## Current V8 And Accepted V9 Policy
|
||||
## Current Source V9 Policy
|
||||
|
||||
The three user-visible profiles remain ceilings rather than promised rates:
|
||||
|
||||
@@ -717,9 +717,10 @@ The three user-visible profiles remain ceilings rather than promised rates:
|
||||
| 1080p30 | 1920x1080 at 30 fps | 5 Mbps |
|
||||
| 720p30 | 1280x720 at 30 fps | 3 Mbps |
|
||||
|
||||
Current v8 defaults to `1080p60`; accepted v9 changes the default to the middle
|
||||
`1080p30` ceiling. Choosing it trades a 60 fps ceiling for a 1080p capture bound.
|
||||
The recommended set remains exactly the three profiles above. Accepted v9 adds
|
||||
Exact v9 source defaults to the middle `1080p30` ceiling; production v8 still
|
||||
defaults to `1080p60`. Choosing the v9 default trades a 60 fps ceiling for a
|
||||
1080p capture bound. The recommended set remains exactly the three profiles
|
||||
above. V9 adds
|
||||
`480p` only as an advanced `854x480` resolution whose frame rate and bitrate are
|
||||
selected independently, not as a fourth profile or preset ID. LiveKit currently
|
||||
uses the same 1080p30 at 5 Mbps screen-share preset, but neither preset
|
||||
@@ -735,12 +736,12 @@ guarantees the emitted resolution, frame rate, or bitrate.
|
||||
and `maintain-framerate` choices. None promises an emitted resolution or rate.
|
||||
- `maxBitrate` and `maxFramerate` are ceilings. They are neither minimums nor
|
||||
target guarantees, and the project does not use SDP bitrate hacks.
|
||||
- The current v8 Share advanced settings panel accepts only
|
||||
720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, the three preferences, and
|
||||
Automatic/H.264/VP8. Its 64/128/256 kbps audio ceiling, default 128, is
|
||||
selected before sharing and applied when senders are created; v8 locks audio
|
||||
mutation during an active share. Accepted v9 adds advanced `854x480` and
|
||||
live-switchable audio ceilings on the existing Opus path. The first
|
||||
- Exact v9 source's Share advanced settings panel accepts
|
||||
480p/720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, the three preferences, and
|
||||
Automatic/H.264/VP8. The `480p` choice is only advanced `854x480`, not a
|
||||
fourth recommended profile. Its 64/128/256 kbps audio ceiling, default 128,
|
||||
is live-switchable on the existing Opus path. Production v8 has neither the
|
||||
480p advanced resolution nor live audio mutation. The first
|
||||
codec-switch product boundary is a generation-fenced renegotiation and SFU
|
||||
republish while sharing is explicitly paused; pause or `replaceTrack()` alone
|
||||
does not switch codec. Display capture does not standardize channel-count or
|
||||
@@ -763,8 +764,9 @@ guarantees the emitted resolution, frame rate, or bitrate.
|
||||
- Pausing sharing disables every track in the current capture stream, producing
|
||||
black video and silence without closing the room or media connection.
|
||||
|
||||
The deployed implementation deliberately stops at manual bounded controls. It
|
||||
adds no composite score, periodic adjustment, automatic codec forcing, SDP bitrate
|
||||
The exact v9 source implementation deliberately stops at manual bounded
|
||||
controls and is not yet deployed. It adds no composite score, periodic
|
||||
adjustment, automatic codec forcing, SDP bitrate
|
||||
manipulation, or scene detector. Three consecutive samples of one non-`none` native
|
||||
`qualityLimitationReason` produce one explanatory warning; a reason change or
|
||||
recovery resets it and never triggers a media action.
|
||||
|
||||
+33
-18
@@ -32,23 +32,32 @@ Current high-signal boundary:
|
||||
- docs/status.md owns the exact source/production/hold state.
|
||||
- docs/todo.md is the only executable work ledger and owns ordering.
|
||||
- ADR-0005 owns the accepted automatic route model: direct/STUN peer followed
|
||||
only by the dedicated LiveKit SFU/UDP fallback. Current source and production
|
||||
release `8f5b3f1` implement that two-stage model. Real external-network
|
||||
and SFU media evidence remains open. Do not rerun broad or
|
||||
executable/native test suites unless the current acceptance boundary
|
||||
specifically requires them.
|
||||
- ADR-0002 owns the source-implemented room model: one process-memory RoomStore,
|
||||
only by the dedicated LiveKit SFU/UDP fallback. Current Browser v9 source
|
||||
checkpoint `d543f38aacad3df5ef65fde1055cc8e733972afe` and production v8 release
|
||||
`8f5b3f1` implement that two-stage model. Current source uses one short-lived
|
||||
exact-generation decoded-frame observer only for the pending candidate inside
|
||||
the existing total deadline; the periodic stats sampler still owns active-path
|
||||
diagnostics and stalls. Real external-network and SFU media evidence remains
|
||||
open. Do not run executable/native suites unless their later acceptance
|
||||
boundary specifically requires them.
|
||||
- ADR-0002 owns the implemented room model: one process-memory RoomStore,
|
||||
random free four-digit codes, configurable 24-hour dormant leases, restart
|
||||
loss, local Host preference replay, an independent expiring Viewer grant, and
|
||||
open/password/disabled code entry. Current source and production use the
|
||||
single `screener-v8` wire and no SQLite runtime; exact rollback artifacts are
|
||||
owned by the deployment document.
|
||||
- The owner authorized one atomic Browser v9 Now ledger. Accepted but not yet
|
||||
implemented work includes live audio mutation, paused codec switching, typed
|
||||
Viewer presentation, Host-on-demand route diagnostics, neutral room-code denial, responsive entry controls, a
|
||||
default `1080p30` recommendation, and `480p` only as an advanced 854x480
|
||||
resolution. Production remains exact v8 release `8f5b3f1` until a later
|
||||
verified deployment; do not report any of this batch as implemented or live.
|
||||
open/password/disabled code entry. Current source uses the single
|
||||
`screener-v9` wire, production uses the single `screener-v8` wire, and neither
|
||||
runtime uses SQLite; exact rollback artifacts are owned by deployment.
|
||||
- Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` implements the atomic Browser
|
||||
v9 batch: live audio-ceiling mutation, paused codec switching with exact Resume
|
||||
source authority and proof, typed Viewer presentation, Host-on-demand route
|
||||
diagnostics, neutral room-code denial, responsive entry controls, default
|
||||
`1080p30`, and advanced-only `854x480`. It passed 670 Web tests in 48 files,
|
||||
typecheck, client/server builds, access/privacy, dependency audit, and
|
||||
repository hygiene. Its exact 20-Viewer Chrome run passed on
|
||||
direct loopback with no SFU publication, so it is not SFU or public-network
|
||||
evidence.
|
||||
- Production remains exact `8f5b3f192ddd010ca01c969008e512191312736a`,
|
||||
release `8f5b3f1`, wire `screener-v8`. Do not report v9 as deployed until one
|
||||
atomic cutover and postflight prove the exact release.
|
||||
- Ordinary browser ICE owns direct reachability. Do not build port prediction,
|
||||
guessed candidates, NAT classification, TCP probes, quality scores, or
|
||||
quality-driven reparenting. Current-path quality is diagnostic; a healthy
|
||||
@@ -65,7 +74,13 @@ Current high-signal boundary:
|
||||
material. Do not merge or delete them until their unique changes, references,
|
||||
and reparse/junction safety are checked against current main.
|
||||
|
||||
Follow the current ledger in order, checkpoint accepted truth before dependent
|
||||
implementation, and report each verified integration/deployment boundary without
|
||||
reviving stale agent or branch facts.
|
||||
Current TODO priority is the atomic v9 deployment, then physical codec and audio
|
||||
validation, controlled H.264 and Host-background diagnosis, and representative
|
||||
direct/peer-relay/SFU network acceptance. Mobile lifecycle, distribution/native
|
||||
packages, retained product decisions, and the read-only simplification audit stay
|
||||
at their later boundaries.
|
||||
|
||||
Follow the current ledger in order and report each verified integration,
|
||||
deployment, and physical-evidence boundary without reviving stale agent or branch
|
||||
facts.
|
||||
```
|
||||
+16
-19
@@ -14,35 +14,32 @@ This is the current execution index. Git history owns completed timelines; [veri
|
||||
|
||||
## Current Source
|
||||
|
||||
- Canonical source is the root `main` branch; auxiliary branches and worktrees do not own current truth. Audit/integration boundaries require a clean root, and dependent work starts only from its exact commit.
|
||||
- Current source implements one steady outbound media-copy cap for every non-server endpoint: default `2`, static `1/2/3`, a peer child or Host publication consumes one slot, upstream receive is free, and no Browser/UA/visibility tier exists. One shared guard owns the accounting and transition work is bounded by `min(C + 1, 3)`.
|
||||
- Current source and production admission support one Host plus up to `20` Viewers. Focused controller coverage includes `C=1/2/3`, a 20-Viewer graph, and the passed local 20-Viewer Browser smoke; real external-network media and resource measurements remain open.
|
||||
- The single Browser `screener-v8` boundary rejects stale Browser and executable-sender wires before room authority and carries the exact deployment cap plus exact prepare candidate in the authenticated route contract.
|
||||
- Current source also implements explicit no-default SFU ingress/egress capacities and exact `reserved | committed | draining` accounting. The listener owner reconciles its dedicated non-auto-create LiveKit namespace before serving, creates each generation before token issuance, releases only after delete-plus-absence proof, and reclaims Host-offline generations through bounded exact-participant checks. Ordinary peer ICE is STUN-only and the sole application fallback is SFU/UDP; stale TURN environment keys fail startup.
|
||||
- Current source implements automatic P2P-first routing with one active upstream, an acyclic source-reachable graph, an exact prepare tuple, first-decoded-frame make-before-break, strict rollback revision, and bounded wait or failure.
|
||||
- One event-driven controller owns the committed graph and at most one room-serial child operation. The operation owns one deterministic candidate list/cursor, one current candidate and reservations, fact version, and one total deadline split only across direct and SFU stages. Failed candidates are operation-local; exhaustion retires invalid/overflow edges but retains healthy bootstrap media. Bounded-gap preflights resources, replans the remaining suffix, and restores a healthy retired edge through one final ordinary candidate. Admission commits synchronously before graph promotion; release uses the resource-set difference and room disposal returns all resources. One Host publication serves exact Viewer subscription handles, and actual global SFU release wakes registered waiting rooms.
|
||||
- Parent selection is deterministic rather than random or score-based: hard eligibility filters, then shallowest result, remaining sender capacity, stable join order, and peer identity. When a departed Host root frees a Host slot, a new or reparable child prefers that shallower Host result over another root's free slot; healthy old edges remain sticky and the controller does not periodically rebalance the whole graph. The exact candidate child's first new decoded frame is the only application media-ready event. Moderate bitrate, FPS, resolution, loss, or visual degradation remains with stock WebRTC/LiveKit adaptation and diagnostics; only hard connection failure or a non-paused decoded-frame stall invalidates a route edge. Current-path stats do not authorize quality-driven reparenting.
|
||||
- The direct-to-SFU source passed repository typecheck, all 531 Web tests in 41 files, and client/server production builds. Focused tests cover the direct-stage wake, Host-full virtual SFU bootstrap without a premature route error, SFU reuse, and a departed-root scenario in which new demand uses the freed Host slot without moving healthy descendants. No Native/executable suite or heterogeneous-network media run was performed.
|
||||
- Browser initial capture and source switching already request available share audio through one shared capture boundary and visibly retain video-only sharing when no audio track is returned.
|
||||
- Live 64/128/256 audio-ceiling mutation and paused-share video codec switching are accepted but not yet implemented on canonical `main`. Current source still defaults to `1080p60`, lacks advanced 480p, renders Viewer state from scattered free text, and carries an unused parent-edge quality-proof message. The accepted v9 cleanup changes the default to `1080p30`, adds only advanced `854x480`, introduces typed Viewer failure/route timing, and deletes that unused message; none of v9 is implemented or deployed. H.264 startup blur/low-FPS reports remain an evidence-led diagnosis, not a presumed codec defect.
|
||||
- Current source and production implement one process-memory RoomStore with random four-digit codes, a default 24-hour dormant lease, exact-Host resume, restart loss, local Host preference replay, independent token invitations and `open | password | disabled` code entry, and no SQLite runtime. The Host UI separates the invitation, room-code entry policy, password, grant update, and grant revoke controls.
|
||||
- Canonical integration truth remains the root `main` branch. The current Browser v9 source checkpoint is exact `d543f38aacad3df5ef65fde1055cc8e733972afe`; auxiliary branches and older worktrees do not supersede it, and it is not the deployed release.
|
||||
- The single Browser `screener-v9` boundary rejects stale clients before room authority. Current source implements one Host plus up to `20` Viewers, one steady outbound media-copy cap for every non-server endpoint (default `2`, static `1/2/3`), and the process-memory room model with random four-digit codes, a default 24-hour dormant lease, exact-Host resume, restart loss, local Host preference replay, independent token invitations, `open | password | disabled` code entry, and no SQLite runtime.
|
||||
- One event-driven controller owns the committed graph and at most one room-serial child operation. It uses one deterministic candidate list/cursor and one total direct-then-SFU deadline; exact admission and physical resources remain charged through drain, and the pending candidate commits only after an exact-generation decoded-frame proof. A short-lived 100 ms stats observer proves only that candidate inside the existing deadline, while the 2-second sampler remains responsible for active-path diagnostics and decoded-frame stalls. Healthy edges remain sticky and current-path quality does not authorize reparenting.
|
||||
- Ordinary peer ICE is STUN-only and the only application fallback is the dedicated LiveKit SFU over UDP. SFU generations use explicit no-default ingress/egress capacities, exact `reserved | committed | draining` accounting, delete-plus-absence release, one Host publication, and exact Viewer subscription handles. No Screener TURN, ICE/TCP, media TCP, or TLS-relayed media route exists.
|
||||
- Current source implements live 64/128/256 kbps audio-ceiling mutation for current and future Host/relay/SFU senders. It also implements paused `automatic | H.264 | VP8` switching as one generation-fenced prepare/Resume/source-ack/proof transaction with a server-monotonic `resumeAttempt`, authoritative re-pause, bounded rollback preparation, and no automatic Resume.
|
||||
- Current source implements typed first-frame Viewer presentation and recovery, Chinese user-facing route/access failures, privacy-safe failed-page export, authenticated Host-only on-demand route snapshots, neutral `ROOM_ACCESS_DENIED` room-code admission, and responsive entry controls. The unused parent-edge quality-proof protocol is absent.
|
||||
- Recommended quality remains exactly `1080p60`, `1080p30`, and `720p30`; current source defaults to `1080p30`. Advanced resolution adds `480p` as `854x480` without adding a fourth preset, and advanced FPS and bitrate remain independent.
|
||||
- Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` passed 670 Web tests in 48 files, TypeScript typecheck, client/server production builds, the access/privacy gate, production-dependency audit, and repository hygiene. Its exact one-Host/20-Viewer Chrome loopback passed every gate with all Viewers decoding and Host/relay fanout bounded by cap `2`; all observed media routes were local direct peers and no SFU publication appeared. This is source, state-machine, build, privacy, and direct-loopback evidence, not deployment, public-network, SFU, mobile, physical audio/codec, H.264 performance, or Host-background evidence.
|
||||
|
||||
## Current Milestone
|
||||
|
||||
1. Build one atomic Browser v9 release: implement live audio-ceiling mutation and paused-share codec switching, then remove the unused quality-proof wire, add first-frame/typed-failure Viewer presentation and privacy-safe on-demand route diagnostics, repair room entry/UI, set the default to `1080p30`, and add `480p` only to advanced resolution. Do not deploy a protocol subset.
|
||||
2. Reproduce the dated H.264/blur report and current Host background report before any mechanism change.
|
||||
3. Finish standard ICE/SFU real-network acceptance. Browser port prediction, NAT classification, TCP probing, and quality-driven reparenting are outside the accepted route model.
|
||||
1. Integrate and deploy Browser v9 as one atomic release; production stays on exact v8 until postflight proves the full checkpoint.
|
||||
2. Validate codec switching and live audio ceilings on physical direct, peer-relay, and SFU media paths, including actual codec/bitrate, first-frame, source authority, rollback, audible quality, and A/V synchronization.
|
||||
3. Reproduce the dated H.264/blur and Host background/minimized reports under controlled conditions, then finish representative ICE/STUN/SFU acceptance including mobile networks. Browser port prediction, NAT classification, TCP probing, fake page keepalive, and quality-driven reparenting remain outside the accepted model.
|
||||
|
||||
## Active Boundaries
|
||||
|
||||
- `fix/configurable-relay-cap` is an old, incomplete draft and must not be merged as-is.
|
||||
- Open PR #192 and the Native stack are evidence/research, not pending product releases. Native senders, capture helpers, shared-encode executables, and their test binaries are outside the current Browser milestone and are not built or run.
|
||||
- Deployed surfaces and retained candidates that still need product decisions are indexed only in [the TODO ledger](./todo.md); do not extend or roll them back automatically.
|
||||
- Room lifetime, authorization/storage semantics, v8 stale-client rejection, restart loss, and rollback restoration are aligned between current source and production. The room-entry presentation and responsive-control changes listed above are accepted v9 work, not current behavior.
|
||||
- Room lifetime, authorization/storage semantics, restart loss, and rollback restoration remain aligned. Source uses the v9 stale-client boundary and new room-entry presentation; production remains on the v8 boundary and v8 UI until atomic deployment.
|
||||
- Real SFU recovery, heterogeneous networks, mobile lifecycle, audio/A-V device behavior, and endurance/resource measurements remain external acceptance evidence, not blockers for unrelated reversible work.
|
||||
|
||||
## Current Hold
|
||||
|
||||
The Browser route, SFU subscription reconciliation, two-stage deadline, direct-to-SFU transport,
|
||||
and memory-room slices are deployed. There is no active P0/P1 hold. The owner authorized the current
|
||||
Browser TODO sequence; Native/executable work and broad repository cleanup remain outside it.
|
||||
There is no active P0/P1 source hold. Browser v9 is implemented and locally gated but not deployed;
|
||||
production remains on exact v8 until the atomic release and postflight complete. Physical codec/audio,
|
||||
heterogeneous-network, SFU, H.264/background, and mobile evidence remains open. Native/executable work
|
||||
and broad repository cleanup remain outside this release boundary.
|
||||
+13
-16
@@ -6,28 +6,24 @@ Only items in **Now** are executable after their stated decision gate. A branch
|
||||
|
||||
## Now
|
||||
|
||||
1. **Switch screen-audio ceilings live.** Keep Opus fixed and serialize 64/128/256 kbps desired profile updates across every current Host, relay, and SFU audio sender. Each endpoint uses fresh `getParameters()`/`setParameters()` readback, ignores stale completions, retains media plus its prior applied value on failure, and applies the latest desired value to future edges. Do not add a remote applied-ack protocol or claim room-wide atomic convergence in this slice.
|
||||
2. **Switch video codec while paused.** Serialize the change with route mutation, freeze exact current route/media bindings, hold new joins, and boundedly renegotiate P2P/relay plus republish SFU while paused. Preparation must not resume a share the user explicitly paused: after it settles, stop the deadline but retain the single room-serial codec owner as one bounded prepared record with no timer. The user's normal Resume first atomically revalidates those bindings, allocates the next server-monotonic resume attempt, and starts one bounded resume/proof deadline. The server keeps the share authoritatively paused until the Host enables its source and returns the exact source-enabled acknowledgement; only then does it announce Resume and issue proof requests. Authorization, acknowledgement, proof request, and proof response carry that exact attempt; Pause invalidates it, so a delayed acknowledgement or proof from an earlier Resume cannot settle a later window. A binding invalidated by leave or hard route failure exits this proof set and waits for routing to rebuild after the transaction settles. Preparation failure remains paused; proof failure first restores authoritative pause and performs bounded rollback preparation, whose proof waits for the user's next Resume. Rollback proves the prior preference was reapplied plus a newly negotiated actual codec and decoded progress, not literal equality with the old actual codec. No path auto-resumes. Zero targets update only the future-edge preference. Reset `automatic` with empty codec preferences. This is a paused bounded-gap transaction; unpaused make-before-break is a later extension.
|
||||
3. **Diagnose the dated H.264/blur report.** Reproduce the 2026-08-19 then-production release `769de201f7cc` against current `main` on the same Host/browser/driver/game and one wired direct Viewer, changing one variable at a time. Implement a codec-specific repair only if codec/encoder evidence identifies it; otherwise fix the proven capture, sender, BWE, or lifecycle cause.
|
||||
4. **Diagnose desktop Host background/minimized performance.** On the same machine, dynamic scene, direct Viewer, browser, and fresh share, compare foreground, occluded, minimized, and hidden states while separately verifying actual negotiated VP8/H.264. Correlate capture settings/FPS, frames captured/encoded/sent/decoded, bitrate, `qualityLimitationReason`, encode time, browser/game CPU and GPU, and the existing local-preview pause boundary. Treat browser scheduling/throttling and codec effects as hypotheses until measured. Do not spoof page activity, add keepalive timers, or misuse Wake Lock; if current Chrome/Edge cannot preserve the accepted Host behavior through standard Web APIs, record that capability boundary before considering the packaged/native Host.
|
||||
5. **Remove the unused parent-edge quality-proof surface.** Delete the client reporter, protocol message, server dispatch/ack flag, benchmark sender, and tests whose only purpose is the unconsumed `parent-edge-quality-evidence` message. Keep the existing exact Viewer evidence generation/session gate used by diagnostics. Do not replace the deleted magic packet/loss thresholds with another score, probe, blacklist, or route input.
|
||||
6. **Make Viewer connection progress, failure recovery, and route timing explicit.** Derive one stable stage from existing access, Host presence, route, peer/SFU, media, autoplay, recovery, and bounded-failure state. Keep the overlay until a composited current-generation frame, show `Play` only for actual autoplay rejection, preserve a still-valid old frame during recovery, and expose retry only through the bounded controller. Use one Chinese user-facing vocabulary and typed failures for server restart/room loss, Host stop, Host signaling loss with healthy media, Host crash/offline, route exhaustion, SFU admission, autoplay, and stale-client refresh without exposing private topology identifiers. A failed mobile page must retain privacy-safe stage/revision/code buckets even with zero connection metrics. When the authenticated Host downloads diagnostics, request one current route snapshot containing only each current child's latest demand, queue wait, candidate start, first decoded frame, final route, and closed outcome bucket; do not continuously push it or retain an event history. Use a 20-Viewer burst to report sample count, raw sorted durations, and nearest-rank p50/p95/max before changing the single room-serial operation or its total deadline.
|
||||
7. **Clarify malformed and unusable room entry without enumeration.** Reject non-four-digit input locally with a direct format message. For a well-formed code that cannot be admitted, stop automatically implying that a password is required; show one `ROOM_ACCESS_DENIED` result covering an unknown/expired room, disabled code entry, wrong/absent password, room full, or bounded admission failure. Offer an optional password retry and tell the Viewer to use the invitation link supplied by the Host; do not imply that the server can provide one. Keep `open` rooms direct and do not expose room existence or code-entry policy through distinct errors.
|
||||
8. **Refine quality selection.** Keep exactly three recommended profiles: `1080p60`, `1080p30`, and `720p30`. Change the default recommended profile to the middle `1080p30` option. Add `480p` (`854x480`) only to the advanced resolution selector; advanced frame rate and bitrate remain independently selected, and no `480p` recommended profile or preset ID is added. Update capture constraints, current/future peer/relay/SFU sender settings, the current-page initial desired state, readback, and tests together; do not persist quality selection or add a label that leaves old sender parameters active.
|
||||
9. **Finish the room-code entry UI as one responsive control.** Preserve the invitation-link controls that are already direct, but redesign the code-only policy/password area to match the compact work surface instead of a nested form panel. The fixed action row contains only `Start sharing`, its divider, and `Join room`; disclosure is a following sibling and cannot participate in action-row sizing. Prove stable button rectangles, desktop/mobile wrapping, focus, password and disabled states, and no horizontal overflow before integrating the retained UI candidate.
|
||||
1. **Deploy Browser v9 atomically.** Integrate the exact `d543f38aacad3df5ef65fde1055cc8e733972afe` runtime checkpoint with its aligned truth, build the clean exact integration commit, and cut one release containing the single `screener-v9` wire. Verify archive identity, stale-v8 rejection before room authority, service health, public assets, media listeners, zero restarts, secret-safe logs, and rollback readiness before declaring production v9. Do not publish a protocol subset or mix v8 and v9 assets.
|
||||
2. **Physically validate paused codec switching.** On current Chrome and Edge, exercise `automatic | H.264 | VP8` across a direct Viewer, a browser-relay descendant, and an SFU subscriber. Capture the exact negotiated outbound/inbound codec, source-enabled acknowledgement boundary, newly decoded progress, and first presented frame after each normal Resume. Exercise a real preparation or proof failure and prove authoritative re-pause plus old-preference rollback on the next user Resume; local state-machine tests alone do not close this item.
|
||||
3. **Physically validate live screen-audio ceilings.** With audible game/music capture, switch 64/128/256 kbps while sharing across direct, browser-relay, and SFU paths, then create a new edge and switch source. Record sender readback, observed bitrate, route continuity, audible result, and A/V synchronization. Treat every setting as a ceiling, and do not infer fidelity or room-wide atomic convergence from configuration alone.
|
||||
4. **Diagnose the dated H.264/blur report.** Reproduce the 2026-08-19 then-production release `769de201f7cc` against current source on the same Host/browser/driver/game and one wired direct Viewer, changing one variable at a time. Implement a codec-specific repair only if codec/encoder evidence identifies it; otherwise fix the proven capture, sender, BWE, or lifecycle cause.
|
||||
5. **Diagnose desktop Host background/minimized performance.** On the same machine, dynamic scene, direct Viewer, browser, and fresh share, compare foreground, occluded, minimized, and hidden states while separately verifying actual negotiated VP8/H.264. Correlate capture settings/FPS, frames captured/encoded/sent/decoded, bitrate, `qualityLimitationReason`, encode time, browser/game CPU and GPU, and the existing local-preview pause boundary. Treat browser scheduling/throttling and codec effects as hypotheses until measured. Do not spoof page activity, add keepalive timers, or misuse Wake Lock; if current Chrome/Edge cannot preserve the accepted Host behavior through standard Web APIs, record that capability boundary before considering the packaged/native Host.
|
||||
6. **Finish real-network route acceptance.** Measure standard ICE/STUN direct paths, peer relay, SFU ingress/subscription, exact first-frame commit and rollback, relay-ingress repair with subtree retention, disconnect/capacity drain, pause/Resume, and bounded explicit failure across representative IPv4/IPv6 NATs and mobile networks. Include an all-UDP-blocked case to establish the current failure boundary. Do not add port prediction, NAT classification, TCP probes, guessed candidates, route scores, or quality-driven reparenting.
|
||||
|
||||
## Accepted Later Roadmap
|
||||
|
||||
These items are real product work, ordered after the current release boundary; they are not permission to resume an old branch wholesale.
|
||||
|
||||
1. **Mobile Viewer lifecycle.** Run Android Chrome and iOS Safari device matrices for autoplay gesture, foreground/background audio, foreground video recovery, lock/page reclamation, rotation, Wi-Fi/cellular migration, and an assigned relay's outbound-media survival or controller failover across those transitions. Preserve the same media element and current reconnect path; do not promise background video composition, background relay continuity, or unsupported lock-screen behavior.
|
||||
2. **Real network acceptance.** Measure standard ICE/STUN, IPv4/IPv6, independent STUN destinations, SFU ingress/subscription, and bounded failure across representative NATs and mobile networks. Include an all-UDP-blocked case to measure the current explicit failure boundary. Browser pages do not predict ports, classify NATs, probe TCP reachability, or synthesize remote ICE candidates; raw-IP ranking and a room-wide quality score remain out of scope. Parent choice uses the accepted route controller, while current-path local stats and exact Viewer quality evidence remain diagnostic only.
|
||||
3. **Platform output only when real.** Revisit AirPlay/Cast only when a target browser and physical receiver prove the live `MediaStream` contract. System mirroring remains external; no placeholder button, custom receiver, or server transcode is scheduled.
|
||||
4. **Public-server one-click deployment package.** After every current functional and real-network item is settled, package the exact application, STUN/SFU, reverse proxy, secrets, health checks and rollback flow for a user-owned public server. Require real domain/TLS/firewall/capacity inputs and never ship credentials or call a partial deployment ready.
|
||||
5. **Fully local one-click Host-server package.** Package Windows/macOS/Linux Host capture, the exact application server and local state without requiring source, Node or any external Screener/STUN/SFU/tunnel/relay service. Preserve usable direct P2P and use the completed NAT/network evidence to maximize reachability, while reporting public-origin, gateway, NAT and firewall limits truthfully; loopback/LAN success is not universal Internet success.
|
||||
6. **Native Host capture backend.** At lowest priority, productize a proven Windows native capture path before expanding capture/audio backends to macOS and Linux. Its capture, hardware encode, audio, identity and lifecycle evidence becomes an input to later packages; a source checkout, helper executable, or evaluation ZIP is not a distributable product.
|
||||
7. **Native shared encode, Windows first.** At lowest priority, prove one hardware encoder output can serve two independent Viewer transports and the Host-SFU publication while each connection retains its own RTP/RTCP, pacing, encryption, feedback, and upload accounting. Productize only after actual access-unit identity, Viewer2/FIFO, heterogeneous feedback, game-resource, browser/hardware, A/V, packaging, and licensing gates pass. Web Host/relay remains standard per-`RTCPeerConnection` encoding because no portable shared-encoder contract exists.
|
||||
8. **Repository simplification audit.** After the current route and room checkpoints and their scoped implementation work settle, inventory components, configuration, migrations, gates, timers, compatibility paths, and tests using the [maintenance review](./maintenance.md#机制减负审查). Begin read-only: identify the user behavior each mechanism preserves, quantify the impact of removing it, and compare that value with its whole-system cost. Any accepted feature tradeoff updates its owning contract before complete unused surfaces are removed. This later audit must not become a broad refactor or block current product work.
|
||||
2. **Platform output only when real.** Revisit AirPlay/Cast only when a target browser and physical receiver prove the live `MediaStream` contract. System mirroring remains external; no placeholder button, custom receiver, or server transcode is scheduled.
|
||||
3. **Public-server one-click deployment package.** After every current functional and real-network item is settled, package the exact application, STUN/SFU, reverse proxy, secrets, health checks and rollback flow for a user-owned public server. Require real domain/TLS/firewall/capacity inputs and never ship credentials or call a partial deployment ready.
|
||||
4. **Fully local one-click Host-server package.** Package Windows/macOS/Linux Host capture, the exact application server and local state without requiring source, Node or any external Screener/STUN/SFU/tunnel/relay service. Preserve usable direct P2P and use the completed NAT/network evidence to maximize reachability, while reporting public-origin, gateway, NAT and firewall limits truthfully; loopback/LAN success is not universal Internet success.
|
||||
5. **Native Host capture backend.** At lowest priority, productize a proven Windows native capture path before expanding capture/audio backends to macOS and Linux. Its capture, hardware encode, audio, identity and lifecycle evidence becomes an input to later packages; a source checkout, helper executable, or evaluation ZIP is not a distributable product.
|
||||
6. **Native shared encode, Windows first.** At lowest priority, prove one hardware encoder output can serve two independent Viewer transports and the Host-SFU publication while each connection retains its own RTP/RTCP, pacing, encryption, feedback, and upload accounting. Productize only after actual access-unit identity, Viewer2/FIFO, heterogeneous feedback, game-resource, browser/hardware, A/V, packaging, and licensing gates pass. Web Host/relay remains standard per-`RTCPeerConnection` encoding because no portable shared-encoder contract exists.
|
||||
7. **Repository simplification audit.** After the current Browser, physical-media, route, and room checkpoints settle, inventory components, configuration, migrations, gates, timers, compatibility paths, and tests using the [maintenance review](./maintenance.md#机制减负审查). Begin read-only: identify the user behavior each mechanism preserves, quantify the impact of removing it, and compare that value with its whole-system cost. Any accepted feature tradeoff updates its owning contract before complete unused surfaces are removed. This later audit must not become a broad refactor or block current product work.
|
||||
|
||||
## Product Decisions Needed
|
||||
|
||||
@@ -36,6 +32,7 @@ These items are real product work, ordered after the current release boundary; t
|
||||
| #169 connection self-check | Keep, revise, or remove the user-facing pre-share probe. Do not extend it automatically. |
|
||||
| #181 selected-pair response counter | Keep as local observation or remove; it has no route authority. |
|
||||
| #182 signaling watchdog | Confirm Host and mobile/background session semantics before calling it complete. |
|
||||
| Project license and closed-commercial boundary | Decide the repository and distribution license before any public release or package distribution. GPL/AGPL implementations remain research-only until then. |
|
||||
|
||||
These decisions are not permission to continue old branches, and current deployment remains unchanged until a scoped decision is made.
|
||||
|
||||
|
||||
+18
-14
@@ -14,19 +14,23 @@ Capacity and routing entries below describe the exact tested source or release o
|
||||
- The 2026-08-24 postflight found `screener`, LiveKit, coturn, and nginx active with zero restarts; local/public health returned 200. The deployed browser asset `assets/index-sh8IJzwp.js` matches local SHA-256 `7fbc2f7ed67478c160e0122873116c03539084d637f3495cc588af70058364fc` and contains exactly one `screener-v8` wire literal.
|
||||
- Production uses endpoint capacity `2`, room admission `20`, random free four-digit memory rooms, `ROOM_LEASE_SECONDS=86400`, a dedicated LiveKit instance with `room.auto_create: false` and `max_participants: 21`, and global SFU admission `1` ingress / `20` egress. Coturn is `stun-only`, `no-tcp`, and `no-tls`; only UDP 3478 listens, a randomized external STUN Binding request returned a matching 40-byte success response, and nft exposes only UDP 3478/7882 for Screener media. The v8 service has no StateDirectory or SQLite descriptor and the old live SQLite path is absent.
|
||||
- A public control-plane smoke passed site access, four-digit creation, Host v8 authentication, stale-v7 rejection before authority, independent code-only and grant Viewer admission, grant rotation that preserved the code Viewer, signaling challenge, and exact room abandonment. A separate service-restart smoke rejected the old Host token and Viewer grant, atomically created a password-policy replacement room, and abandoned it.
|
||||
- The first reported SFU failure showed Host screen tracks published and the Viewer active over LiveKit UDP, but the candidate was deleted before a usable route committed. Exact `d3ff9e7` fixed missing subscription reconciliation across pending connect, participant/track arrival, activation, and reconnect; 550 Web tests passed, and a production Host plus three zero-downstream Viewer canary forced one peer and two active 1920x1080 SFU roots. Room `9403` then exposed a separate blocker: repeated cellular attempts never entered LiveKit because silent earlier route work consumed the total deadline before `sfu:reuse`. Current `8f5b3f1` leaves only direct and SFU shares, including a virtual Host-full bootstrap stage; local runtime tests pass and an exact heterogeneous-network field recheck remains open.
|
||||
- The first hotfix cutover start raced LiveKit ActiveState before TCP 7880 and produced one supervised Screener restart. A production-only `After/Wants` plus bounded listener-ready `ExecStartPre` was installed; a simultaneous LiveKit/Screener restart then completed with both `NRestarts=0`. Pinned LiveKit info records were confirmed to contain raw PublisherOffer SDP, so production now uses warn/Pion-error. Three archived journal files containing prior raw SDP (90,404,248 bytes total) were deleted; the active journal and the post-hardening SFU canary contain zero `PublisherOffer` record.
|
||||
- Rollback evidence confirms release `6b87732` and its old environment/runtime imports are readable, every precutover backup checksum passes, the backed-up database remains schema v3/integrity-ok with six rooms, and the current and rollback release regular-file sets share zero inodes.
|
||||
- A production Host plus three zero-downstream Viewers forced one peer and two active 1920x1080 SFU roots over LiveKit UDP. This proves the deployed v8 SFU path can publish, subscribe, decode, and clean up under that controlled canary; the reported heterogeneous and cellular failures still require an exact current-release field recheck.
|
||||
- The production service orders Screener after LiveKit and boundedly waits for its control listener. Current journals use warn/Pion-error logging, contain zero `PublisherOffer` record, and the current/rollback release regular-file sets share zero inodes.
|
||||
- Share audio presets, local-only diagnostics, self-check, diagnostic export, and signaling-partition recovery remain deployed surfaces. Their remaining real-route media behavior and target-browser field availability remain unverified.
|
||||
|
||||
## Retained Functional Evidence
|
||||
|
||||
- Exact source `31e323e2754fcfde15f7b4d69e925ec71e2795cb` passed repository hygiene, TypeScript typecheck, client/server builds, all 660 Web tests, all Native Go packages, `go vet`, and two independent P0/P1 reviews. The tests cover strict v6 endpoint-cap authentication, ordinary active/waiting authorization and grace promotion, Native cap propagation and admission, and peer-assisted cap rejection. This is local state-machine/build evidence, not a complete Native physical-slot matrix or production, browser-quality, or heterogeneous-network acceptance.
|
||||
- Exact Wave B implementation source `815c6174d411c55648daf5cd6ac4c42f64effff9`, based on `d1ba2e52919225e67ad0c8dd2b660f333fbc88fd`, passed its 372-test SFU-admission gate, all 690 Web tests, TypeScript typecheck, and client/server builds. Focused tests cover reserved/committed/draining accounting, reserve-before-token enforcement, explicit room lifecycle, listener-owner startup fencing, restart cleanup, stale-token replay boundaries, Host-offline reclamation, shutdown races, and two-room global admission. This is fake-control-plane and local state-machine evidence, not a real LiveKit process/JWT replay, packet-flow, multi-process, deployment-capacity, or production proof.
|
||||
- Exact route source `4e0a922` passed repository hygiene, TypeScript typecheck, and 84 focused controller, runtime, protocol, transition, decoded-stall, and SFU-admission tests. During the same convergence, the full 557-test Web suite and client/server builds passed before the final no-consumer cleanup. The two independent route audits' model and wire findings are incorporated, and final static review found no remaining P0/P1. No Browser, Native, Go, helper, hardware, or executable test ran in the final pass; this is source and state-machine evidence, not production or heterogeneous-network proof.
|
||||
- Exact source `6b87732` passed the required one-Host/20-Viewer local Browser smoke on Windows Node 24.19.0 / Chrome 151.0.7922.174, using 720p30 synthetic canvas media, endpoint cap `2`, a 30-second measurement window, and report SHA-256 `1347E036BB9AC35D51B4ED9B215290E742715128C7F3056D8FEB185CC84456D6`. All six checks passed: 20/20 Viewers had live clean upstreams and increasing decoded frames, Host and relay sender counts stayed at two, topology was coherent, and no orphan SFU publication appeared. Maximum authenticate-to-first-decoded latency was 958 ms; the final graph had two Host children and eighteen peer descendants. The application Node process consumed about 0.95 additional CPU seconds over the 54-second observation window, peaked near 284 MiB RSS, and settled near 162 MiB; isolated Chromium averaged 761% CPU over its valid 25.97-second process interval. The harness did not configure LiveKit, so its observed SFU usage was zero and this loopback is not a public-network, SFU, quality, or endurance claim.
|
||||
- Exact memory-room source `2ba155782bd5c4a193484c007ee69007e562ddb9` passed repository hygiene, TypeScript typecheck, all 548 Web tests, and client/server builds. The focused room/access set contributed 239 passing tests across allocation, dormant leases, grant/code orthogonality, password KDF bounds, stale generation atomicity, protocol v8, HTTP/signaling, local ownership, and creation-profile replay. A fresh Chrome access/privacy gate also passed all fixed checks: pre-DOM fragment consumption, room-scoped session grant, zero request/nginx/application-log secret hits, zero persistent room files, strong grant rotate/revoke coverage, and cleanup. This is local source, state-machine, and loopback-browser evidence; it is not production deployment, restart/rollback, heterogeneous-network, or media-quality evidence.
|
||||
- The direct-to-SFU source passed repository typecheck, all 531 Web tests in 41 files, and client/server production builds. The server build clears its fixed generated-output directory before compilation so the artifact contains only current route modules. Focused controller/runtime coverage proves direct-stage wake, Host-full virtual SFU bootstrap without a premature route error, SFU reuse, and a departed-root sequence where new demand takes the freed Host slot while healthy descendants remain sticky. This is local state-machine/build evidence; no Native/executable suite or heterogeneous-network media run was performed.
|
||||
- Exact Browser v9 source `d543f38aacad3df5ef65fde1055cc8e733972afe` passed repository hygiene, TypeScript typecheck, client and server production builds, all 670 Web tests in 48 files, the access/privacy Browser gate, and the production-dependency audit with zero vulnerability. This covers the in-memory room/access boundary, v9 protocol, one serialized exact-candidate route controller, generation-bound first-decoded-frame proof, physical resource retention, SFU lifecycle and admission, paused codec transition/rollback/source authority, live media-setting mutation, on-demand route observability, Viewer presentation state, and entry UI. It is local source/state-machine/build evidence and is not deployed production, heterogeneous-network, target-device quality, or real LiveKit packet-flow proof.
|
||||
- The same exact v9 source passed the required one-Host/20-Viewer local Browser gate with endpoint cap `2`; report SHA-256 is `EB879256C13C75FF1C7F3BC313DE1681FC5D89FC1F51D63B96ECD3ECB93DD09F`. Queue, candidate, first-frame, and final timing each recorded `20/0` populated/missing values. The sorted millisecond evidence is:
|
||||
|
||||
| Timing | Raw values (ms) | p50 / p95 / max (ms) |
|
||||
| --- | --- | --- |
|
||||
| Queue | `1, 975, 1170, 1339, 1691, 1814, 2008, 2258, 2508, 2749, 2998, 3237, 3467, 3695, 3795, 4037, 4268, 4498, 4629, 4862` | `2749 / 4629 / 4862` |
|
||||
| Candidate | `2, 976, 1171, 1339, 1692, 1814, 2008, 2258, 2509, 2749, 2998, 3237, 3467, 3695, 3795, 4037, 4268, 4498, 4629, 4863` | `2749 / 4629 / 4863` |
|
||||
| First frame | `1334, 1349, 1421, 1708, 1824, 2062, 2258, 2506, 2749, 3017, 3239, 3478, 3706, 3835, 4036, 4272, 4508, 4641, 4874, 5104` | `3017 / 4874 / 5104` |
|
||||
| Final | `1334, 1349, 1421, 1708, 1824, 2062, 2258, 2506, 2749, 3017, 3239, 3478, 3706, 3835, 4036, 4272, 4508, 4641, 4874, 5104` | `3017 / 4874 / 5104` |
|
||||
|
||||
This gate used local direct loopback media and observed no SFU route. It proves bounded local direct convergence at exact admission `20`; it does not prove public-network traversal, SFU fallback, visual quality, target-device resources, or endurance.
|
||||
- Chrome 151 synthetic `1/3/5/8` and 720p30 runs preserved bounded fanout and decoding. A `9461e20`-era three-Viewer rerun kept Host2/Browser Viewer1, decoded every Viewer, and recovered a hard first-level relay departure in 5,380 ms; it did not exercise quality-triggered MBB. Sixteen-Viewer cap2/cap3 loopbacks decoded all Viewers and a ten-second follow-up recorded guarded sender and aggregate Chromium CPU evidence, but resolution/FPS remained low and the ordinary-PC data cannot rank caps, select product policy, or prove sustainable heterogeneous-network quality. See [peer-assisted media](./research/peer-assisted-media.md).
|
||||
- Chrome 151 with local LiveKit 1.13.5/client 2.22.0 proved one SFU/UDP root after `q,f` -> `q,h`: decoded frames advanced 3 -> 23, rendered frames reached 26, Host used one edge, and leaves were clean. This is functional evidence only. See [low-server media routes](./research/low-server-media-routes.md).
|
||||
- Host A+B and authenticated P2P Viewer C evidence is sanitized, generation-bound, diagnostic-only, and closed for stale or ambiguous reports. Production can expose a Host-local current SFU `h` snapshot and clears it on publisher identity changes; raw media metadata is not retained. See [realtime quality adaptation](./research/realtime-quality-adaptation.md).
|
||||
@@ -35,16 +39,16 @@ Capacity and routing entries below describe the exact tested source or release o
|
||||
|
||||
## Open Proof Boundaries
|
||||
|
||||
- Routing: verify current-release initial ingress, exact candidate first-frame commit and rollback, direct-stage exhaustion and SFU admission, relay-ingress reparent with subtree retention, disconnect/effective-capacity drain, pause/resume, and bounded failure on real browser/media paths. Silent control partitions may wait 30 to 60 seconds for heartbeat detection before the default five-second grace; a non-paused decoded-frame stall may invalidate only that exact child edge.
|
||||
- Routing: exact v9 local direct loopback covers admission-20 initial ingress and exact-candidate first-frame commit timing. Still verify rollback, direct-stage exhaustion and SFU admission, relay-ingress reparent with subtree retention, disconnect/effective-capacity drain, pause/resume, and bounded failure on real browser/media paths. Silent control partitions may wait 30 to 60 seconds for heartbeat detection before the default five-second grace; a non-paused decoded-frame stall may invalidate only that exact child edge.
|
||||
- Quality: production startup blur remains unclassified. One same-`balanced` local A/B retained route/PC/SSRC/track while 720p rose to 1080p, but natural ramp prevents causal, production, or SFU conclusions. Correlate Host capture/encode/send with Viewer receive/decode before changing policy.
|
||||
- Relay resources: source and production expose the authenticated `1/2/3` endpoint-cap boundary, and the 20-Viewer loopback observed maximum Host and relay active-media edges of two at cap `2`. CPU/GPU/upload and heterogeneous-network measurements remain diagnostics, not authority to change the contract.
|
||||
- Room scale: source and production admit 20 Viewers plus one Host, and focused plus local Browser convergence evidence covers that bound. A real public-network 1:20 session, quality, and sustained resource matrix remain open.
|
||||
- Relay resources: v9 source and v8 production expose the authenticated `1/2/3` endpoint-cap boundary, and local loopback evidence covers 20 Viewers at cap `2`. CPU/GPU/upload and heterogeneous-network measurements remain diagnostics, not authority to change the contract.
|
||||
- Room scale: v9 source and v8 production admit 20 Viewers plus one Host, and the exact v9 local Browser convergence gate covers that bound on direct loopback. A real public-network 1:20 session, quality, and sustained resource matrix remain open.
|
||||
- SFU: production uses one Host publication with exact Viewer subscriptions and no fixed root-count or room-wide lease rule. Real LiveKit lifecycle/JWT replay, shaped packet-flow, BWE downshift/recovery, resource use, and SFU-fed first-child evidence remain open. Use Linux `tc` or an authorized public canary for per-leaf shaping, never CDP throttling.
|
||||
- Audio: verify the 64/128/256 kbps Share settings, negotiated/observed bitrate, audible music quality, route changes, and game A/V synchronization. Bitrate settings are ceilings, not quality proof.
|
||||
- Observability: the v8 UI and local stats/export plumbing are deployed, including identity-bound relayed-detail retention and export freshness guards, but target-browser availability and actual values remain unverified for Host SFU sender evidence, codec/fmtp and encoder implementation/power efficiency, media-source and encoded FPS, A/V playout/jitter/concealment, selected candidate endpoints, and bounded same-pair STUN-response observation. The accepted v9 Viewer stage and on-demand Host route snapshot are not implemented or deployed. All of these fields diagnose and have no recovery authority; they do not by themselves prove hardware encode, synchronization quality, or route quality.
|
||||
- Observability: the v8 UI and local stats/export plumbing are deployed, including identity-bound relayed-detail retention and export freshness guards. Exact v9 source also implements the Viewer stage and on-demand Host route snapshot, but those v9 surfaces are not deployed. Target-browser availability and actual values remain unverified for Host SFU sender evidence, codec/fmtp and encoder implementation/power efficiency, media-source and encoded FPS, A/V playout/jitter/concealment, selected candidate endpoints, and bounded same-pair STUN-response observation. All fields diagnose and have no recovery authority; they do not by themselves prove hardware encode, synchronization quality, or route quality.
|
||||
- Mobile: Android/iOS remain Viewer-only. Autoplay, rotation, iOS lock-screen/page reclamation, background reconnection, and network changes need device evidence. AirPlay/system mirroring is Viewer-local output, not a portable in-app WebRTC output contract.
|
||||
- Native: ADR-0006 has one-Viewer evidence only. Viewer2/FIFO, hardware diversity, endurance, downloaded-package/public-host, game A/V, and browser diversity remain open.
|
||||
- Access and operations: v8 production preflight/postflight, atomic environment/unit replacement, stale-v7 rejection, restart-wide room loss, password-profile creation, grant/code orthogonality, rotate/revoke, no-SQLite runtime, and exact current journal/nginx sentinel checks passed. Three historical rotated nginx access logs containing the current site secret in old `/r/...` request paths were deleted, and all remaining plain/gzip nginx logs plus Screener/LiveKit journals rescanned with zero site-secret hit. Headful same-browser preference replay and the isolated VM/IP clean-port matrix remain open; shared-IP smoke cannot approve them.
|
||||
- Access and operations: exact v9 source passed the access/privacy Browser gate. V8 production preflight/postflight, atomic environment/unit replacement, stale-v7 rejection, restart-wide room loss, password-profile creation, grant/code orthogonality, rotate/revoke, memory-only runtime, and exact current journal/nginx sentinel checks passed. Headful same-browser preference replay and the isolated VM/IP clean-port matrix remain open; shared-IP smoke cannot approve them.
|
||||
|
||||
## Evidence Rules
|
||||
|
||||
|
||||
+14
-22
@@ -2,7 +2,7 @@
|
||||
|
||||
## 2026-08-18 14:45
|
||||
|
||||
状态:首个 PoC 基线及其已接受扩展的当前设计。Production 普通 peer ICE 保持 STUN-only,并已部署 ADR-0005 的完整 Browser route controller 与易失房间模型。下一 Browser v9 与 live-media mutation 已接受但尚未实现或部署;精确 deployed behavior 只由 deployment 文档记录。
|
||||
状态:exact source commit `d543f38aacad3df5ef65fde1055cc8e733972afe` 已实现单一 `screener-v9` Browser 合同,包括 live-media mutation、Viewer presentation/diagnostics、无枚举房间准入和质量选择。Production 仍是 exact `8f5b3f192ddd010ca01c969008e512191312736a`、release `8f5b3f1`、wire `screener-v8`,尚未部署 v9;精确 deployed behavior 只由 deployment 文档记录。
|
||||
|
||||
## 目标与边界
|
||||
|
||||
@@ -108,7 +108,7 @@ Web Host 在 `authenticate` 中显式发送 `viewerPresence: true` capability,
|
||||
|
||||
WebSocket 使用无 query/fragment 的同源 `/signal`。upgrade 只记录 site-access cookie 是否有效;连接后五秒内第一条消息必须是当前单一 wire 的房间级 `authenticate`。Host 消息携带 room、role、内部 Host token 与 clientId,并由 socket state 复核 site access。Viewer 消息携带 room、role、clientId,以及 exact-room Viewer grant 或当前 code-only password attempt。成功的 `authenticated` 快照始终携带部署最终值 `endpointMediaCopyCapacity: 1|2|3`;普通模式的服务端 admission 只向 Host 激活稳定加入顺序中的至多该数量 Viewer,并在离开后提升等待者,双方信令只允许 active child。Browser Host 与 Viewer 严格校验该值,房间 `maxViewers` 只约束观看者总 admission。Native/executable sender 停留在旧 wire 并在 authority 前拒绝。服务端设置 64 KiB `maxPayload`、关闭 `perMessageDeflate`,并以 ping/pong 清理失效连接。单进程固定限制 2048 条总连接和 256 条未鉴权连接,容量在 upgrade 前预留,超限直接返回 HTTP 503 并销毁 socket。
|
||||
|
||||
下表先列 current v8 已实现消息,再以 `v9` 明示 accepted delta;实现分片可以排序,但 server 与 Browser assets 只发布一个完整 v9 wire,不存在中间协议:
|
||||
下表描述当前 source 的单一 `screener-v9` wire;server 与 Browser assets 必须作为一个完整合同原子发布,不存在 v8 parser、writer、translator 或中间协议子集:
|
||||
|
||||
| 方向 | 消息 | 作用 |
|
||||
| --- | --- | --- |
|
||||
@@ -122,13 +122,13 @@ WebSocket 使用无 query/fragment 的同源 `/signal`。upgrade 只记录 site-
|
||||
| viewer -> host | `answer` / `candidate` | 只能回给本房间分享者 |
|
||||
| viewer -> host | `restart-request` | 以 `rebuild` 标志请求 ICE restart 或完整重建该 peer |
|
||||
| host -> server | `stop-sharing` | 结束当前发布、清理本轮连接并让观看者等待;不删除房间或使邀请失效 |
|
||||
| server -> viewer | `route-status` | v9 strict union 只允许 `{ revision, state: "waiting", reason: "sfu-admission" }` 或 `{ revision, state: "failed", reason: "route-exhausted" }`;只补充现有 `route-update` 无法表达的 SFU admission waiting 与 bounded route exhaustion,不携带 raw text、identity、candidate 或 retry hint |
|
||||
| host -> server -> host | `request-route-diagnostic` / `route-diagnostic-snapshot` | v9 仅在 authenticated Host 点击下载诊断时请求并返回一份当前快照;不连续推送、不参与 route authority |
|
||||
| host/viewer -> server | codec transition apply/proof | v9 精确绑定 share/codec generation 与 frozen media binding,独立于 `route-ready` 和 Viewer quality evidence |
|
||||
| server -> viewer | `route-status` | strict union 只允许 `{ revision, state: "waiting", reason: "sfu-admission" }` 或 `{ revision, state: "failed", reason: "route-exhausted" }`;只补充现有 `route-update` 无法表达的 SFU admission waiting 与 bounded route exhaustion,不携带 raw text、identity、candidate 或 retry hint |
|
||||
| host -> server -> host | `request-route-diagnostic` / `route-diagnostic-snapshot` | 仅在 authenticated Host 点击下载诊断时请求并返回一份当前快照;不连续推送、不参与 route authority |
|
||||
| host/viewer -> server | codec transition apply/proof | 精确绑定 share/codec generation、`resumeAttempt` 与 frozen media binding,独立于 `route-ready` 和 Viewer quality evidence |
|
||||
|
||||
Zod 在服务边界拒绝未知类型、超长字符串、非法 SDP/candidate 结构和越权目标。服务端根据已认证会话决定路由:观看者不能给另一名观看者发信令,分享者也不能向本房间以外的 peerId 发送。
|
||||
|
||||
当前唯一 source wire 是 `screener-v8`,不做版本协商、旧 parser、双写或 translator。任何旧页面或 executable sender 的首条消息在角色进入 room lookup 前得到同一个 fatal refresh 终态并停止自动重连。缺失、过期、跨房、已 rotate 或 revoked 的当前 Viewer grant 使用同一个通用“邀请无效或已失效”结果,不区分房间不存在与凭据错误。Accepted v9 对已通过 site access 的 well-formed code-only 预期拒绝只发 `ROOM_ACCESS_DENIED`:unknown/expired room、disabled policy、absent/wrong password、room full 与其他已知有界 admission refusal 使用同一 code、公开文案和 close 行为。Grant 继续使用 `INVALID_TOKEN`,Host 鉴权保持独立,unexpected internal failure 使用不含 raw message 的 `SERVER_ERROR`。
|
||||
当前唯一 source wire 是 `screener-v9`,不做版本协商、旧 parser、双写或 translator。任何旧页面或 executable sender 的首条消息在角色进入 room lookup 前得到同一个 fatal refresh 终态并停止自动重连。Client 与 server 共用 `SIGNAL_CLOSE_CODES` 定义会话替换、鉴权失败和 Viewer access revoke 的终态语义,避免双方对重连边界产生分叉;非法协议消息使用 WebSocket policy-violation close code。缺失、过期、跨房、已 rotate 或 revoked 的当前 Viewer grant 使用同一个通用“邀请无效或已失效”结果,不区分房间不存在与凭据错误。已通过 site access 的 well-formed code-only 预期拒绝只发 `ROOM_ACCESS_DENIED`:unknown/expired room、disabled policy、absent/wrong password、room full 与其他已知有界 admission refusal 使用同一 code、公开文案和共享 authentication-failed close code。Grant 继续使用 `INVALID_TOKEN`,Host 鉴权保持独立,unexpected internal failure 使用不含 raw message 的 `SERVER_ERROR`。
|
||||
|
||||
## WebRTC 协商
|
||||
|
||||
@@ -270,7 +270,7 @@ media-proof 和 bounded-recovery 不变量的 scoped code 与 tests。
|
||||
|
||||
每两秒在本地采集一次 `getStats()`。首版不上传原始统计。折叠详情可在产生该连接的本机显示 selected local/remote candidate 的 `address`/`port`,但浏览器未暴露时保持未知;地址不得进入信令、Viewer C、日志、诊断导出、持久化或路由/质量决策。在 peer 边中它只是当前连接端点,在 SFU 边中通常是 SFU 端点,不能标成参与者的权威公网 IP。
|
||||
|
||||
v9 在 authenticated Host 点击下载诊断时发送一次 `request-route-diagnostic`;服务端当场返回严格 Host-only 的 `route-diagnostic-snapshot`,不连续推送。每个 current child 只保留 latest demand timestamp、operation/candidate start、first-frame/final outcome;新的 route demand 覆盖该 child 的旧 record,confirmed departure 与 room delete 立即删除,且不增加 timer、事件环或第二张 graph。`queueWaitMs` 只从同一个 current demand 到其 room-serial operation start计算;没有权威 demand timestamp 就是 `null`。Serializer 为本次导出分配 snapshot-local opaque ordinal,用它表达 current parent/SFU 关系、effective capacity、child count、current operation reason/stage/cursor/candidate count与相对 timing;`finalRoute` 只允许 `direct | sfu | waiting | failed`,`rejectionBucket` 只允许 `none | stale | endpoint-capacity | sfu-admission | candidate-failed | first-frame-timeout | operation-deadline | aborted`。Ordinal 不是身份或稳定 ID。Snapshot 不包含 room code、display name、真实 peer/session/connection/generation、地址、URL、SDP、token、raw error或 planner 曾过滤的 parent,不形成路由输入或前台第二套状态。Viewer 的 schema v2 只携带自身 stage、粗粒度 revision、typed failure、signal/Host state、route kind 与 current-frame proof,即使 connection list为空也可下载;未鉴权 `ROOM_ACCESS_DENIED` 分支只含 stage/code,不含 Host、route、revision或timing。
|
||||
当前 `screener-v9` 在 authenticated Host 点击下载诊断时发送一次 `request-route-diagnostic`;服务端当场返回严格 Host-only 的 `route-diagnostic-snapshot`,不连续推送。每个 current child 只保留 latest demand timestamp、operation/candidate start、first-frame/final outcome;新的 route demand 覆盖该 child 的旧 record,confirmed departure 与 room delete 立即删除,且不增加 timer、事件环或第二张 graph。`queueWaitMs` 只从同一个 current demand 到其 room-serial operation start计算;没有权威 demand timestamp 就是 `null`。Serializer 为本次导出分配 snapshot-local opaque ordinal,用它表达 current parent/SFU 关系、effective capacity、child count、current operation reason/stage/cursor/candidate count与相对 timing;`finalRoute` 只允许 `direct | sfu | waiting | failed`,`rejectionBucket` 只允许 `none | stale | endpoint-capacity | sfu-admission | candidate-failed | first-frame-timeout | operation-deadline | aborted`。Ordinal 不是身份或稳定 ID。Snapshot 不包含 room code、display name、真实 peer/session/connection/generation、地址、URL、SDP、token、raw error或 planner 曾过滤的 parent,不形成路由输入或前台第二套状态。Viewer 的 schema v2 只携带自身 stage、粗粒度 revision、typed failure、signal/Host state、route kind 与 current-frame proof,即使 connection list为空也可下载;未鉴权 `ROOM_ACCESS_DENIED` 分支只含 stage/code,不含 Host、route、revision或timing。
|
||||
|
||||
Host 与 relay Viewer 复用同一个纯 presentation reducer 保存远端 `ViewerQualityEvidence`。代际 identity 是 exact route identity(room/share、viewer/parent、assignment/connection、route revision 和当前可用的 media-binding generation,及适用时 publication generation);只有同 identity 且 sequence 严格递增才可承接旧字段,其他输入从新 evidence 重建。每个非空 metric 保存自己的本地观测时间并在五秒边界清空,页面 timer 总是指向最早边界;整块详情不因某个采样缺字段而卸载。`new`/`connecting`/`disconnected` 保留同 identity shell 但立即标为非 fresh,`failed`/`closed` 或 route identity 变化清空。任何依赖当前状态的消费者只读取 fresh presentation;该层不新增 wire 字段或服务端 route authority。
|
||||
|
||||
@@ -336,7 +336,7 @@ Token-free SFU standby prewarm 保持可选且不创建 participant、publicatio
|
||||
- RoomStore 的四位空闲码随机分配/释放、活动分享免过期、dormant lease/续租、Host token/Viewer grant 摘要验证、`open/password/disabled` 正交 policy、grant 到期/跨房拒绝、rotate/revoke 强撤销、Viewer 上限和超额拒绝;
|
||||
- 进程重启后 room state 全空、旧 Host/grant/password fail closed、释放号码复用时旧 grant 仍失败;`ROOM_LEASE_SECONDS` 的默认/边界与 `MAX_ROOMS <= 9000` 校验,已删除的 `ROOM_DATABASE_PATH`/`ROOM_TTL_SECONDS` fail-fast;
|
||||
- STUN UDP URL 严格校验、生产 STUN 必需、LiveKit tuple 完整性/TTL/secret 独立性、当前 route/session/revision/connection generation guard,以及 ordinary peer、未授权房间和 Native 的 STUN-only isolation;LiveKit `sfu-config` 保持独立边界;
|
||||
- 下一不兼容实现 checkpoint 的单一 `screener-v9` 信令 literal、任意旧客户端 mismatch 的一次性刷新终态,以及协议非法 JSON、未知消息、错误方向、跨房间目标和 payload 限制;v9 原子删除无消费者的 parent-edge quality proof,加入 paused codec transaction proof、`ROOM_ACCESS_DENIED`、typed Viewer route failure 与 Host-only 按需脱敏 route snapshot,不保留 v8 parser、writer 或别名;`qualityProfileIdSchema` 保持三档不变,只有 `qualityResolutionSchema` 增加 `480p`,`DEFAULT_QUALITY_SETTINGS` 改为 1080p30;
|
||||
- 当前 source 的单一 `screener-v9` 信令 literal、任意旧客户端 mismatch 的一次性刷新终态,以及协议非法 JSON、未知消息、错误方向、跨房间目标和 payload 限制;v9 不含无消费者的 parent-edge quality proof,包含 paused codec transaction proof、`ROOM_ACCESS_DENIED`、typed Viewer route failure 与 Host-only 按需脱敏 route snapshot,不保留 v8 parser、writer 或别名;`qualityProfileIdSchema` 保持三档不变,只有 `qualityResolutionSchema` 包含 `480p`,`DEFAULT_QUALITY_SETTINGS` 为 1080p30;
|
||||
- 质量协议的 host 授权、严格字段与数值范围、last-wins、当前/未来 relay、已配置 SFU 和普通 P2P wire 兼容;sender 的四字段 requested/applied 读回、拒绝与持续受限提示;screen audio SDP helper 覆盖 CRLF、existing/missing fmtp、unknown key preservation、幂等、payload number、rejected/non-audio,以及 empty/no-Opus/ambiguous/parser 异常原对象 fallback,`ViewerPeer` 覆盖 initial/restart/rebuild 时同一结果的 local/signaling 一致性,SFU publisher 精确断言 high-quality stereo/forceStereo/DTX 且不覆盖 RED;
|
||||
- HTTP/WS 集成:nginx per-source site-access `limit_req` 与统一错误不泄露 secret,应用不建立 IP limiter 状态;Viewer 可在 Origin/容量门下 upgrade;Host 首条鉴权同时要求 upgrade-captured site access 与 Host token,Viewer 要么持 exact-room grant,要么先有 site access 再按 `open/password/disabled`。覆盖原子创建 profile、同浏览器重建偏好、fragment/sessionStorage/replaceState、无 site access 的 code-only 统一拒绝、unknown/expired/disabled/wrong-or-absent-password/full code-only 使用完全相同的 `ROOM_ACCESS_DENIED` code/文案/close、grant 与 Host 的独立错误、raw grant/密码不进入 URL/日志/服务端持久状态,以及 rotate/revoke 后 grace/route 清理、旧 Viewer/上游媒体边关闭和 reconnect 拒绝;
|
||||
- 客户端策略与 stats 解析:会话替换不重连,ordinary ICE 与 LiveKit protocol 分离;
|
||||
@@ -357,20 +357,12 @@ Token-free SFU standby prewarm 保持可选且不创建 participant、publicatio
|
||||
|
||||
真实矩阵未完成前,状态文档必须把这些列为待验证,而不是把设计目标写成产品能力。
|
||||
|
||||
## 实施顺序
|
||||
## 当前交付边界
|
||||
|
||||
1. 建立单 package 工程、共享 schema、配置与 CI。
|
||||
2. 完成 RoomStore、建房 API、WebSocket 鉴权和定向信令。
|
||||
3. 完成屏幕捕获、逐观看者 PeerConnection、观看播放和手动质量档。
|
||||
4. 完成连接恢复和 stats 面板,并保持 ordinary peer 的 STUN-only snapshot。
|
||||
5. 在 endpoint cap/accounting 与 SFU lifecycle 边界上,以单一 Browser wire 完成 route controller convergence:一个 event-driven reconcile 和一个 room-serial candidate 统一处理 deterministic allocation、child reparent 和 relay drain;该 wave 独立可回滚。
|
||||
6. 已部署路由与易失房间边界保持不变;在同一个尚未部署的 Browser v9 checkpoint 中依次实现 live screen-audio ceiling 和 paused video-codec transaction。
|
||||
7. 在该 checkpoint 中删除未消费的 parent-edge quality proof,加入 first-frame-driven Viewer stage/typed failure、按需脱敏 route timing、无枚举房间准入结果,并把默认推荐档改为 1080p30、只在高级分辨率增加 480p;全部 v9 protocol source 一起原子部署,不发布中间 wire 子集。
|
||||
8. 对 H.264/模糊与 Host 后台性能只做受控根因诊断;标准 ICE/SFU 按真实异构网络验收,不增加 guessed candidate、NAT classifier、TCP probe 或 quality-driven reparent。
|
||||
9. 当前发布只验收 Web Host、Viewer 和 browser relay。Native sender、capture helper 与 shared-encode executable 排在整体 roadmap 最后,不参与当前构建、路由验收或发布。
|
||||
10. 当前功能与真实网络 TODO 完成后,再分别设计和交付公网服务器一键部署包与完全不依赖外部服务的本地 Host-server 包。
|
||||
|
||||
生产当前运行 `screener-v8` room/route release `8f5b3f1`;上述 v9 是 accepted design,尚未实现或部署。内部不兼容 wire/schema 原子切换,Git commit 与部署备份承担回滚,不保留兼容实现。
|
||||
- Exact source `d543f38aacad3df5ef65fde1055cc8e733972afe` 是一个完整的 Browser v9 release candidate;live audio ceiling、paused codec transaction、first-frame Viewer presentation、typed failure、按需 route timing、无枚举房间准入、1080p30 默认值和 advanced 480p 必须随单一 `screener-v9` wire 一起原子部署,不发布协议子集。
|
||||
- Production 当前仍运行 exact `8f5b3f192ddd010ca01c969008e512191312736a`、release `8f5b3f1`、wire `screener-v8`。Git commit、部署备份和原子 symlink cutover 承担回滚;生产切换前不得把 source v9 行为写成已部署。
|
||||
- H.264/模糊与 Host 后台性能继续按受控根因证据诊断;标准 ICE/SFU 继续按真实异构网络验收,不增加 guessed candidate、NAT classifier、TCP probe 或 quality-driven reparent。
|
||||
- 当前发布面只包含 Web Host、Viewer 和 browser relay。Native sender、capture helper、shared-encode executable 与两个分发包仍由各自后续决策和物理证据驱动,不参与 Browser v9 部署。
|
||||
|
||||
任何一步只实现当前消费者需要的接口。不得为未来账号、第三种媒体拓扑或原生 sender 预建抽象层。
|
||||
|
||||
@@ -388,7 +380,7 @@ Pending candidate 的 receive-transport owner 只在当前 exact generation 下
|
||||
|
||||
首次需要 SFU 但 Host 已满时不建立另一条 rescue 流程。若 affected child 已是 Host direct child,就用它;否则用稳定加入顺序中最新的 connected Host direct child。`reconcileRoom` 为该 child 建立普通 SFU candidate,创建唯一 publication,保持其 subtree,并在该 child 首帧后提交。Host publication 取代释放的 peer slot,随后同一 fixed-point scan 再处理原 waiting/failed child。若旧 Host peer sender 在首帧前仍保留,candidate 必须持有同一 `TransitionOverlapSlot`。没有 overlap 时先 preflight server resource,再退休 exact producer;operation 保持原总 deadline、按新 graph 重算未尝试 suffix,并为被切断的 healthy direct edge追加一个最后 restore tuple。Forward candidate 与 restore 都失败才进入 waiting,不得产生第四份 endpoint copy或另一套恢复状态。
|
||||
|
||||
所有非服务器端点共享一个 server-authoritative steady outbound media-copy cap `C`:唯一配置 `ENDPOINT_MEDIA_COPY_CAPACITY` 默认 `2`,只接受 `1`、`2` 或 `3`,不兼容旧键。服务端将 endpoint 当前广告的 `0..C` 可用值 clamp 为唯一 `effectiveDownstreamCapacity`;该值只表示当前可用 sender slots,不建立角色、浏览器、UA、设备或页面可见性 tier。peer child 或 Host publication 各占一份,upstream receive 免费;SFU subscriber egress 使用独立 server admission。Browser `screener-v8` 的 `authenticated.endpointMediaCopyCapacity` 把部署 `C` 交给 Host 与 Viewer;两端用该值校验稳态 assignment,并让 active+pending 的物理 copy 只在 controller 已授权 overlap 时达到 `min(C + 1, 3)`。代码通过一个 shared accounting guard 执行,不重复 magic number;旧 wire 与 Native executable 在 authority 前拒绝,不保留兼容路径。`C=3` 满载时先收到 active bounded-gap retirement再收到 prepare;任何会产生第四份 endpoint media copy 的 token都不得签发。
|
||||
所有非服务器端点共享一个 server-authoritative steady outbound media-copy cap `C`:唯一配置 `ENDPOINT_MEDIA_COPY_CAPACITY` 默认 `2`,只接受 `1`、`2` 或 `3`,不兼容旧键。服务端将 endpoint 当前广告的 `0..C` 可用值 clamp 为唯一 `effectiveDownstreamCapacity`;该值只表示当前可用 sender slots,不建立角色、浏览器、UA、设备或页面可见性 tier。peer child 或 Host publication 各占一份,upstream receive 免费;SFU subscriber egress 使用独立 server admission。Browser `screener-v9` 的 `authenticated.endpointMediaCopyCapacity` 把部署 `C` 交给 Host 与 Viewer;两端用该值校验稳态 assignment,并让 active+pending 的物理 copy 只在 controller 已授权 overlap 时达到 `min(C + 1, 3)`。代码通过一个 shared accounting guard 执行,不重复 magic number;旧 wire 与 Native executable 在 authority 前拒绝,不保留兼容路径。`C=3` 满载时先收到 active bounded-gap retirement再收到 prepare;任何会产生第四份 endpoint media copy 的 token都不得签发。
|
||||
|
||||
SFU admission 在应用单进程中只有一个注入实例。LiveKit credential tuple 完整时,必须同时显式配置私有 `LIVEKIT_API_URL`、正 safe integer `SFU_INGRESS_CAPACITY` 与 `SFU_EGRESS_CAPACITY`;容量没有产品默认值,也不从 Viewer 数、endpoint cap 或历史 root 数推导。专用 LiveKit 实例设置 `room.auto_create: false`。账本只保存全局已用计数,以及 exact room/share/publication generation 下的一份 publication handle 和按 Viewer identity 索引的 subscription handle。Publication 的 `reserved | committed | draining` 占 ingress `1`,每份 subscription 的同类状态占 egress `1`;离开 route 的 subscription 在该 generation 删除并确认 absent 前保持 draining,同一 Viewer handle 再次启用不重复计数。首个 SFU child 先 reserve publication 与自己的 subscription,再通过 `RoomService.CreateRoom` 创建 exact-generation room,成功后才 prepare 和签发 token;后续 child 只在同 generation reserve subscription 并复用 Host publication。exact candidate child 的 first decoded frame 后 commit 自己的资源;publication replacement 将旧 generation 及其全部 subscription 一起转入 draining,失败、超时、失联、分享换代、停止或删房按资源所有权进入同一生命周期。只有 `RoomService.DeleteRoom` 成功且 follow-up lookup 证明 room 不存在,账本才扣除该 generation 的 ingress/egress;auto-create 关闭后,旧 token 不能重建已删除 room。进程启动在接受流量前拒绝任何非 Screener room,删除全部遗留 Screener room并确认 namespace 为空;关闭时先 drain 再遗忘计数。Host 信令离线期间若 exact room 中的 Host participant 仍存在就继续计费并保留健康媒体,participant 消失后在一个有界轮询周期内回收。稳态 reserve/commit/drain bookkeeping 仍是 Map lookup 与整数加减;只有 generation 创建、删除、启动清场和 Host 离线检查访问 LiveKit control plane。多应用进程部署必须先增加共享原子 admission/lifecycle owner。
|
||||
|
||||
|
||||
+6
-7
@@ -45,7 +45,7 @@ SFU 的 publication、subscription、server-resource accounting、临时 overlap
|
||||
- 独立房间入口:合法 `/r/{code}#v={grant}` 邀请直接进入对应房间;没有 grant 的 `/join` 或 `/r/{code}` 先显示明确标为“站点访问”的整站验证,再提交 code-only 尝试。`open` 且可准入时直接进入;其余预期拒绝统一为 `ROOM_ACCESS_DENIED`,不得先披露 `password | disabled` policy。有效 site-access cookie 只跳过第一步,两类密码不得复用或互相替代。Host 可设置、轮换或移除房间观看密码;密码接受 1 至 64 个可见 ASCII 字符,异步 scrypt 全局最多执行 2 个、等待 16 个派生。Host 可把房间密码保存在同源本地创建偏好中;它不得进入 URL、cookie、日志或服务端持久存储。未知或过期房间、disabled policy、缺失/错误密码、队满和其他已知有界 admission refusal 都返回相同 code、公开文案和 close 行为。
|
||||
|
||||
- 统一自动路由:旗舰部署开启 `PEER_ASSISTED_MEDIA` 后,每个房间使用一个服务端 route controller,独占 committed graph、一个 pending child operation 和 event-driven reconcile。运行时只增加一个串行pump、一个operation deadline timer与typed resource lifecycle task,不维护另一份graph。operation持有candidate list/cursor、一个current candidate/资源预留、fact version和一个总deadline。committed graph必须无环、source-reachable,且每名Viewer同时至多一个active upstream。加入、容量变化、节点离开、exact edge `failed/closed`,以及非暂停decoded-frame stall都只唤醒同一个reconcile;其余健康分支保持sticky。候选按当前身份、source reachability、无环、effective capacity与sender transition硬过滤,再按结果深度、剩余sender slots、稳定加入顺序和peer identity排序;因此 Host root退出后新释放的Host槽位比已有root的更深槽位优先,但健康旧边不因周期性平衡而迁移。server admission在执行当前cursor时独立reserve。candidate child首帧ready触发`settleReady(commitAdmissionSync)`,成功prepare `P`提交active `P`;失败释放资源并用同一monotonic allocator广播`R>P` rollback,cursor推进但总deadline不重置。bounded-gap先preflight server resource,再retire producer、按新graph重算未尝试suffix,并在切断healthy edge时追加一个普通restore candidate。relay自身ingress失效时作为child换父并保留subtree;relay disconnect/cap降低让overflow children逐个走同一事务,childless后删除。房间admission和无环约束最大深度,不设独立hard cap。普通peer始终STUN-only,SFU使用独立admission。
|
||||
- 观看端连接反馈:Viewer 舞台从鉴权到首帧使用一个由现有权威状态推导的稳定阶段反馈,至少区分等待分享、分配线路、建立 P2P、连接备用线路、等待首帧、浏览器阻止自动播放、恢复连接和有界失败。阶段不是进度百分比,也不产生 route authority;只有当前 video element 实际提交首帧后才显示正在播放。播放按钮只在 `play()` 因自动播放策略被拒绝时稳定出现,连接中不得忽隐忽现;明确失败后才提供一次受现有 controller 约束的重试入口。make-before-break 或仍有可见旧帧时保留画面并叠加恢复状态,不为显示反馈主动清空健康 media element。Host 离线本身不使仍健康的媒体失效;exact upstream 硬失败或 route terminal failure 必须使 current frame proof 失效,已证明画面至多降为 retained background,不能遮蔽离线或有界失败。Accepted v9 的服务端补充消息是 strict、current-revision-fenced `route-status` union,且只允许 `waiting/sfu-admission` 与 `failed/route-exhausted` 两种配对;它不携带 raw text、身份、candidate 或 retry hint,prepare/active、access、Host、signal、media 与 autoplay 继续由各自权威输入派生。
|
||||
- 观看端连接反馈:Viewer 舞台从鉴权到首帧使用一个由现有权威状态推导的稳定阶段反馈,至少区分等待分享、分配线路、建立 P2P、连接备用线路、等待首帧、浏览器阻止自动播放、恢复连接和有界失败。阶段不是进度百分比,也不产生 route authority;只有当前 video element 实际提交首帧后才显示正在播放。播放按钮只在 `play()` 因自动播放策略被拒绝时稳定出现,连接中不得忽隐忽现;明确失败后才提供一次受现有 controller 约束的重试入口。make-before-break 或仍有可见旧帧时保留画面并叠加恢复状态,不为显示反馈主动清空健康 media element。Host 离线本身不使仍健康的媒体失效;exact upstream 硬失败或 route terminal failure 必须使 current frame proof 失效,已证明画面至多降为 retained background,不能遮蔽离线或有界失败。当前 `screener-v9` 服务端补充消息是 strict、current-revision-fenced `route-status` union,且只允许 `waiting/sfu-admission` 与 `failed/route-exhausted` 两种配对;它不携带 raw text、身份、candidate 或 retry hint,prepare/active、access、Host、signal、media 与 autoplay 继续由各自权威输入派生。
|
||||
- 质量控制扩展:推荐档位仍是默认入口且只保留 1080p60/1080p30/720p30 三档,当前页面初始 desired state 选择 1080p30 且不写 `localStorage`;“分享高级设置”允许 480p(854x480)/720p/1080p/1440p 分辨率 ceiling、15 至 60 的整数 FPS ceiling、2 至 12 Mbps 视频码率 ceiling、清晰/平衡/流畅三种偏好、`自动 | H.264 | VP8` 编码选择,以及离散的 `64 kbps 省流 | 128 kbps 音乐默认 | 256 kbps 极高音质` 屏幕音频 sender ceiling;480p 不形成推荐档或固定 FPS/码率组合,初始值采用 `balanced`、`automatic` 与 128 kbps。视频档位和音频档位在直播中显式应用;音频变更只更新固定 Opus 路径的 sender ceiling,不是 codec 切换。所有当前及以后建立的 Web Host、Browser relay 与 SFU sender 都读取最新 desired 档位,并各自在 fresh sender parameters 上应用和读回 endpoint-local applied 值;实际码率仍由浏览器拥塞控制和 stats 决定。Peer 只在唯一明确的 active Opus audio 上做结构化、幂等的 Viewer answer `stereo=1;maxaveragebitrate=256000` 变换,使同一接收上限覆盖三档;非目标形态或 parser/writer 异常必须原样保留浏览器 answer,不得阻断可继续的 mono/video edge。不得用宽泛字符串/正则改 SDP,也不得改写浏览器协商的 FEC。SFU 将三档映射到 pinned SDK 的 music stereo preset 或显式 256 kbps ceiling,统一保留 `forceStereo = true`、`dtx = false` 与 pinned room default `red = true`。不得暴露任意码率、声道、采样率、audio codec、stereo、DTX、RED 或 FEC 控件;256 kbps 只是显式较高上限,不是无损、高保真或最低码率保证。实际质量持续低于所选上限时必须显示可解释的限制原因;在真实拥塞、编码器和接收端证据完成前,不把任一组合称为“完美平衡”。未来 microphone voice 若进入范围,必须是独立 track/处理链,不能把 AEC/降噪/DTX 套到电影或游戏音频。
|
||||
- SFU 质量设置生命周期:直播中修改档位时,浏览器 sender 的 applied readback、LiveKit `LocalVideoTrack` 保存的 degradation preference、track restart options 与 `LocalTrackPublication` 的 republish options 必须作为同一次串行操作更新;任一步失败恢复旧 sender 与旧 SDK 保存态,旧 publication generation 不得回写当前状态。LiveKit full reconnect/republish 不得恢复初次发布的旧偏好。该修复不改变 codec、码率上限、层数或路由,也不能单独解释即时低帧。
|
||||
- 本地预览性能候选:在相同动态场景中 A/B 测量 Host 动态预览开/关的 CPU、GPU、encode time 和实际 FPS。只有稳定收益且捕获、编码和远端画面不受影响时,才允许直播开始后默认显示静止缩略图或隐藏动态预览并提供按需展开;未测前不宣称收益。
|
||||
@@ -102,7 +102,7 @@ SFU 的 publication、subscription、server-resource accounting、临时 overlap
|
||||
- Given production site access secret 短于 8 bytes、长于 128 bytes 或含非可见 ASCII,When 新 release 启动,Then 必须 fail-fast。Given 同一来源连续提交错误 secret,When 超过 deployment `limit_req` 的 `5r/m`、`burst=5 nodelay` 边界,Then nginx 返回不含凭据的通用限频结果;允许请求的错误值仍使用常量时间比较和统一鉴权失败,limiter 只使用 nginx 内存且不新增 IP 持久化/日志字段。
|
||||
- Given 新建房间,When 服务端返回邀请并由 Host/Viewer 客户端读取,Then grant 含 256-bit 随机量、只授权该房间 Viewer、只在该页面会话的 room-scoped `sessionStorage` 留存,并在首次读取后从地址栏移除;HTTP request-target、WebSocket URL、Referrer、cookie、`localStorage`、服务端持久存储、日志和错误均不包含 grant。
|
||||
- Given 本地输入不是 `1000..9999` 的四位房间码,When 用户提交或直接访问 malformed `/r/...`,Then 客户端保留原输入并直接提示“四位数字”,不发起房间鉴权、不静默删除字符。Given 默认新房,When 已通过站点访问的 Viewer 只输入合法房间码,Then 无房间密码即可在现有 Viewer/连接/egress 上限内加入,同时 Host 的主分享动作仍复制独立的 token-bearing link。
|
||||
- Given 已通过 site access 的 well-formed code-only 尝试对应未知/过期房间、disabled policy、缺失/错误密码、满房或其他已知有界 admission refusal,When 首条信令鉴权到达,Then 服务端返回唯一 v9 `ROOM_ACCESS_DENIED`,同一公开文案和 close 行为不得泄露房间存在性或 policy;UI 不自动断言“需要密码”,只允许可选密码重试并提示使用 Host 提供的邀请链接,不向服务器索取链接。Given grant 无效,Then 继续使用通用 `INVALID_TOKEN`;Host 鉴权保持独立错误,unexpected internal failure 使用不含 raw message 的 `SERVER_ERROR`。Given Viewer 持另一个房间的 grant、缺失/过期 grant 或自行把 role 改为 Host,Then 不能建房、发布、跨房或绕过 Host token。
|
||||
- Given 已通过 site access 的 well-formed code-only 尝试对应未知/过期房间、disabled policy、缺失/错误密码、满房或其他已知有界 admission refusal,When 首条信令鉴权到达,Then 服务端返回当前 `screener-v9` 唯一 `ROOM_ACCESS_DENIED`,并使用共享的 authentication-failed close code;同一公开文案和 close 行为不得泄露房间存在性或 policy。UI 不自动断言“需要密码”,只允许可选密码重试并提示使用 Host 提供的邀请链接,不向服务器索取链接。Given grant 无效,Then 继续使用通用 `INVALID_TOKEN`;Host 鉴权保持独立错误,unexpected internal failure 使用不含 raw message 的 `SERVER_ERROR`。Given Viewer 持另一个房间的 grant、缺失/过期 grant 或自行把 role 改为 Host,Then 不能建房、发布、跨房或绕过 Host token。
|
||||
- Given Host rotate/revoke Viewer grant,When 新内存摘要和 authorization generation 提交,Then 服务端清除 Viewer grace/路由,命令 Viewer 关闭接收 PC/媒体并让每个 Host/relay/SFU upstream owner 关闭对应旧代次媒体边,最后关闭旧 Viewer socket;rotate 返回一次新 grant,revoke 锁定 grant 路径,code-only policy 保持独立。
|
||||
- Given Host 同源本地偏好保存了 `open | password | disabled` 和可选房间密码,When 服务器重启后该 Host 下一次显式开始分享,Then 客户端清除失效 ownership、创建新四位房间并原子重放偏好;新的 Host token 和 grant 替换旧值。Given 换浏览器、换设备或清理站点数据,Then 不恢复这些偏好或所有权。
|
||||
- Given 应用进程重启,When 旧 Host、Viewer 或 code-only 链接再次访问,Then 所有旧房间、租约、摘要、密码 verifier 和参与者均已丢弃并 fail closed;运行时不读取数据库、迁移旧 schema 或恢复旧号码。
|
||||
@@ -180,12 +180,11 @@ SFU 的 publication、subscription、server-resource accounting、临时 overlap
|
||||
- Given 用户在自有公网服务器运行一键部署包,When 域名、TLS、端口、secret、容量、存储和回滚预检通过,Then 同一版本的应用、STUN/SFU 与反向代理可重复启动并输出精确健康结果;任一必需条件失败时不得显示 ready、写入占位凭据或留下半配置服务。
|
||||
- Given 用户安装本地自包含一键包,When 在 Windows、macOS 或 Linux 启动分享,Then 包内 Host、当前应用服务器和受限本地状态使用同一协议与配置合同启动,用户无需源码、Node 或任何外部服务,Viewer 邀请仍由普通浏览器打开。Given 公网 HTTPS/WSS、信令入口、媒体 UDP、网关映射或 NAT 路径任一不可用,Then 预检必须如实显示受限或不可达,不得把 loopback/LAN 成功写成公网成功;可工作的 direct P2P 保留,失败进入明确终态。
|
||||
|
||||
## 待确认事项
|
||||
## 决策与证据归属
|
||||
|
||||
- 哪些分享端硬件、质量档位和网络条件能让 20-Viewer admission 全部持续取得目标画质,以及生产默认值应在 20 人 smoke 后设为多少。
|
||||
- 项目许可证及是否计划闭源商用。
|
||||
- 首批用户主要位于香港、内地还是跨地区;该信息决定 SFU 部署位置和测试矩阵。
|
||||
- iOS Safari 与 Android Chrome 作为 Viewer 的最低版本及是否要求平板适配。
|
||||
- 项目许可证及闭源商用边界由发布和分发前的 license decision 持有;决定前 GPL/AGPL 实现保持研究用途,不进入产品源码。
|
||||
- SFU 部署区域不由假定的用户地域决定;真实公网、跨地区、CGNAT、双 NAT、移动热点与普通家宽结果归入 real-network acceptance,测量后再决定部署位置和容量。
|
||||
- iOS Safari、Android Chrome 的最低版本与平板适配范围归入 mobile Viewer lifecycle acceptance;物理设备矩阵完成前不宣称后台播放、锁屏、网络迁移或 relay 延续保证。
|
||||
|
||||
基础 WebRTC 证据与难度评估见 `docs/research/webrtc-p2p-screen-sharing.md`;peer-assisted、shared encode 和浏览器限制见 `docs/research/peer-assisted-media.md`。
|
||||
|
||||
|
||||
Reference in new issue
Block a user