# atlibs-webrtc The WebRTC transport for [atlibs](https://knotless.dev/johnandersen777.bsky.social/atlibs) (`knotless://johnandersen777.bsky.social/atlibs`): `com.fedfork.exec.webrtc.connect` in a browser page. `WebrtcHost` finds a guest's offer, answers, opens a datachannel and can run ssh over it. It also fetches `webrtc:` URLs: HTTP/1.1 to a guest's tunnel tag over datachannels (the guest agent's MCP server is `webrtc:////shell/mcp`). Signalling goes through the module (sealed with age); connectivity is STUN only. `webrtc-peer` is the guest side on your machine, for local checks: it answers sealed offers and forwards each datachannel label to a TCP port. ## Layout - `js/`: the transport for pages (`WebrtcHost`, `localSignals`, `httpChannel`: HTTP/1.1 over a datachannel), no dependencies - `src/`: `webrtc-peer` (str0m), a crate on `atlibs-host` - `web/loopback.html`: a page that drives the transport against a local `webrtc-peer` - `tests/`: the loopback check; `lexicons/`: `com.fedfork.exec.webrtc.connect` ## Get the core The crate takes `atlibs-host` as a git dependency (pinned by `Cargo.lock`). The page and the tests import the core's JavaScript from a sibling checkout: ```bash git clone https://knotless.dev/johnandersen777.bsky.social/atlibs ../atlibs && git -C ../atlibs checkout -q 88b2ba8 ``` ## Build ```bash cargo build --release ``` ## Run a guest Forward the `ssh` datachannel to a local sshd, and let one page origin signal it: ```bash target/release/webrtc-peer --tunnel ssh=22 --origin http://127.0.0.1:8000 ``` It prints `{signal, fingerprint, ...}`: open `web/loopback.html?signal=&user=$USER` from that origin. ## Use it from a page `npm install @publicdomainrelay/atlibs-webrtc @publicdomainrelay/atlibs` (Node >= 25), or map both in the page's import map: `@publicdomainrelay/atlibs-webrtc/` to `js/` (here, `web/webrtc` is a symlink to `../js`) and `@publicdomainrelay/atlibs/` to the core's `javascript/src`: ```html ``` Then route the calls to the transport, as `web/loopback.html` does: ```js import { WEBRTC_STATE, WebrtcHost, localSignals } from "@publicdomainrelay/atlibs-webrtc/index.js"; const host = new WebrtcHost({ store, events, signal, makePeer: (c) => new RTCPeerConnection(c), age, ssh }); const call = hostCall({ ...rest, calls: host.calls }); // webrtc.connect await runModule({ ...rest, fetchers: { ...host.fetchers() } }); // webrtc: URLs for com.fedfork.exec.http.fetch ``` `events({receipt, tag})` returns the guest events of that receipt and tag: a fetch peer is its own peer connection, beside the page's. `fetchers(options)` is `new WebrtcHost(options).fetchers()` for a page that only fetches (`store` is optional). ## Mesh peers: `recordSignals` `recordSignals({repo, subhandle})` (`js/record-signals.js`) is `{events, signal}` for `WebrtcHost` over exact-key records instead of a requester: the receipt is a peer's `computer.socialweb.peer/` strong ref (`peerReceipt(repo, did, subhandle)`); its `webrtc` DID's `computer.socialweb.ingress.listen/` gives the age key and DTLS fingerprint; the sealed offer goes to `computer.socialweb.ingress.offer/.` in the page's repo and the answer comes back at `computer.socialweb.ingress.answer/.`. `repo` is the page's PDS access (`did`, `getRecord`, `putRecord`, `deleteRecord`). `meshChannel(host, receipt, {channel})` then speaks atirc's mesh channel lines over the `atirc/mesh/1` datachannel; atirc's `tests/webrtc_loopback.ts` drives it in headless Chrome against `atirc mesh run`. ## HTTP to a bidder: endpoint URLs A kcp bidder's offering lists `{kind: "webrtc", address}`, `address` = base64url of `{"did", "listen"}`. `webrtc://
/` (`endpointUrl({did, listen}, path)`) is HTTP/1.1 to that bidder's market XRPC over a `market` datachannel, signalled with `new WebrtcHost({records: recordSignals({repo, subhandle}), ...})`: the receipt is its `computer.socialweb.ingress.listen/` uri, so the answerer is that DID itself. atlibs hosts send module calls with `endpoints` there with service auth aud `#pdr_temp_market`. ## HTTP to a guest: `webrtc:` URLs `webrtc://///`, `ref` = base64url of the JSON `{"receipt": {"uri", "cid"}, "bidder"?}` (`webrtcUrl({receipt, bidder}, tag, path)` builds one, `parseWebrtcUrl` reads it). The fetcher is `async ({method, url, headers, body, signal}) => ({status, headers, body})` (body text). A module reaches it through `com.fedfork.exec.http.fetch` with `webrtc://*` in its `http` list and the host's allowlist. Each `(receipt, tag)` gets one peer connection, kept for later fetches and dialled again once it fails, and a pool of datachannels, each one HTTP/1.1 connection serving one request at a time (keep-alive; a `Connection: close` response retires it): a request in flight holds its channel, so concurrent requests (a long-poll `read` beside an `exec`) each open or reuse their own. MCP session headers are the client's (`Mcp-Session-Id` rides through like any header). `host.close()` closes them all. ## Test `tests/loopback.test.mjs` runs `web/loopback.html` in headless Chrome against `webrtc-peer` in front of a throwaway sshd and a stub MCP server. It skips (with the reason) without Chrome (`CHROME=`), OpenSSH or the build. ```bash cargo build --release && cargo test --release node --test js/*.test.mjs tests/*.test.mjs ``` ## Publish `js/` is `@publicdomainrelay/atlibs-webrtc` on npm (`npm publish --access public`; the tests stay out of the tarball). The crate is not on crates.io: depend on it by git. ```toml atlibs-webrtc = { git = "https://knotless.dev/johnandersen777.bsky.social/atlibs-webrtc" } ``` > **STUN only.** There is no TURN relay: two peers behind symmetric NATs will not connect.