Sloppy Voice DevelopersSloppy Voice ↗

Managed presentation trial

Document: SPEC-025. Revision: 0.4. Date: 2026-11-03. Lifecycle: bounded implementation/trial authorized under D-169; real-content admission remains conditional on the compatibility checks below. Supersedes: no native transport; completes the scoped trial stages selected in SPEC-024.

Authority and scope

D-169 requests the SFU approach after alpha.38 remained unsatisfactory on real clients. The human separately authorizes creation of sloppy-voice-sfu-trial and the initial ten-run generated-media envelope. Implement standard WebRTC presentation video and optional presentation audio through Cloudflare Realtime SFU, with existing microphone transport, capture consent, channel policy and listener controls. SPEC-024 supersedes conflicting timing/enforcement clauses in the older trial proposal. No production-ready or physical-performance claim follows from provisioning an app.

Start with a backend-only provider adapter and a bounded generated-media harness. Check actual Cloudflare delivery, Chromium/Firefox interoperability and provider track closure before allowing real sources. Integrate the validated adapter behind the existing share/watch interface; a publication selects one transport and never silently duplicates or downgrades. The human selected all channels on sloppyvoice.com for the first usable trial; channel sharing permissions still apply. Other servers retain native delivery. SPEC-008 owns that deployment scope. Required encryption fails closed on unsupported clients.

Provider boundary

The fixed Connection API origin is https://rtc.live.cloudflare.com/v1; only the backend holds the App Secret. Create sessions without automatic creation retries: Cloudflare's correlation label is not an idempotency key. Invoke a stored Workers built-in fetch without an object receiver, use manual redirect handling supported by Workers, and reject every 3xx response without following it or forwarding the App Secret. Retain each returned resource immediately, serialize session mutations through completed negotiation, check both HTTP/top-level and every track result, and retain partial successes for cleanup. A lost response remains uncertain. A retry cannot invent confirmed absence.

Application APIs accept application-owned connection/publication IDs, not arbitrary provider paths or someone else's session IDs. Provider sessions/tracks belong to exact host, call, approved device, publication, role and authority context. Recheck that context and the initiating request’s current login registry/device/delegation after asynchronous operations, including Home protection and final role cleanup; close late unauthorized allocations through their exact owned connection. Recheck and shorten the remaining login/retirement deadline after waits. Reconciliation enumerates resource IDs, then reloads each current row inside its serial resource queue; an earlier resource’s provider wait cannot overwrite a later allocation, renewal or closure with a captured snapshot. Forced close is confirmed per requested mid, accepting a matching successful result or documented close_track_error absence. Unreported/other failed items remain pending. Safe provider error codes/status remain available to the client without provider descriptions, SDP, identifiers or secrets. Endpoint closure does not establish provider closure. A documented session_error with HTTP 410 confirms session expiry; a generic error or empty inspection cannot confirm completion of a lost mutation. Retain its cleanup fence until that operation has a known outcome or the provider session expires. After channel deletion, retain pending ownership only for reconciliation and remove it immediately once closure is confirmed.

Native WebRTC owns packetization, pacing, congestion control, retransmission and codec recovery. Do not reproduce native fragments, tiny SCTP queues or the five-second native keyframe timer. Start with a single VP8 video rendition and Opus presentation sound; add codec/layer combinations only after measured compatibility. All-screen capture remains a single combined consented track. Linux mixed PCM feeds an uncompressed audio graph/MediaStream destination, not an Opus decode/re-encode bridge.

Browser-granted sound tracks feed RTP directly to preserve their original capture timestamps; do not route them through an unnecessary sender AudioContext. Track enablement and authenticated frame authority gate their output independently of microphone PTT. Only native PCM needs the bounded mixing graph. An SFU publication with sound carries audioClock: captured-track|mixed-pcm, derived from the consented source, not an end-user setting; omission means captured-track for compatibility. Native publications cannot carry it. Bind this immutable value in the authenticated frame context. Audio/video senders share one MediaStream for standard synchronization. Receiver audio uses the existing final local gain/mute/ducking worklet, while a muted video element consumes both received tracks to activate Chromium playback. Fixed trial compatibility preferences use the browser's nonbinding video jitterBufferTarget where supported: 100 ms for Chromium; Firefox uses 30 ms for captured tracks and 60 ms for mixed PCM, accounting for its 40 ms mix alignment. Native adaptive buffering applies elsewhere. These are initial presentation preferences, not a hard queue cap or microphone delay. Keep actual timing evidence for each source/browser combination. Combined-canvas capture is manually clocked in Chromium; the Gecko compatibility path paints at at most twice the selected cadence (bounded at 120 Hz), with native canvas capture and RTP encoding still capped at the selected frame rate. Record actual cadence rather than promising every capture becomes a delivered picture.

