diff --git a/docs/adr/0002-memory-resident-protected-rooms.md b/docs/adr/0002-memory-resident-protected-rooms.md index 168ba5a..a2bb890 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: Optional Durable Room Authority And Scoped Viewer Access -- Status: Accepted; implementation pending +- Status: Accepted; implemented; stable mode deployed - Date: 2026-08-26 ## Context diff --git a/docs/adr/0007-path-isolated-representation-quality.md b/docs/adr/0007-path-isolated-representation-quality.md index d375cb2..b92698b 100644 --- a/docs/adr/0007-path-isolated-representation-quality.md +++ b/docs/adr/0007-path-isolated-representation-quality.md @@ -1,6 +1,6 @@ # ADR-0007: Framework-Owned Media Quality Adaptation -- Status: Accepted; adaptive gate deployed, local selector implementation pending +- Status: Accepted; adaptive gate and local selector deployed - Date: 2026-08-19 - Last reviewed: 2026-08-26 diff --git a/docs/deployment.md b/docs/deployment.md index 1fd322f..ca6c34b 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,9 +1,9 @@ # Minimal Deployment -Last verified against upstream documentation: 2026-08-24. +Last verified: 2026-08-26. This page records production running exact deployed application/runtime revision -`55511773b2f9118802440fdec90ad06080b19772`, release `5551177`, and the single +`3037c0a8f0d16e0df791eb8e64003a7a6660d521`, release `3037c0a`, and the single Browser `screener-v12` contract. Product direction and pending work are owned by [project memory](./project-memory.md) and [the TODO ledger](./todo.md). @@ -25,32 +25,31 @@ 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 `55511773b2f9118802440fdec90ad06080b19772`, release -`5551177`, from `/opt/screener/releases/5551177`. The immutable runtime tar +Production runs exact `3037c0a8f0d16e0df791eb8e64003a7a6660d521`, release +`3037c0a`, from `/opt/screener/releases/3037c0a`. The immutable runtime tar SHA-256 is -`3688b348fd8757e19fc1acb5523d2ecc90aa3553a30cd3a640e0a9a011e696e3`. -Its 39-file path/size/hash manifest SHA-256 is -`279fb808ccff21c0fa07d0ff7f7afddc1d33de09446ca8446489e4970ee6d8d5`. +`f84cb7f7acbfbd950ecfdac13da9a0d6e99a4c10eb221c6948ce81b47ce2b5f6`. +Its 41-file path/size/hash manifest SHA-256 is +`e2fc8115c9cb7d6455027dc2af6f29229834774ed12de334832a8d53d6fc6262`. Local and public `/healthz` return 200; Screener, LiveKit, coturn, and nginx are active with `NRestarts=0`. The public main Browser asset is -`assets/index-uQk7n3NE.js` with SHA-256 -`14012100209475b3fc8851774a1985b3fb17de18a1f80e6fb0c0cf773dcfe0ff`. +`assets/index-Y4pFjW_S.js` with SHA-256 +`a3df27613dfb10fb45297197afdf2500e2953c2b5330b13e5bb49eafdc3e1447`. The release deploys the single Browser `screener-v12` wire and one internal, content-independent H.264 sender gate with VP8 fallback across Browser direct, browser-relay, and SFU video. It retains pinned LiveKit default screen-share representations, Dynacast and server send-side BWE without an application layer -selector, video `contentHint = "motion"`, no -codec selector/state/wire, random four-digit memory rooms with a 24-hour dormant lease, -a room-lived 22-character grant, +selector, video `contentHint = "motion"`, and a local share-locked +`VP8 | Auto | H264` Host selector without codec wire state. Random four-digit +rooms use a 24-hour dormant lease, one room-lived 22-character grant, orthogonal `open | private` grant/code admission, 20-Viewer room admission, the uniform endpoint media-copy cap `2`, and the -one-controller exact-candidate route runtime. Exact predecessor release -`b68c471` production postflight verified open code entry, private invitation-only -and password entry, grant rotation/revocation, typed missing-room failure, and -stale `screener-v11` rejection with -`INVALID_MESSAGE` and WebSocket close 1008 before room authority. The service unit -has no writable room StateDirectory; all room authority is process-memory-only. +one-controller exact-candidate route runtime. Production configures +`ROOM_DATABASE_PATH=/var/lib/screener/rooms.sqlite`; systemd owns that single +`0700` StateDirectory and the database is `0600`. Only room authority survives +an application restart; participants, routes and media rebuild from fresh +process state. LiveKit is dedicated, has `room.auto_create: false` and `max_participants: 21`, and the application derives deployment admission as 9,000 publication ingress plus `9000 * MAX_VIEWERS_PER_ROOM` subscription @@ -82,9 +81,9 @@ browser <---------- DTLS-SRTP/UDP ----------> LiveKit :7882 The minimum runtime is Node.js 24 LTS and coturn 4.17.2 or a newer patched release. Provision a valid TLS certificate for the Web name. Enable operating system time synchronization and keep the Node application port reachable only from its -reverse proxy. A single Node process is intentional. Rooms, participants, and -signaling remain in process memory; a restart intentionally invalidates every -room and does not restore live WebRTC connections. +reverse proxy. A single Node process is intentional. Participants, signaling, +routes and media remain in process memory; optional SQLite preserves only room +authority and does not restore live WebRTC connections. Build and validate the exact revision on a build host before starting it: @@ -240,6 +239,7 @@ PUBLIC_BASE_URL=https://share.example.com ALLOWED_ORIGINS=https://share.example.com SITE_ACCESS_PASSWORD= ROOM_LEASE_SECONDS=86400 +ROOM_DATABASE_PATH=/var/lib/screener/rooms.sqlite MAX_VIEWERS_PER_ROOM=20 ENDPOINT_MEDIA_COPY_CAPACITY=2 @@ -296,7 +296,7 @@ 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 `5551177` runs the deployed server and Browser assets +Production release `3037c0a` runs the deployed server and Browser assets atomically on `screener-v12`; every stale Browser or executable-sender wire fails before room authority. Native senders and helpers are outside this release. @@ -391,8 +391,9 @@ optional room passwords. In stable mode those room-authority values, policy, authorization generation and lease use one exact SQLite schema; live members, routes and SFU state remain memory-only. The same browser keeps its raw Host token, creation profile and non-expiring preferred code locally. Production stable-mode -activation requires a separately verified writable directory, configuration and -database backup/restore boundary before setting `ROOM_DATABASE_PATH`. +uses the service-owned `/var/lib/screener` StateDirectory. Any new activation or +path change requires a separately verified writable directory plus configuration +and database backup/restore boundaries before changing `ROOM_DATABASE_PATH`. Production startup requires one to eight syntactically valid `stun:` URLs before the server listens. This validates shape only; it does not prove DNS, diff --git a/docs/project-memory.md b/docs/project-memory.md index 31655e8..53bf2dd 100644 --- a/docs/project-memory.md +++ b/docs/project-memory.md @@ -14,7 +14,7 @@ Last updated: 2026-08-26 and Viewer activity never renews. `ROOM_DATABASE_PATH` absent is the default lightweight process-memory mode; configured is the optional SQLite stable mode that persists only room authority across application restart. Production - will enable stable mode after its state/recovery gate passes. + enables stable mode; live participants, routes and media remain process-only. - Every room has one 128-bit/22-character Viewer grant bound to that exact room incarnation and independent `open | private` code entry. Private without a password is invitation-only; adding a password also permits matching code @@ -75,10 +75,9 @@ Last updated: 2026-08-26 with a deterministic moving probe track sized to the current share target, so captured-content motion cannot decide encoder capability. Proved senders prefer H.264 with native VP8 fallback, while failed/inconclusive senders remain - VP8-only. The accepted next Host UI adds a local pre-share - `VP8 | Auto | H264` selector with Auto default; it locks while sharing, is not - persisted, and creates no codec wire/state or active-edge switching. Viewer - relays remain Auto. Current production does not yet include that selector. + VP8-only. The Host UI provides a local pre-share `VP8 | Auto | H264` selector + with Auto default; it locks while sharing, is not persisted, and creates no + codec wire/state or active-edge switching. Viewer relays remain Auto. Display video uses `contentHint = "motion"` and display audio uses `contentHint = "music"`. - Windows Chrome 151 uses software VP8 and exposes no Web control for selecting @@ -121,11 +120,11 @@ Last updated: 2026-08-26 ## Current Source And Production -- Production and canonical source use strict `screener-v12`, but the pending - application candidate adds the internal H.264/VP8 decision while production - remains on its prior fixed-VP8 artifact. Exact identities are indexed by +- Production and canonical source use the same strict `screener-v12` + application with adaptive H.264/VP8 selection, local Host codec override and + optional SQLite room authority. Exact identities are indexed by [status](./status.md). -- Production uses random memory rooms, 20-Viewer admission, endpoint cap `2`, +- Production uses SQLite-backed random rooms, 20-Viewer admission, endpoint cap `2`, fixed 9,000-publication ingress and `9000 * 20` subscription egress admission, STUN UDP 3478, LiveKit media UDP 7882, and Web TCP 80/443. Node 8787 and LiveKit 7880 remain private. @@ -135,11 +134,7 @@ Last updated: 2026-08-26 ## Current Priority -1. Implement optional SQLite stable room authority, remove the client preferred- - room expiry/timer, and add exact Host room replacement. -2. Add the locked pre-share Host codec selector and resolved Host codec display - without codec wire state. -3. Complete the mobile Viewer lifecycle matrix and representative real-network +1. Complete the mobile Viewer lifecycle matrix and representative real-network route/media acceptance. Quality-driven parent selection, Native sender work, distribution packages, diff --git a/docs/research/browser-background-capture.md b/docs/research/browser-background-capture.md index fe76fdb..2572bf0 100644 --- a/docs/research/browser-background-capture.md +++ b/docs/research/browser-background-capture.md @@ -46,7 +46,7 @@ does not say that minimizing the separate capturing page must reduce capture. Capture constraints are post-selection preferences and allow frame decimation, so requested `maxFramerate` is not proof of actual source or encoded frame rate. -Production candidate `5551177` may select H.264 or VP8 through its bounded sender +Current production may select H.264 or VP8 through its bounded sender gate. Every run still records the actual negotiated codec and encoder rather than inferring either from configuration. Encoder implementation is a diagnostic variable only after a stable baseline exists; neither codec nor encoder choice @@ -67,10 +67,10 @@ not inherit this desktop Host result. ## Current Source Boundary -Canonical source remains fixed VP8 while production candidate `5551177` uses the -content-independent H.264 gate with VP8 fallback. Both use video -`contentHint = "motion"`, audio `contentHint = "music"`, and no codec selector or -wire field; their exact identity is owned by [status](../status.md). +Canonical source and production use the content-independent H.264 gate with VP8 +fallback, video `contentHint = "motion"`, audio `contentHint = "music"`, and the +local share-locked Host codec selector without a codec wire field. Their exact +identity is owned by [status](../status.md). The current path does not claim or implement a page-keepalive mechanism, and deployment health supplies no physical background-capture evidence. diff --git a/docs/research/cross-restart-room-recovery.md b/docs/research/cross-restart-room-recovery.md index 83a6432..031e0bd 100644 --- a/docs/research/cross-restart-room-recovery.md +++ b/docs/research/cross-restart-room-recovery.md @@ -1,7 +1,7 @@ # Cross-Restart Room Recovery - Research date: 2026-08-26 -- Status: SQLite stable room-authority mode accepted; implementation pending +- Status: SQLite stable room-authority mode implemented; production enabled ## Current Conclusion @@ -12,8 +12,8 @@ code-entry policy, password verifier and dormant lease. It is not a generic store for future features. `ROOM_DATABASE_PATH` absent remains the default lightweight mode. Configuring an -exact file enables stable mode over the same RoomStore contract. Production will -use stable mode after persistent-state deployment and recovery acceptance. +exact file enables stable mode over the same RoomStore contract. Production uses +stable mode after passing its persistent-state and restart-recovery gate. ## Stored Authority diff --git a/docs/research/realtime-quality-adaptation.md b/docs/research/realtime-quality-adaptation.md index 02c4d64..2fbefde 100644 --- a/docs/research/realtime-quality-adaptation.md +++ b/docs/research/realtime-quality-adaptation.md @@ -7,9 +7,9 @@ ## Current Conclusion -Production `5551177` and canonical source use a content-independent H.264 sender -gate with VP8 fallback. A local pre-share Host `VP8 | Auto | H264` selector is -accepted but not yet implemented; Viewer relays remain automatic. +Current production and canonical source use a content-independent H.264 sender +gate with VP8 fallback. The local pre-share Host `VP8 | Auto | H264` selector is +implemented and Viewer relays remain automatic. Display video uses `contentHint = "motion"`, and display audio uses `contentHint = "music"`. The three recommended profiles and advanced settings are ceilings, not delivery guarantees. WebRTC owns direct and peer congestion diff --git a/docs/session-handoff.md b/docs/session-handoff.md index 25ca553..d81fbf8 100644 --- a/docs/session-handoff.md +++ b/docs/session-handoff.md @@ -17,10 +17,11 @@ staged diff, and worktrees. Canonical main and repo truth override old branches, worktrees, handoffs, agent memory, and chat summaries. Current production and main identity is owned by docs/status.md. ADR-0002 owns -memory rooms; ADR-0005 owns automatic direct/peer-to-LiveKit routing; ADR-0007 +lightweight/stable room authority; ADR-0005 owns automatic direct/peer-to-LiveKit routing; ADR-0007 owns framework media adaptation. Current routes are sticky and quality evidence -is diagnostic only. Browser video is VP8 with contentHint=motion; LiveKit owns -SFU representations; Screener configures no TURN or media TCP. +is diagnostic only. Browser video uses the share-scoped H.264/VP8 decision with +contentHint=motion; LiveKit owns SFU representations; Screener configures no TURN +or media TCP. Follow docs/todo.md in order. Do not run Native/executable work, broad repository cleanup, quality-driven reparenting, or another transport unless its decision diff --git a/docs/status.md b/docs/status.md index 53862bb..8125834 100644 --- a/docs/status.md +++ b/docs/status.md @@ -8,28 +8,31 @@ This is the current execution index. Git history owns completed timelines; ## Production - `https://share.bonfire.icu` runs exact application/runtime revision - `55511773b2f9118802440fdec90ad06080b19772`, release `5551177`, wire - `screener-v12`, from `/opt/screener/releases/5551177`. The immutable runtime + `3037c0a8f0d16e0df791eb8e64003a7a6660d521`, release `3037c0a`, wire + `screener-v12`, from `/opt/screener/releases/3037c0a`. The immutable runtime tar SHA-256 is - `3688b348fd8757e19fc1acb5523d2ecc90aa3553a30cd3a640e0a9a011e696e3`; - its 39-file manifest SHA-256 is - `279fb808ccff21c0fa07d0ff7f7afddc1d33de09446ca8446489e4970ee6d8d5`. -- The served Browser entry references `assets/index-uQk7n3NE.js`; the public - asset is 500228 bytes with SHA-256 - `14012100209475b3fc8851774a1985b3fb17de18a1f80e6fb0c0cf773dcfe0ff`. + `f84cb7f7acbfbd950ecfdac13da9a0d6e99a4c10eb221c6948ce81b47ce2b5f6`; + its 41-file manifest SHA-256 is + `e2fc8115c9cb7d6455027dc2af6f29229834774ed12de334832a8d53d6fc6262`. +- The served Browser entry references `assets/index-Y4pFjW_S.js`; the public + asset SHA-256 is + `a3df27613dfb10fb45297197afdf2500e2953c2b5330b13e5bb49eafdc3e1447`. Public `/healthz` returns 200. The release postflight found Screener, LiveKit, coturn, and nginx active with zero restarts. -- Production uses process-memory four-digit rooms, a 24-hour dormant lease, - room-lived 22-character grants, `open | private` code entry, one Host plus 20 - Viewers, endpoint copy cap `2`, and no SQLite runtime. +- Production enables SQLite room authority at + `/var/lib/screener/rooms.sqlite`; live participants, routes and media remain + process-only. A controlled restart retained a temporary room's exact Host + authority and normal room deletion removed it afterward. Lightweight mode + remains available when `ROOM_DATABASE_PATH` is unset. - Public media listeners remain STUN-only UDP 3478 and LiveKit UDP 7882. Web ingress is TCP 80/443; Node 8787 and LiveKit control/signaling 7880 are private. TURN, ICE/TCP, media TCP, TLS relay, port 5349, and relay ranges are disabled. - Browser video uses the content-independent H.264 sender gate with VP8 - fallback and retains `contentHint = "motion"`; audio uses `music`. The SFU - publisher uses the Host's one codec decision, leaves representation - construction to pinned LiveKit, + fallback, plus the locked pre-share `VP8 | Auto | H264` Host selector and + resolved codec display. Video retains `contentHint = "motion"`; audio uses + `music`. The SFU publisher uses the Host's one codec decision, leaves + representation construction to pinned LiveKit, enables Dynacast, uses server send-side BWE, keeps AdaptiveStream disabled, and configures no external ICE servers on Browser SFU PCs. - Screen audio provides live 64/128/192 kbps ceilings with 128 default. SFU @@ -38,9 +41,10 @@ This is the current execution index. Git history owns completed timelines; ## Current Source -- Canonical `main` integrated the production-tested adaptive H.264 application at - merge `636e0ac`. Production remains exact runtime `5551177`; their application - trees match while production room storage remains process memory. +- Canonical `main` and production both run exact `3037c0a`. Optional SQLite + stable authority, non-expiring local preferred code, atomic room replacement, + graceful service-restart presentation and the Host codec selector are + implemented; production selects stable storage. - One event-driven controller owns the committed graph and one room-serial child operation. Initial direct acquisition uses a five-second foreground window; exact transport-connected progress may retain that candidate through the @@ -58,14 +62,8 @@ This is the current execution index. Git history owns completed timelines; ## Current Milestone -1. Implement and verify optional SQLite stable room authority; lightweight mode - remains default in config, while production activation follows as a separate - persistent-state deployment with recovery. -2. Remove the client preferred-room expiry/renewal timer, add atomic room - replacement, and add the locked pre-share `VP8 | Auto | H264` Host selector - with resolved codec display. -3. Finish the Android Chrome and iOS Safari Viewer lifecycle matrix. -4. Finish representative public-network direct, peer-relay, SFU, recovery, +1. Finish the Android Chrome and iOS Safari Viewer lifecycle matrix. +2. Finish representative public-network direct, peer-relay, SFU, recovery, Pause/Resume, screen-audio, real-game A/V, and all-UDP-blocked acceptance without adding another transport or quality controller. diff --git a/docs/todo.md b/docs/todo.md index 2e6af3c..af0bdce 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -7,26 +7,12 @@ later topic is not implementation authority by itself. ## Now -1. **Stable room authority and room controls.** Add optional - `ROOM_DATABASE_PATH` SQLite stable mode over the existing RoomStore aggregate; - production will enable it through a separate persistent-state cutover after - restart/recovery acceptance. Keep lightweight mode default when unset. Remove - the client preferred-code expiry and renewal timer. Add the Host refresh action - immediately before Copy: atomically allocate a different room, retire all old - authority/media state, end an active share, and remember the new code. -2. **Host codec selector.** In advanced settings, add the local - `VP8 | Auto | H264` selector with Auto centered/default. It is editable only - before sharing and locked for the whole share generation. Manual modes are - strict, Auto runs the current gate, Viewer relays remain Auto, and no codec - wire/cache/parallel publication or active-edge switching is added. Two-line - subtitles are `兼容优先 | 自动选择/实际 codec | 硬件优先`; connection details - show the resolved Host codec and each edge's actual negotiated codec. -3. **Mobile Viewer lifecycle.** Run Android Chrome and iOS Safari matrices for +1. **Mobile Viewer lifecycle.** Run Android Chrome and iOS Safari matrices for autoplay gesture, foreground/background audio, foreground video recovery, lock/page reclamation, rotation, Wi-Fi/cellular migration, and assigned-relay survival or controller recovery. Web does not promise background video or relay execution after OS suspension. -4. **Finish real-network route and media acceptance.** Exercise direct peer, +2. **Finish real-network route and media acceptance.** Exercise direct peer, Browser relay, SFU, relay-ingress recovery with subtree retention, disconnect/capacity drain, Pause/Resume, source replacement, screen-audio continuity, and real-game A/V sync across representative IPv4/IPv6, diff --git a/docs/verification-status.md b/docs/verification-status.md index 64e814a..0aa188a 100644 --- a/docs/verification-status.md +++ b/docs/verification-status.md @@ -8,24 +8,26 @@ may be interpreted. Git and pull requests own routine completed checks. ## Current Production Identity - Production runs exact application/runtime revision - `55511773b2f9118802440fdec90ad06080b19772`, release `5551177`, wire - `screener-v12`, from `/opt/screener/releases/5551177`. + `3037c0a8f0d16e0df791eb8e64003a7a6660d521`, release `3037c0a`, wire + `screener-v12`, from `/opt/screener/releases/3037c0a`. - Runtime tar SHA-256: - `3688b348fd8757e19fc1acb5523d2ecc90aa3553a30cd3a640e0a9a011e696e3`. - The 39-file manifest SHA-256 is - `279fb808ccff21c0fa07d0ff7f7afddc1d33de09446ca8446489e4970ee6d8d5`. -- Public Browser asset `assets/index-uQk7n3NE.js` is 500228 bytes with SHA-256 - `14012100209475b3fc8851774a1985b3fb17de18a1f80e6fb0c0cf773dcfe0ff`. + `f84cb7f7acbfbd950ecfdac13da9a0d6e99a4c10eb221c6948ce81b47ce2b5f6`. + The 41-file manifest SHA-256 is + `e2fc8115c9cb7d6455027dc2af6f29229834774ed12de334832a8d53d6fc6262`. +- Public Browser asset `assets/index-Y4pFjW_S.js` has SHA-256 + `a3df27613dfb10fb45297197afdf2500e2953c2b5330b13e5bb49eafdc3e1447`. Public `/healthz` returns 200. Release postflight found Screener, LiveKit, coturn, and nginx active with zero restarts. ## Completed Product Evidence - Strict `screener-v12` rejects stale Browser/executable wires before room - authority. Process-memory rooms, 24-hour dormant leases, restart loss, + authority. Lightweight and SQLite-stable rooms, 24-hour dormant leases, room-lived Viewer grants, `open | private` code entry, invitation-only private - rooms, password entry, grant rotate/revoke, and `ROOM_NOT_FOUND` have passed - source and production gates. + rooms, password entry, grant rotate/revoke, atomic room replacement and + `ROOM_NOT_FOUND` have passed source gates. Production stable mode retained an + exact Host authority across a controlled restart and then deleted the test + room through the normal lifecycle. - Endpoint capacity `1/2/3`, default `2`, one active upstream, acyclicity, source reachability, deterministic parent order, exact candidate identity, transport-connected wake extension, first-decoded-frame commit, strictly newer @@ -55,16 +57,16 @@ may be interpreted. Git and pull requests own routine completed checks. ordinary peers retain deployment STUN. The reported Host/Viewer TUN conflict was closed by that isolation without adding TURN or TCP media. -## Current Source And Candidate Evidence +## Current Source Evidence -- Canonical application tree `b6a8a5f` uses Browser VP8 with - `contentHint = "motion"`, reapplies the +- Canonical and production application tree `3037c0a` uses the adaptive + H.264/VP8 sender decision with `contentHint = "motion"`, reapplies the selected video profile after answer negotiation, leaves SFU representation construction to pinned LiveKit, keeps Dynacast/send-side BWE, disables AdaptiveStream, and disables SFU audio RED. -- Production candidate `5551177` adds the content-independent H.264 sender gate, - VP8 fallback and actual negotiated codec details without codec wire state, - cache, parallel media or active-edge switching. +- The local `VP8 | Auto | H264` Host selector is locked to a share generation; + Viewer relays remain Auto and no codec wire state, cache, parallel media or + active-edge switching exists. - Viewer page-resume decoded-stall rebaselining, current-frame presentation authority, hidden-page stall suppression, retryable failure reporting, native controls, and same-route manual reconnect are current source behavior. Mobile