Conversation
RemotePermission::WebRtc is scoped to the browser sandbox and enforced by removing RTCPeerConnection from a web realm, so a PolkaVM product cannot place a call at all today. RFC 0029 proposes a Media service where the host owns the peer connection, capture devices, codecs, audio route and video compositing, while the product carries opaque host-sealed signalling over its own channel and positions rectangles the host draws. Frames never enter the product: the PolkaVM surface is already a host-owned view in a host-owned container, so a composited video sibling needs no new texture path, whereas delivering decoded frames to the guest would put camera output inside the product.
The host owns candidate gathering, STUN, TURN credential minting and the selected pair, so the product never learns either party's address: sealed signalling, session state, coarse quality and remote-track flags carry no candidate, selected-pair or relay information. Address privacy between the two devices is a separate host policy, because ICE with a default transport policy still trades candidates end to end: relay-only hides both addresses at the cost of latency and egress, while direct-preferred lets the devices see each other. The host chooses, the product cannot detect or override it, and a messaging host should default to relay-only.
…acking draft An incoming call is not an input-modality dispatch. The input modality decides which product handles input the user just produced here; an invitation arrives from a remote party on a product's own channel, so the product is already fixed by channel ownership and the only hard question is whether that party may interrupt the user — which the product answers, because it authenticates the channel. Background delivery needs a wake contract (which executable, which capabilities, how long, how often a peer may trigger it). That is shared with every product reacting to a remote event, so it is deferred to its own RFC; this API works without it and gains background ringing when it lands. Adds the tracking-issue draft in the shape of #550.
…nt routes The worker is part of the product, so it sits inside the same boundary as the surface. An invitation is sealed to the host's media key, whose public half the product publishes itself, so the worker stores and forwards bytes it cannot open and the host unseals only when the surface answers. No call hands a worker an address, candidate, SDP fragment, relay identity or device id, and the session stream is unavailable to it; the worker knows only which of the product's own contacts is calling, which it already knew. RFC 0030 adds the missing inbound primitive: a durable statement-store topic route the host matches while the product is not running, which forms a Worker Lifecycle reference, wakes the worker, delivers matched statements with a durable cursor, and is rate-limited and user-revocable because it lets a remote party start code and interrupt the user. Topics stay opaque, so the host learns nothing about the peer. A call invitation becomes one statement on one route, so RFC 0029 needs no media-specific inbound path.
Matches the house tracking-issue shape: Problem, Goal, Requirements, core implementation scope, implementation references, tasks. Scope limits move into Goal and the open decisions become tasks, so the issue asserts the contract without restating the RFC's reasoning. Drops permission-internals framing, type names, and transport jargon, and states the problem as products each shipping their own stack with media and peer addresses inside product code, leaving the user no single place to see or stop a call. Nothing is runtime-specific: the gap applies to web products and to every other kind.
Calls are no longer limited to two people: the goal, requirements and scope now speak about participants, per-participant video rectangles, participants joining and leaving, and conference topology as a Host choice alongside direct and relayed carriage. Screen sharing and recording stay out. Removes the named-host reference, which asserted an engine and relay deployment that this issue does not need to claim.
Both RFCs move to unnumbered filenames and leave the index, matching the repository's existing unnumbered drafts; a number can be assigned on acceptance rather than claimed while the text is still moving. The tracking issue now allows calls between more than two people without prescribing how, and states that no new server infrastructure is in scope.
Cuts the draft from 373 to 161 lines: the method signatures and wire-id block move to the tracking issue, and the input-modality table, worker walkthrough, quoted lifecycle passages and separate obligations sections collapse into the sections that needed them. A session now holds a set of participants rather than one peer, with per participant signalling, rectangles and track reporting; how a host connects a group is deliberately unspecified and needs no new server. The rendering argument no longer depends on one runtime: every host already composites the product's surface, whether that is a canvas, a web view or a native view.
Drops the internal prior-art paragraph, the quoted lifecycle passages, the filter bound citations and the numbered delivery steps, keeping the concept: a durable topic route the host matches while a product is closed, a bounded wake with the matched statements, and a capability the user can revoke.
The previous text had the product name participants with its own identifiers, which claimed a privacy property while creating a leak: product identities are contacts, usernames and account keys, and handing them to the host puts them in host memory, logs and crash reports for no benefit. A participant is now a host-minted handle, meaningless outside its session and carrying no product-side identity. It exists only to correlate a signalling message, an arriving track and a video rectangle with one peer, which is all the transport needs; identity stays product-side, where the callee's media key was already obtained.
Offering and answering were properties of the session, which only holds for two people: in a group call a product may be answering one peer while inviting another. add_participant now takes an optional received invitation, so the host answers it or produces one, and returns a participant handle; remove_participant drops a peer without ending the call. Consent follows from that: starting, joining and answering all pass through create_session and take one decision there, while inviting a further peer into a call the user is already in does not ask again. The tracking issue gains the service table, the participant-handle guarantee, the consent wording, and the honest group-call ceiling.
The previous problem statement opened with a missing API and described a web product as bringing its own stack, which it does not: the browser engine is already there, and the real complaint is that the camera and microphone feed and both sides' addresses pass through product code. It now opens with what cannot be done — a messaging product holds a conversation with a contact and cannot turn it into a call — then why the only route today is wrong, then the consequence: one implementation per product, and no Host-level way to show a call is running or stop it.
Products do not call people. Both documents now open with what the user cannot do — make a call from a product — and what the product consequently cannot offer, rather than personifying the product.
Replaces two vague phrases in the problem statement: an ambiguous pronoun in a sentence holding both a user and a product, and the euphemisms 'offer to ring' and 'carries a live call'. Both documents now say the product cannot start a voice or video call because no Host service carries real-time audio or video.
The routes and wake draft is not published, so neither the RFC nor the tracking issue points at its path. Both now say background delivery to a product that is not running is out of scope and handled separately, and the RFC keeps the reason it is separate: every product reacting to a remote event needs the same thing.
The issue no longer restates the problem and goal in its own words. It opens with the RFC's summary, says plainly that it tracks the implementation, and names WebRTC as the first one while keeping the service transport-neutral. Scope limits that had their own paragraph — group size, screen sharing, recording, codecs, background wake — become requirements, and the reference flow drops decline, which is the product not answering rather than a method.
Replacing WebRTC would need both ends of a call to agree, so it is a host-to-host compatibility question rather than something a product-facing API can promise, and neither document defines transports coexisting. Both now state only what is true today: the first implementation is WebRTC, and nothing a product sees says so, because it handles sealed messages and host-minted handles rather than transport detail.
Audio-only calls are ordinary rather than a degenerate case: video is optional per participant and per direction, and a product places a rectangle only for a participant actually sending video. The tracking issue lists the products this is for: DIM2 and Chat as SPAs, T3ams for audio and video calling, Meet, and anything else needing peer-to-peer audio, video or sharing.
Meet and T3ams already share screens over WebRTC, so excluding it would leave both unable to move onto this service. Screen is a third local track beside microphone and camera: create_session may declare it, set_local_tracks starts and stops it, and an incoming shared screen is a picture the host draws into a rectangle like a camera. Consent differs from a device permission, so the host runs its own picker: the user chooses what is shared and the product never names a window or a display. Ending a share stops that track and leaves the call running. Recording and product-chosen codecs stay out.
A product-level audio route implied the product picks earpiece or speaker and names a camera, which the OS overrides anyway: iOS moves input and route when a headset appears, and another app can take the camera. set_local_tracks now states intent — microphone, camera, screen, and a preference such as the front-facing camera or a speakerphone-style call — while the host owns device choice, gain, echo cancellation, routing and the switching affordance, and reports what is live. Products never enumerate devices, which would be a fingerprinting surface. The tracking issue loses the service table: the method set is what RFC review will change, so pinning it outside the RFC creates a second source of truth, and the requirements already state every capability.
Live or not live is too thin for a call UI: a product showing a speaker button needs to know the call is on speaker, and one showing a camera flip needs to know which camera is sending. The subscription now reports the kind of device the host chose — earpiece, speaker, headset, front or rear camera — alongside what is actually live, while names, models and lists stay out as a fingerprinting surface.
The media key existed so a caller could encrypt an invitation before the callee had a session. An invitation does not need to be secret: it needs to carry no addresses. The host now puts its per-session public key and negotiation terms in it and nothing that locates the device, candidates follow sealed to the pair of per-session keys, and the long-lived identity disappears along with its rotation, revocation and cross-product linkability. It also bought less than it looked: the product distributes any key it publishes, so a product willing to substitute one could read what follows either way. That limit is now stated as a trade-off, with the trust-root question as an open one. Also fixes terminology left over from screen sharing — pictures rather than video where a shared screen is included — and stops calling screen capture a device permission.
The first paragraph narrated around the problem. It now names it: a product has no way to make a call without running the WebRTC stack in its own code, which a web product can do and no other kind can. The second paragraph says why that place is wrong — feed and addresses inside the product, host-owned devices and codecs, one stack and one set of bugs per product. Also drops the claim that nothing in the transport needs to know who anyone is, which overstated the design: the host sees who is called, when, and with how many people.
The parenthetical about which permission ungates RTCPeerConnection was mechanism inside a sentence about what a product cannot do.
The routes and wake draft is unreviewed and unreferenced, so it belongs on its own branch rather than in this PR. The tracking-issue draft has served its purpose and now lives as #838; keeping a copy in tree would only let the two drift.
19 tasks
|
CI Status: 19 required jobs green, 12 passed and 7 skipped by path filter. All job results
Commit |
Adopts the model Epoca already ships: the host offers, answers and exchanges candidates over its own channel, and the product only names a peer. Nothing to seal, no key to distribute, and no session description or candidate in product code — so the invitation, per-session keys and the key-substitution trade-off all go, replaced by a host obligation to have a channel at all. Consent follows RFC 0002 instead of inventing a per-call prompt: calling is one permission, asked once and remembered, with allow-once as prompt behaviour rather than a second kind of grant. What keeps a remembered grant from becoming an invisible call is host control — an unsuppressable in-call indicator the user can end the call from, teardown when camera or microphone access is withdrawn, and teardown when the calling permission is revoked. States that there is no statistics call: candidate pairs carry the addresses this design keeps from products, and a coarse quality level covers what a call UI can act on. Placement is rectangles on the wire for every product, with an element-based attach helper named as SDK sugar over the same contract, which is what the shipped implementation already does by reporting element geometry. Incoming calls now arrive through the session stream from the host channel rather than as a product-carried invitation, and the product still decides whether the caller may interrupt the user.
Addresses were argued in the concepts, the transport section and the trade-offs, and the absent statistics call was stated twice. The transport section now carries the relay policy alone, the device paragraph absorbs the statistics point in one clause, the rectangle concept and its SDK helper are one paragraph, and the rendering section is two.
Settles the open question rather than keeping both options: the host offers, answers and exchanges candidates over the statement store, owns that subscription, and calling therefore needs no statement permission of its own. The trade-off is stated as the tie to that transport, and the shipped implementation already signals this way.
Calling gets its own permission rather than reusing the WebRTC grant, whose load-time resolution exists so a browser realm can be locked down before page script runs and says nothing about placing a call. A call needs no visible surface: audio connects while the product is still opening, so a background worker can answer and the picture arrives once there is somewhere to draw it. Quality reporting is cut. A product cannot act on a bitrate or a round-trip time, candidate pairs carry what this design keeps out of product code, and a coarse signal can be added later without breaking anything. One open question remains: the group size a host attempts before refusing.
Six is what a shared game needs and what a mesh carries comfortably, so a host must support at least that many. Above it a host may refuse, and the refusal is reported so a product can grey out its control instead of failing mid-call — which leaves the ceiling a host policy rather than a number this RFC has to guess. No open questions remain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Proposes a
Mediaservice: a product runs an audio or video call, with screensharing, without running a realtime stack in its own code. The host owns the
connections, capture devices, codecs, audio route and the pictures on screen;
the product carries opaque signalling over a channel it already has, places the
rectangles the host draws into, and is told how the call is going.
One file,
docs/rfcs/realtime-media-sessions.md. No code.Tracking issue: #838.
What it decides
host composites each incoming picture into rectangles the product places.
per session, so there is no long-lived identity to publish, rotate or
correlate across products.
cancellation, routing, and the switching affordance. A product states intent
and may express a preference; it is told the kind of device actually in use,
never a name, model or list.
before anything reaches the network; withdrawing camera or microphone access
ends the call.
group is deliberately unspecified, and a small call needs no new server.
The first implementation is WebRTC. Nothing a product sees says so.
Out of scope
Recording, product-chosen codecs, large calls needing conference
infrastructure, and waking a product that is not running for an incoming call —
the last is media-neutral and will be handled separately.
Base
Off
main, parallel to #540 rather than stacked on it, so it can be combinedwith #540 and #709 in an integration branch for testing.
Open questions
Carried in the RFC: whether per-call consent reuses
RemotePermission::WebRtc;whether a call may start before a surface is visible, which would let a
background worker begin an audio-only call; whether the coarse quality level is
worth sending; and whether a host could prove a key came from it, given the
product is the signalling channel.