Compatibility gates

Before encrypted real-content admission, document and test the exact authenticated encoded-frame profile, ephemeral key distribution/lifetime, source/role/publication binding, replay rejection, codec-prefix handling and epoch transitions. A shared group secret alone cannot establish sender identity. Unsupported transforms, missing keys, unready recipients and verification failures cannot fall back to plaintext. Track the managed key-epoch separation requirement of SPEC-024 explicitly; a transitional stricter native-key adapter is not labelled complete managed key migration.

Trial wire and cryptographic profile

ScreenPublication.transport, omitted for native compatibility, selects sfu-rtp/1. The state capability advertises sfu-rtp/1, sfu-frame/1, rateEnforcement: cooperative, and placement: global. Old endpoints cannot decode this publication using the native fragment parser. Supporting clients select SFU on enabled hosts and disclose provider routing; there is no automatic transport fallback.

POST /screen/sfu uses the existing authenticated channel API. Each command contains an application UUID id, current voiceId and action. Create also contains mode: publish|watch and publication UUID; publisher tracks contains SDP description and ordered video/audio mids; subscriber tracks names are resolved exclusively by the server; answer contains answer SDP. Refresh includes a positive monotonically increasing safe-integer renewal; replaying its current value returns the remaining original window, lower values fail and expired authority cannot be revived. Close references only the owned application ID. Create and fresh refresh force current visitor approval outside microphone processing and bound the grant by the remaining login/retirement and home-identity deadlines. A shorter window refreshes by half its remaining duration, never later than the normal five-second interval. The final transform separately intersects this authority with each original key deadline. Responses give profile, finite validForMS, state, and negotiation descriptions/results when applicable. Provider session IDs remain internal except opaque references inherently present in provider-generated SDP; clients cannot choose them. SDP exchanges are serialized through the subscriber's answer. A pending answer has a fixed 20-second completion window. A duplicate create cannot repeat provider creation. Persist uncertain creation outcomes and block conflicting replacements until resolved; do not pretend an unknown resource was closed.

Every encoded VP8/Opus frame retains its codec prefix (VP8 key 10 bytes, VP8 delta 3, Opus TOC 1). Append a 61-byte header, payload and 64-byte Ed25519 signature. The header is magic SVF1 (4), prefix length with high-bit encrypted flag (1), key UUID bytes (16), unsigned big-endian counter (8), XChaCha20 nonce (24), original encoded timestamp as unsigned big-endian integer (8). Signed bytes are context digest, clear prefix, header and protected payload. The context digest is BLAKE2b-256 of canonical JSON containing profile: sfu-frame/1, serverId, room, voice session, publication, source device, audio/video role, revision, encryption mode and audioClock when present. The key is BLAKE2b-256 of that digest keyed by the ephemeral 32-byte sender media secret. E2EE uses XChaCha20-Poly1305 over the remainder with digest/prefix/header as associated data. Server-readable mode signs the clear remainder, sets the encrypted flag false and key UUID zero; it does not claim E2EE. The random nonce is generated for each encrypted frame. The original timestamp is authenticated metadata; never require SFU RTP timestamps to remain identical.

A dedicated transform validates the expected approved device signing key and complete context before enqueueing plaintext. It accepts at most a 2 MiB original frame and a 2,048-counter reorder/replay window per key/context; reject duplicate/old counters without advancing on invalid signatures. Key expiry uses the earlier original monotonic deadline; refresh cannot extend it or revive an expired ID. Retain at most 512 original key deadlines per context, failing closed if exceeded. Worker messages carry deadlines on the shared performance time-origin scale so queue delays consume validity. Worker authority has its own finite deadline and a final output check. Drop bad/unready frames, request codec recovery through native RTP feedback, and retain the last good picture only while authorized. Stop and known permission/source loss close local media immediately.

Before native codec admission, validate VP8 keyframe synchronization bytes and positive coded dimensions within SPEC-023's 8,192-per-dimension / 8,294,400-pixel envelope. The trial Opus profile accepts at most 1,275 original bytes per frame; protected audio is at most 1,416 bytes. Invalid decoded format cannot advance replay state. Browser codec/decode and timing measurements remain distinct from these application-owned input bounds.

The first integrated adapter reuses SPEC-011/016's acknowledged ephemeral sender-media keys through memory-only copies in dedicated workers. It never uses recovery material or static identity-box keys. Current native membership revisions may cause additional rekeys, including cosmetic changes; this trial does not claim completion of SPEC-024's independent cryptographic epoch migration. An increasing revision replaces transform context and requests a keyframe while preserving the RTP connection; older-context frames cannot decrypt into it. Removed recipients receive no new native sender secret. The native microphone key/barrier behavior is unchanged. This first adapter still requires the existing live voice attendance/publication and native authorization barrier at the server; the managed 30-second maximum is not a promise that presentation survives every shorter native authorization interruption. Record this stricter transitional dependency as a limitation, rather than treating it as completion of independent managed authority migration.

