diff --git a/AGENTS.md b/AGENTS.md index 6c59647..93c5616 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,7 @@ - Keep the canonical repository root on a clean, current `main` at audit and integration boundaries. Create branches and worktrees from that exact commit; an auxiliary worktree or old branch is never a truth source. - Merge an accepted truth checkpoint before integrating dependent candidates. Rebase or rebuild each retained candidate from that exact `main` once, preserve main's owning truth on conflicts, and transplant only approved scoped code, tests, and new facts. +- Routine application-only releases verify a new immutable artifact, switch to it atomically, and guarantee the pre-cutover application release only through bounded health and postflight checks; it has no retention contract afterward and is not a maintained backup. A task that changes infrastructure, configuration, secrets, persistent state, or an irreversible surface must define recovery for only those touched surfaces before changing them. - Cleanup is last. Preserve user work and verify integration, open references, branch equivalence, and link/reparse safety before removing a worktree or branch. - Follow `CONTRIBUTING.md` for branch, research, validation, PR, merge, cleanup, and local-hook details. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06cb8cd..ff0c6c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,8 @@ Direct commits to `main` are reserved for an explicit user-approved exception. Never force-push shared branches or rewrite shared history without explicit approval. +For deployment work, a routine application-only release verifies a new immutable artifact, switches to it atomically, and guarantees the pre-cutover application release only through bounded health and postflight checks; it has no retention contract afterward and is not a maintained backup. Define recovery only for the infrastructure, configuration, secrets, persistent state, or irreversible surfaces the task actually touches, before changing them. + Clean up worktrees and branches only after semantic review and integration are complete and the normal merged-head, open-reference, clean-tree, and non-following link checks pass. Never merge an old branch wholesale after a newer truth checkpoint. ## Research Standard diff --git a/README.md b/README.md index f0b44f2..6ed19f5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Screener is a private, low-latency screen-sharing project for one game player an The first measurable Web proof of concept is implemented. A host captures a screen, window, or browser tab and shares a random four-digit room code plus an independent fragment invitation. During a share, the host can change between three ceiling profiles without reopening the source picker, pause, and switch sources. Browser video is fixed VP8 and leaves the video content hint unset. Code-only entry defaults to `open` after site access and may instead require a room password or be disabled; a valid grant enters directly. Rooms are process-memory state with a configurable dormant lease and disappear on restart. Lightweight deployments may keep independent direct WebRTC connections. The flagship deployment enables the all-room controller with bounded peer descendants and one LiveKit Host publication serving admitted Viewer subscriptions. Production admits 20 viewers; that admission limit is not a performance promise. -This is not yet a broadly validated product release; [Current status](./docs/status.md) owns the deployed revision and validation evidence. The deployed flagship boundary is the all-room bounded peer/SFU-UDP controller plus the memory-resident room/access model on `screener-v10`, with ordinary peer ICE kept STUN-only and no TURN or TCP media route. The optional Windows native sender remains source-only and is not a production runtime. Mobile devices are Web viewers only; ordinary Android and iOS browsers do not expose reliable screen capture for a Web Host. Real cross-network SFU media, game audio, a sustained public-network 1:20 session, mobile browser lifecycle behavior, and latency targets still require the documented manual test matrix. +This is not yet a broadly validated product release; [Current status](./docs/status.md) owns the deployed revision and validation evidence. The deployed flagship boundary is the all-room bounded peer/SFU-UDP controller plus the memory-resident room/access model on `screener-v11`, with ordinary peer ICE kept STUN-only and no TURN or TCP media route. The optional Windows native sender remains source-only and is not a production runtime. Mobile devices are Web viewers only; ordinary Android and iOS browsers do not expose reliable screen capture for a Web Host. Real cross-network SFU media, game audio, a sustained public-network 1:20 session, mobile browser lifecycle behavior, and latency targets still require the documented manual test matrix. ## Run locally @@ -29,9 +29,9 @@ HTTPS and the production ICE configuration documented separately. `SITE_ACCESS_PASSWORD` supplies site access; valid room fragment grants bypass that gate only for the exact Viewer role, while code-only entry requires site access before code policy is evaluated. Ordinary ICE contains only self-hosted -STUN. LiveKit serves -normally one or two SFU/UDP roots; the controller may authorize short-lived TURN -only for a selected exceptional media edge. HTTPS/WSS remains TLS/TCP independently. +STUN. LiveKit supplies bounded SFU/UDP subscriptions when direct/peer paths +cannot serve an edge; Screener configures no TURN or TCP media route. HTTPS/WSS +remains TLS/TCP independently. Run the complete automated validation with: diff --git a/docs/README.md b/docs/README.md index 89b0728..df7a1e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,10 +4,10 @@ - [方案设计](./方案设计.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): exact integrated/deployed v10 gates, current production evidence, open physical proof boundaries, and expensive-test applicability. +- [Verification status](./verification-status.md): exact integrated/deployed v11 gates, current 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 v10 production and rollback configuration, release operations, and transport evidence boundaries. +- [Deployment](./deployment.md): exact current v11 production, immutable release operations, scoped recovery rules, 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, exact-route implementation evidence, and remaining physical gates. @@ -19,7 +19,7 @@ - [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): fixed Browser VP8/no-video-hint evidence, manual quality ceilings, 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, v10 live sender-ceiling readback, Opus negotiation boundaries, and remaining audible/device evidence. +- [Browser screen-audio quality](./research/browser-screen-audio-quality.md): capture/source compatibility, 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. diff --git a/docs/adr/0001-p2p-first-media-topology.md b/docs/adr/0001-p2p-first-media-topology.md index 48108ca..4726c89 100644 --- a/docs/adr/0001-p2p-first-media-topology.md +++ b/docs/adr/0001-p2p-first-media-topology.md @@ -31,11 +31,12 @@ contradict the private-room cost model or duplicate mature WebRTC capabilities. ## Current Ownership [ADR-0005](./0005-automatic-hybrid-media-routing.md) owns current routing -invariants and assisted-route roles. Exact current source `f5a295c` uses the -strict `screener-v11` Browser wire; exact production -`2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, remains on -`screener-v10`. Both use the same accepted route model, while real-network -validation remains open. [ADR-0004](./0004-peer-assisted-media-experiment.md) +invariants and assisted-route roles. Production runs exact deployed +application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release +`679fe3e`; canonical `main` contains the same runtime code. Current source and +production use the strict `screener-v11` Browser wire and the same accepted route +model. Real-network validation remains open. +[ADR-0004](./0004-peer-assisted-media-experiment.md) owns only historical peer-assisted evidence. Access and room lifetime are owned by [ADR-0002](./0002-memory-resident-protected-rooms.md). diff --git a/docs/adr/0002-memory-resident-protected-rooms.md b/docs/adr/0002-memory-resident-protected-rooms.md index d6dce03..e3eac73 100644 --- a/docs/adr/0002-memory-resident-protected-rooms.md +++ b/docs/adr/0002-memory-resident-protected-rooms.md @@ -1,6 +1,6 @@ # ADR-0002: Memory-Resident Rooms And Scoped Viewer Access -- Status: Accepted; implemented and deployed in exact release `2726edd` +- Status: Accepted; implemented and deployed in exact release `679fe3e` - Date: 2026-08-23 ## Context @@ -160,10 +160,11 @@ the generic `SERVER_ERROR`. None of those paths is folded into ## Implementation Status -Exact current source `f5a295c` runs the strict `screener-v11` wire. Exact -production `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, runs -`screener-v10`; `screener-v11` is not deployed. Both implement this complete -room boundary, including allocation, leases, orthogonal grant/code admission, +Production runs exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical +`main` contains the same runtime code. Current source and production run the +strict `screener-v11` wire and implement 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 diff --git a/docs/adr/0005-automatic-hybrid-media-routing.md b/docs/adr/0005-automatic-hybrid-media-routing.md index 48d6fd9..3e4a634 100644 --- a/docs/adr/0005-automatic-hybrid-media-routing.md +++ b/docs/adr/0005-automatic-hybrid-media-routing.md @@ -1,6 +1,6 @@ # ADR-0005: Automatic Hybrid Media Routing -- Status: Accepted; deployed in exact release `2726edd` +- Status: Accepted; deployed in exact release `679fe3e` - Date: 2026-08-20 - Last updated: 2026-08-24 @@ -352,9 +352,10 @@ deployment-wide. ## Current Source And Deployment Boundary -Exact current source `f5a295c` runs the strict `screener-v11` Browser wire. Exact -production `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, runs -`screener-v10`; `screener-v11` is not deployed. Both use the direct/STUN peer and +Production runs exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical +`main` contains the same runtime code. Current source and production run the +strict `screener-v11` Browser wire and use the direct/STUN peer and LiveKit SFU/UDP route model, including the route, first-frame, typed-status, and Host-only diagnostic contract described above. The operation owner is only `route`. Exact operational evidence is owned by the deployment document; real diff --git a/docs/deployment.md b/docs/deployment.md index f61cab7..5f22ddf 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -2,9 +2,9 @@ Last verified against upstream documentation: 2026-08-24. -This page records the production deployment of exact integrated main -`2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, and the single -Browser `screener-v10` contract. Product direction and pending work are owned by +This page records production running exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`, and the single +Browser `screener-v11` contract. 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 @@ -22,26 +22,26 @@ ICE/UDP only. Ordinary peer ICE remains STUN-only and production coturn uses the tracked STUN-only configuration with TCP/TLS disabled. The source and production configure no TURN, ICE/TCP, media TCP, or TLS-relayed media. -Production runs exact `2726edde9b87f31fd76e749de47972ef817a9bd5`, release -`2726edd`, from `/opt/screener/releases/2726edd`. The immutable runtime ZIP +Production runs exact `679fe3e7af634309322bea83b316641f51ad3d09`, release +`679fe3e`, from `/opt/screener/releases/679fe3e`. The immutable runtime ZIP SHA-256 is -`07fa6500300bfedc9cedccb0db761b70a9ada8dd77608153728e34835f4e1b78`. +`fc96137f0fdc4dc4d8e083513d8fe99b9c55f7067a8b1de9202551edc3776c50`. Its 38-file path/size/hash manifest SHA-256 is -`c4ed6321fa3ac8b4c766a7e575d26f8fd4ec2ea929a9addefb7944abafa5b663`. -The verified pre-v10 rollback boundary is -`/opt/screener/backups/2726edd-pre-v10-20260824T025357Z`; its internal -`SHA256SUMS` hash is -`62d19aa0a3ec400477a4c76121eb0e9e4d553d024404f3da054fa1e95f251939`. +`73f67c530acd5d4c77b546c19b5752c066f82682a584a72cd7ca596d6c5bec7e`. Local and trusted-IP public `/` and `/healthz` return 200; Screener, LiveKit, -coturn, and nginx are active with `NRestarts=0`. The public main Browser asset is -`assets/index-BtFMxoNI.js` with SHA-256 -`885b9e50c57c64ac92cc8ffe5488d597bbb6fde6423e7a0b328efb9d1e943ad2`. +coturn, and nginx are active with `NRestarts=0`, and all six served client files +match the immutable release. The public main Browser asset is +`assets/index-CGzoJSvs.js` with SHA-256 +`31cf628d96be88f373cd0c3ac09e4966929fe320414aa1f0f9841264708d5a7e`; +the retired v10 asset `assets/index-BtFMxoNI.js` returns 404. -The release deploys the single Browser `screener-v10` wire, random four-digit +The release deploys the single Browser `screener-v11` wire, fixed VP8 for Browser +direct, browser-relay, and SFU video, no video `contentHint`, no codec UI/state/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`, and the one-controller exact-candidate route runtime. A credential-free postflight sent -a well-formed authentication shape with stale `screener-v9`; it received +a well-formed authentication shape with stale `screener-v10`; it received `INVALID_MESSAGE` and WebSocket close 1008 before room authority without creating a room. The service unit has no writable room StateDirectory; all room authority is process-memory-only. LiveKit is dedicated, has `room.auto_create: false` and @@ -91,7 +91,7 @@ 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 -remove: metadata changes would also mutate the rollback release. A copy or +remove: metadata changes would also mutate another immutable release. A copy or 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. @@ -101,14 +101,22 @@ 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. +switching. Archive format alone is neither integrity nor recovery evidence. + +A routine application-only cutover leaves infrastructure, configuration, secrets, +and persistent state untouched. Verify a new immutable artifact, switch to it +atomically, and guarantee the pre-cutover application release only through +bounded health and postflight checks. It has no retention contract afterward and +is not a maintained backup. When a task actually changes infrastructure, +configuration, secrets, persistent state, or an irreversible surface, define and +verify recovery only for the touched surfaces before changing them. 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 -intentionally stopped service returns a nonzero status and can trigger a false rollback. Read +intentionally stopped service returns a nonzero status and can trigger a false failure path. Read `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 +keep stop, symlink switch, start, health check, and any release restoration as +separate steps. Preflight every inspection dependency before changing 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. @@ -139,7 +147,7 @@ as a `screener.service.d` drop-in on this layout. `After=` orders the services, while the bounded `ExecStartPre` waits for TCP 7880 readiness; ActiveState alone does not prove the LiveKit control listener is accepting connections. -## Candidate boundary and rollback +## Candidate boundary and recovery The accepted target for `share.bonfire.icu` remains distributed and automatic: direct/peer UDP first, with one Host publication available to authorized SFU @@ -157,18 +165,20 @@ separate UDP-only ICE domain. `PEER_ASSISTED_MEDIA=true` is the process-wide topology/SFU switch. Every normal room gets its own bounded controller state and ordinary peer connections remain -STUN-only. Run candidate releases on an isolated -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. +STUN-only. Run route or infrastructure candidates on an isolated +instance/hostname and keep the current release unchanged. If the candidate fails, +stop it and restore only the recovery boundary defined for its changed surfaces; +do not add a permanent dual-transport branch. 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. +room. No room state crosses a release boundary in either direction. Guarantee the +pre-cutover immutable application release only through the bounded postflight; it +has no retention contract afterward and is not a maintained backup. An +application-only cutover leaves the environment, unit, LiveKit, coturn, firewall, +and reverse proxy unchanged; any task that changes one of those surfaces must +establish its scoped recovery before the change. 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 @@ -222,8 +232,9 @@ SFU_EGRESS_CAPACITY= Supplying any stale `PEER_ICE_TURN_*` or `SELECTED_EDGE_TURN_*` key, even blank, fails startup. Production contains none of those keys, uses the tracked STUN-only coturn configuration, and keeps TCP 3478/5349 plus every relay range -closed. A rollback restores an exact recorded release, environment, coturn -configuration, and firewall snapshot as one unit. +closed. An application-only release leaves the environment, coturn configuration, +and firewall unchanged; a task that changes any of them must define recovery for +that exact set before modification. `PEER_ASSISTED_ROOM_IDS` is retired. Supplying it, even blank, fails startup so that a stale room-1 deployment cannot silently retain the old scope. With @@ -244,7 +255,7 @@ lifetime. An actively connected Host prevents expiry; explicit stop or Host disconnect starts the lease, and only the exact Host token renews it before expiry. Viewer activity never renews ownership. `ROOM_DATABASE_PATH` and `ROOM_TTL_SECONDS` fail startup even when blank. -Production `2726edd` accepts 1 through 20 and explicitly selects 20. This is an +Production `679fe3e` accepts 1 through 20 and explicitly selects 20. This is an admission limit, not evidence that every publisher, network, or quality profile can sustain that many streams. `ENDPOINT_MEDIA_COPY_CAPACITY` defaults to 2 and accepts only 1, 2, or 3. It is @@ -257,11 +268,9 @@ must fail or wait before a fourth endpoint copy is issued. Supplying the removed `MAX_PEER_RELAY_DOWNSTREAM_EDGES`, even blank, fails startup. -Production release `2726edd` runs the deployed server and Browser assets -atomically on `screener-v10`; every stale Browser or executable-sender wire fails +Production release `679fe3e` runs the deployed server and Browser assets +atomically on `screener-v11`; every stale Browser or executable-sender wire fails before room authority. Native senders and helpers are outside this release. -Restore only an exact recorded release with its matching environment, unit, -LiveKit, coturn, and firewall snapshot when rolling back. The four `LIVEKIT_*` values must either all be absent or all be present, and a complete tuple requires `PEER_ASSISTED_MEDIA=true` plus explicit positive diff --git a/docs/project-memory.md b/docs/project-memory.md index 64c351b..c19b4fd 100644 --- a/docs/project-memory.md +++ b/docs/project-memory.md @@ -18,28 +18,29 @@ Last updated: 2026-08-24 - 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 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. -- Browser display video leaves `contentHint` unset so Chromium retains its screen-capture classification; returned audio tracks keep `contentHint = "music"`. Browser video is fixed to VP8 across direct, browser-relay, and SFU paths; codec selection, fallback to another media codec, quality-state/wire fields, and share-lifetime codec branches are not product features. Actual codec/profile/encoder stats remain diagnostic only. Production still sets video `motion` and exposes the v10 selector until the implemented v11 contract is deployed atomically. +- Browser display video leaves `contentHint` unset so Chromium retains its screen-capture classification; returned audio tracks keep `contentHint = "music"`. Browser video is fixed to VP8 across direct, browser-relay, and SFU paths; codec selection, fallback to another media codec, quality-state/wire fields, and share-lifetime codec branches are not product features. Actual codec/profile/encoder stats remain diagnostic only. - 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. +- A routine application-only deployment verifies one new immutable artifact and switches to it atomically while reusing unchanged environment, service, LiveKit, coturn, reverse-proxy, and firewall configuration. The pre-cutover application release is guaranteed only through bounded health and postflight checks; it has no retention contract afterward and is not a maintained backup. A future change to infrastructure, secrets, persistent data, or irreversible state must define recovery only for the surfaces it actually changes. - 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 -- Exact source implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` uses the single strict `screener-v11` Browser wire. Production remains exact integrated main `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, on `screener-v10`; auxiliary branches and older worktrees do not supersede canonical `main`. +- Production runs exact deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`, on the single strict `screener-v11` Browser wire; canonical `main` contains the same v11 runtime code, and auxiliary branches or older worktrees do not supersede it. - Current source and production admit up to `20` Viewers and implement the uniform `1/2/3` endpoint cap, 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 use 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 a writable room StateDirectory are absent from the current runtime. -- Exact production artifacts, rollback boundary, services, and ports are owned by [deployment](./deployment.md) and indexed by [status](./status.md). -- Current source requires v11, fixes Browser direct, browser-relay, and SFU media to VP8, leaves video `contentHint` unset, and contains no codec UI, quality state, wire field, fallback media codec, or share-lifetime codec branch. Production remains v10 with the pre-share selector fixed for one share and video `contentHint = "motion"`. Source and production use `shareGeneration`-fenced Pause/Resume and implement live 64/128/256 kbps audio-ceiling mutation, typed first-frame Viewer presentation, privacy-safe on-demand route diagnostics, neutral room-code denial, responsive entry controls, exactly three recommended profiles with `1080p30` as default and advanced-only `854x480`. +- Exact production artifacts, services, ports, and any change-scoped infrastructure recovery boundary are owned by [deployment](./deployment.md) and indexed by [status](./status.md). +- Current source and production require v11, fix Browser direct, browser-relay, and SFU media to VP8, leave video `contentHint` unset, and contain no codec UI, quality state, wire field, fallback media codec, or share-lifetime codec branch. Both use `shareGeneration`-fenced Pause/Resume and implement live 64/128/256 kbps audio-ceiling mutation, typed first-frame Viewer presentation, privacy-safe on-demand route diagnostics, neutral room-code denial, responsive entry controls, exactly three recommended profiles with `1080p30` as default and advanced-only `854x480`. - 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. -- Exact v11 source passed the current automated source gates but is not deployed and has no physical direct, browser-relay, or SFU codec evidence. Retained v10 source and production postflight evidence remain bounded to their tested revisions. Exact results are owned by [verification status](./verification-status.md), while artifacts and rollback are owned by [deployment](./deployment.md). +- Exact v11 implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` passed the current automated source gates and is integrated into deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09` and canonical `main`; deployment postflight passed. It still has no physical direct, browser-relay, or SFU codec evidence. Retained v10 evidence remains bounded to its tested revisions. Exact results are owned by [verification status](./verification-status.md), while current artifacts and operations are 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. Deploy the implemented single-wire Browser v11 contract atomically, then physically validate exact direct, browser-relay, and SFU codec behavior. -2. Queue Browser VP8 hardware-acceleration evidence separately; do not add another codec, custom encoder, or Native helper without a new accepted decision. Diagnose screen audio and invitation UI, then finish background/mobile/network evidence at their stated boundaries; Native/executable, distribution, and repository-wide simplification remain later phases. +1. Diagnose desktop Host background/minimized behavior under controlled capture, codec, send, receive, decode, CPU, and GPU measurement, then diagnose the screen-audio `1 kbps` readout and physically validate live audio ceilings. +2. Then physically validate exact direct, browser-relay, and SFU codec behavior on deployed Browser v11, refine the invitation UI, and finish real-network route acceptance. Browser VP8 hardware acceleration and mobile Viewer lifecycle remain Accepted Later; Native/executable, distribution, and repository-wide simplification remain later phases. ## Working Rules diff --git a/docs/research/advanced-peer-distribution.md b/docs/research/advanced-peer-distribution.md index d9aa6b7..333a7a7 100644 --- a/docs/research/advanced-peer-distribution.md +++ b/docs/research/advanced-peer-distribution.md @@ -7,9 +7,12 @@ - Status: deterministic peer distribution and provisional make-before-break are retained evidence. Exact Browser v10 source `fdd5a4a529ff297f41c05ea3388bf484d76afe8f` implements ADR-0005's single - child-reparent reconciliation and exact first-frame transaction; production - runs that v10 contract, and advanced encoded-media routes remain - unimplemented candidates + child-reparent reconciliation and exact first-frame transaction and remains + dated implementation evidence. Production runs exact deployed + application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release + `679fe3e`; canonical `main` contains the same runtime code. Current source and + production run the strict `screener-v11` form of that route contract. + 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 @@ -262,7 +265,7 @@ 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 deployed v10 diagnostic exposes only a closed local +transport failure. The production diagnostic 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. diff --git a/docs/research/browser-background-capture.md b/docs/research/browser-background-capture.md index 6d68fe3..0e4925b 100644 --- a/docs/research/browser-background-capture.md +++ b/docs/research/browser-background-capture.md @@ -45,18 +45,17 @@ mechanism. ## Current Source Boundary The no-video-hint review baseline is -`a26eb39dc677003110787b0ed1581c208f894fd7`. Exact current source -`f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` uses strict `screener-v11`, -fixed VP8, no video hint, and no codec UI, quality state, or wire field. -Production remains exact `2726edde9b87f31fd76e749de47972ef817a9bd5`, -release `2726edd`, on v10 with video `motion` and the pre-share codec selector. +`a26eb39dc677003110787b0ed1581c208f894fd7`. Production runs exact deployed +application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release +`679fe3e`; canonical `main` contains the same runtime code. Current source and +production use strict `screener-v11`, fixed VP8, no video hint, and no codec UI, +quality state, or wire field. The current path does not claim or implement a page-keepalive mechanism, and deployment health supplies no physical background-capture evidence. - Current `src/client/media/quality.ts` obtains one `getDisplayMedia()` stream, applies ideal/max capture constraints, leaves the video hint unset, and - applies sender bitrate, frame-rate, and degradation ceilings. Production - still marks video as `motion` until v11 is deployed. Explicit + applies sender bitrate, frame-rate, and degradation ceilings. Explicit sharing pause and authoritative reconnect re-pause change capture tracks' `enabled` state; neither manufactures foreground activity. Audio `contentHint = "music"` remains unchanged. @@ -151,6 +150,14 @@ its own timer or animation-frame lifecycle would confound the capture result. Keep the selected source foreground, visible, and changing throughout the first matrix. +After Screener reproduces a stable baseline, the locally available NetEase UU +Remote client may be run against the same scene and network as a black-box +comparison for process CPU/GPU use, network traffic, latency, and visible +quality. User-observed low-latency behavior is a reason to measure it, not +evidence of its transport, codec, encoder, background policy, or applicability +to a browser Host; those properties remain unknown unless the comparison exposes +them directly. + Open `chrome://webrtc-internals` on Host and Viewer before sharing and keep its overhead constant across every run. Leave audio-debug recording, event-log recording, packet capture, and media recording disabled. The current Screener diff --git a/docs/research/browser-screen-audio-quality.md b/docs/research/browser-screen-audio-quality.md index d939cb1..b19a9fb 100644 --- a/docs/research/browser-screen-audio-quality.md +++ b/docs/research/browser-screen-audio-quality.md @@ -6,14 +6,13 @@ 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. Exact current Browser source -`f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` uses strict `screener-v11`, -fixed VP8, no video hint, and no codec UI, quality state, or wire field. It -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 exact -`2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, on v10 with -video `motion` and the pre-share codec selector; it deploys the same audio path. +explicit. Production runs exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical +`main` contains the same runtime code. Current Browser source and production use +strict `screener-v11`, fixed VP8, no video hint, and no codec UI, quality state, +or wire field. They expose bounded 64/128/256 kbps choices, +apply them to new P2P, browser-relay, and SFU senders, and implement serialized +live mutation with applied readback. Target-device audible proof remains open. ## Scope And Decision @@ -450,9 +449,9 @@ existing generic Pion two-second outbound snapshot, which also missed fresh deltas in earlier video loopbacks. This is functional evidence, not packaging, real-game sync, second-Viewer, SFU/UDP, or endurance evidence. -Run one bounded matrix rather than a full route Cartesian product: exact -production and exact v11 source `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` -on Windows Chrome/Edge for tab/window/monitor, +Run one bounded matrix rather than a full route Cartesian product against +production release `679fe3e` and its matching current runtime source on Windows +Chrome/Edge for tab/window/monitor, audio selected/unselected, and a simultaneous voice call; then the native candidate on current Windows 11 with game parent and child audio, an independent voice process, notifications, no render stream, and process restart. Windows 10 diff --git a/docs/research/low-server-media-routes.md b/docs/research/low-server-media-routes.md index 90c4d05..09cd77c 100644 --- a/docs/research/low-server-media-routes.md +++ b/docs/research/low-server-media-routes.md @@ -5,10 +5,11 @@ 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). - Exact current Browser source `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` - implements the route transaction and diagnostics on strict v11; production - remains exact `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, - on v10, while real-network validation remains in + Production runs exact deployed application/runtime revision + `679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical + `main` contains the same runtime code. Current Browser source and production + implement the route transaction and diagnostics on strict `screener-v11`. + Real-network validation remains in [verification status](../verification-status.md). ## Current Transport Conclusion @@ -302,7 +303,7 @@ ingress/egress caps. ## Privacy-Safe Route Diagnostic Boundary Start with locally retained, redacted `webrtc-internals` as manual ground truth. -Production v10 deploys the user-initiated local stats export and route-timing +Production deploys the user-initiated local stats export and route-timing snapshot. 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 @@ -558,10 +559,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 | Current v11 source and v10 production both admit up to 20; the exact v10 local direct loopback gate 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 | Current v11 source and production both admit up to 20; the exact v10 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 | Production deploys the v10 topology/admission; local LiveKit functional evidence exists, while heterogeneous-network/resource validation remains open | +| SFU service | SFU emits authorized subscription copies | Service pays bounded egress; an authoritative Host publisher supplies media | Production deploys the current topology/admission; local LiveKit functional evidence exists, while heterogeneous-network/resource validation remains 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 | diff --git a/docs/research/peer-assisted-media.md b/docs/research/peer-assisted-media.md index 9a2b2d8..4dac410 100644 --- a/docs/research/peer-assisted-media.md +++ b/docs/research/peer-assisted-media.md @@ -5,8 +5,11 @@ trusted viewers; exact v10 direct-loopback evidence reaches twenty and the earlier resource/quality gate remains eight - Status: historical evidence plus the bounded controller. Exact Browser v10 - source `fdd5a4a529ff297f41c05ea3388bf484d76afe8f` implements the current - ADR-0005 route transaction, and production runs that v10 contract. A + source `fdd5a4a529ff297f41c05ea3388bf484d76afe8f` remains the dated local + direct-loopback implementation evidence. Production runs exact deployed + application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release + `679fe3e`; canonical `main` contains the same runtime code. Current source and + production run the strict `screener-v11` ADR-0005 route transaction. A historical v8 canary decoded SFU media. Heterogeneous-network, mobile, resource, and endurance evidence remains open. @@ -240,7 +243,7 @@ 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, includes it in peer-assisted authenticated snapshots, and broadcasts Host changes to online -Viewers. Production v10 uses the same 1080p30 default and advanced 480p boundary. +Viewers. Production uses the same 1080p30 default and advanced 480p boundary. The value survives a stopped share, is removed with the room, and is not persisted. Ordinary P2P authentication remains unchanged and @@ -529,7 +532,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. -Deployed v10 sends +Production sends `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 @@ -547,7 +550,7 @@ 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 v10; +This response-only watchdog is deployed in production; real silent-partition timing remains a production/browser evidence boundary. The response is socket-local, contains no room state, secret, candidate, or raw diff --git a/docs/research/realtime-quality-adaptation.md b/docs/research/realtime-quality-adaptation.md index 18aa4ab..32da7ab 100644 --- a/docs/research/realtime-quality-adaptation.md +++ b/docs/research/realtime-quality-adaptation.md @@ -11,9 +11,11 @@ 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. -Exact current Browser source `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` -uses strict `screener-v11`, fixed VP8, no video `contentHint`, and no codec -UI, quality state, or wire field. It uses `balanced` as the recommended profile +Production runs exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical +`main` contains the same runtime code. Current Browser source and production use +strict `screener-v11`, fixed VP8, no video `contentHint`, and no codec UI, +quality state, or wire field. They use `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 @@ -158,8 +160,8 @@ Screener's accepted boundary forbids adding application SDP bitrate hacks. The same release was also reported to reduce game-stream frame rate and consume noticeable Host resources. That report applies only to -`769de201f7cc`, not to current production or automatically to the newer -diagnostics in exact source `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9`. +`769de201f7cc`, not to current production or automatically to the current v11 +diagnostics. The Web sender creates one independent `RTCRtpSender` per viewer and has no cross-PC shared-encoder guarantee; its muted local preview creates no media edge or @@ -190,8 +192,8 @@ A 2026-08-21 report says that selecting fluid preference on an SFU path could retain low received FPS without reducing the visible resolution. This is not proof that the preference was ignored: `maintain-framerate` is a degradation tradeoff rather than an FPS target. It is also not evidence of SFU temporal -downlayering: the current deployed v10 Browser defaults to VP8 but still lets the -Host select another value before starting the share. Pinned LiveKit server 1.13.5 installs a temporal selector +downlayering: the then-deployed v10 Browser defaulted to VP8 but let the Host +select another value before starting the share. Pinned LiveKit server 1.13.5 installs a temporal selector for VP8, but its H.264/H.265 path installs only the simulcast spatial selector. With H.264 selected, a `HIGH` ceiling may therefore let BWE choose the lower-resolution `q` representation, not a lower temporal layer at the same @@ -222,7 +224,7 @@ 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. Production -deploys this v10 publisher view; its target-browser fields and values still need +deploys this publisher view; its target-browser fields and values still need physical evidence. ## Browser Codec And Content-Hint Evidence @@ -233,10 +235,10 @@ as their only video media codec, while RTX/RED/FEC may remain repair formats; SFU publication explicitly uses VP8 with no backup codec. The UI and quality wire expose no codec choice. Codec/profile/encoder stats remain diagnostic and do not authorize automatic switching, route changes, or another controller. -Exact source `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` implements this strict -`screener-v11` contract. Production remains exact -`2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, on v10 with -video `contentHint = "motion"` and the pre-share codec selector. +Production runs exact deployed application/runtime revision +`679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`; canonical +`main` contains the same runtime code. Current source and production implement +this strict `screener-v11` contract. Chromium maps video `contentHint = "motion"` to libwebrtc `kFluid`, and libwebrtc clears `is_screencast` for that mode, replacing the display-capture @@ -712,7 +714,7 @@ 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 v11 source and v10 production default to the middle `1080p30` ceiling. +Current v11 source and production default to the middle `1080p30` ceiling. Choosing that default trades a 60 fps ceiling for a 1080p capture bound. The recommended set remains exactly the three profiles @@ -726,23 +728,20 @@ guarantees the emitted resolution, frame rate, or bitrate. - A live profile change uses `track.applyConstraints()` and updates every current sender with `RTCRtpSender.setParameters()`. It does not reopen the source picker or renegotiate healthy peer connections. -- Current source leaves the video hint unset so display capture retains - browser screen semantics; production still sets `contentHint = "motion"` - until v11 deployment. Recommended profiles and the advanced initial value use - `balanced`, with explicit `maintain-resolution` and +- Current source and production leave the video hint unset so display capture + retains browser screen semantics. Recommended profiles and the advanced + initial value use `balanced`, with explicit `maintain-resolution` 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 deployed `screener-v10` Share advanced settings panel accepts - 480p/720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, the three preferences, and - Automatic/H.264/VP8 before sharing starts and defaults to 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 deploys both the - advanced 480p resolution and live audio mutation. Current v11 source keeps - those controls, fixes Browser media to VP8, and exposes no codec UI, - quality-state field, or wire field. Display capture does not standardize channel-count or - sample-rate control. The peer receive - contract permits Opus `stereo=1;maxaveragebitrate=256000`, paired with pinned +- The strict `screener-v11` Share advanced settings panel in current source and + production accepts 480p/720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, and + the three preferences. Browser media is fixed VP8, with no codec UI, + quality-state field, or wire field. 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. Display + capture does not standardize channel-count or sample-rate control. The peer + receive contract permits Opus `stereo=1;maxaveragebitrate=256000`, paired with pinned LiveKit's explicit high-quality stereo/forceStereo option; the selected sender ceiling remains separate from negotiated and observed bitrate. DTX stays fixed off, RED retains the pinned SDK default, and FEC remains @@ -761,11 +760,11 @@ 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. -Current source and deployed v10 stop at manual bounded controls. -They add no composite score, periodic adjustment, runtime codec switching, 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. +Current source and production stop at manual bounded controls. +They add no composite score, periodic adjustment, runtime codec switching, 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. A Chrome 151 loopback smoke with one host, three viewers, and synthetic 720p30 video propagated balanced then clarity settings to every participant. Every diff --git a/docs/session-handoff.md b/docs/session-handoff.md index dbd1a5f..668d0da 100644 --- a/docs/session-handoff.md +++ b/docs/session-handoff.md @@ -32,9 +32,10 @@ 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. Exact source implementation - `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` uses strict `screener-v11`; - production remains on `screener-v10`. The route uses one short-lived + only by the dedicated LiveKit SFU/UDP fallback. Production exact deployed + application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, + release `679fe3e`, uses strict `screener-v11`; canonical `main` contains the + same v11 runtime code. The route 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 @@ -44,13 +45,14 @@ Current high-signal boundary: 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. Neither source nor production uses SQLite; - exact rollback artifacts are owned by deployment. + exact release operations and change-scoped infrastructure recovery are owned + by deployment. Routine application-only releases do not maintain a full + rollback/configuration backup. - Browser display video must leave `contentHint` unset; audio keeps `music`. Browser video is fixed VP8 across direct, browser-relay, and SFU paths; the - current strict v11 source has no codec UI/state/wire or fallback media codec. - It passed the current automated source gates. - Production still uses v10, the selector, and video `motion`; v11 is not deployed - and has no physical direct, browser-relay, or SFU codec evidence. + current strict v11 source and production have no codec UI/state/wire or + fallback media codec. Automated source gates and deployment postflight passed, + but no physical direct, browser-relay, or SFU codec path has been verified. - 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 diff --git a/docs/status.md b/docs/status.md index 50aed73..e808afa 100644 --- a/docs/status.md +++ b/docs/status.md @@ -6,43 +6,43 @@ This is the current execution index. Git history owns completed timelines; [veri ## Production -- `https://share.bonfire.icu` runs exact integrated main `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, wire `screener-v10`, from `/opt/screener/releases/2726edd`. Its immutable runtime ZIP SHA-256 is `07fa6500300bfedc9cedccb0db761b70a9ada8dd77608153728e34835f4e1b78`; `/opt/screener/backups/2726edd-pre-v10-20260824T025357Z` is the verified rollback boundary and its `SHA256SUMS` hash is `62d19aa0a3ec400477a4c76121eb0e9e4d553d024404f3da054fa1e95f251939`. -- `screener`, LiveKit, coturn, and nginx are active/running; local/public `/` and `/healthz` return 200 and all four services report `NRestarts=0`. Every served client file matches the immutable release, `assets/index-BtFMxoNI.js` has SHA-256 `885b9e50c57c64ac92cc8ffe5488d597bbb6fde6423e7a0b328efb9d1e943ad2`, contains one `screener-v10` literal and no `screener-v9`, and the retired v9 asset returns 404. -- The release is root-owned, has no group/world-writable real file or directory, has no dangling or escaping symlink, and shares zero regular-file inodes with release `39fcf93`. The service process resolves its working directory to exact release `2726edd`; it has no writable room `StateDirectory` or SQLite runtime. +- `https://share.bonfire.icu` runs exact deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`, wire `screener-v11`, from `/opt/screener/releases/679fe3e`; that revision is integrated in canonical `main`. Its immutable runtime ZIP SHA-256 is `fc96137f0fdc4dc4d8e083513d8fe99b9c55f7067a8b1de9202551edc3776c50`, and its 38-file manifest SHA-256 is `73f67c530acd5d4c77b546c19b5752c066f82682a584a72cd7ca596d6c5bec7e`. +- `screener`, LiveKit, coturn, and nginx are active/running; local/public `/` and `/healthz` return 200, all six served assets match the immutable release, and all four services report `NRestarts=0`. `assets/index-CGzoJSvs.js` has SHA-256 `31cf628d96be88f373cd0c3ac09e4966929fe320414aa1f0f9841264708d5a7e`; the retired v10 asset `assets/index-BtFMxoNI.js` returns 404, and stale v10 authentication receives `INVALID_MESSAGE` with close 1008 before room authority. +- The service process resolves its working directory to exact release `679fe3e`; it has no writable room `StateDirectory` or SQLite runtime. - Production admits one Host plus 20 Viewers, applies endpoint capacity `2`, rejects stale Browser and executable-sender wires before room authority, and runs the one-controller exact-candidate route model with one staged total deadline. Rooms use random free four-digit codes, a 24-hour dormant lease, restart loss, local Host creation preferences, independent Viewer grants, and `open | password | disabled` code entry. LiveKit remains dedicated with `room.auto_create: false`, `max_participants: 21`, and global admission `1` publication ingress / `20` subscription egress. -- Production media ports are STUN-only UDP 3478 and LiveKit UDP 7882; coturn TCP/TLS and relay ranges are disabled. The public Web ingress remains TCP 80/443; Node 8787 and LiveKit control/signaling 7880 are internal-only. These deployment facts do not close the real heterogeneous-network, SFU, mobile, or endurance evidence boundaries. -- SFU subscribers reconcile Host screen publications across connect, activation, participant arrival, track publication, and reconnect. LiveKit runs at `warn`/Pion `error`, and the Screener unit waits boundedly for the same-host LiveKit control listener before startup. Postflight left the LiveKit namespace empty, so physical direct, peer-relay, and SFU behavior remains an open current-release gate. -- Production sets video `contentHint = "motion"` and audio `contentHint = "music"`; the no-video-hint source change is not deployed. +- Production media ports remain STUN-only UDP 3478 and LiveKit UDP 7882; coturn TCP/TLS, port 5349, media TCP, and relay ranges are disabled. The public Web ingress remains TCP 80/443; Node 8787 and LiveKit control/signaling 7880 are internal-only. The nft ruleset SHA-256 is `a895f0bb7b66ffe21071ee18acd84d4ea43ca16d4bca9517e6272e14d0ecced4`. +- SFU subscribers reconcile Host screen publications across connect, activation, participant arrival, track publication, and reconnect. LiveKit runs at `warn`/Pion `error`, and the Screener unit waits boundedly for the same-host LiveKit control listener before startup. Postflight found zero warning-level Screener journal entries and an empty LiveKit namespace, so physical direct, browser-relay, and SFU codec behavior remains an open current-release gate. +- Production leaves video `contentHint` unset, keeps audio `contentHint = "music"`, and fixes Browser direct, browser-relay, and SFU video to VP8 without codec UI, quality state, wire fields, or fallback media codecs. ## Current Source -- Canonical root `main` is the integration truth; auxiliary branches and older worktrees do not supersede it. Exact source implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` uses one strict Browser `screener-v11` boundary and rejects every other wire before room authority. Production remains exact release `2726edd` on v10; v11 is implemented but not deployed. +- Canonical root `main` is the integration truth; auxiliary branches and older worktrees do not supersede it. Current canonical `main` contains the same strict Browser `screener-v11` runtime code as deployed application revision `679fe3e7af634309322bea83b316641f51ad3d09`, rejecting every other wire 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 leaves video `contentHint` unset and keeps audio `contentHint = "music"`. Browser direct and browser-relay offers contain VP8 as their only media codec, and SFU publication explicitly uses VP8 with no backup codec. Codec UI, quality state, wire fields, fallback media codecs, and share-lifetime codec branches are absent. Live 64/128/256 kbps audio-ceiling mutation and ordinary `shareGeneration`-fenced Pause/Resume remain unchanged. - 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. - 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 v11 source implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` passed the current automated source gates. It has no deployment or physical direct, browser-relay, or SFU codec evidence; retained v10 loopback evidence applies only to that older source. Exact results are owned by [verification status](./verification-status.md). +- Exact v11 implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9`, integrated into deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09` and canonical `main`, passed the current automated source gates. Deployment postflight passed, but no physical direct, browser-relay, or SFU codec path was exercised; retained v10 loopback evidence applies only to that older source. Exact results are owned by [verification status](./verification-status.md). ## Current Milestone -1. Deploy the validated `screener-v11` fixed-VP8/no-video-hint Browser source atomically, then verify direct, browser-relay, and SFU paths from actual codec stats. Production remains v10 with the pre-share selector and video `motion` until that cutover. -2. Diagnose the reported screen-audio `1 kbps` readout and validate live audio ceilings, then refine the Host invitation controls without changing grant semantics implicitly. -3. Reproduce the Host background/minimized report under controlled conditions while recording the actual negotiated codec. -4. 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. +1. Reproduce the Host background/minimized report under controlled conditions while recording actual capture, codec, send, receive, decode, CPU, and GPU evidence. +2. Diagnose the reported screen-audio `1 kbps` readout and physically validate live audio ceilings. +3. Physically verify the deployed `screener-v11` fixed-VP8/no-video-hint direct, browser-relay, and SFU paths from actual codec, capture, outbound, and decoded stats. +4. Refine the Host invitation controls without changing grant semantics implicitly. +5. 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, restart loss, entry/presentation, and rollback restoration remain aligned. The source v11 codec contract is intentionally ahead of production v10 until atomic cutover. +- Room lifetime, authorization/storage semantics, restart loss, entry/presentation, and the Browser v11 contract are aligned between source and production. This application-only cutover reused unchanged infrastructure and configuration and does not maintain a full rollback/configuration backup; any future infrastructure or irreversible-state change requires recovery scoped to the surfaces it actually changes. - 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 -There is no active P0/P1 source or deployment hold. The fixed-VP8/no-video-hint v11 source is implemented and source-validated; production remains on the postflight-clean -Browser v10 contract until atomic deployment. Physical v11 direct, browser-relay, and SFU codec evidence, plus audio, heterogeneous-network, SFU, +There is no active P0/P1 source or deployment hold. The fixed-VP8/no-video-hint Browser v11 contract is source-validated, deployed, and postflight-clean. Physical direct, browser-relay, and SFU codec evidence, plus audio, heterogeneous-network, SFU, background, and mobile evidence remains open. Native/executable work and broad repository cleanup remain outside the current evidence boundary. diff --git a/docs/todo.md b/docs/todo.md index 58b771b..6e52fab 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -6,10 +6,10 @@ Only items in **Now** are executable after their stated decision gate. A branch ## Now -1. **Deploy and physically validate fixed Browser VP8 with no video hint.** Exact source implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` already provides the strict v11 wire, VP8-only direct/browser-relay offers, VP8 SFU publication with no backup codec, unset video `contentHint`, and no codec UI/state/wire. Deploy server/assets atomically, then confirm actual codec/capture/outbound/decoded stats on direct, browser-relay, and SFU paths. Production remains v10 with video `motion` and the selector until that cutover. +1. **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 recording the actual negotiated codec. 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. 2. **Diagnose and physically validate live screen audio.** First reproduce the Host diagnostic remaining at `1 kbps` after switching the shared source, and distinguish stale/reset statistics from an actual RTP bitrate problem. The app already requests audio and supplies system/window audio hints; record the selected surface, returned audio track, sender/receiver counters, and audible result because the browser owns the picker and may still return video only. Then switch the 64/128/256 kbps ceilings across direct, browser-relay, and SFU paths, create a new edge, and switch source while checking route continuity and A/V synchronization. Do not infer fidelity or room-wide atomic convergence from configuration alone. -3. **Redesign the Host invitation controls.** Replace the current raw-link-heavy block with a compact copy-first layout while preserving the accepted copy-link, copy-room-code, code-entry policy, rotate, and revoke actions. Keep the credential out of logs and request URLs; changing the copied credential length is blocked on the explicit product decision below. -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 recording the actual negotiated codec. 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. +3. **Physically validate fixed Browser VP8 with no video hint.** Production exact deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`, already runs the strict v11 wire, VP8-only direct/browser-relay offers, VP8 SFU publication with no backup codec, unset video `contentHint`, and no codec UI/state/wire. Confirm actual codec, capture, outbound, and decoded stats on direct, browser-relay, and SFU paths; deployment postflight alone does not close this gate. +4. **Redesign the Host invitation controls.** Replace the current raw-link-heavy block with a compact copy-first layout while preserving the accepted copy-link, copy-room-code, code-entry policy, rotate, and revoke actions. Keep the credential out of logs and request URLs; changing the copied credential length is blocked on the explicit product decision below. 5. **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 @@ -19,7 +19,7 @@ These items are real product work, ordered after the current physical-media, rea 1. **Browser VP8 hardware-acceleration evidence.** After the v11 physical codec gate, measure supported Chrome/Edge platform combinations under real game load using actual negotiated codec, encoder implementation, power-efficient status, encode time, frame rate, and CPU/GPU attribution. Capability advertisement alone is not proof. Do not add another codec, a custom encoder, GPU-selection workaround, codec ladder, or Native helper without a new accepted decision. 2. **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. 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. +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, and health checks for a user-owned public server. Require real domain/TLS/firewall/capacity inputs and recovery scoped to any infrastructure or irreversible state the installer actually changes; routine application-only immutable updates do not create or maintain a full configuration backup. 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. diff --git a/docs/verification-status.md b/docs/verification-status.md index 7561a51..7bd5dfe 100644 --- a/docs/verification-status.md +++ b/docs/verification-status.md @@ -10,35 +10,34 @@ Capacity and routing entries below describe the exact tested source or release o ## Deployment Evidence -- Production runs exact `2726edde9b87f31fd76e749de47972ef817a9bd5`, release `2726edd`, wire `screener-v10`, from `/opt/screener/releases/2726edd`. The immutable runtime ZIP SHA-256 is `07fa6500300bfedc9cedccb0db761b70a9ada8dd77608153728e34835f4e1b78`; its 38-file manifest SHA-256 is `c4ed6321fa3ac8b4c766a7e575d26f8fd4ec2ea929a9addefb7944abafa5b663`. -- `/opt/screener/backups/2726edd-pre-v10-20260824T025357Z` retains the exact pre-v10 current target plus application environment, systemd, LiveKit, coturn, nft, and nginx boundary; its internal manifest SHA-256 is `62d19aa0a3ec400477a4c76121eb0e9e4d553d024404f3da054fa1e95f251939`. -- Postflight found `screener`, LiveKit, coturn, and nginx active with zero restarts and no warning-level Screener journal entries. Local/public root, health, and all six client files matched the release; `assets/index-BtFMxoNI.js` has SHA-256 `885b9e50c57c64ac92cc8ffe5488d597bbb6fde6423e7a0b328efb9d1e943ad2` and contains `screener-v10` with no `screener-v9`. A stale-v9 authentication received `INVALID_MESSAGE` and close 1008 before room authority. +- Production runs exact deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`, release `679fe3e`, wire `screener-v11`, from `/opt/screener/releases/679fe3e`; that revision is integrated in canonical `main`. The immutable runtime ZIP SHA-256 is `fc96137f0fdc4dc4d8e083513d8fe99b9c55f7067a8b1de9202551edc3776c50`; its 38-file manifest SHA-256 is `73f67c530acd5d4c77b546c19b5752c066f82682a584a72cd7ca596d6c5bec7e`. +- Postflight found `screener`, LiveKit, coturn, and nginx active with zero restarts and no warning-level Screener journal entries. Local/public root, health, and all six served assets matched the release; `assets/index-CGzoJSvs.js` has SHA-256 `31cf628d96be88f373cd0c3ac09e4966929fe320414aa1f0f9841264708d5a7e`, and retired `assets/index-BtFMxoNI.js` returns 404. A stale-v10 authentication received `INVALID_MESSAGE` and close 1008 before room authority. - 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. Room authority is process-memory-only. Coturn is `stun-only`, `no-tcp`, and `no-tls`; only UDP 3478 listens, and nft exposes only UDP 3478/7882 for Screener media. -- The production service orders Screener after LiveKit and boundedly waits for its control listener. The LiveKit namespace is empty, the release shares zero regular-file inodes with `39fcf93`, and the nft ruleset matches the rollback snapshot. -- This postflight proves archive identity, atomic application/assets, stale-wire rejection, process health, listener/firewall continuity, log sentinels, and rollback readiness only. It did not exercise direct, peer-relay, SFU, audio, codec, mobile, quality, or endurance behavior. +- The production service orders Screener after LiveKit and boundedly waits for its control listener. The LiveKit namespace is empty; media listeners remain STUN-only UDP 3478 and LiveKit UDP 7882 with no 5349, coturn TCP/TLS, relay range, ICE/TCP, or media TCP listener. The unchanged nft ruleset has SHA-256 `a895f0bb7b66ffe21071ee18acd84d4ea43ca16d4bca9517e6272e14d0ecced4`. +- This application-only postflight proves archive identity, atomic application/assets, stale-wire rejection, process health, listener/firewall continuity, and log sentinels. It did not exercise direct, browser-relay, SFU, audio, codec, mobile, quality, or endurance behavior. Routine application-only releases do not maintain a full rollback/configuration backup; any future infrastructure or irreversible-state change requires recovery scoped to what it changes. ## Retained Functional Evidence -- Exact v11 source implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9` passed TypeScript typecheck, all 605 Web tests in 45 files, client and server production builds, the 254-test SFU gate in 10 files, and the access/privacy gate with explicit Chrome. This validates source structure, strict-wire behavior, fixed-VP8 path configuration, build output, and the covered Browser gate only. It is not deployed and provides no physical direct, browser-relay, or SFU codec evidence. +- Exact v11 implementation `f5a295c52e0ac7d18e5a7949217861c7aa74e9c9`, integrated into deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09` and canonical `main`, passed TypeScript typecheck, all 605 Web tests in 45 files, client and server production builds, the 254-test SFU gate in 10 files, and the access/privacy gate with explicit Chrome. This validates source structure, strict-wire behavior, fixed-VP8 path configuration, build output, and the covered Browser gate only; it provides no physical direct, browser-relay, or SFU codec evidence. - Exact v10 runtime source `fdd5a4a529ff297f41c05ea3388bf484d76afe8f` passed repository hygiene, TypeScript typecheck, client and server production builds, all 610 Web tests in 45 files, the 257-test SFU admission gate, the access/privacy Browser gate, and the production-dependency audit with zero vulnerabilities. Its one-Host/20-Viewer Chrome 151 direct-loopback report passed all seven assertions: all Viewers decoded, the selected VP8 settings reached every participant, Host and relay media edges stayed within cap `2`, no orphan SFU publication appeared, and all four current-child timing distributions contained 20 values. The report SHA-256 is `802CBB7632E9AEA3C39FE831A7F7880D24140B2E83942CE0DFDA05DE4B408EED`. This validates the v10 source boundary, not H.264 quality, hardware attribution, physical capture, public-network behavior, or SFU packet flow. - 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). -- Fresh Chrome 151 tab-capture probes identified two separate Browser boundaries. Removing video `contentHint = "motion"` changed VP8 from 14.93 fps at 428x208 to 29.73 fps at 1904x928 under the same 5 Mbps balanced setup. No-hint AMD H.264 remained about 14--16 fps with hundreds of outer rate-limiter drops, while the same MFT reached 29.93 fps under diagnostic Desktop SW BRC; OpenH264 was slower. The page cannot enable that Chromium field trial or select an adapter/MFT. No-hint VP8 used libvpx software and held about 30 fps in one- and two-sender loopback on the test machine, but the process CPU totals include capture and local decode and are not real-game or weaker-device evidence. This evidence selects fixed VP8 for the Browser contract; current source implements that decision, while production still sets `motion` and exposes the v10 selector. See [realtime quality adaptation](./research/realtime-quality-adaptation.md), [Native H.264](./research/native-h264-hardware-decision.md), and [Native shared encode](./research/native-shared-encode-sender.md). +- Fresh Chrome 151 tab-capture probes identified two separate Browser boundaries. Removing video `contentHint = "motion"` changed VP8 from 14.93 fps at 428x208 to 29.73 fps at 1904x928 under the same 5 Mbps balanced setup. No-hint AMD H.264 remained about 14--16 fps with hundreds of outer rate-limiter drops, while the same MFT reached 29.93 fps under diagnostic Desktop SW BRC; OpenH264 was slower. The page cannot enable that Chromium field trial or select an adapter/MFT. No-hint VP8 used libvpx software and held about 30 fps in one- and two-sender loopback on the test machine, but the process CPU totals include capture and local decode and are not real-game or weaker-device evidence. This evidence selects fixed VP8 for the Browser contract; current source and production implement that decision, while physical production codec behavior remains unverified. See [realtime quality adaptation](./research/realtime-quality-adaptation.md), [Native H.264](./research/native-h264-hardware-decision.md), and [Native shared encode](./research/native-shared-encode-sender.md). - Native Windows 11 process audio is source-only and default-off. A retained run isolated the target by about 4018x and delivered 495 Opus packets to one Viewer; package download, game sync, Windows 10, and other routes remain open. See [ADR-0008](./adr/0008-window-scoped-audio-capture.md). ## Open Proof Boundaries -- Routing: exact v10 local direct loopback covers admission-20 initial ingress and exact-candidate first-frame commit timing for that source. The v11 fixed-VP8 source still needs physical direct, browser-relay, and SFU codec verification, together with 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 v10 local direct loopback remains historical evidence for admission-20 initial ingress and exact-candidate first-frame commit timing on that source. Current v11 production still needs physical direct, browser-relay, and SFU codec verification, together with route 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: current source and v10 production expose the authenticated `1/2/3` endpoint-cap boundary, and retained v10 local loopback evidence covers 20 Viewers at cap `2`. CPU/GPU/upload and heterogeneous-network measurements remain diagnostics, not authority to change the contract. +- Relay resources: current v11 source and production expose the authenticated `1/2/3` endpoint-cap boundary, and retained v10 local loopback evidence covers 20 Viewers at cap `2` only for that source. CPU/GPU/upload and heterogeneous-network measurements remain diagnostics, not authority to change the contract. - Room scale: current source and production admit 20 Viewers plus one Host, and the retained v10 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 v10 Viewer stage, local stats/export plumbing, identity-bound relayed-detail retention, export freshness guards, and on-demand Host route snapshot are 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. +- Observability: the current v11 Viewer stage, local stats/export plumbing, identity-bound relayed-detail retention, export freshness guards, and on-demand Host route snapshot are 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: exact v11 source passed the access/privacy gate with explicit Chrome. Production passed atomic v10 cutover/postflight plus stale-v9 rejection before authority. Restart-wide room loss, password-profile creation, grant/code orthogonality, rotate/revoke, memory-only runtime, and exact current journal/nginx sentinel checks retain their focused evidence. 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 v11 implementation passed the access/privacy gate with explicit Chrome. Production passed atomic v11 application cutover/postflight plus stale-v10 rejection before authority. Restart-wide room loss, password-profile creation, grant/code orthogonality, rotate/revoke, memory-only runtime, and exact current journal/nginx sentinel checks retain their focused evidence. Headful same-browser preference replay and the isolated VM/IP clean-port matrix remain open; shared-IP smoke cannot approve them. ## Evidence Rules diff --git a/docs/方案设计.md b/docs/方案设计.md index 651cf9e..aad9303 100644 --- a/docs/方案设计.md +++ b/docs/方案设计.md @@ -2,7 +2,7 @@ ## 2026-08-18 14:45 -状态:current source 精确 `f5a295c` 使用严格单一 `screener-v11`;Browser display video 不设置 `contentHint`、audio 保留 `music`,唯一视频媒体 codec 为 VP8,UI、quality state/wire 和分享生命周期均没有 codec 分支。Production 精确 `2726edde9b87f31fd76e749de47972ef817a9bd5`、release `2726edd`,仍使用 `screener-v10`、设置 video `contentHint = "motion"` 并保留 pre-share codec selector;`screener-v11` 尚未部署。Pause/Resume 继续由当前 Host session 和 `shareGeneration` 约束。 +状态:production 运行精确 deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`、release `679fe3e`;canonical `main` 包含同一套 v11 runtime code。两者使用严格单一 `screener-v11`;Browser display video 不设置 `contentHint`、audio 保留 `music`,唯一视频媒体 codec 为 VP8,UI、quality state/wire 和分享生命周期均没有 codec 分支。Pause/Resume 继续由当前 Host session 和 `shareGeneration` 约束。 ## 目标与边界 @@ -36,7 +36,7 @@ 不选 Go 的原因是 Node 不承载正常情况下的媒体流量,换语言不会减少分享者上行或 SFU 成本。不选 C++ 的原因是浏览器能力尚未被实测证明不足;只有捕获、应用音频或硬件编码出现明确瓶颈时才增加原生 helper。 -最终分发有两个独立产物。公网服务器一键部署包面向用户自有服务器,编排同一版本的 Web/信令应用、易失房间状态、STUN/SFU 和反向代理,不建立第二套应用;域名、TLS、secret、端口、容量、健康检查和回滚继续 fail closed。它不提供托管基础设施,也不把占位配置写成可用服务。 +最终分发有两个独立产物。公网服务器一键部署包面向用户自有服务器,编排同一版本的 Web/信令应用、易失房间状态、STUN/SFU 和反向代理,不建立第二套应用;域名、TLS、secret、端口、容量、健康检查和恢复继续 fail closed。普通 app-only 更新先验证新的不可变 artifact 再原子切换,切换前 application release 只保证保留到有限 health/postflight 完成;之后没有保留合同,也不作为维护中的备份。只有任务实际改动基础设施、配置、secret、持久或不可逆状态时,才为被触及表面定义恢复。它不提供托管基础设施,也不把占位配置写成可用服务。 本地自包含一键包在分享者电脑上同时承载 Host、当前 Web/信令应用服务器与本地状态,Viewer 仍使用普通浏览器。它不调用任何外部 Screener、STUN、SFU、隧道或 relay 服务,并复用唯一协议、房间、鉴权和路由实现;公网覆盖取决于本机入口、可信 origin、网关映射和 NAT 条件,direct P2P 可用时保留,受限时明确失败。具体 runtime、进程边界、签名、更新、证书、网关与 NAT 方案等当前功能和真实网络 TODO 完成后再由证据确定,不得先用某个打包器或自动映射技巧反向定义产品。 @@ -349,7 +349,7 @@ Token-free SFU standby prewarm 保持可选且不创建 participant、publicatio - 同机双标签页与同 LAN direct; - 真实跨网络、同房间 direct/peer UDP + SFU/UDP root; -- CGNAT、双 NAT、移动热点和普通家宽下按 accepted route model 验证 direct/peer、SFU/UDP、恢复和 bounded failure;记录 RSS/CPU/latency 与 RX/TX/loss。人为封锁全部 UDP 时仍必须有界明确失败。旧 production release 留在独立实例作为回滚,不在同进程保留旧 transport; +- CGNAT、双 NAT、移动热点和普通家宽下按 accepted route model 验证 direct/peer、SFU/UDP、恢复和 bounded failure;记录 RSS/CPU/latency 与 RX/TX/loss。人为封锁全部 UDP 时仍必须有界明确失败。涉及路由或基础设施变更的候选保留独立实例和被触及表面的恢复边界,不在同进程保留旧 transport; - Windows Chrome/Edge 的 tab/window/monitor 音轨有/无、同时语音通话和 video-only;一个可区分左右声道的 fixture 只验证 Web P2P 与 SFU 的 channel correctness、协商 fmtp 和 route switch 后持久 `MediaStream`,不做听感/性能标定;Windows 11 原生候选另测 game process tree、通知、无渲染流与进程重启,Windows 10 记录明确不支持/未解决; - Android Chrome 与 iOS Safari 的播放手势、旋转、前后台和网络切换; - 一名分享者、20 名观看者先运行 bounded smoke,记录路由深度、queue wait、candidate start、first decoded frame、final route、sender 数、应用 CPU/内存和 SFU 用量,并报告 current-child sample/pending count、原始排序时长与 nearest-rank p50/p95/max;端点 `C=1/2/3` 分开解释。未接受产品体验上限前不把观测值变成新的 magic timeout。30 分钟异构公网 endurance 仍是后续质量证据,不阻止已经有界失败的 20-Viewer admission。 @@ -359,7 +359,7 @@ Token-free SFU standby prewarm 保持可选且不创建 participant、publicatio ## 当前交付边界 -- Current source 精确 `f5a295c` 使用严格单一 `screener-v11` wire,Browser 固定 VP8 且 video 不设置 `contentHint`;codec selector、quality-state/wire 字段与分享生命周期 codec 分支不存在。Production 精确 `2726edde9b87f31fd76e749de47972ef817a9bd5`、release `2726edd`,仍使用 `screener-v10`、设置 video `motion` 并保留 pre-share codec selector;`screener-v11` 尚未部署。 +- Production 运行精确 deployed application/runtime revision `679fe3e7af634309322bea83b316641f51ad3d09`、release `679fe3e`;canonical `main` 包含同一套 v11 runtime code。Current source 与 production 使用严格单一 `screener-v11` wire;Browser 固定 VP8 且 video 不设置 `contentHint`,codec selector、quality-state/wire 字段与分享生命周期 codec 分支不存在。 - Pause/Resume 由当前 Host session 和 `shareGeneration` 约束。Host 后台性能和 VP8 hardware acceleration 分别按受控根因证据诊断;标准 ICE/SFU 按真实异构网络验收。 - 当前发布面只包含 Web Host、Viewer 和 browser relay。Native sender、capture helper、shared-encode executable 与两个分发包仍由各自后续决策和物理证据驱动,不属于当前 Browser release。 diff --git a/docs/需求理解.md b/docs/需求理解.md index 5033dd1..62fa1d8 100644 --- a/docs/需求理解.md +++ b/docs/需求理解.md @@ -40,7 +40,7 @@ SFU 的 publication、subscription、server-resource accounting、临时 overlap - 本地 Host 偏好:房间所有权继续使用随机 Host token,不使用 IP、UA、硬件信息或浏览器指纹。Host 浏览器在同源 `localStorage` 保存 raw Host token,以及昵称、code-only policy 和可选房间密码组成的创建偏好;服务器只在当前进程内保存 token 摘要和密码 verifier。旧房间不存在时,下一次显式分享创建新房间并原子应用本地偏好,同时生成新的四位房间号、Host token 和 Viewer grant。换设备、换浏览器或清理站点数据即不恢复。 ### P1 - 重要功能 -- 公网服务器一键部署包:在用户自有的公网服务器上一键部署当前 Web/信令应用、易失房间状态和所需 STUN/SFU/反向代理组件,复用唯一配置、secret、路由、健康检查和回滚合同。它不是托管服务,不能内置真实凭据或跳过域名、TLS、防火墙、端口、容量和回滚预检。 +- 公网服务器一键部署包:在用户自有的公网服务器上一键部署当前 Web/信令应用、易失房间状态和所需 STUN/SFU/反向代理组件,复用唯一配置、secret、路由、健康检查和恢复合同。普通 app-only 更新先验证新的不可变 artifact 再原子切换,切换前 application release 只保证保留到有限 health/postflight 完成;之后没有保留合同,也不作为维护中的备份。只有实际改动基础设施、配置、secret、持久或不可逆状态时,才为被触及表面定义恢复并预检。它不是托管服务,不能内置真实凭据或跳过域名、TLS、防火墙、端口、容量和适用的恢复预检。 - 本地自包含一键包:提供 Windows、macOS 和 Linux 可分发的桌面包,在分享者电脑上同时运行 Host、当前 Web/信令应用服务器和本地状态,朋友继续通过普通浏览器邀请观看。安装后启动不要求源码 checkout、Node 或任何外部 Screener、STUN、SFU、隧道或 relay 服务;它尽力使用本机可达入口和 direct P2P,但必须明确报告公网入口、可信 HTTPS/WSS、网关映射或 NAT 路径缺失,不能承诺任意 CGNAT/严格防火墙必通。两种分发包都排在当前功能、路由、NAT 和真实网络 TODO 完成之后。 - 独立房间入口:合法 `/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 行为。 @@ -177,7 +177,7 @@ SFU 的 publication、subscription、server-resource accounting、临时 overlap ### 仓库可移植性 - Given 在新的 Windows、macOS 或 Linux 工作副本中克隆仓库,When 打开项目,Then 需求、调研、项目记忆、`AGENTS.md` 与 `.codex/` 均存在且处于 Git 跟踪状态,不依赖原开发机器的绝对路径或密钥。 -- Given 用户在自有公网服务器运行一键部署包,When 域名、TLS、端口、secret、容量、存储和回滚预检通过,Then 同一版本的应用、STUN/SFU 与反向代理可重复启动并输出精确健康结果;任一必需条件失败时不得显示 ready、写入占位凭据或留下半配置服务。 +- Given 用户在自有公网服务器运行一键部署包,When 域名、TLS、端口、secret、容量、存储和适用的恢复预检通过,Then 同一版本的应用、STUN/SFU 与反向代理可重复启动并输出精确健康结果;普通 app-only 更新必须先验证新的不可变 application artifact,再原子切换,切换前 release 只保证保留到有限 health/postflight 完成且之后没有保留合同。实际触及基础设施、配置、secret、持久或不可逆状态的任务必须先为这些表面建立恢复。任一必需条件失败时不得显示 ready、写入占位凭据或留下半配置服务。 - Given 用户安装本地自包含一键包,When 在 Windows、macOS 或 Linux 启动分享,Then 包内 Host、当前应用服务器和受限本地状态使用同一协议与配置合同启动,用户无需源码、Node 或任何外部服务,Viewer 邀请仍由普通浏览器打开。Given 公网 HTTPS/WSS、信令入口、媒体 UDP、网关映射或 NAT 路径任一不可用,Then 预检必须如实显示受限或不可达,不得把 loopback/LAN 成功写成公网成功;可工作的 direct P2P 保留,失败进入明确终态。 ## 决策与证据归属