fix: finish native client lifecycle and capture aspect

Accepted native Client lifecycle and Windows capture aspect phase. All Web/Server and three-platform Client checks passed.
This commit is contained in:
AAA authored and GitHub committed 2026-09-05 22:41:46 +08:00
1 parent f95c420717
commit 9579a91168
181 files changed
+19370 -4239

No files matched your search

+2
View File
@@ -47,6 +47,8 @@ ordinary implementation and UI detail.
audio boundary.
- [ADR-0009](./adr/0009-optional-nat-prediction.md): optional connection-local
NAT prediction.
- [ADR-0011](./adr/0011-browser-assisted-native-fanout.md): Browser capture with
Client encoded fanout.
- [ADR-0010](./adr/0010-cross-platform-client-runtime.md): cross-platform Client
runtime and self-contained package boundary.
@@ -89,8 +89,16 @@ root. A fresh healthy candidate sender proof is required before make-before-brea
commit. Success moves one branch toward even fanout; failure consumes that one-shot intent. No timer,
continuous rebalance or general load score is introduced.
A failed Peer tuple is consumed for its exact parent and endpoint sessions plus
endpoint-transition strength. SFU create, reuse, and replace are resource
A Peer opportunity is fenced by its exact parent and endpoint sessions plus
endpoint-transition strength. With NAT traversal disabled, one failed attempt
consumes it. With traversal enabled, availability acquisition and direct
continuation may create at most three candidate connections for that opportunity.
Only an admitted candidate creation spends an attempt; duplicate failure and
timeout messages cannot spend it again. A preparation rejection that cannot
create a connection remains terminal. A new session or strictly stronger
transition reopens the opportunity; worse transitions use only its remaining
budget. Quality and root-convergence experiments retain their existing one-shot
consumption rules. SFU create, reuse, and replace are resource
actions for one logical SFU opportunity keyed by the child and Host sessions;
the publication generation remains a physical fence, not retry authority.
Controller-owned publication creation, replacement, or teardown cannot make the
@@ -140,6 +148,18 @@ Peer candidates converge behind working media, one at a time and round-robin
across SFU Viewers. New join or repair work preempts this background convergence.
Exhausting direct candidates simply keeps the working SFU route.
When NAT acquisition exhausts one operation but retains candidate budget, the
same serial scheduler starts the next operation after rollback and resource
release. Each operation retains its full original deadline; neither a failed
candidate nor a retry extends a running operation. Untried parents precede
repeat attempts, and waiting Viewers with fewer started Peer attempts go first.
The foreground SFU window is unchanged. SFU commitment carries the same
remaining Peer budget into the existing round-robin direct continuations, not
another retry ledger. No usable SFU route is interrupted by these attempts.
This permits longer total acquisition while keeping every operation bounded and
requiring no polling or independent retry timer. The prepare message reports
the actual candidate's connection attempt, not inferred UI progress.
The five-second no-transport-progress window advances a silent Peer only while
another bounded candidate or SFU fallback remains. It is a route scheduling
boundary, not evidence that Browser ICE has reached a terminal state. The final
+52 -25
View File
@@ -1,20 +1,21 @@
# ADR-0009: Optional Connection-Local NAT Prediction
- Status: accepted as an optional deployment capability
- Status: accepted as an optional Site and Client capability
- Date: 2026-09-02
## Context
The self-hosted deployment has two auxiliary STUN-only listeners for controlled
NAT measurements. A disposable namespace lab showed that a sequential,
NAT measurements, while a self-contained Client needs public discovery without
depending on that deployment. A disposable namespace lab showed that a sequential,
endpoint-dependent mapping can sometimes be reached when the remote peer
receives a small set of adjacent server-reflexive candidates. The result is
network- and generation-specific; it is not a reliable NAT type or participant
capability.
The existing deployment configuration, route policy, and ICE candidate owners
are sufficient. The mechanism needs no NAT label, route-controller state, new
media path, or third-party STUN dependency.
The existing ICE configuration, route policy, and candidate owners are
sufficient. Prediction needs no NAT classification or new media path; bounded
connection acquisition reuses the route controller's existing opportunity ledger.
## Decision
@@ -23,35 +24,47 @@ media path, or third-party STUN dependency.
policy. An enabled deployment exposes `NAT traversal`; the pre-share switch
defaults on, remains Host-controllable, locks while sharing, and is not
persisted beyond that share generation.
2. An enabled deployment derives ports 3479 and 3480 from the first ordinary
STUN authority on UDP 3478. When the share switch is on, every Browser P2P
connection adds those same-host endpoints. This covers Host direct, Viewer
upstream, and Viewer relay connections. SFU PeerConnections remain
unchanged. No independent or third-party STUN endpoint is added.
2. An enabled Site derives ports 3479 and 3480 from its first ordinary STUN
authority on UDP 3478. Public-link Client mode instead supplies one ordinary
public STUN destination and two fixed public survey destinations; pure LAN
mode supplies none. The same share switch and ICE configuration cover every
Browser or Native P2P edge. SFU PeerConnections remain unchanged.
3. For one ICE gathering generation, the adapter observes only UDP `srflx`
candidates whose Browser-reported STUN URL belongs to the self-hosted
3478/3479/3480 survey set. If three or more distinct survey candidates for
candidates belonging to the exact three-destination survey set. Browser
candidates are matched by their reported STUN URL. Native Pion uses its
`UniversalUDPMux` to perform the survey on the media socket and emits standard
srflx-shaped observations marked with an `ns` foundation. Only those explicit
survey observations feed prediction; port-mapped candidates remain ordinary
ICE inputs. If three or more distinct survey candidates for
one media section and public address form an arithmetic port sequence, the
adapter appends at most eight bounded candidates outward from both sequence
endpoints: four above the high endpoint and four below the low endpoint.
Every ordinary candidate keeps normal Trickle ICE timing; no ordinary
Every host and observed srflx candidate keeps normal Trickle ICE timing; no ordinary
candidate is delayed, rejected, or replaced. A Browser that does not expose
candidate source URLs keeps ordinary ICE without prediction. Rejection of a
predicted remote candidate discards only that optional candidate; ordinary
candidate errors retain their normal connection error semantics.
4. Candidate observations remain in memory for that connection only. No NAT
label, raw address, port, score, hard candidate skip, periodic probe, or
route-controller branch is introduced. Sanitized diagnostics record only
4. Candidate observations remain in memory for that connection only. They do
not create persistent NAT labels, endpoint addresses, routing scores, hard
candidate skips, or periodic probes. Sanitized diagnostics record only
whether a signaled candidate was ordinary, predicted, or end-of-candidates,
and whether the selected remote foundation was ordinary, predicted, or
unknown.
5. The same per-share gate also enables the three-attempt Peer acquisition
budget in [ADR-0005](0005-automatic-hybrid-media-routing.md). Browser and
Native candidates use the ordinary server-owned prepare/rollback lifecycle;
no Viewer-owned ICE-restart loop is added. Local public-link and Hosted
peer-only rooms can exhaust that budget without SFU, while mixed rooms use
the remaining attempts behind a working SFU route. A new connection can
provide another mapping opportunity, not guaranteed independent randomness:
in particular, Native connections may retain the same shared UDP socket.
## Consequences
Positive:
- deployments that operate the required STUN listeners can offer the bounded
mechanism while smaller deployments retain stock ICE;
- Sites with their own listeners and self-contained public-link Clients can use
the same bounded mechanism while pure Browser/LAN operation stays unchanged;
- normal ICE remains immediately available, so an unavailable auxiliary STUN
listener or unsuccessful prediction cannot delay direct or SFU fallback;
- prediction is bounded to one connection generation and one small candidate
@@ -63,24 +76,38 @@ Negative:
- the Browser may still reject synthetic candidates or the NAT may not have a
matching mapping;
- enabled P2P connections perform two extra self-hosted STUN transactions and
may add connectivity checks for up to eight adjacent UDP ports; and
- enabled P2P connections perform two extra STUN transactions and
may add connectivity checks for up to eight adjacent UDP ports;
- two endpoint-dependent NATs remain sensitive to candidate scheduling and
intervening mappings, so room-wide coverage is not a success guarantee.
intervening mappings, so room-wide coverage is not a success guarantee; and
- enabled acquisition can occupy up to three ordinary operation windows when
one parent remains, yielding between operations to other waiting Viewers.
Predicted connectivity checks originate from participant Browsers and target
the other participant's bounded candidate window. The Screener server only
receives Binding requests on its fixed 3478/3479/3480 listeners; enabling this
capability does not make the cloud host scan external ports.
Predicted connectivity checks originate from participant ICE agents and target
the other participant's bounded candidate window. A Site server only receives
Binding requests on its fixed listeners; it does not scan external ports.
Public survey operators receive STUN metadata but never the media stream.
The controlled lab result (14/15 sequential-to-restricted runs with a bounded
prediction set) is mechanism evidence, not a production success guarantee.
Target-network prevalence, candidate direction, and long-run resource impact
remain acceptance work.
A Windows Native Host preflight first exposed three different temporary local
ports under Pion's ordinary srflx gatherer. Replacing that boundary with Pion's
`UniversalUDPMux` produced an srflx observation whose related port exactly
matched the one media listener. A subsequent public-link run delivered 35 H.264
RTP packets to an independent Linux Pion Viewer over a selected direct
host-to-srflx pair. This proves the shared-socket candidate carries DTLS-SRTP;
the tested TUN network exposed only one distinct public mapping, so it does not
prove a predicted candidate win.
The current Native path maps selected Pion candidate foundations to the same
anonymous `ordinary | predicted | unknown` evidence as Browser ICE.
## Primary Sources
- [RFC 8445: Interactive Connectivity Establishment](https://www.rfc-editor.org/rfc/rfc8445)
- [RFC 4787: UDP NAT behavioral requirements](https://www.rfc-editor.org/rfc/rfc4787)
- [RFC 5780: NAT Behavior Discovery Using STUN](https://www.rfc-editor.org/rfc/rfc5780)
- [W3C WebRTC candidate model](https://www.w3.org/TR/webrtc/)
- [Pion Universal UDP mux](https://github.com/pion/ice/blob/main/udp_mux_universal.go)
+102 -48
View File
@@ -1,7 +1,7 @@
# ADR-0010: Cross-Platform Client Runtime
- Status: accepted Client/Local foundation and Windows native Host media boundary
- Date: 2026-09-04
- Status: accepted capability-provider architecture and native endpoint media boundary
- Date: 2026-09-05
## Context
@@ -14,27 +14,37 @@ Existing Screener room, admission, signaling, and routing behavior already has
one TypeScript implementation. Rewriting that behavior in the Helper would make
Hosted and Local deployments diverge without improving the media path.
Room authority and local media capability are independent choices. A room may
come from Local, a temporary public link, or a configured Site while each
participant independently uses Browser media or available Client capability.
Binding these choices into exclusive Client modes prevents mixed Browser/Native
topologies and makes a saved Site unavailable while another room source runs.
## Decision
1. The TypeScript/Node core remains the sole owner of HTTP, room authority,
admission, signaling, and routing in every deployment. A future local package
runs that same server and the same built Browser assets with local
configuration; Go does not reimplement the product core.
2. The packaged product is Screener Client. Its Go process is the one
cross-platform user entry and future native-media owner.
Its current process exposes `/health` and one `/control` WebSocket on IPv4
loopback within ports `39721` through `39730`.
2. The packaged product is Screener Client. Its Go process is the cross-platform
entry and native capability provider. It starts `/health` and one `/control`
WebSocket on IPv4 loopback within ports `39721` through `39730` before room-
source selection and retains them until the process exits.
3. Discovery returns a per-process `instanceToken`. The WebSocket subprotocol
echoes it so the Browser connects to the process it discovered. This value is
public process identity, not authentication. Origin and Host validation plus
the Browser's local-network permission own the current Browser boundary.
4. Loopback v5 starts with a strict `hello` handshake. Health discovery reports
only separately probed native capture booleans. An active control session may
public process identity, not authentication. The listener accepts the current
Local Host origin and the one user-saved Site origin. Origin and Host
validation plus the Browser's local-network permission own this boundary.
4. Loopback v8 starts with a strict `hello` handshake. Health discovery reports
only separately probed native capture booleans. Native Viewer receive/NAT
remains available when capture or hardware encode is absent. An active control session may
list local screen/window choices, request bounded previews, and own one share's generation-fenced SDP/ICE
edges, including at most one loopback media bridge outside route-copy
capacity; it carries no room password, Host token, Viewer grant, or route
policy. The Browser forwards current Site signaling and remains the
participant.
policy. An activated participant tab opens this connection lazily on its first
native action, reuses it across successive media generations, and closes it
with the page; a picker, share, or room source does not own the socket. The
Browser forwards current signaling and remains the participant.
5. A platform package contains the Go entry, a pinned Node runtime, and the same
server/client build used by Hosted Screener. It may also carry one process-
isolated capture binary and the pinned `cloudflared` sidecar. The Go entry
@@ -42,7 +52,11 @@ Hosted and Local deployments diverge without improving the media path.
metadata around that entry, not another long-running wrapper or UI.
6. The system Browser remains the UI. Browser extensions, userscripts, Electron,
Tauri, and resident services need new evidence before they can replace this
smaller boundary.
smaller boundary. A Client-opened Site stores a non-secret opt-in at that
exact Browser origin, so later manually opened pages may discover the running
Client. Pages without that opt-in do not probe localhost or request local-
network permission. Clearing Site data simply requires opening it from the
Client again.
7. A self-contained Local deployment serves reachable LAN peers without a
central Screener service. Its explicit `--link` mode starts one accountless
Cloudflare Quick Tunnel for the same Node HTTP/WebSocket surface, injects the
@@ -52,13 +66,24 @@ Hosted and Local deployments diverge without improving the media path.
Browser. Cloudflare terminates this temporary control path; WebRTC media stays
P2P and uses public STUN. The link ends with the Client and is not a persistent
Site, SFU, or TURN fallback.
8. Native media is selected for an entire Host share generation. One isolated
8. Browser and Native are local media adapters beneath the same authenticated
participant, connection identity, copy capacity, and committed route graph.
Browser-to-Browser, Native-to-Browser, and Native-to-Native edges use the same
WebRTC signaling contract. Native media currently covers the Host adapter;
Viewer receive/relay follows the same boundary rather than adding a second
participant or route protocol. A Native Viewer accepts only a negotiated
H.264/VP8 and Opus offer, forwards encoded RTP into bounded local sources, and uses
the existing Browser bridge for playback; compatible child edges reuse those
sources. Unsupported media or a failed native bridge falls back to the
existing Browser peer. One isolated
platform capture feeds one encoded source and bounded independent Pion
transports, with process-loopback audio for windows or system-loopback audio
for screens sharing the same PeerConnection when available. Each Site or one-link share makes one bounded, best-effort PCP,
UPnP, or NAT-PMP mapping for that same Pion UDP socket before its first edge
gathers ICE; pure LAN Local mode does not. Absence or rejection leaves
ordinary ICE/STUN unchanged. Native P2P quality uses Pion's transport-wide
gathers ICE; pure LAN Local mode does not. Once ordinary STUN observes a
public address, Native advertises the mapped port as one lower-priority,
srflx-shaped candidate on that address. Absence or rejection leaves ordinary
ICE/STUN unchanged. Native P2P quality uses Pion's transport-wide
GCC estimate only after feedback and source-frame progress, comparing its
target payload bitrate with the measured encoded payload supplied to that
edge. The existing route evidence windows own persistence; no custom score,
@@ -69,24 +94,44 @@ Hosted and Local deployments diverge without improving the media path.
implement LiveKit or another representation policy. The Client discovers
packaged capture capability at startup. Its Host page offers the Browser's
standard picker and each exact Client-owned screen/window; the user must select one
and the Client never guesses a target. Only a consumed Client-launch marker
travels in the URL fragment. Exact target identity travels over loopback.
9. The Client uses the system Browser as its only UI. A lightweight loopback
launcher selects Local, one-link, or a saved Site before starting that
composition, then navigates into the same application. Command-line mode
and the Client never guesses a target. The same room quality settings select
native capture size, frame rate, video/audio bitrate, and the platform
encoder's quality-versus-speed hint. A live quality or source change prepares
a replacement capture/encoder generation and swaps it behind the existing
Pion source; room, route, and PeerConnections do not change. Windows uses
Graphics Capture, Media Foundation, and WASAPI; macOS uses ScreenCaptureKit,
VideoToolbox, and AudioToolbox; Linux uses the ScreenCast Portal, PipeWire,
and an installed GStreamer hardware-H.264 element. These adapters end at the
same bounded encoded-frame protocol and do not own WebRTC or product state.
The shared Native encode is the normal path. For a persistently degraded
Native sender edge, the existing quality operation may prepare an overlapping
stock Browser sender from the stable local bridge; existing evidence alone
decides commit or rollback, and a later operation may return to Native. No
extra threshold, timer, score, route operation, or representation ladder is
added. Only a consumed Client-launch marker travels in the URL fragment.
Exact target identity travels over loopback.
9. The Client uses the system Browser as its only UI. On every launch, the current
lightweight control center offers Local, public link, and Site. The Site value
is stored in Client configuration and remains one click on later launches;
selecting Local or public link does not disable background RPC access for the
saved Site. Command-line mode
selectors remain automation inputs rather than the normal interface. An
embedded shell requires a reproduced product failure and one comparative
decision.
10. Local mode is one explicit server composition: static current assets,
memory-only rooms, peer-assisted media, no SQLite, no SFU, no NAT prediction,
and localhost plus current LAN IPv4 origins. It uses no STUN by default;
`--link` adds its exact temporary HTTPS origin and Cloudflare's public STUN
to existing Browser media edges. It changes no Hosted shutdown or
persistence behavior.
11. One Client configuration owns the optional Site origin and a generated
Local access password. The Client bootstraps its own Host page through a
fragment that is consumed before authentication; friends use the existing
room invitation grant. No Client-specific authorization system is added.
memory-only rooms, peer-assisted media, no SQLite or SFU, and localhost plus
current LAN IPv4 origins. It uses no public discovery by default. Public-link
mode adds its temporary HTTPS origin, one ordinary public STUN destination,
and two bounded public survey destinations. Site mode consumes that Site's
configured STUN survey. Both feed the same connection-local prediction
adapter; Native additionally owns its UDP socket and best-effort port mapping.
11. One Client configuration owns the optional Site origin and an optional,
user-chosen Local access password. A blank value leaves the Local site open;
a value gates that site through the existing SiteAccess authority. The
Client bootstraps a selected page through a fragment consumed before
authentication; the page retains only the non-secret Client opt-in at its
origin. Friends use the existing room invitation grant. No Client-specific
room authorization system is added.
12. Local authority shutdown first ends every in-memory room through the current
`room-closed` path, then closes signaling and HTTP. The Go supervisor closes
Node stdin, waits, and applies one bounded process timeout. It does not
@@ -105,8 +150,9 @@ Hosted and Local deployments diverge without improving the media path.
## Consequences
Hosted and Local operation share one product contract and one route model. The
Go runtime acts as the Client's native-media owner when a Site or Local page
discovers it, but it is not a second room product. It stays small until a
Go runtime acts as an optional capability provider when an activated Site or
Local page discovers it, but it is not a second room product. A mixed room does
not expose endpoint implementation to routing policy. The runtime stays small until a
proven native capability needs a protocol field. The local package may contain
two internal processes while presenting one user entry; the supervisor, not a
compatibility protocol, owns their lifetime.
@@ -117,33 +163,41 @@ process-isolated hardware-H.264/Pion edges sharing one encoded source and
decoded by Chrome, and bounded process cleanup. The native Host path uses the
current Browser route; its explicit in-page window picker also passes source
end, same-room reselection, and restored Viewer delivery. A remote Pion gate has
received video over a direct
`srflx`-to-`srflx` pair. A Windows Browser gate also receives process-loopback
received 30+ H.264 RTP packets over a selected direct
`srflx`-to-`srflx` pair; this proves packet delivery, not decoded Browser video.
A Windows Browser gate also receives process-loopback
Opus on both native edges. A separate remote gate proves that `--link` generates
the ordinary public invitation and carries the unchanged Viewer page and
WebSocket control path, then disappears when the Client exits. An isolated
LiveKit gate also proves native capture through the loopback Browser bridge and
the existing SFU publisher to 1280x720 Viewer playback with complete cleanup.
Physical macOS capture, Linux native capture, and one-link Browser media remain
separate gates.
the existing SFU publisher, including a live 1080p-to-480p profile change and
complete cleanup.
A Browser-Host-to-Native-Viewer gate additionally proves that the Viewer claims
the v8 Client control session and Chrome decodes the 1280x720 source; the encoded
downstream edge has a separate H.264/Opus RTP integration gate.
One-link Browser media and physical non-Windows capture remain separate gates.
GitHub runners compile all three platform adapters. The macOS arm64 sidecar also
creates a hardware-only VideoToolbox encoder and produces a constrained-baseline
SPS/PPS/IDR; its ScreenCaptureKit video/audio permission, source lifecycle,
static-screen recovery, and endurance still require a physical Mac. The Linux
candidate probes and packages its Portal/PipeWire/GStreamer adapter, while a
real desktop, hardware encoder, system audio, and lifecycle still require a
physical Linux gate. Source previews remain best-effort on both platforms.
The macOS arm64 capture sidecar compiles on GitHub `macos-15` and its
permission-free VideoToolbox self-test produces a constrained-baseline SPS/PPS/
IDR. Real ScreenCaptureKit permission, source lifecycle, static-screen recovery,
and endurance remain physical acceptance gates. Source previews are best-effort;
the current macOS sidecar falls back to its source glyph until a physical
ScreenCaptureKit preview gate justifies a platform-specific implementation.
The v5 loopback gate also proves that a real Chrome receiver produces Pion
The v8 loopback gate also proves that a real Chrome receiver produces Pion
transport feedback and that a non-unknown native sender-quality window reaches
the existing route controller. Unknown feedback and stopped-source windows stay
ineligible.
The local trust boundary is intentionally per-user: the instance token identifies
the running Client but is not authentication against another local process.
A physical home-router gate created and removed a UPnP mapping for an ephemeral
UDP listener. The integrated native Host gate still passed capture, two-edge
delivery, source restart, and cleanup with mapping enabled. A selected mapped
path across a pair that fails with STUN alone remains field evidence rather than
an architectural claim.
delivery, source restart, and cleanup with mapping enabled. The mapped port is
now advertised on the observed public address; a selected mapped path across a
pair that fails with STUN alone remains field evidence rather than an
architectural claim.
## Primary Sources
@@ -0,0 +1,70 @@
# ADR-0011: Browser-Assisted Native Encoded Fanout
- Status: accepted
- Date: 2026-09-05
## Context
The product media goal is one encoded source per capable distribution layer.
Pure Browser WebRTC does not expose a way to inject a received encoded frame
into another `RTCPeerConnection` sender. `RTCRtpScriptTransform` can observe or
modify an existing sender or receiver pipeline, but it does not provide an RTP
transport fanout API. Browser simulcast and Dynacast remain appropriate for a
single Browser-to-SFU publication; they do not make independent P2P senders
share one encoder.
The repository already has the required Native boundary: a Pion receiver can
accept H.264/Opus RTP and its encoded source can feed bounded downstream Pion
edges without decoding or re-encoding. The system Browser remains the product
UI and capture owner.
## Decision
1. A Client-launched Browser Host whose current codec is H.264 and whose
topology optimization is enabled uses one local Browser-to-Native ingress.
The existing `HostPeer` owns its sender, clone, quality settings and pause;
the existing Native receiver owns encoded fanout. VP8 and disabled quality
convergence retain ordinary Browser senders.
2. The ingress is generation-fenced beneath the existing room and route
controller. It adds no participant, route operation, quality score, or
representation policy. Steady P2P output uses one Browser encoding; a LiveKit
publication still owns its separate framework-managed encodings.
3. Native downstream edges reuse the existing `NativeSenderPeer` and
`sourceConnectionId` path. A quality operation may still use the existing
Browser sender candidate for one degraded edge; that candidate is an
exception when sharing an encoding cannot sustain that path. Savings never
justify worse delivery or disabling native Browser quality adaptation.
4. If the Client is absent or the local ingress fails, the Host keeps the
Browser capture and recreates affected senders on their current assigned
routes. Capture and room authority survive loss of the optional fanout.
Ordinary Browser pages retain their existing media path.
5. Native capture, Browser capture through the local ingress, Native Viewer
relay, and LiveKit SFU publication remain separate media adapters. No
application RTP protocol, WebCodecs object protocol, SVC ladder, codec
parser, or custom congestion controller is added.
## Consequences
- A Client-launched Browser Host can use one Browser encode plus Native encoded
fanout while preserving the existing UI and room behavior.
- A pure Browser relay remains framework-owned and may encode per child because
the standard Browser API has no cross-connection encoded-frame injection
surface.
- A Browser-to-Native loopback edge adds local WebRTC overhead, but it replaces
repeated remote Browser encoders. Pion owns downstream ICE, DTLS and RTCP;
its existing GCC observations feed route convergence, not a new layer selector.
- A Browser source switch with unchanged media kinds reuses the local sender.
A change in audio presence prepares a new ingress before replacing its Native
children; failure retains Browser capture through ordinary sender recovery.
- A Windows/Chrome two-child gate proves one local encode, 1080p at about 30 fps
on both Viewers, live quality changes, pause/resume, source audio changes, and
Client-exit recovery. Weak-path automatic adaptation and its cost remain
physical acceptance, not a consequence of the clean-path result.
## References
- [Advanced peer distribution research](../research/advanced-peer-distribution.md)
- [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/)
- [WebRTC SVC](https://www.w3.org/TR/webrtc-svc/)
- [Pion encoded broadcast](https://github.com/pion/webrtc/blob/main/examples/broadcast/main.go)
- [Pion RTP forwarding and padding](https://github.com/pion/webrtc/blob/main/track_local_static.go)
+6
View File
@@ -63,6 +63,12 @@ is one of `windows-amd64`, `linux-amd64`, or `darwin-arm64`; every supplied
runtime must match it. It does not create an installer, auto-updater, release
tag, or compatibility bundle.
The platform package also carries its native presentation metadata: Windows
embeds the icon in the Go executable, Linux emits a freedesktop desktop entry
under `share/`, and macOS emits a thin `.app` launcher with an ICNS resource.
The latter two are packaging metadata only and do not duplicate the Client or
Node runtime.
CI and local release-candidate builds use the same wrapper on the target's
native operating system:
+6 -6
View File
@@ -4,7 +4,7 @@
仓库是跨 session、设备和 agent 的持久事实源。会影响后续工作的结论必须先进入其权威文档;聊天、agent 摘要、旧分支和临时计划都不能替代仓库真相。
常驻上下文只保留每次工作都需要的边界和导航。细节按需读取,完成历史交给 Git。任务范围、最小正确结果和 current-result 交付遵循 `AGENTS.md` 摘要并完整读取仓库内的 [`stop-that-shit` skill](../.agents/skills/stop-that-shit/SKILL.md);本文件只维护仓库特有的事实治理规则。
常驻上下文只保留每次工作都需要的边界和导航。细节按需读取,完成历史交给 Git。任务范围和交付遵循 `AGENTS.md`,最小正确结果遵循仓库内的 [`ponytail` skill](../.agents/skills/ponytail/SKILL.md);本文件只维护仓库特有的事实治理规则。
## 事实分层
@@ -25,13 +25,13 @@
## 顶级指令卫生
[官方调研](./research/agent-context-governance.md)的共同结论是:顶层指令应是短而稳定的地图,流程和专业细节按需加载,并定期删除冲突或陈旧规则。检查器把官方边界分成推荐线和硬顶:Codex 项目指令链在默认 32 KiB 上限的 80% 提示、到上限失败;Claude 的有效启动指令在官方建议的 200 行提示,并容许到 250 行的项目缓冲;Hermes 在动态下限 20,000 字符的 80% 提示、到下限失败。`CLAUDE.md` 导入的 `AGENTS.md` 计入有效行数;每次任务必读的 repo STS skill 使用同一保守包络。
[官方调研](./research/agent-context-governance.md)的共同结论是:顶层指令应是短而稳定的地图,流程和专业细节按需加载,并定期删除冲突或陈旧规则。检查器把官方边界分成推荐线和硬顶:Codex 项目指令链在默认 32 KiB 上限的 80% 提示、到上限失败;Claude 的有效启动指令在官方建议的 200 行提示,并容许到 250 行的项目缓冲;Hermes 在动态下限 20,000 字符的 80% 提示、到下限失败。`CLAUDE.md` 导入的 `AGENTS.md` 计入有效行数;必读的 repo Ponytail skill 使用同一保守包络。
推荐线只触发园艺提醒,不是压缩目标。硬顶失败时应把目录专属规则迁到嵌套或 path-scoped context,把流程和参考资料迁到按需 skill/文档,简化重复措辞,并删除已完成历史或陈旧结论;不要为了过闸损失仍有消费者的约束。模块特有规则只有在对应代码存在且每次读取确有必要时,才放到最近目录的 `AGENTS.md`;流程说明进入 `CONTRIBUTING.md`,当前工作进入 `docs/todo.md`,证据与理由进入其 owner。
## 真相更新顺序
1. 按 [`stop-that-shit`](../.agents/skills/stop-that-shit/SKILL.md) 的 authority 和 scope 规则,对照完整产品模型、当前代码、测试和证据确定目标;语义未定时只冻结依赖项。
1. 按 [`AGENTS.md`](../AGENTS.md) 的 authority 和 scope 规则,对照完整产品模型、当前代码、测试和证据确定目标;语义未定时只冻结依赖项。
2. 就地更新受影响的 product、ADR 或 research owner;仅在快照确实变化时同步 memory/status,未决执行项进入 TODO。
3. 检查各 current owner 一致并形成 Git-tracked truth checkpoint,再开始依赖该结论的实现或派发。
@@ -39,7 +39,7 @@
## 机制减负审查
机制减负遵循 [`stop-that-shit`](../.agents/skills/stop-that-shit/SKILL.md) 的 scope 和 smallest-correct-result 规则。本仓库的审查先只读清点机制服务的用户行为,量化移除后的真实退让,并与代码、配置、迁移、测试、运行时状态、部署和故障面的整体成本比较。文档也是需要同步和审查的机制:长期真相只保留产品合同、非显然不变量、算法/模型、关键取舍和外部证据,普通 UI 与实现细节由代码、测试和 PR 持有。若 owner 接受有界退让,先更新产品合同再删除失去必要性的完整表面;否则保留合同并寻找更小实现。审查不以行数为目标,也不授权无边界重构。
机制减负遵循 [`ponytail`](../.agents/skills/ponytail/SKILL.md) 的最小正确结果原则,范围由 `AGENTS.md` 约束。本仓库的审查先只读清点机制服务的用户行为,量化移除后的真实退让,并与代码、配置、迁移、测试、运行时状态、部署和故障面的整体成本比较。文档也是需要同步和审查的机制:长期真相只保留产品合同、非显然不变量、算法/模型、关键取舍和外部证据,普通 UI 与实现细节由代码、测试和 PR 持有。若 owner 接受有界退让,先更新产品合同再删除失去必要性的完整表面;否则保留合同并寻找更小实现。审查不以行数为目标,也不授权无边界重构。
TURN 的减负证据由 [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md) 和相关 transport research 持有;轻量/SQLite 房间取舍由 [ADR-0002](./adr/0002-memory-resident-protected-rooms.md) 持有。这些案例只证明应按当前消费者重审完整成本,不能被推广为永远删除或永远保留某类机制的规则。
@@ -47,7 +47,7 @@ TURN 的减负证据由 [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md)
在需求或优先级被接受、设计或研究结论改变、实质阶段切换、分支/PR/agent 交接、可能 compaction 或长暂停前更新事实 owner。不要为每个函数、工具调用或未形成结论的探索建立检查点。
检查点按 [`stop-that-shit`](../.agents/skills/stop-that-shit/SKILL.md) 的 current-result 规则,只保存以后仍需要的结论、当前状态、下一步和真实阻塞;不保存原始对话或工具输出、完整日志、临时路径、可廉价重查的事实和过程性 TODO 流水账。
检查点按 [`AGENTS.md`](../AGENTS.md) 的 durable-truth 规则,只保存以后仍需要的结论、当前状态、下一步和真实阻塞;不保存原始对话或工具输出、完整日志、临时路径、可廉价重查的事实和过程性 TODO 流水账。
恢复上下文时依次读取 `AGENTS.md`、project memory、status、TODO、相关 product/ADR/research,然后检查当前分支、`git status`、近期 commit 和 PR。摘要与仓库冲突时,以当前代码、测试和权威文档为准。
@@ -63,6 +63,6 @@ TURN 的减负证据由 [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md)
- 迭代中运行覆盖当前改动的最小定向检查;完整套件、真实浏览器矩阵、网络整形、跨设备、长时稳定性和部署检查在相关候选的验收边界批量运行。
- 文档改动默认运行本地 Markdown 链接、whitespace、diff 和 repository-hygiene 检查;不因无关文档变化重跑媒体基准。
- 断链、缺失必需 owner 和格式损坏是 hard failure。每次注入的有效 `AGENTS.md`/`CLAUDE.md` 上下文与每次必读的 repo STS skill 使用“推荐线 warning、硬顶 failure”,避免把建议值误当死线,也避免超过工具加载边界。Product、ADR、research、operations 等按需文档的行数/字节阈值始终只是 warning:它提示检查是否应拆分二级 owner、删除历史或移出过程信息,不要求为通过门禁压缩合理正文,也不是交付质量指标。
- 断链、缺失必需 owner 和格式损坏是 hard failure。每次注入的有效 `AGENTS.md`/`CLAUDE.md` 上下文与必读的 repo Ponytail skill 使用“推荐线 warning、硬顶 failure”,避免把建议值误当死线,也避免超过工具加载边界。Product、ADR、research、operations 等按需文档的行数/字节阈值始终只是 warning:它提示检查是否应拆分二级 owner、删除历史或移出过程信息,不要求为通过门禁压缩合理正文,也不是交付质量指标。
- 昂贵证据记录 commit、环境、结果和适用边界。只有相关路径、协议、配置、依赖、环境或门槛改变,或旧证据失败/含糊时才重跑。
- `.githooks/pre-commit`、`scripts/check-project-state.sh` 和 PowerShell 等价入口共享 `scripts/required-project-paths.txt`。Hook 只做快速、确定、可复现的检查;工程判断留给评审和测试。
+44 -8
View File
@@ -1,6 +1,6 @@
# Capture, Audio, And Media Quality
This file owns the current Browser media contract. Detailed measurements and
This file owns the current Browser and Client media contract. Detailed measurements and
platform limits live in [realtime quality research](../research/realtime-quality-adaptation.md)
and [screen-audio research](../research/browser-screen-audio-quality.md).
[ADR-0007](../adr/0007-path-isolated-representation-quality.md) owns the SFU
@@ -12,21 +12,41 @@ adaptation decision.
stop, synchronously pause/resume audio and video, or switch source.
- A Client-launched Host explicitly chooses either that Browser capture path or
one native screen/window enumerated by the packaged platform capture boundary. The
latter uses one available hardware H.264 path and never infers a target from
latter uses one supported native codec path and never infers a target from
a title. An ordinary Web Host does not probe localhost.
- Windows native capture follows an explicitly stretched active display path
for an entire display or a window covering that display, when the captured
frame matches its desktop source dimensions. Other frames retain their own
aspect ratio. This changes only the encoded presentation, never the game or
display settings; vendor-private scaling is not inferred.
- Share and source-switch requests ask the Browser for available audio by
default. Missing audio is reported clearly but does not block video-only
sharing. Native screen capture can include system playback audio and native
window capture can include selected-process audio when the platform exposes it.
- Authoritative pause disables the current source and sender-owned tracks while
retaining the room and established routes. Black frames, track mute, or
network failure are not interpreted as a user pause.
- Authoritative Browser pause disables the current source and sender-owned tracks
while retaining the room and established routes. Native pause keeps capture
alive but stops session output through the same owner. Black frames, track
mute, or network failure are not interpreted as a user pause.
- The Host preview displays the capture stream directly and creates no Viewer or
media route. Hiding the page may pause only that local video element; it must
not intentionally stop capture, encoding, or upload.
- Live quality changes update the current capture and Host sender-track
constraints plus sender ceilings without reopening source selection or
replacing a healthy route.
- Native quality changes use the same room settings. The Client prepares a new
platform capture/encoder generation, then replaces the old generation behind
the same encoded source and PeerConnections; audio-only changes update the
current Opus encoder directly. Platform quality preference uses the hardware
encoder's standard quality-versus-speed hint, while Pion/WebRTC still own
transport estimation and route evidence.
Windows VP8 uses libvpx's realtime mode; it does not claim the same hardware
quality-versus-speed control or per-edge Browser adaptation.
- With Client available, Browser H.264 capture can use one local sender and the
existing Native encoded fanout while topology optimization is enabled. The
Browser still owns preview, pause, capture settings, and source selection.
Loss of that optional ingress retains capture and rebuilds the assigned
Browser edges. [ADR-0011](../adr/0011-browser-assisted-native-fanout.md) owns
this composition; pure Browser and VP8 sharing retain their normal senders.
## Video Profiles
@@ -70,6 +90,15 @@ The Browser chooses the concrete encoder implementation. A reported H.264 codec
does not by itself prove hardware acceleration, and Web content cannot select a
specific MFT, NVENC, AMF, or QSV implementation.
Windows native capture uses the same VP8/Auto/H264 controls. H264 selects the
hardware path and VP8 the bundled libvpx encoder. Auto measures encoding work
for synthetic NV12 frames at the selected dimensions and frame rate, with a
bounded warmup and sample. If H264 sustains the target it is selected; otherwise
VP8 is measured within the remaining four-second budget. This is a throughput
check, not a perceptual-quality score or a promise under future GPU load.
The returned actual codec owns the shared source, preview, and relay; live
quality/source changes retain it. Other native platform encoders remain H264.
## Framework-Owned Adaptation
Each direct, relay, or SFU video sender owns one clone of its capture or received
@@ -95,9 +124,16 @@ capture therefore retains source resolution and frame-rate ownership across its
loopback Browser bridge; the existing Browser SFU publisher applies only sender
parameters and LiveKit's representation policy to that remote source.
When a live native sender remains persistently limited after capacity recovers,
the route controller may rebuild that exact edge with a fresh connection and
clone; media adaptation still stays entirely framework-owned.
Native sender edges normally reuse one encoded source. A Native Viewer forwards
compatible H.264/VP8 and Opus payload without decoding or re-encoding it; each outbound
PeerConnection owns its RTP identity and transport feedback. When an edge is
persistently degraded, the existing quality operation may prepare a Browser
WebRTC sender from the stable local bridge as that edge's candidate. The old
edge stays live until the Viewer proves the candidate is better; a failed
candidate rolls back, and a later operation may return the edge to the shared
native source. This gives one difficult path stock per-sender adaptation without
lowering the shared representation for healthy paths. It adds no quality score,
timer, or room-wide media setting.
Screener does not maintain an application bitrate/resolution ladder, scene
detector, periodic quality controller, manual SFU layer selector, or
+15 -8
View File
@@ -19,7 +19,8 @@ desktop and mobile viewports.
Screener Client opens this same application in the system Browser. Its small
startup surface selects Local, temporary public invitation, or a configured
Site before entering the Host workspace. Starting a share there offers the
Site before entering the Host workspace. Viewer links opened at that activated
origin may use the same running Client without changing the Viewer UI. Starting a share offers the
Browser picker and any exact Client-owned native windows. It never chooses a
window automatically. A reproduced Browser-window, capture, or background
failure is required before introducing an embedded Web runtime.
@@ -40,9 +41,15 @@ and sequences, and literal scene objects for people, rooms, and media. Glyphs do
not form sentences. Universal digits, transport symbols, URLs, and measured
values remain literal when users need the data.
Motion explains entry, transition, progress, and control feedback. Stable state
stays still; ambient animation and motion added only for ornament are outside
the language. All meaning remains available with reduced motion. Functional
Control hints and state comics use the same television for shared or watched
media. Browser chrome identifies the application UI; a window title bar or a
display stand identifies the capture target. A server in a media path means
actual SFU relay, not merely opening a configured Site.
Motion explains entry, transition, progress, and control feedback. Operational
status stays still; ambient motion is limited to the Client brand mark's
occasional idle wink.
All meaning remains available with reduced motion. Functional
controls remain native buttons or inputs with localized accessible names, and
hover, keyboard focus, and touch receive equivalent guidance.
@@ -123,10 +130,10 @@ that owns that PeerConnection and are never uploaded, persisted, or used for
identity or route selection. The product does not offer a diagnostic-download
button or a raw stats dump.
Current Browser scope includes Web Host, Web Viewer, and Browser relay. Desktop
and mobile Browsers are Viewer targets; mobile Web capture, reliable background
relay, native Client audio/SFU, and non-Windows capture remain later platform
work.
Current scope includes Web Host, Web Viewer, Browser relay, and optional Native
Host/Viewer media beneath those same pages. Desktop and mobile Browsers are
Viewer targets; mobile Web capture, reliable background relay, Client-owned SFU,
and physical non-Windows native acceptance remain later platform work.
## Primary References
+8 -6
View File
@@ -74,13 +74,15 @@ code-only attempts and does not silently revoke invitations.
room authority across application restart.
- **Client Local mode:** the packaged Client composes the same memory RoomStore
and ends every room when its local authority exits. It persists only its Site
choice and generated Local site-access password, not rooms or media state.
choice and optional Local site-access password, not rooms or media state. A
blank password leaves that local site open.
The Client opens its own localhost Host page with that password in a fragment.
The page removes the fragment and uses the existing SiteAccess endpoint; this
does not create a fourth admission authority. LAN and one-link Internet Viewers
use the same room-scoped invitation grant; its fragment never enters the public
tunnel request, and the Host's Local RoomStore remains the only authority.
The Client opens its own localhost Host page with the configured password, when
present, in a fragment. The page removes the fragment and uses the existing
SiteAccess endpoint; this does not create a fourth admission authority. LAN and
one-link Internet Viewers use the same room-scoped invitation grant; its
fragment never enters the public tunnel request, and the Host's Local RoomStore
remains the only authority.
Selecting a Site moves room authority wholly to that Site rather than
synchronizing two stores.
+25 -18
View File
@@ -32,11 +32,16 @@ share generation:
- native-edge topology convergence is enabled by default. The Host may disable
it before sharing; when disabled, quality evidence stays diagnostic and
availability routing is unchanged;
- NAT traversal is absent unless the deployment enables its self-hosted
auxiliary STUN capability. When available, its Host switch defaults on and
augments every Browser P2P edge in that share; the Host may disable it before
sharing. It is not a participant capability, route score, or SFU preference.
Candidate generation and selected-pair diagnostics retain only
- NAT traversal requires an exact three-destination STUN survey: a Site may use
self-hosted 3478/3479/3480, Public Link uses its bounded public survey, and
pure LAN supplies none. Its default-on Host switch augments Browser and Native
P2P edges; Native also uses its media socket and best-effort gateway mapping.
Availability and background direct acquisition share a bounded budget of
three actual connection attempts per eligible parent/session opportunity.
Retries use the same serial controller and normal operation deadlines;
waiting Viewers and untried parents receive their opportunities first.
It is not a participant capability, route score, or SFU preference. Candidate
generation and selected-pair diagnostics retain only
`ordinary | predicted | unknown` provenance.
[ADR-0009](../adr/0009-optional-nat-prediction.md) owns the bounded behavior
and evidence boundary.
@@ -90,6 +95,9 @@ not abandon its only possible route at the foreground boundary.
The pending Viewer transport reports an actual Browser failure but installs no
shorter initial or disconnected-state deadline. Viewer-owned reconnect timing
begins only after that exact candidate commits as the active route.
When NAT traversal is enabled, another operation may use the remaining
connection-attempt budget after rollback. The waiting display uses the actual
server-issued attempt ordinal; it never counts time as an attempted connection.
When a newly committed Host-root Viewer exposes unused downstream capacity while
another Host root has at least two direct children, the same background operation may
@@ -163,17 +171,16 @@ encrypted hop-by-hop with DTLS-SRTP but terminates at the SFU; the product does
not claim operator-blind media without a separately accepted application E2EE
design. All-UDP-blocked networks currently end in bounded failure.
Client Local mode runs this same graph with peer-assisted media enabled and no
SFU or NAT prediction. It uses no STUN by default. Explicit one-link mode carries
only HTTP/WebSocket control through a temporary public tunnel and adds public
STUN to the same ordinary Browser media edges. Reachable peers may form the
ordinary Browser relay tree; an unreachable media path fails without adding a
route type, score, or fallback. Selecting a Site uses that deployment's current
transport configuration instead.
Client Local mode runs this graph without SFU, NAT prediction, or default STUN.
One-link mode tunnels only HTTP/WebSocket control and adds public STUN to the
same Browser/Native P2P edges. Reachable peers form one mixed relay tree; an
unreachable path adds no route type or score. Site mode uses Site transport.
A native Host reserves one additional loopback media edge for its system-
Browser preview and existing Browser SFU publisher. That local edge neither
consumes endpoint route-copy capacity nor starts gateway port mapping. Direct
children still use the native shared encode; if the controller assigns SFU,
the ordinary SFU publication consumes its existing route copy and retains the
same LiveKit representation and recovery policy as Browser capture.
A native Host reserves one loopback edge for Browser preview and the existing
SFU publisher; it consumes no route-copy capacity or gateway mapping. Direct
children share the native encode, while assigned SFU keeps the Browser LiveKit
representation and recovery policy.
A native Viewer terminates only its P2P upstream, reserves one Browser playback
edge, and reuses compatible H.264/Opus for bounded children. Browser keeps SFU,
route revisions, frame proof, and fallback.
+24 -12
View File
@@ -1,6 +1,6 @@
# Project Memory
Last updated: 2026-09-04
Last updated: 2026-09-05
Screener is private, low-latency game screen sharing for one Host and up to 20
authenticated friends. The current product surface is Web Host, Web Viewer, and
@@ -8,18 +8,30 @@ Browser relay; desktop and mobile Browsers are Viewer targets. It is not a
public broadcast service. Source includes a cross-platform Client that reuses
the TypeScript product core in a self-contained Local deployment or opens one
configured Site through the system Browser. Its Go entry presents Local,
temporary public-link, and Site startup choices, supervises Local Node, and owns
the explicit native Host media path selected in the same Host UI. Local remains
temporary public-link, and saved-Site choices on every launch, starts one process-
level loopback capability service, supervises Local Node when selected, and owns
the explicit native media path chosen in the same Web UI. Room source and media
implementation are independent: a topology may mix Browser and Native peers
without changing participant, signaling, capacity, or route identity. Native
Host and H.264/VP8 Viewer receive/relay adapters share the same Pion media edge;
unsupported Viewer media falls back to Browser. A Client-assisted Browser Host
can feed that fanout through one local H.264 sender while quality convergence is
enabled; losing the Client preserves Browser capture. Local remains
a serverless-LAN mode by default; explicit one-link mode exposes that same
authority through a session-scoped public control tunnel while media remains
P2P. Windows capture,
hardware H.264, direct cross-NAT video, Windows process/system audio, and best-effort
mapping of the native edge's sole UDP socket are implemented through the same
media boundary. Native P2P edges feed Pion GCC's categorical payload-capacity
result into the existing route evidence windows. One reserved loopback edge
gives the system Browser a preview and lets native Host media reuse the existing
LiveKit publisher when SFU fallback is assigned; other platform capture and
broader package acceptance remain later gates.
P2P. Minimal Windows, macOS, and Linux adapters terminate at one encoded-frame
boundary; only Windows has completed physical native-media acceptance, while
the other two compile and package. Native capture consumes the same room quality
settings and applies live quality or source changes behind its stable encoded
source and connections. Native P2P edges feed Pion GCC's categorical payload-
capacity result into the existing route evidence windows. Healthy Native sender
edges share one hardware encode; the existing quality operation may test one
stock Browser sender for a persistently degraded edge without lowering the
shared source. One reserved loopback edge gives the system Browser a preview and
lets native Host media reuse the existing LiveKit publisher when SFU fallback is
assigned; broader package acceptance remains a later gate.
The Local launcher keeps an optional user-chosen site-access password; leaving it
blank keeps the self-contained site open.
## Product Map
@@ -72,7 +84,7 @@ implementation and routine UI detail.
## Current Snapshot
Canonical source uses the strict `screener-v19` Browser/server contract with
Canonical source uses the strict `screener-v20` Browser/server contract with
committed media readiness, bounded candidate-relative progress and one-shot
Host-root convergence. It retains optional SQLite room authority and the
H.264/VP8 sender gate. [Status](./status.md) owns the compact current execution
+1 -1
View File
@@ -38,7 +38,7 @@ Screener therefore warns at the published recommendation or 80% of a loader
limit, then fails only at a real loading boundary or a small explicit buffer:
32 KiB for the Codex chain, 20,000 characters for Hermes, and 250 effective
Claude lines above the 200-line recommendation. The same envelope applies to the
repo STS skill because `AGENTS.md` requires it on every task. A failure tells the
repo Ponytail skill because `AGENTS.md` requires it for coding work. A failure tells the
maintainer to use nested/path-scoped context, on-demand skills or linked docs,
remove duplication and completed history, and delete stale conclusions; raising
the ceiling is not the default remedy.
@@ -77,9 +77,10 @@ its `loopback-network` permission; without that permission the cross-origin
request was blocked. The gate checks both expected outcomes.
A self-contained LAN room uses a localhost Host URL and a selected LAN IPv4
Viewer URL backed by the same local TypeScript process. Its generated access
password is persisted once and passed only to the Client's Host page in a
consumed fragment. Explicit `--link` mode launches the packaged Cloudflare Quick
Viewer URL backed by the same local TypeScript process. The Client persists an
optional user-chosen access password and passes it only to its Host page in a
consumed fragment; a blank value leaves the Local site open. Explicit `--link`
mode launches the packaged Cloudflare Quick
Tunnel against that same local HTTP server and supplies its random HTTPS origin
to Node before startup. The public path therefore reuses the exact frontend,
WebSocket signaling, RoomStore, and Viewer grant instead of adding a rendezvous
+40 -10
View File
@@ -1,8 +1,8 @@
# Browser NAT Traversal
# Browser And Native NAT Traversal
Last reviewed: 2026-09-02
This document owns evidence for improving direct Browser ICE without adding a
This document owns evidence for improving direct ICE without adding a
new relay or a custom transport. The current product contract remains standard
WebRTC ICE with STUN discovery and bounded SFU fallback.
@@ -56,21 +56,50 @@ supports an opt-in mechanism, not a default or a participant-wide classifier.
## Current Decision
The ordinary self-hosted endpoint remains the discovery service. NAT prediction
is absent by default; a deployment may enable same-host STUN-only listeners on
3479 and 3480. Enabled deployments expose a per-share Host switch that defaults
on and remains locked while sharing.
An ordinary STUN endpoint remains the discovery service. A Site may enable
same-host STUN-only listeners on 3479 and 3480. Public-link Client mode supplies
one ordinary public endpoint plus two public survey destinations; pure LAN mode
supplies none. Available authorities expose the same per-share Host switch,
which defaults on and remains locked while sharing.
The accepted adapter is deliberately connection-local and additive. It
derives the two auxiliary ports from the existing STUN authority, waits for a
The accepted adapter is deliberately connection-local and additive. It waits for a
clear three-point arithmetic `srflx` shape in one ICE generation, and appends a
small two-sided candidate window from the observed port-sequence endpoint.
Ordinary candidates trickle immediately, so unavailable auxiliary listeners
cannot hold back stock ICE. There is no NAT label, hard candidate skip,
route-controller input, or SFU preference. The switch applies to Host, Viewer
upstream, and Viewer relay P2P connections. Its exact scope is recorded in
route-controller input, or SFU preference. Browser candidates are filtered by
their reported STUN URL. Native Pion performs the same survey through its
`UniversalUDPMux`, so every observation and subsequent media packet uses one
socket. Only explicitly marked Native survey observations feed prediction;
the independently mapped-port candidate does not. The switch applies to Host,
Viewer upstream, and Viewer relay P2P connections. Its exact scope is recorded in
[ADR-0009](../adr/0009-optional-nat-prediction.md).
### Native Shared-Socket Preflight
On 2026-09-05, the unmodified Pion srflx gatherer contacted three public STUN
destinations from three different temporary local ports, despite the media
engine's ordinary UDP mux. This invalidated the prior assumption that Native
STUN and media already shared one socket. Pion ICE's `UniversalUDPMux` was then
used as the media mux and direct STUN observation owner. Its emitted srflx
candidate reported the exact Engine listener as its related port.
The same build carried a public-link session to an independent Linux Pion
Viewer: 35 H.264 RTP packets arrived over a selected direct host-to-srflx pair,
while the Quick Tunnel carried signaling only. The active TUN network yielded
one distinct mapped endpoint across the public survey, so no port sequence or
prediction was claimed. The result proves shared-socket discovery and transport,
not public-survey availability or a predicted-path success rate.
Native Site and public-link shares also request one PCP, UPnP, or NAT-PMP
mapping for that same socket. The returned port is advertised as a
lower-priority candidate using a public address already observed by ordinary
STUN. This is additive and bounded; a VPN, double NAT, or absent mapping service
can make it unusable without delaying or replacing ordinary ICE.
A local UDP-forwarding gate then withheld every ordinary Host candidate and
connected Pion ICE/DTLS in 1.26 seconds through the advertised `mp1` endpoint.
That proves the same-socket ICE mechanism, not rescue through a physical NAT.
### Independent Observation And Attribution Preflight
On 2026-09-01, Chrome 151 on the flagship Windows network gathered one `srflx`
@@ -180,4 +209,5 @@ a new Peer route.
- [libp2p Browser and DCUtR hole-punching boundary](https://github.com/libp2p/specs/blob/master/connections/hole-punching.md)
- [coturn listener and auxiliary endpoint reference](https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf)
- [Cloudflare Realtime STUN service](https://developers.cloudflare.com/realtime/turn/)
- [Pion Universal UDP mux](https://github.com/pion/ice/blob/main/udp_mux_universal.go)
- [WebRTC selected candidate stats](https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatestats-foundation)
+120
View File
@@ -0,0 +1,120 @@
# Native Client Lifecycle
- Reviewed: 2026-09-05
- Scope: Windows capture idle semantics, native loopback input isolation, and
Browser visibility of an unexpected Client disconnect.
- Status: static-source, preview-queue and Client-crash checks pass; Windows 10
monitor startup and game-specific window replacement remain unresolved.
## Current Evidence
An idle WGC source is not a capture-end signal. Source/target closure, control
session loss and individual edge failures have separate owners. A Viewer may
need a keyframe from the retained image while a source remains unchanged.
Source previews use the same serial control connection as share startup. A
virtual-time reproduction with three three-second previews made the real
`NativeClient.startShare()` time out at eight seconds, before its handler ran
at nine seconds. The picker now sends one preview at a time, cancels unsent
work when its source view is retired, and waits for the remaining preview before
starting native media. Refresh and Client replacement fence old results.
## Scope Decisions
- Keep one strict loopback wire and one-share/one-session fence.
- Recovery does not choose a different capture target, codec or media route.
- Do not add a tunnel watchdog, local-process authentication scheme, custom
quality score, or compatibility alias in this phase.
The loopback identifier boundary is now aligned at 8-256 bytes, and the link
gate is included in TypeScript checking. Evidence wording distinguishes scripted
package gates from physical-device gates. The possible cloudflared orphan and
the intentionally per-user local trust boundary remain separate decisions; no
runtime change for either is claimed here.
## Implemented Boundary
- Windows capture treats a quiet frame pool as idle. It retains one latest
converted frame and emits it only when an explicit recovery keyframe request
arrives; source-close and target-process signals remain the end conditions.
- Native Pion candidate input is syntax-checked with the existing ICE parser,
bounded before queuing, and discarded when malformed, stale, or over capacity.
Repeated answers for an edge are idempotent. Profile updates remain fenced to
that same share session in the current loopback v8 wire.
- An unexpected Browser control-socket close now notifies the active Host owner;
intentional user cleanup remains silent and uses the existing share fence.
## Physical Evidence
- On 2026-09-04, a blank Windows Notepad window was selected as a native source.
The capture process stayed alive beyond seven seconds without a new source
change, grew from 7,664 to 16,095 output bytes after a `K` request, and then
exited cleanly. This validates the quiet-source and retained-frame path on the
current Windows host; it does not claim a cross-version Windows matrix.
- The same day, the local Native Host gate started a real Host/Viewer share,
terminated the Client process, and observed the Host return to its start-share
control within the bounded gate. Browser, capture, server, ports, and profile
cleanup all passed.
- On 2026-09-05, a Windows 11 build 26200 display-source check produced 91
hardware-H.264 frames at the 720p30 profile and exited normally. A separate
source-window check stayed alive through 7.02 seconds without frames while
minimized, then produced 17 frames within 540 ms of restoration. All owned
processes, ports and profiles were cleaned up. Neither check establishes the
reported Windows 10 monitor-start failure or game HWND recreation as fixed.
## Open Capture Reports
The Windows 10 report concerns selecting one entire monitor; application/window
capture, including fullscreen games, works. `CreateForMonitor` supports Windows
10 1903 and later; the current single-HMONITOR call, identity checks and
`CreateFreeThreaded` setup match the documented API. No missing general permission
request has been established. Community reports of disabled capture services or
invalid display contexts are leads, not a diagnosis of this user's machine.
Projected WinRT exceptions now reach the existing capture-error boundary instead
of escaping `std::exception`; that correction alone does not prove startup fixed.
The original converter always fitted the captured pixel aspect into the output:
1280x960 into 1920x1080 yielded 1440x1080 content and 240-pixel side bars, even
when Windows explicitly stretched that desktop source onto a 1920x1080 target.
The Windows adapter now reads the active `QueryDisplayConfig` path and applies
its target aspect only for explicit `STRETCHED`, a unique display path, exact
source/frame dimensions, and display or physical full-client-area capture.
Rotation changes the target aspect, not the orientation of already-oriented WGC
pixels. Output profile dimensions and the one-encode media path are unchanged.
Display metadata refreshes on source geometry changes and existing periodic
keyframes, without another timer or display-setting writes. Unknown/custom
scaling, ambiguous cloned outputs and normal windows keep captured aspect.
This does not prove fidelity to monitor-side scaling or vendor-private game
panel-fit/MPO transforms: those may not appear in desktop display-path metadata.
The CPU geometry check covers stretch, ordinary aspect, rotation and already-
scaled input; the reported game's physical comparison remains acceptance work.
Windows explicitly permits a capture item to close when its application silently
replaces the underlying window. Reattaching to another window is not equivalent
to resuming the same selected target; this remains separate from the verified
quiet/minimized-source behavior.
Primary references: [CreateForMonitor](https://learn.microsoft.com/en-us/windows/win32/api/windows.graphics.capture.interop/nf-windows-graphics-capture-interop-igraphicscaptureiteminterop-createformonitor),
[CreateFreeThreaded](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.direct3d11captureframepool.createfreethreaded),
[capture item closure](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscaptureitem.closed),
and [WinRT exceptions](https://learn.microsoft.com/en-us/uwp/cpp-ref-for-winrt/error-handling/hresult-error).
Aspect references: [active display paths](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig),
[scaling modes](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ne-wingdi-displayconfig_scaling),
[active signal size](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-displayconfig_video_signal_info),
and [independent panel fitting/MPO](https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/for-best-performance--use-dxgi-flip-model).
## Acceptance
- Static Windows source remains a live share for a bounded physical run, and a
new Viewer can obtain a keyframe without a source change.
- A malformed/stale ICE candidate or repeated per-edge answer does not close the
control session or terminate sibling edges; a real edge lifecycle failure
remains observable.
- Killing the Client causes the active Host owner to leave the native share
path promptly, while ordinary user-initiated cleanup does not recurse.
- `npm run check` and the relevant Client checks pass; no credentials or raw
media identifiers enter logs or documentation.
Cross-version capture and browser matrices remain separate acceptance work. They
are not claimed by the current Windows gate.
+97 -26
View File
@@ -1,11 +1,10 @@
# Native Client Media Evidence
- Reviewed: 2026-09-04
- Scope: Windows capture, one shared H.264/Opus source, Pion transport, Browser
- Reviewed: 2026-09-05
- Scope: platform capture, one shared H.264/Opus source, Pion transport, Browser
decode, and Browser-mediated SFU fallback
- Status: native Host, cross-NAT video, Windows process/system audio, capture-failure
restart, native P2P quality-evidence, and native-source SFU gates passed;
other platform media remain outside the boundary
- Status: Windows physical native Host and Viewer gates passed; macOS and Linux adapters
compile and package but still require physical media gates
## Result
@@ -20,6 +19,15 @@ received 30 packets over a selected `srflx`-to-`srflx` pair. The validation
session carried signaling through a temporary reverse SSH path only; media was
negotiated directly by ICE.
On 2026-09-05, a separate Windows Local gate used a Browser Host with synthetic
1280x720 motion and a Client-activated Viewer. The Viewer exclusively claimed
the v8 loopback control session and real Chrome decoded 621 frames at 1280x720.
This proves the Browser-to-Native receive/local-bridge path rather than a silent
Browser fallback. A Pion integration gate separately forwards the same H.264
and Opus payload from one inbound receiver source to a bounded downstream edge,
while dropping upstream connection-local extensions so each outbound Pion
interceptor writes its own negotiated TWCC header.
The one-link form carried the current signaling protocol through its temporary
public WSS origin to an independent Linux Pion Viewer. Repeated runs delivered
30+ packets over selected direct paths using a reflexive candidate; the Quick
@@ -27,8 +35,9 @@ Tunnel carried no media.
An isolated LiveKit 1.13.6 gate carried the same native source through one
reserved local Pion edge into the existing Browser `SfuPublisher`; an ordinary
LiveKit Viewer then received 30 consecutive 1280x720 frames. The direct native
P2P path, Browser, Client, LiveKit process, ports, and profiles all cleaned up.
LiveKit Viewer received the default 1080p source, then 15 consecutive 854x480
frames after one live Native/SFU profile change. The direct native P2P path,
Browser, Client, LiveKit process, ports, and profiles all cleaned up.
The opt-in `gate:client-media` run proved, in order:
@@ -65,9 +74,24 @@ requires Go 1.25; Client CI uses Go 1.26.6.
## Current Boundary
The result does not yet prove live native quality-profile changes, macOS/Linux
capture, or endurance. Native Host media is exposed only through the explicit
Client-launched Host selection; these other capabilities remain unavailable there.
Windows now supports hardware H264 and software VP8 behind the same capture,
source and Pion transport boundary. libvpx 1.17.0 accepts the existing NV12 data
with row pitch preserved, without another color-conversion library. In a local
CPU-only 720p30 trial its mean encoding work was 6.112 ms per frame; this excludes
GPU readback and is not game-quality evidence. Separate real Browser gates
proved manual VP8 and Auto-selected H264, including live presets, pause/resume,
source replacement and exact RTP codec preservation. Auto uses a bounded
synthetic target-profile throughput check, not a score or a runtime codec switch.
GPU-heavy selection and cross-device startup remain open acceptance work.
The Windows Browser gate now also keeps two native PeerConnections alive while
the source changes from 720p30 to 1440p60, then changes to 480p15 while paused
and resumes both Viewers. It proves the same Pion source survives two hardware
capture/encoder generations; direct capture probes also produced every current
resolution/FPS extreme, and the same route survives an explicit native source
switch. The result does not yet prove macOS/Linux physical capture or endurance.
Native Host media is exposed only through an explicit Client-launched Host
selection; an ordinary Web Host retains Browser capture.
Native code does not publish directly to LiveKit. One local Pion edge gives the
system Browser a remote `MediaStreamTrack`; WebRTC requires that remote track to
@@ -77,6 +101,16 @@ The bridge adds one local decode for Host preview and a Browser encode only when
SFU publication is active. Direct P2P children continue to reuse the one native
H.264 encode. This preserves the accepted SFU behavior without another LiveKit
SDK or media policy.
A shared encode cannot independently adapt one bitstream for unequal paths.
Rather than lower every Native child, a quality operation originating from a
persistently degraded Native sender edge may prepare one Browser WebRTC sender
from the stable local bridge. The existing overlapping candidate comparison
alone commits or rolls it back, and a later quality operation can return that
edge to Native. Healthy Native edges keep sharing the hardware encode. This is
implemented without a new route reason, timer, score, or representation ladder;
controlled weak-path physical acceptance remains open.
Native P2P edges negotiate transport-wide feedback and use Pion's send-side GCC
with its immediate no-op pacer. The pacer neither queues nor applies one edge's
estimate to the shared encoder. Once real feedback and source frames exist, the
@@ -103,8 +137,11 @@ added about 0.38 MiB to the stripped Windows Client. Mapping begins
with the share, is awaited before the first PeerConnection, refreshes only when
a later edge arrives after half the requested lease, and is removed with the
engine. Failure is cached for that share and leaves ordinary ICE/STUN unchanged.
This proves lifecycle and non-regression, not that a mapped candidate has yet
rescued a pair that public STUN alone could not connect.
The returned port is advertised once per observed public address as a
lower-priority candidate. Only the three explicit same-socket survey candidates
feed NAT prediction; the mapped candidate does not. This proves lifecycle and
signaling, not that the mapped candidate has yet rescued a pair that public STUN
alone could not connect.
The retained hardware fixture also changed one live NVIDIA MFT through
`3 Mbps -> 1.5 Mbps -> 3 Mbps` without recreating it. Equal 120-frame phases
@@ -120,34 +157,63 @@ loopback audio; a window uses WGC plus process-tree loopback when that actual
capability probe succeeds. Preview failure is advisory and cannot tear down
the control session.
A controlled 144 fps window on Windows build 26200 exposed two capture-cadence
failures: WGC's zero `MinUpdateInterval` delivered 48 fps, and sampling from the
last emitted timestamp reduced 48 fps input to 24 fps for a 30 fps profile.
Using WGC's supported non-zero interval plus a phase-continuous output cadence
delivered 29.99 fps and 60.00 fps from the same source. Older Windows versions
without the optional session interface retain their existing WGC behavior.
A Browser-capture/Pion fanout gate exposed a separate RTP input defect:
payload-empty padding was rejected and ended the video or audio reader. Chrome
then fell to a 30 kbps video target even at zero media loss and about 1 ms RTT.
Discarding padding kept the reader alive but introduced downstream sequence
gaps, NACKs and reduced decoded cadence. Forwarding valid padding through Pion's
existing `WriteRTP`, while excluding it from media-frame counts, restored two
1080p Viewers to about 30 fps with zero packet loss. The same gate passed live
quality, pause/resume, audio-presence source changes and Client-exit fallback.
Native payload-capacity evidence does not require decoded dimensions; encoded
receivers can supply real transport evidence without opening a decoder.
DevTools accepted weak-network emulation commands in this loopback run, but RTP
traffic exceeded the requested limit without the requested loss. That run does
not establish weak-network quality or automatic fallback performance.
Non-Windows capture keeps the existing process/frame boundary and replaces only
the platform sidecar. The macOS candidate enumerates `SCShareableContent`, fences
the platform sidecar. The macOS adapter enumerates `SCShareableContent`, fences
the selected process/window generation, receives change-driven
`CMSampleBuffer`s, and requires VideoToolbox constrained-baseline hardware H.264.
It retains one latest pixel buffer so an existing PLI/FIR can encode a fresh IDR
even while the screen is unchanged; capture timestamps drive the shared Pion RTP
clock. A GitHub `macos-15` arm64 runner compiled the sidecar, created the same
hardware-only encoder, and encoded an in-memory 420v frame into a validated
`42c01f` SPS/PPS/IDR. ScreenCaptureKit permission, real capture, static-frame
recovery, and endurance still require a physical Mac.
`42c01f` SPS/PPS/IDR. The current adapter also emits ScreenCaptureKit application
or display audio through the common PCM boundary. ScreenCaptureKit permissions,
real video/audio capture, static-frame recovery, and endurance still require a
physical Mac.
Linux native capture remains no-go for the current stage. The XDG ScreenCast
Portal owns a user-selected session and restricted PipeWire file descriptor, not
the Windows-style pre-enumerated window target or an encoded stream. A thin
future Wayland-only gate may dynamically use the distribution's GStreamer for
`pipewiresrc`, one bounded queue, one proved hardware H.264 element, `h264parse`,
and `appsink`; it must not ship GStreamer, add `webrtcbin`, implement X11 capture,
or pretend that the portal provides target-process audio. Without that system
runtime, direct DMA-BUF import plus VAAPI/Vulkan encoding is not a small adapter.
The Linux adapter lets the XDG ScreenCast Portal own source selection, consumes
its restricted PipeWire stream through one bounded GStreamer pipeline, and
requires an installed element classified as a hardware H.264 encoder. It emits
the same Annex-B protocol and can use the PulseAudio-compatible default monitor
for system audio; it does not claim per-process audio. GitHub Linux CI compiles,
probes, packages, starts, and stops the candidate. A real Portal desktop,
hardware encoder, audio source, Browser decode, and recovery still require a
physical Linux gate. GStreamer stays a system dependency: bundling another RTC
or an 80+ MB media runtime would defeat the thin-adapter boundary, while an
unavailable dependency cleanly leaves Browser capture available.
## Implementation Boundary
- `nativecapture` owns the child process, source identity, and bounded frame protocol.
- `mediaedge` owns the stable Pion API, one UDP mux, shared H.264/Opus sources,
and independent PeerConnections.
- `nativehost` composes one capture generation with its bounded edges.
- `nativecontrol` maps only local source/share/edge commands and exact native
sender quality windows to the loopback v5 wire.
- `nativehost` owns the current capture generation and its bounded stable edges.
- `nativeviewer` owns one native inbound H.264/Opus source and its encoded child
edges; it does not own room or route state.
- `nativecontrol` maps local source/share/receive/edge commands and exact native
sender quality windows, live profile updates, and source replacement to the
loopback v8 wire.
The deleted sender application, UI, room client, and old wire are not
compatibility inputs. Historical measurements remain in the separately marked
@@ -156,6 +222,8 @@ compatibility inputs. Historical measurements remain in the separately marked
## Primary Sources
- [Windows Graphics Capture](https://learn.microsoft.com/en-us/windows/apps/develop/media-authoring-processing/screen-capture)
- [WGC `MinUpdateInterval`](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturesession.minupdateinterval)
- [Sunshine WGC update interval](https://github.com/LizardByte/Sunshine/blob/master/src/platform/windows/display_wgc.cpp)
- [WGC `CreateForWindow`](https://learn.microsoft.com/en-us/windows/win32/api/windows.graphics.capture.interop/nf-windows-graphics-capture-interop-igraphicscaptureiteminterop-createforwindow)
- [WASAPI process loopback](https://learn.microsoft.com/en-us/samples/microsoft/windows-classic-samples/applicationloopbackaudio-sample/)
- [OBS application-audio capture guide](https://obsproject.com/kb/application-audio-capture-guide/)
@@ -167,14 +235,17 @@ compatibility inputs. Historical measurements remain in the separately marked
- [Pion no-op pacer RTX issue](https://github.com/pion/interceptor/issues/406)
- [WebRTC remote-track constraints](https://www.w3.org/TR/webrtc/#mediastreamtrack-network-use)
- [Pion single-port ICE](https://github.com/pion/webrtc/tree/master/examples/ice-single-port)
- [Pion broadcast relay](https://github.com/pion/webrtc/tree/master/examples/broadcast)
- [gopus pure-Go Opus codec](https://github.com/thesyncim/gopus)
- [Tailscale port mapper](https://github.com/tailscale/tailscale/tree/main/net/portmapper)
- [libp2p NAT port mapping](https://github.com/libp2p/go-libp2p/blob/master/options.go)
- [NetBird standalone Go NAT](https://github.com/netbirdio/go-nat)
- [WebRTC signaling and ICE](https://webrtc.org/getting-started/peer-connections)
- [Apple ScreenCaptureKit](https://developer.apple.com/documentation/screencapturekit)
- [ScreenCaptureKit audio output](https://developer.apple.com/documentation/screencapturekit/scstreamoutputtype/audio)
- [ScreenCaptureKit idle frames](https://developer.apple.com/documentation/screencapturekit/scframestatus/idle)
- [Apple VideoToolbox hardware encoder requirement](https://developer.apple.com/documentation/videotoolbox/kvtvideoencoderspecification_requirehardwareacceleratedvideoencoder)
- [XDG ScreenCast Portal](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html)
- [libportal](https://libportal.org/libportal.html)
- [PipeWire DMA-BUF contract](https://docs.pipewire.org/devel/page_dma_buf.html)
- [GStreamer PipeWire source](https://gstreamer.freedesktop.org/documentation/pipewire/pipewiresrc.html)
+2 -1
View File
@@ -160,7 +160,8 @@ configuration checks and media evidence would still be required.
This research uses W3C/IETF specifications, BSD-licensed libwebrtc source,
public browser-compatibility data, and an official TeamSpeak technical
statement. No GPL/AGPL implementation code was copied. GPL/AGPL projects remain
study-only until the repository license and distribution model are decided.
study-only unless a separate distribution decision accepts their obligations;
the repository's own [MIT license](../../LICENSE) does not replace theirs.
## Primary Sources
+100
View File
@@ -0,0 +1,100 @@
# Server Consolidation
- Reviewed: 2026-09-05
- Status: design candidate for the phase after Browser/Client acceptance
- Scope: one shared Go backend for Hosted and self-contained Client operation
## Current Ownership
Hosted [`index.ts`](../../src/server/index.ts) and Local
[`local-index.ts`](../../src/server/local-index.ts) already compose the same
[`createScreenerServer`](../../src/server/app.ts). The Go
[`clientapp`](../../native/client/internal/clientapp/app.go) supervises that Node
process and supplies native media; it does not duplicate room or route authority.
Consolidation replaces this sole backend rather than merging two implementations.
[`RoomStore`](../../src/server/room-store.ts) owns room authority and participants;
[`RoomDatabase`](../../src/server/room-database.ts) persists only access and lease
material. [`RoomRouteController`](../../src/server/room-route-controller.ts)
decides graph transitions without network I/O;
[`HybridMediaRouter`](../../src/server/hybrid-media-router.ts) executes signaling
and SFU effects. These are useful boundaries, not duplication to remove.
## Preparatory Simplification
1. Unify room termination cleanup. `SignalingServer.closeRoom` and `expireRooms`
in [`signaling.ts`](../../src/server/signaling.ts) delete the same timers,
connection evidence, share state and router resources before closing sockets.
Use one cleanup owner with the existing termination reason as input.
2. Remove the Hybrid connection-ID mirror. `connectionIdsByViewer` is populated
through router callbacks, then `handleViewerQualityEvidence` checks that map
and `resolveActiveViewerMediaEdge` again. Derive the Hybrid identity from the
committed edge; retain the evidence gate's own last-observation identity.
Cover reauthentication and in-place connection adoption before removing the
callbacks. The ordinary routing mode remains a separate consumer until its
product decision is made.
These are structural simplifications, not reproduced functional failures.
## Product Choice
`PEER_ASSISTED_MEDIA=false` remains a documented, default configuration in
[`.env.example`](../../.env.example). It owns a separate Host-star path in
`SignalingServer`, including child admission and SDP-answer readiness; Local and
production use the graph controller. Decide whether this mode still belongs to
the product before translating it. If automatic Peer topology is universal,
remove the switch and ordinary path together; otherwise retain its explicit
contract; this reachable mode is not dead code.
## Target Composition
```text
one Go module
cmd/screener-server -> shared server application
cmd/screener-client -> shared server application + launcher + native media
internal/server -> rooms, routing, HTTP/signaling, persistence, SFU adapters
internal/client -> current native and platform boundaries
one React/Vite static artifact
```
Use packages for cohesive owners and files for local concerns. Do not introduce
a framework layer per file, a second product API, or a multi-module build.
Move the existing `native/client` Go module into the common scope during the
migration. Hosted and Local construct the same validated application options;
entry points own their defaults, reachability and lifecycle policy.
Each room keeps one serialized state owner. Go HTTP/WebSocket handlers must not
mutate room, share and route maps independently. Run password derivation and
LiveKit I/O outside that owner, then apply results only while the exact session,
share and operation identities still match. Preserve the existing distinction
between decisions and effects rather than replacing Node serialization with
unrelated locks around each map.
## Reusable Components
- Use standard [`net/http`](https://pkg.go.dev/net/http) and
[`embed`](https://pkg.go.dev/embed) for HTTP and built assets.
- Reuse the Client's [`coder/websocket`](https://pkg.go.dev/github.com/coder/websocket)
for signaling and the official [LiveKit Go SDK](https://github.com/livekit/server-sdk-go)
for room control and tokens.
- Keep the current SQLite schema and transactions. Compare a small
[`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite) build/runtime gate
against package and locking needs before selecting a driver; no ORM is needed.
- Follow [Go's shared `internal` and multiple `cmd` guidance](https://go.dev/doc/modules/layout).
Retain current Pion, capture sidecars and control-tunnel ownership.
## Minimum Acceptance
Preserve exact Host/session replacement, grant revocation, atomic room replacement,
SQLite restart continuity and Local shutdown semantics. Exercise one graph and
one operation through Peer, SFU, same-edge regeneration, bounded overlap and
rollback, including stale asynchronous completions. Preserve short-lived SFU
authority and release resources only after physical drain confirmation.
Use the same Browser contract scenarios against Hosted and Local, plus one
physical mixed Browser/Client media flow. Measure package size, startup and idle
memory before and after. Keep one strict wire contract and one schema owner;
avoid hand-maintained parallel TypeScript/Go validation rules. Retire the Node
server, bundled runtime, supervisor and old packaging paths in the same accepted
integration boundary, preserving [ADR-0005](../adr/0005-automatic-hybrid-media-routing.md)
and [ADR-0010](../adr/0010-cross-platform-client-runtime.md).
+70 -27
View File
@@ -1,6 +1,6 @@
# Current Status
Last updated: 2026-09-04
Last updated: 2026-09-05
This is the current execution index. Git history owns completed timelines;
[verification status](./verification-status.md) owns evidence boundaries.
@@ -50,7 +50,7 @@ This is the current execution index. Git history owns completed timelines;
## Current Source
- Canonical source uses the strict `screener-v19` Browser/server contract.
- Canonical source uses the strict `screener-v20` Browser/server contract.
Optional SQLite stable authority, non-expiring local preferred code, atomic
room replacement and the Host codec selector remain implemented; production
selects stable storage. Graceful restart, crash, timeout and network loss use
@@ -76,10 +76,12 @@ This is the current execution index. Git history owns completed timelines;
sender windows retain the bounded Peer operation; failed exact candidates stay
consumed until the sender or candidate opportunity changes. The pre-share gate
defaults on and locks while sharing.
- Source keeps NAT prediction deployment-gated and disabled by default. When a
deployment enables it, Host Advanced settings shows a per-share switch that
defaults on. Every Browser P2P role uses the same bounded, connection-local
adapter with only the deployment's self-hosted 3478/3479/3480 STUN endpoints;
- NAT prediction stays authority-gated and defaults on only when offered. Browser
and Native P2P use one connection-local rule with Site or Public-Link survey
endpoints; pure LAN, route scoring, and SFU preference remain unchanged.
Enabled acquisition now shares a three-attempt budget in the route controller,
across foreground P2P and background direct continuation. Viewer progress
reflects actual candidate creation; quality trials retain their prior budget.
candidate and selected-path logs retain anonymous
`ordinary | predicted | unknown` provenance. Ordinary candidates and SFU
fallback remain unchanged.
@@ -94,7 +96,10 @@ This is the current execution index. Git history owns completed timelines;
visible/resume/pageshow rebaseline and rearm current-frame proof. Host and
Viewer SFU clients disable LiveKit page-leave auto-disconnect, so SFU recovery
cannot stop Host-owned capture. Native Viewer controls still own playback,
and manual reconnect stays on the current route.
and manual reconnect stays on the current route. A Client-launched Viewer can
receive H.264/VP8 and Opus through a native encoded source, bridge one local Browser
preview, and reuse that source for compatible P2P children; unsupported codec
or failed bridge returns to the Browser peer.
- Viewer presentation derives access, Host, route, playback, and runtime state
from separate owners. An exact media generation plus monotonic proof epoch
owns visible-frame truth; every invalidation rearms proof, while stale epochs
@@ -121,35 +126,73 @@ This is the current execution index. Git history owns completed timelines;
including bounded same-edge connection regeneration. Both default it on with a
pre-share Host opt-out. Weighted/global optimization remains parked.
- Source contains the cross-platform Client outside the current Web release.
One Go entry opens a lightweight system-Browser launcher for Local, temporary
public-link, or Site operation, supervises the same Node application when
needed, and owns loopback native media. The same Host UI explicitly selects
Browser capture or an exact native screen/window. Windows gates prove
One Go entry starts a process-level loopback capability service, then opens the
current system-Browser launcher for Local, temporary public-link, or saved-Site
operation. The saved Site remains allowed while another room source runs; the
same Host UI explicitly selects
Browser capture or an exact native screen/window. Windows Native now shares
the VP8/Auto/H264 selector, with libvpx software VP8 and hardware H264; Auto
uses a bounded target-profile throughput check. Both VP8 and Auto-selected
H264 passed real Browser delivery and codec-stable live/source changes.
Windows gates prove
packaged Local/Site operation, WGC hardware-H.264 plus process/system audio, bounded
shared-encode P2P, and cleanup. The window arm also proves source end and
same-room reselection; display-source lifecycle remains a separate physical
gate. An explicit target assembler also produces a
Linux amd64 package with proved Local/one-link runtime and a structurally
verified but unexecuted macOS arm64 package; its capture sidecar now passes an
arm64 compile plus hardware-H.264 IDR self-test. Client-scoped pull requests
build and Local-smoke all three candidates before merge; a validated `main`
push emits them as short-lived Actions artifacts. One-link mode preserves the Host Local
authority while exposing its ordinary invitation and HTTP/WebSocket control
path through a session-scoped Quick Tunnel. An independent Linux Pion Viewer
verified macOS arm64 package. Its capture sidecar passes an arm64 compile,
hardware-H.264 IDR self-test, and audio-adapter build, but the package and
capture path remain unrun on a physical Mac. The Linux package now includes a
thin Portal/PipeWire/GStreamer hardware-H.264 capture adapter and passes its
compile, probe, package, startup, and shutdown checks; real desktop capture,
system audio, and recovery remain unproved. Client-scoped pull requests build
and run the scripted Local smoke on all three candidates before merge; a
validated `main` push emits them as short-lived Actions artifacts. One-link
mode preserves the Host Local authority while exposing its ordinary invitation
and HTTP/WebSocket control path through a session-scoped Quick Tunnel. An
independent Linux Pion Viewer
has received that native media over a direct ICE pair; remote Browser media
remains unproved. Site and one-link native shares also make one bounded
best-effort PCP/UPnP/NAT-PMP mapping for their sole UDP socket without
changing ICE when no gateway accepts it; pure LAN Local mode does not.
best-effort PCP/UPnP/NAT-PMP mapping and Pion Universal-UDP-mux STUN discovery
on their media socket. A public-link run delivered 35 H.264 packets to an
independent Linux Viewer; no predicted-path win is yet claimed.
Native P2P edges now negotiate TWCC and send Pion GCC's target-versus-source
payload category through the existing sender-quality evidence path without
pacing or changing the shared encode. A local bridge now supplies native Host
media to the existing Browser LiveKit publisher; an isolated LiveKit gate
proves 1280x720 delivery and complete cleanup without a second SFU client.
Non-Windows capture remains outside the release. The default packaged Client
launcher performs a non-blocking official GitHub Release check and the
deployment tree provides a read-only operator check against the same full-SHA
release identity; neither installs or interrupts a running share.
pacing or changing the shared encode. The existing quality operation may test
a Browser sender for one persistently degraded Native edge without lowering
healthy shared edges. A local bridge supplies native Host media to the existing
Browser LiveKit publisher; an isolated LiveKit gate
proves default 1080p delivery, a live change to 480p, and complete cleanup
without a second SFU client.
A Windows gate proves Browser Host to Client-native Viewer delivery and Chrome
decode; an RTP gate covers bounded H.264/Opus child relay and cleanup.
Client-assisted Browser H.264 capture now reuses one local sender for Native
fanout while quality convergence is enabled. Its two-Viewer gate covers
cadence, live controls and Client-exit recovery; weak-network acceptance
remains open. Native RTP forwarding preserves padding and its sequence
continuity without requiring decoded dimensions for capacity evidence.
Native control treats malformed/stale/repeated per-edge signaling as disposable;
unexpected Client close reaches the current Browser participant through the
existing fence. Windows
retains one converted frame for quiet-source keyframe recovery. Its live
profile gate keeps two PeerConnections while moving the hardware source from
720p30 to 1440p60, then to 480p15 while paused, and resumes both Viewers;
current source-switch, static, and crash gates pass, while cross-version
capture remains open.
Local Client access is open by default and can be protected with a user-chosen
password from the Browser launcher; Hosted Site access is unchanged.
Non-Windows native media remains outside physical acceptance. The default
packaged Client launcher performs a non-blocking official GitHub Release check;
the deployment tree provides a read-only operator check against the same full-
SHA release identity. Neither installs or interrupts a running share.
## Current Hold
No source or deployment P0/P1 is open; sanitized route logging remains enabled.
Client acceptance remains open for the reported Windows 10 display startup and
game-specific source behavior. Windows 11 display, minimized-source recovery,
live presets and source-switch checks pass, but do not establish those reports
as resolved. NAT acquisition has controller and signaling coverage, not a new
public-network success-rate claim. Its added prepare progress field requires a
coordinated private-wire release: old strict pages cannot consume it safely.
Native codec and capability fields likewise require the matching bundled UI.
Production is unchanged; [TODO](./todo.md) owns acceptance and release work.
+45 -60
View File
@@ -1,44 +1,39 @@
# Current TODO Ledger
Last reviewed: 2026-09-04
Last reviewed: 2026-09-05
Only **Now** is executable. Observations, old branches, experiments, and parked
topics are not implementation authority.
## Now
1. **Native Client media boundary.** Windows native Host video now runs through
the current Browser-owned room and route contract. Local Client startup,
hardware-H.264 capture, bounded Pion fanout, and a remote `srflx`-to-`srflx`
video gate now pass. Process-loopback audio passes the Windows Browser gate
on the same PeerConnection with a video-only fallback. Pion TWCC/GCC now
supplies exact native P2P sender-quality evidence through the existing route
windows without a custom score. A reserved local bridge also passes the
native-source SFU gate through the existing Browser LiveKit publisher and
its existing representation policy.
Capture-source failure now has a physical end/restart gate through the same
room and Viewer. Clean-revision Windows and Linux packages pass their Local
runtime gates; Linux also passes one-link startup and shutdown. The macOS
arm64 sidecar passes hosted compilation and a synthetic VideoToolbox hardware-
H.264 IDR gate; physically run ScreenCaptureKit capture and recovery on macOS.
Linux retains Browser capture. Reopen a
Wayland-only Portal/PipeWire sidecar only with a real desktop/GPU gate and a
decision to reuse system GStreamer without bundling it; do not add a second
RTC, X11 capture stack, or hand-built DMA-BUF/encoder matrix.
The system-Browser launcher now owns Local, temporary public-link, and Site
selection, and the Host explicitly chooses Browser capture or an exact
Client-owned screen/window without command-line target input. Its Windows
physical gates pass window and display capture, bounded source previews,
process/system audio, and restored Viewer delivery. The window arm also
proves source end and same-room reselection; display-source lifecycle remains
a separate physical gate.
No-Site Internet mode now has one ordinary public invitation link that retains
the Host's Local authority and passes remote control-path and independent
Linux Pion media gates. Prove decoded one-link Browser media on a physical
second device and a selected mapped path on a pair that public STUN alone
cannot connect. Port mapping is implemented and physically creates/releases
a router mapping, but it cannot replace an SFU/TURN fallback for a restricted
pair.
1. **Client physical acceptance.** Physically validate macOS and Linux
capture/audio/recovery, a decoded public-link Browser Viewer, and a NAT case
rescued beyond ordinary STUN, plus one mixed Browser/Native Viewer relay.
2. **Client owner acceptance.** Review the localized terminal, Browser capture
through Native fanout, live controls and Client-exit recovery before merging.
Confirm weak-path convergence with effective media throttling; a DevTools
command succeeding without limiting RTP is not a weak-path pass.
3. **Capture startup and controls.** Diagnose Windows 10 selecting an entire
display failing to start while window capture succeeds; do not conflate it
with exclusive-fullscreen game capture. Current Windows 11 checks cover
display startup, minimized-source recovery and live presets. Verify remaining
game HWND/device-loss behavior and physically compare the implemented Windows
display-aspect correction with the reported stretched game, including GPU
scaling not exposed by the active display path. Compare bitrate changes against
same-content delivery before changing encoder settings.
4. **NAT acquisition and release acceptance.** Validate the implemented bounded
three-attempt owner on restricted Browser/Native pairs; a new connection does
not guarantee a different NAT mapping. The added prepare progress field is
incompatible with old strict pages. Obtain owner approval for the coordinated
Web/Server/Client update and its active-share impact before merging/deploying;
do not mix the new field into an ongoing old-version share.
5. **Native codec acceptance.** Windows Native VP8 and Auto-selected H264 now
pass real Browser playback, live presets, paused changes and source-switch
checks while preserving the selected codec. Validate GPU-heavy Auto selection
and the tight four-second probe/five-second startup budget on other hardware;
do not claim throughput screening guarantees perceptual quality or later
load. Other native platform capture encoders remain H264.
## Parked Product Work
@@ -58,22 +53,10 @@ topics are not implementation authority.
Auto or move an unproved result into the availability path. Validate whether
H.264 qualification can choose a slower VP8 fallback under mixed GPU/CPU load
before adding comparative or dual-codec probing.
3. **Browser encoded-frame reuse.** After the current Browser experience release,
gate one narrow video-edge adapter that preserves signaling, routing,
ICE/TURN, DTLS, DataChannel congestion control, upstream WebRTC playback and
SFU fallback while reusing one encoded representation across every compatible
P2P descendant. First determine whether current Browser APIs or a mature
module can preserve per-edge adaptation without re-encoding; do not accept a
standard-WebRTC weak-edge fallback as the default merely because it is easy.
Compare one layered SVC encode with a small room-wide simulcast set before any
per-edge encoder; select layers from current edge capacity rather than
classifying Viewers into permanent strong/weak groups.
A strong/weak two-child gate must keep the strong path at full quality,
recover the weak path without globally lowering the shared representation,
bound queues and temporary copies, and measure CPU, bytes, latency, keyframe
recovery, A/V timing and supported mobile Browsers. Reuse existing quality
evidence and route operations; add no custom score, global worst-viewer rule,
periodic ladder or unproved codec representation.
3. **Browser encoded reuse.** Pure Browser operation keeps normal WebRTC
senders; users can use Client to reduce repeated encoding. Revisit Browser
reuse only for a small mature adapter that preserves adaptation and media
quality without an application-owned transport or layer controller.
4. **Control-plane resource fairness.** Calibrate one deployment-wide LiveKit
control queue and simple HTTP/WebSocket ingress budgets before implementation;
prove a self-hosted stale-token revocation boundary before independently
@@ -116,10 +99,10 @@ topics are not implementation authority.
Keep injected candidates additive and diagnostic; do not make them a
participant-wide NAT label, hard candidate skip, route score, or SFU
preference. Validate selected predicted paths and bounded resource impact
before recommending it as a default for other deployments. Direct
convergence currently consumes each deferred Peer parent once; add no
cross-generation retry budget until field evidence proves that fresh ICE
generations expose a repeatable prediction opportunity.
plus one selected Native mapped-port path that fails with STUN alone before
recommending either as a default for other deployments. Measure benefit of
the implemented three-attempt budget before increasing it or claiming that
independent connection attempts are independent NAT mappings.
14. **Unresolved route-state ownership claims.** Reopen lower-revision
reauthentication, active SFU failure during an unrelated prepare, and
multi-child relay-evidence ownership only from an exact current-wire
@@ -141,10 +124,12 @@ topics are not implementation authority.
boundaries are complete. First measure package, startup, and maintenance
gains. If accepted, migrate Hosted and Local together and delete the Node
server in the same boundary; do not create or retain two room, signaling,
persistence, or route-controller implementations.
## Decision Needed
- Choose the repository and distribution license before public release or
package distribution. GPL/AGPL implementations remain research-only until
then.
persistence, or route-controller implementations. The
[consolidation draft](./research/server-consolidation.md) identifies the two
preparatory simplifications, the routing-mode product choice, and the shared
module and concurrency model; implementation follows Client acceptance.
17. **Opt-in diagnostics.** Replace ad hoc console logging with an explicit
Client/Server debug option and bounded diagnostic export. Include revision,
runtime state and sanitized capture/connection events; exclude credentials,
media and raw process memory by default. A compressed feedback bundle needs
deliberate collection and retention, not automatic uploads.
+57 -13
View File
@@ -1,6 +1,6 @@
# Verification Status
Last updated: 2026-09-04
Last updated: 2026-09-05
This file owns only cross-module physical evidence that still changes how the
current product may be described. [Status](./status.md) owns exact source and
@@ -77,21 +77,26 @@ address selection, Browser launch commands, bounded Node supervision, its
loopback port/Origin/Host/session contract, and exact package revision. The same
source cross-builds for Windows amd64, macOS arm64, and Linux amd64.
On Windows, Chrome for Testing 151.0.7922.138 completed the built Local page's
On Windows, Chrome 151.0.7922.175 completed the built Local page's
fragment access, Host surface, LAN invitation, and clean Client/Node/Browser
shutdown. A separate synthetic run formed a P2P-only Host-plus-three-Viewer tree
with one Browser relay and advancing frames at every Viewer. The Hosted Site
completed loopback `hello`/`ping` after CDP granted `loopback-network`; without
completed v8 loopback `hello`/`ping` after CDP granted `loopback-network`; without
that permission Chrome blocked it as expected.
The exact clean-revision Windows package assembled with Node 24.19.0 passed both
Client gates; changing `app/REVISION` made it fail before opening a listener.
The current clean-revision Windows package assembled with Node 24.19.0 passed the
package checks recorded for that revision; changing `app/REVISION` made it fail
before opening a listener. Its fresh Site loopback gate closed the Client,
Browser, port, and temporary profile.
The same explicit-target assembly produced a Linux amd64 package that ran its
bundled Node application and loopback runtime on an independent Ubuntu host.
Its full package also created a public link reachable from another network and
closed that link and all local ports on exit. The macOS arm64 output contains
matching Mach-O arm64 Client and Node binaries; its capture sidecar compiles on
the macOS runner and passes the synthetic hardware-H.264 IDR self-test, but the
full package and ScreenCaptureKit path have not run on a physical Mac.
matching Mach-O arm64 Client and Node binaries. Its prior fixed-profile capture
baseline and current variable-profile/audio sidecar compile on the macOS runner;
the latter passes the synthetic hardware-H.264 IDR self-test. The full package
and ScreenCaptureKit video/audio path have not run on a physical Mac. Linux CI
also compiles, probes, packages, starts, and stops its Portal/PipeWire/system-
GStreamer hardware-H.264 adapter; it has not captured on a physical desktop.
Still required are physical second-device LAN playback, first-run LNA prompt,
no-STUN mDNS behavior, macOS package execution, and equivalent non-Windows
@@ -105,16 +110,24 @@ verifies decoded audio energy. The native Host gate passes display and window
source selection and bounded previews; its window arm closes the captured
source, observes the current share end, restarts capture in the same room, and
requires the existing Viewer to receive a different media object plus 30 new
frames. Display-source lifecycle is not claimed by that arm.
frames. The same product gate changes a live Native Host from default 1080p30
to 1440p30, then changes resolution while paused and resumes the same Viewer
media object through the Host UI. A separate two-Viewer media gate changes
720p30 to 1440p60, then changes to 480p15 while paused and resumes both
connections. Display-source lifecycle is not claimed by that arm.
Native P2P quality evidence and the Browser-mediated native-source SFU happy
path now have bounded gates. Production package integration, SFU recovery and
endurance, and physical non-Windows capture remain unproved.
path now have bounded gates. A focused unit gate proves that a quality candidate
from a current Native sender edge selects the stock Browser sender, while ordinary
candidates remain Native. Controlled weak-path commit/rollback, production
package integration, SFU recovery and endurance, and physical non-Windows
capture remain unproved.
A five-second headless topology run with 20 Viewers passed the existing route
capacity, no-orphan-publication, quality-propagation, and decoded-frame checks;
it is topology evidence, not a long-running or public-network quality claim.
An isolated LiveKit Server 1.13.6 run also delivered 30 decoded 1280x720 native
frames through the existing Browser SFU publisher and completed cleanup.
An isolated LiveKit Server 1.13.6 run also delivered default 1080p native media,
then 15 decoded 854x480 frames after a live source/publisher profile change,
through the existing Browser SFU publisher and completed cleanup.
The one-link gate started a session-scoped public origin from a Windows Client,
confirmed that its ordinary invitation used that origin, and served the Viewer
@@ -124,6 +137,27 @@ Pion Viewer; repeated runs delivered 30+ H.264 RTP packets over selected direct
paths using a reflexive candidate. Client exit stopped Node and the public link.
This still does not prove decoded Browser media on a physical second device.
On 2026-09-05, a local Windows gate proved a Browser Host feeding a Client-
activated Native Viewer: the Viewer held the v8 control session while Chrome
decoded 621 frames at 1280x720. A separate Pion integration gate proves that the
same inbound source forwards H.264 and Opus RTP to one downstream edge without
re-encoding. A physical mixed-device relay and public-link Browser Viewer remain
open.
On 2026-09-05, a Native preflight showed that Pion's ordinary srflx gatherer
used three temporary local ports for three public STUN destinations. The current
Universal UDP mux adapter instead emitted an srflx observation related to the
Engine's sole media port. A public-link run then delivered 35 H.264 RTP packets
from that Windows Native Host to the independent Linux Pion Viewer over a
selected direct host-to-srflx pair. The active TUN path exposed only one distinct
mapping, so this proves shared-socket discovery and transport, not prediction.
On 2026-09-04, a bounded Windows physical check held a static Notepad source
open beyond seven seconds, produced a requested recovery keyframe from its
retained image, and exited cleanly. The Native Host crash gate also terminated the
Client process and observed the Host return to its start-share control within
the bounded check. These results cover the current Windows build only.
The same clean revision assembled into a self-contained Windows package with a
matching Node runtime, application tree, and native capture process. Chrome 151
passed the package's Local startup gate and both Site loopback-permission arms;
@@ -131,8 +165,18 @@ revision mismatch remains fail-closed before a listener starts.
## Interpretation Rules
The current Windows Native candidate additionally passed manual VP8 and Auto-
selected H264 playback, with actual Host/Viewer RTP codec checks across live
presets, paused changes and source replacement. A three-attempt NAT acquisition
has deterministic controller/signaling coverage, not a field success-rate
claim. The completed checks do not resolve the reported Windows 10 monitor
startup or game-specific aspect/window-replacement issues. The matching bundle
must be accepted before any coordinated private-wire deployment.
- Configuration, unit tests, loopback, and synthetic signaling prove invariants,
not target-network or device behavior.
- An automated/package gate is evidence from its scripted runner; a physical
gate requires the named target device, network, or desktop environment.
- Missing RTCStats fields are unknown, not zero.
- Requested quality values are ceilings; actual sender/receiver stats are truth.
- A page-lifecycle correction is recovery logic, not a keepalive guarantee.