Trial enforcement and rate evidence

Audio-only moderation retains independently permitted video. Durable resource state records optional trackRoles (mid to video/audio/unknown), closedMids tombstones, an audioStopped deny latch, audioClosed completion and subscriber source application connection. These are operational ownership fields, not configuration. Positively classify roles from the original ordered offer or exact provider track names; unknown allocations close conservatively. Latch audio denial for exact publisher resources and associated subscribers, preserving unrelated watching. Reconcile audio separately from whole-resource revocation and retain pending enforcement until managed close confirmation; native acknowledgement alone is insufficient. Late provider results cannot revive tombstoned mids or overwrite a newer movement fence. Legacy subscribers lacking role evidence may need reconnection. Closed audio is not automatically renegotiated on unmute; restoring it may require explicit re-sharing, without automatic capture or plaintext fallback.

Application records persist exact owner, device, call, publication, role, provider session/mids, negotiation state and cleanup status. Reconcile invalid/expired ownership and partial/uncertain operations outside microphone renewal; inspect dedicated owned sessions to recover unreported mids, and verify each close result. Documented session_error HTTP 410 establishes expired session absence; a generic 404, stopped browser or silent endpoint does not. Closed records are retained one hour; uncertain resources remain visible for reconciliation. Alpha.40 performs one Worker-only recovery for alpha.39 creation records without a returned session, mids or pending mutation: both confirmed fetch incompatibilities rejected before dispatch, so those records can be closed without provider allocation. Persist a migration marker before accepting new operations; never apply this exception to Node, an allocated session, pending mutation or a future uncertain result. Known membership removal, shared mute, disabled sharing, source stop and policy violation close affected provider resources. Conflicting replacements remain pending until old closure is confirmed.

Before allocating a foreign presenter's SFU connection, its host uses the existing audience-bound directory delegation at POST /federation/presentation to register the current call/device for managed cleanup at home. Both the Node and Worker public-network adapters explicitly permit the fixed api/federation/presentation POST alongside api/federation/inspect, including supported service-base paths. They share method/path/JSON-body validation, reject queries on these POSTs, retain the existing 4 KiB body bound, and never carry ambient authorization or cookies. Other POST endpoints remain blocked and existing public-address, redirect and timeout protections remain. Failed home confirmation reports sfu_home_unavailable without exception details; ended/uncertain SFU operations retain typed authority/pending errors. This only tightens an existing approved matching reservation; it cannot create or renew a call or execute account commands. End/release/expiry of a protected reservation retains a durable home fence. New call authority remains withheld until the old native authority window has elapsed and the pinned old host confirms all owned SFU resources closed. GET /screen/sfu-closure?account=UUID&call=UUID returns only profile, echoed unpredictable call UUID and closed boolean, with rate limiting; it exposes no identity, room, media or provider identifiers and performs no mutation. Home verifies the old server identity/capability over the existing public-network transport. Failure, changed identity or pending closure preserves the fence. Require the complete current account fence set to be empty at the final synchronous grant boundary, including inside local admission’s transaction; a delayed result for another call key cannot authorize a new call while a newer fence survives. Local moves similarly mark owned resources closing and return pending until reconciled. Cosmetic revisions keep RTP sessions while stricter native cryptographic contexts rekey.

Cloudflare Connection API exposes track state and supported close operations, but no verified per-publication byte-rate meter or ingress policer is available to this adapter. Therefore there is no trusted per-publisher bitrate window, burst threshold or automatic meter-based exceedance action in this trial. This absence is advertised as cooperative enforcement. Admission atomically reserves publication/subscription targets against SPEC-023's server ingress/egress budgets, retaining reservations while cleanup is pending. Clients set aggregate RTP encoder targets under the selected/channel ceiling: video uses 90% of the remainder after a 128 kbps optional sound reservation; Opus targets 96 kbps; transport/recovery overhead is additional and measured separately. Native enforcing delivery remains available on disabled hosts. This is a scoped testing deployment, not a production malicious-publisher or invoice-cap guarantee.

Managed authority validity, refresh, revocation, movement, cooperative-rate and global-placement semantics remain canonical in SPEC-024. Publish actual provider enforcement capability. Record available trusted telemetry, measurement windows, transient-burst allowance and exceedance action before enabling real-content trials; unavailable per-publisher policing is stated explicitly rather than inferred from browser statistics. No provider call runs inside the microphone renewal critical path.

Configuration

SPEC-008 owns deployment enablement, channel/device eligibility and backend credentials. They default to disabled/empty; missing required credentials fail validation. Secrets are kept in ignored local credential files with owner-only permissions and deployed secret storage; never in client bundles, diagnostics or committed artifacts.

The initially authorized harness has one presenter, at most two viewers, at most 4,000,000 bit/s aggregate presentation encoding, at most 600 seconds per run and initially ten persisted runs. On 2026-10-25 the human approved exceeding that allowance in response to the five-additional-run request, increasing this test to at most fifteen total runs with the same per-run constraints. On 2026-11-02 the human approved five additional runs to reproduce and verify the Qt setup failure, increasing the cumulative allowance to twenty with unchanged per-run constraints. On 2026-11-03 the human approved up to two additional generated-media runs for the source-identification correction, increasing the cumulative allowance to twenty-two with unchanged per-run constraints. Record a run before provisioning; failures/restarts do not reset the allowance. A local deadline closes endpoints and the backend reconciles known provider resources. These cooperative limits are not a malicious-client invoice cap. Generated content only until the compatibility gates pass. Existing channel permission/bitrate defaults retain their SPEC-023 owner.

The ignored harness ledger's approvedLimit defaults to ten; changing it requires explicit human authorization, and the harness currently accepts at most twenty-two. An exhausted allowance fails before provider creation and releases its lock. A crash preserves the lock for reconciliation. Browser subprocess environments exclude backend SFU/Cloudflare credential variables. Native PCM source queues and final audio gates retain their bounded rules; a requested total rate at or below the fixed 128 kbps sound reservation reports an actionable error rather than supplying a negative video rate or exceeding the selected encoder target.

Testing

Harness lifecycle regressions exercise the actual shared generated/application cleanup seam with fake provider results and persistent ledgers/SQLite. Persist creation, track and answer intent before dispatch; a failed pre-dispatch write must prevent dispatch. Lost creation survives restart without a blind retry or complete-cleanup claim. Empty inspection or closure of known mids retains pending mutation ownership; only a known operation outcome or documented session-error HTTP 410 establishes absence. Generic errors cannot clear uncertainty. Persist each reconciliation transition back to the owning resource and run ledger, and fail rather than silently opening an empty replacement database. tests/sfu-harness.test.ts covers these failure paths separately from real-provider compatibility and approved live-run accounting.

Units cover fixed provider origin and identifier validation, bounded SDP/response handling, no blind creation retry, request/track errors, partial allocations (including failed entries with an empty mid beside successful allocations), exact close matching, serialized negotiation, deadline/cancellation cleanup, persistent run accounting and redaction. Cryptographic and authority tests precede encrypted admission. Run create/tracks/answer/inspect/close in workerd with fake outbound responses to catch built-in invocation and option differences from Node. Test rejection of redirects, safe HTTP error projection and the one-time pre-dispatch recovery fence. Exercise foreign-presenter registration through the production public-network guard in Node and workerd against the home-service validator, including denied call/device authority and failure before provider allocation.

The live generated-media harness uses real RTP tracks through the isolated SFU application, backend signaling, Chromium publication and Chromium/Firefox reception. Measure unique rendered pictures, actual codec/dimensions, native delivery/decoding counters, capture-to-display latency, frozen duration, sound, source teardown and provider closure separately. Compare the same scene/budget to native results. Real operating-system capture, GPU use, cross-network behavior and invoice totals remain distinct evidence.

Public-handler concurrency regressions suspend old-Host cleanup while a different call creates a newer Home fence; local admission must return pending without a grant. Two-resource manager barriers complete allocation, renewal or closure while earlier cleanup awaits, then require the later durable ownership unchanged. Revoke the initiating login key during create/tracks/answer and require refusal plus exact-resource cleanup; unrelated connections survive. Current deadline shortening applies to both durable expiry and returned validity. These generated fake-provider checks do not establish live provider forwarding or closure. See tests/audit-server-async.test.ts.

Acceptance criteria

  • The App Secret is never returned to clients or diagnostic output, and only fixed validated Connection API operations are available.
  • Approved run accounting (initially ten, extended to fifteen, twenty and then twenty-two) survives process restart; every run has a finite deadline and durable resource/cleanup outcome.
  • Chromium and Firefox receive correctly rendered generated video/audio through the actual SFU, with numerical delivery/latency evidence and verified provider track cleanup.
  • Missing/partial/ambiguous provider results cannot be labelled successful closure or cause uncontrolled duplicate sessions.
  • Encrypted real content remains unavailable until authenticated frame transforms, key lifetimes/replay/epoch behavior and permitted channel/device admission are validated.
  • Existing microphone authorization/PTT, optional game sound and listener mute/ducking remain independent and effective in integrated tests.
  • Deployment records distinguish a working trial, remaining integration/physical gaps, configured rollout and observed version traffic.