youros

Choices

D-3 and D-7 to D-9 are under review: proposed, not settled. D-10 to D-21 are new; D-29 to D-31 are access and MCP.

D-1 Name — made

your.os; each instance is <you>.os.

D-2 Signatures: hybrid Ed25519 + ML-DSA-65 where they live long — proposed

A signature cannot be harvested now and forged later: forging needs a quantum computer at the time, so a short-lived proof is safe classical. A long-lived key is not: whoever can forge the account key mints delegations. So the account key, node keys and delegations are hybrid (both must verify) from the first release; per-call proofs are Ed25519 alone, since they are live and ML-DSA-65 makes each one ~3.3 KB against 64 B. Limit: a passkey or hardware key signs only classical, so a chain rooted at one is classical at its root.

D-3 Five concepts, our own fabric — proposed, under review

Node, log, pipe, session, service (README.md). A relay is a node that forwards {to, ciphertext} by key and learns nothing else. Not zenoh as the fabric: spikes/zenoh/ used six calls and pulled 435 crates, and a router that routes on key expressions sees which verb, on which node, how often. The spike kept: sealing costs 25 B a message; a chain bound to its session costs almost nothing.

D-4 Services are processes on a local socket — proposed

Any language, so existing services port unchanged. In-process (a Rust trait) where there are no processes; WASM components later, for services nobody vouches for.

D-5 Licence — open, narrowed by D-16

Argued in README.md (Licence), with pricing: free for people, paid for businesses. D-16 leaves two: Apache-2.0, or AGPL-3.0 with a contributor licence agreement, since our relay builds on the open crates and an AGPL contribution could not reach it without one.

D-6 Domain and passkey RP ID — open

Fixed for ever once the first credential is minted. Per-person subdomains need the domain on the Public Suffix List. The docs' current host is a stand-in, and user origins can't go under it: its subdomains already serve third-party pages through a tunnel, and a key-holding origin must not be same-site with those. The landing needs a registrable domain of its own. A self-hosted account brings its own domain and needs no listing (relay/own-domain); the Public Suffix List is for names on ours, and its review takes weeks, so the submission goes in as soon as the domain is chosen. Names leak three ways, and each is closed before the first is minted: Certificate Transparency logs every name a certificate covers, for ever and in public, so names share a wildcard certificate; SNI names the host in the clear, so the edge offers ECH; DNS asks in the clear, which DoH (the browser's, 1.1.1.1 or any) hides from the network.

D-7 Sessions outlive pipes; pipes bring reliability — proposed, under review

Messages are numbered and sealed one by one; reliable ones are acknowledged end to end and resent only after a pipe change; datagrams use a replay window. Congestion control is the pipe's (TCP, SCTP, QUIC). Rejected: a session that dies with its pipe (a handshake per flap, and no lossy streams); writing our own QUIC (loss recovery and congestion control are its years). Open before accepting (spikes/sessions/): TCP_NODELAY on every stream pipe (Nagle cost a 43 ms p95); protect the header, so a relay cannot read a message's kind and counter; bound the unacknowledged buffer and end the session when it overflows; build the rekey ratchet with its generation number.

D-8 Sessions — proposed, under review

Peers' keys come from the log, so a handshake carries ciphertexts only and takes one round trip, the first request riding in it. HKDF over the handshake, one key per direction, counter nonces, rekey by ratchet on time or count, with the key generation in each message. The chain is verified once; the session ends when it expires or is revoked. Nothing here is a setting.

D-9 Pipes: a WebSocket floor, the rest optional — proposed, under review

Required: WebSocket to a relay. Optional: WebTransport to a node that made itself reachable (a self-signed ECDSA certificate of ≤ 14 days, its hash sent sealed), WebRTC for every other NAT (ICE; DTLS underneath, not trusted; TURN is a relay that sees ciphertext), native QUIC between nodes. Rejected: iroh as the fabric — relay-only in a browser, and its hybrid key exchange comes from aws-lc-rs, which may not build for wasm; ours ran in a browser in 23 ms. Open before accepting (spikes/sessions/): drop or shorten to on a direct pipe (32 of a message's 66 bytes); the relay drops datagrams under load and never reliable messages (it forwards everything today). Measured on msi: no mapping protocol, no IPv6, port-dependent filtering — WebTransport-direct could not work there, so WebRTC carries the direct path on such networks. More candidates: WebRTC-direct (with the certificate hash known, sent sealed, a browser opens WebRTC to a public UDP port with no signalling, as libp2p does; Safari included); port mapping (UPnP, NAT-PMP, PCP); IPv6 host candidates; port spraying on native nodes for hard NATs; another mesh's addresses (a tailnet, the LAN) as one more candidate. When punching fails the WebSocket floor has no datagrams, so a screen or a voice queues behind everything; Cloudflare Realtime DataChannels are a datagram relay that sees ciphertext, a fallback beside TURN. MoQ relays (WebTransport to the edge, which a Worker can't serve) are worth watching while the spec is a draft. Rejected: WARP and Zero Trust device-to-device (every user enrolled in our organisation, no browser, Cloudflare's identity instead of the log); Cloudflare Tunnel (one more relay, an account per machine).

D-10 The account log: a signed DAG, and a revocation wins — proposed

Each entry names its parents by hash, so the log is a grow-only set that is also tamper-evident and causal: genesis (the account key), add device (key, Admin or Member, name), revoke key, grant, ungrant. Hybrid-signed (D-2) by the account key or an Admin device whose authority is in the entry's own past; the signer's keys come from that past, never from the entry. A revocation is final and is never undone by what it did not see; anything else the revoked key signed that the revocation did not see is void, so a lost laptop's earlier enrolments stand and its later ones do not. Two adds of one key that disagree take the lower role. Encoding is hand-rolled, one form per entry: core/crypto-crates-only allows crypto crates only, and a signature needs canonical bytes that serde formats do not promise. Rejected: a linear chain (two devices append at once, and one of them would lose); the account key signing everything (it lives in one browser, and enrolment must work from any Admin); postcard (a dependency, and canonical only by convention).

D-11 Pairing: 128 bits in the code, no PAKE — proposed

An Admin makes the code: 16 random bytes, Crockford base32 (26 characters), then @relay. It is pasted (sh -s <code>) or scanned, never typed, so it can carry a full key. HKDF over it gives the mailbox the relay routes on and one key per message. The new node sends its hybrid public key, a name and a proof of possession, sealed; the Admin appends the add and replies with the log, sealed under a key bound to that hello and a fresh salt. The relay sees a mailbox id, salts and AEAD ciphertext; changing a byte fails the AEAD. One code admits one key, until an expiry on the Admin's clock; the same hello again gets a fresh reply and no second entry. Rejected: SPAKE2 for a typed six-digit code (classical, so a recorded transcript opens to a quantum computer later; and nothing in the flow is typed); TOFU on the relay's word. Not covered: a code read off a screen or shell history before use admits the reader instead, which the owner sees in the device list. Revisit for codes of words (pairing/typed-code, pairing/backup-code, planned): a short live code needs a PAKE, used only to authenticate an ML-KEM exchange, which answers the objection above.

D-12 Grants live in the log; chains narrow them — proposed

A grant is a log entry: subject key, a key-expression scope, rights (read, write, call) and an optional expiry. Any key can be a subject (an agent, an MCP door, another person's node), so every principal is a key holding grants. The holder asks the log may(key, rights, resource, now): the account key and Admin devices may do anything, a revoked key nothing, anyone else what an unexpired grant covers. Ungrant and revoke are final. Delegation chains (seal) stay the offline, short-lived way to pass on part of a grant. Rejected: roles with fixed permission sets (they widen as features land); grants outside the log (two sources of truth for one question).

D-13 Content in Loro, code in git, only built code runs — proposed

Why, and what it was chosen over: shell.md, Content and code and Sharing: tabs first, then your devices.

D-14 A pane is contained to its rectangle; the shell is not editable — proposed

Why, and what it was chosen over: shell.md, Four rings.

D-15 Escapes independent of what they rescue — proposed

Why, and what it was chosen over: shell.md, Escapes.

D-16 The wire and a relay are open; running ours is not — proposed

The wire (relay.md, version 1, frozen), the client, the core and a reference relay (relay/) are open, and one suite (e2e/relay/client) holds every relay to the wire. Our relay is closed: it depends on the open crates and adds what running it takes (limits by plan, metering, abuse handling, TURN credentials, names on our domain), never a change to the wire; a wire change lands open, as a new version, first. Hole-punching is the client's: offers and answers are sealed frames through any relay, STUN is public, so a direct pipe needs nothing of ours. The value we keep is operational (a domain on the Public Suffix List, uptime, TURN, support), the same line as Tailscale's open client and closed coordination server. Rejected: all of it open (whoever hosts it best takes the business, and nothing is left to pay for); the wire closed (the client could not honestly be called open, and can I leave? would be no); hole-punching on our server (the client does the work either way, and its server side is too small to be worth keeping). What it costs: ours sees metadata (relay/ours-sees lists it), and the reference relay must keep up with ours for one account (relay/reference).

D-17 The log syncs three ways — proposed

Every session carries both sides' log heads and the difference crosses in it (log/heads-in-session); a mailbox per device at the relay holds new entries, sealed, until it attaches (wire2/mailbox); short-lived chains end what never arrives. A holder drains its mailbox and compares heads before it answers (log/sync-before-serve), and an account may set how stale a holder may be (log/freshness). Signatures make an entry tamper-proof; sealing hides the names and grants in it. Rejected: the mailbox alone (a relay could withhold a revocation; it can delay one here, but a session or an expiry gets past it); pushing to every device directly (the ones away miss it); asking a server for the log (a server's word).

D-18 Wire version 2: mailboxes and the relay's key — proposed

Version 1 stays, frozen; version 2 is a new attach context with its own suite (relay.md). One mailbox serves pairing, messages for devices that are away, log entries and wake-ups, rather than three mechanisms. The relay signs its challenge with a key in the log (wire2/relay-signs). Rejected: pinning certificates (they rotate, self-hosting breaks, and a pin says less than a key the account chose); trusting TLS at all (a proxy with its own CA opens it, and sees only what the relay sees, which is why nothing depends on it).

D-19 A closed browser is woken by Web Push — proposed

A browser node is online only while a tab is open. Web Push (RFC 8291) carries an encrypted payload through the browser vendor's push service to a closed browser; the relay sends it when a mailbox gets a frame for a device that left an endpoint, sealed to that device, so the vendor sees that a wake happened and nothing else. Native phones use APNs and FCM the same way, later. Rejected: a tab kept open; polling.

D-20 Delivering the browser's code — proposed, open

Native releases are signed, the installer checking against a key it carries, so only the first download is trust on first use. In a browser, files other than the page are content-hashed and loaded with Subresource Integrity, and the service worker accepts an update only when it is signed by the release key. What that can't stop: the page and the service worker's own script come from the network, and a proxy that can mint certificates for the origin can replace them (core.md, What isn't promised). What more limits a page that isn't ours: D-21.

D-21 Native mode, and noticing interception — future

browser.md. A hostile page gets little: the core asks its host to sign and never holds a key (WebCrypto keys that can't be exported; a TPM, Secure Enclave or security key), the account key leaves the browser for a passkey or a native Admin, a browser's delegation lasts hours, its stored bytes are sealed under its passkey, and widening the account takes a second device. Native mode takes the network out of the page's source: the page from your own node on localhost, or an extension, running releases the account approved in its log. Interception is noticed where something outside the page can see it: a native node trusting only bundled roots and our CAA issuers; a challenge bound to the TLS channel where a relay ends its own TLS; a relay seeing a TLS fingerprint that disagrees with the browser; a page finding its update unsigned; a node elsewhere checking what our origin serves. Findings go to the other devices sealed, and the browser stays locked until a person clears it. Rejected: a challenge from the page to the server (a proxy that ends TLS forwards both halves); certificate pinning (browsers removed it); a build per person (a proxy replaces it as easily). Limit: an attacker who replaces the page before any clean load, without native mode, still wins.

D-22 The OS's state is docs in three tiers — proposed

devices.md, Three tiers of state. Account state (workspaces, theme, keybindings) is a doc per kind synced to every device; device state (zoom, which workspace each screen shows) is a doc per device, synced too and overriding the account's; the moment's (focus, selection) is presence or nothing. The schema fixes each setting's tier. Apps are registry components whose state is docs of their own (app/component). Rejected: one doc for all the shell's state (a grant on it would be all or nothing, and it would sync at the rate of its busiest part); settings in browser storage (lost with the browser, never reach another device); device state kept only on the device (lost with it, and can't be copied to a new one).

D-23 Files stay files; a Loro doc sits beside them — proposed

files.md. A folder's files are the truth for your tools; a doc beside it gives sync, history and merging, and a git repo is mirrored from its working tree, never its store. Code edits sync live only between your own devices: anyone else's arrive on a fork merged after its diff is seen, and a change from git is merged at once only when no line was changed on both sides, otherwise held as a conflict. This narrows D-13 rather than reversing it: the spike showed same-line edits merging into code that compiles (645), so nothing merges code silently. History: every change for a window, an exact cut, thinned checkpoints outside. Rejected: converting files into docs (breaks every tool, and leaving); whole-file sync with conflicted copies (loses the edit, not the file); code as a plain CRDT (D-13's reason, now measured); keeping all history in the doc (only grows).

D-24 A phone runs one app: the shell, laid out for one pane — proposed

shell.md. A phone is one node with one key, so it runs one app, the shell, as a PWA and later one native app. It shows one pane at a time as a stack of cards. A tablet or a phone held sideways shows two side by side, from the same layout tree (D-22: the layout is device state). Asks go in a strip that no pane can draw on. The terminal gets a row of keys a phone lacks (Esc, Ctrl, Tab, arrows). A pane opens straight from a link (each pane has a URL), from the icon's shortcuts, or when something is shared to it. Rejected: an app per pane, because each home-screen web app on iOS has its own storage. That would mean a key, a pairing and a revoke for each app, each app redoing the shell's asks and grants, and an App Store review for each one.

D-25 One way to someone else's site: a browser in a box on a node — proposed

browsing.md. A site renders in a real browser on a node, in a profile that is a box of its own, and the pane is a view of its tab: a copy of the document where there is one, video where there isn't, the node choosing; pictures first. A site's script never runs on a key-holding origin. Rejected, each from the prototype, which built them all: framing through a tunnel at a second hostname (the wrong origin's cookies, and a site D-6 must keep keys away from); a worker in between, rewriting addresses, or a service worker keeping cookies (each breaks at the same-origin rule); a cookie jar on a server (half a jar, and every third-party session in one place); a reader beside it (a view of the same tab instead); a switch between engines (a framed page can't say it failed, so the person had to guess); an extension in your own browser (only where installed, never a phone, and a second authority across the origin boundary). What it costs is stated in browsing.md: passkeys, calls, a node that has to be on.

D-26 Untrusted code runs in a box whose only way out is the node — proposed

sandbox.md. One service makes every box, as the node's own user: Linux namespaces, seccomp, a cgroup, and a network namespace whose one interface is a TUN the node holds, so the node is the gate for TCP, UDP and DNS and writes the audit outside the box. A browser profile and each instance of an extension are boxes; an extension's manifest is its grant (D-12). Rejected: a proxy setting (an HTTP-layer rule; the prototype's browser WebRTC'd around it); pasta or slirp as they are (they forward everything, and decide nothing per flow); rootful containers (the first minute is an ordinary user); seeing inside TLS (a CA of our own is an inspection box). Open: Ubuntu's AppArmor restriction on user namespaces, measured before this is accepted.

D-27 Watching the whole node is opt-in, and stopping needs root — proposed

sandbox.md, The rest of the node. Without root the node reads every socket from /proc/net and raises what it sees; blocking or seeing every flow is a separate helper installed with sudo by someone who asked, never in the one line. Rejected: root for every node (the first minute, and a root node is a bigger prize); trusting a watcher on the machine it watches once that machine may have fallen (later, a node that is the network's router or resolver watches from outside).

D-28 Nothing is kept until chosen; a folder is what's kept — proposed

files.md, Open, keep, hold. Opening a file is live collaboration through the machine it is on, with nothing kept. Keeping is a choice made on a folder, an account-wide list; holding is each device's choice of which kept folders it carries and where, including none. Leaving something out is an ignore rule inside the folder, .gitignore honoured and .git never synced. Un-keeping deletes nothing. Rejected: keeping whatever is opened (a glance fills every device and syncs what was never meant to leave); keeping the whole home folder (builds, caches, secrets); keeping single files (a project's files refer to each other; a file is left out of a kept folder instead); every device holding everything (a phone, a borrowed browser).

D-29 Every caller is a key, and its holder checks it — proposed

access.md. Devices, machines, each service and extension instance on a node, each MCP client and someone else's node are all keys, and every call is may(key, class, resource, now) asked by the node holding the resource. A service's or a client's key is held by its node, which signs for it, so a service is a caller of its own to other nodes without ever holding a key (service/no-keys). Resources are key expressions down to a path; a verb's class (read, write, run, reach) is the holder's manifest's, never the caller's. A grantee is never a device, so it can't add, grant or answer an ask. Rejected: a caller named by a string (the prototype's gateway told callers apart by browser, agent, mcp, and two MCP clients were one caller); service-level scopes only (a grant on files is every file); services trusted to confine themselves with no check by the node (a bug is then a grant); one key per node for everything on it (the far machine can't tell the MCP door from the files service).

D-30 Ask is part of the grant, and an answer is a grant — proposed

access.md, An ask, from call to answer. A grant carries ask, so whoever grants decides whether a person is asked, and no grant means no. An ask is sealed to every Admin device's mailbox and woken by Web Push; a person answers with a signature bound to the exact call: once, for this task (a short chain) or always (a grant in the log). Widening answers need a passkey with user verification. Rejected: ask as a property of the verb (it puts the decision with whoever wrote the verb, the prototype's first gateway); a policy doc beside the log (two sources of truth for one question, which D-12 rules out); approvals only in an open tab (in the prototype every ask timed out when no desktop tab was open); a classifier that approves (only a person answers; one may deny or raise); MCP elicitation as the prompt (the client draws it, and the client is asking).

D-31 MCP is a built-in service, off until settings turn it on — proposed

mcp.md. Every node ships the MCP service, off. One switch in settings turns it on and makes a client, a key the node holds, with default grants shown before they're written: read this node's kept folders, write them under ask, nothing else. The page gives the line for Claude Code, Codex and any client that runs a command; the command is a stdio pipe to the service's local socket. Rejected: on by default (a door nobody asked for); a command that sets it up (setup is choices about grants, which a page shows and a command hides); HTTP on localhost (DNS rebinding, other users); the door as a node of its own (it would need a pairing of its own and a copy of the log, to do what a service on a node already can); one key for every client (two agents would share every yes). Open: a door for agents in the cloud, and where it is hosted.

D-32 A node updates itself from a signed, numbered release, when idle — proposed

machines.md, Updates. A release is a manifest of a number and each build's hash, signed with one hybrid release key whose public half every node is built with; the node refuses anything unsigned, signed otherwise, or not newer than itself. It downloads at once and restarts only when no session has carried anything for three minutes, or after a day, into a trial the old build watches: a release that doesn't come up within a minute is rolled back and never tried again. The manifest names its component, so services and extensions can be released under rules of their own (D-33); the account approving releases in its log (browser/approved-releases) adds to our signature, it doesn't replace it. D-33's wait for a change to the base sits on top of this and isn't built yet: today every node release is taken once idle. The core and today's services ship and update as one binary; services updated on their own, and services that outlive a restart of the node, are the next step; an extension is released and signed by its publisher, never in ours. Rejected: trusting the download host (it serves the installer, but anyone who takes it over would run code on every machine); updating the moment a release appears (it would drop a shell mid-command); asking on every machine (a fleet nobody updates stays vulnerable); a package manager per distribution (one static file covers every Linux, and the installer needs no root).

D-33 Releases are tiered; the base waits where it can be seen — proposed

trust.md. A small named trusted base (core, access checks, updater, sandbox boundary); every release reproducible and in a public log before any device runs it. Services update as soon as they're idle; a change to the base runs once the account approves it, automatically after 48 hours in the log with no rebuild disagreeing, or asked each time if you choose; an urgent base fix skips the wait only with a second release key. Anything that widens what code can reach waits for a person, and a device undoes a release that fails on it. Rejected: asking a person for every update (they can't judge the diff, so it delays fixes and assures nothing); updating everything at once (one key decides what runs everywhere, unseen); never updating the base without a person (security fixes stall on the slowest device).

D-34 There is no "public"; a node faces outward under its grants — proposed

access.md, Nodes that face outward. A site's worker or an agents' door is a grantee: its grants bound what anyone can get through it, its manifest says who it serves and the grant page shows that, and it holds a copy of a pinned version. Writes from outside are append. Rejected: a public flag on folders (a second way to say who may read, beside grants); a worker calling through to the machine per request (the site sleeps with the laptop); asks on outward grants (strangers would page you).

D-35 One renderer, MDX for structure, apps edited as source — proposed

building.md. The editor's renderer builds this site, pinned by the commit, and CI fails when it and the released editor disagree. Pages' and apps' rectangles and words are MDX with literal props and nothing that runs; layout is Stack, Row, Grid, Box; a component made of others is a file, one with behaviour is code from a release. An app is edited from a machine's working tree with a preview in a frame of its own, and reaches devices only as a release. Rejected: the site keeping its own renderer (the pane and the site drift); full MDX (expressions and imports are code in a file anyone can edit); editing the running shell (D-14); visual edits that rewrite code (unreviewed code changes); a package for other people's sites now (one user, and the contract isn't settled).

D-36 News of a release rides the relay's socket; the page is its own Worker — proposed

relay.md, Release hints and A deploy is a blip. Every node already holds a hibernating socket to its account's object, so a release is a hint on it (release <node> <page>, wire 1.3): sent on attach and on a wake, and, on ours, pushed by publishing, one request per account. The hint only says when to look; a node fetches and checks the manifest itself, and also every fifteen minutes from the static files, so a relay that holds it back delays a node, never strands it. The page and the releases are a Worker of their own on the origin, serving files before any script runs and handing the rest to the relay's Worker over a service binding, so publishing never restarts the relay and a page load stays a free cache hit. Rejected: a server-sent-events stream (a second connection beside a socket that is already there); polling alone (the delay is the interval, and a short interval only moves the cost); one Worker for page and relay (every page change restarts every account's object and drops every socket); the relay in front, handing the page its files (every page load would run a script); routes splitting the origin between the two (they'd have to name every file path, and the relay's paths are account ids).

D-37 Contain CSS, don't filter it: one document frame — proposed

building.md, Two frames, and any CSS. Pages, published pages and the browser's copy of a site are drawn in one frame, sandboxed without scripts, under default-src blob: data:, its DOM written from outside and its assets ids held as blobs; inside it any CSS is allowed. Code runs only in a live frame on an origin of its own. The allowlist stays for edits and for the shell's realm. Rejected: an allowlist as the limit (no design beyond its list); scoping user CSS to a pane's root in the shell's realm (url(), fixed positioning and selectors leak or escape unless every one is caught); loading public assets straight from the page (the prototype's mirror does, so a page's CSS can reach the internet from the shell's origin).

D-38 The remote desktop is a headless shell of the user's own — proposed

machines.md, The desktop. youros-desktop, beside the node, starts gnome-shell --headless as the user on a private D-Bus, with no logind session, and the pane is that desktop. The screen at the machine is shown only on request, and only while it is unlocked. It needs no root, and works whether the machine is unlocked, locked or logged out. Rejected:

  • Mirroring by default, with an extension that keeps casting through the lock. Remote access then reaches past a screen somebody locked.
  • A root helper starting a logind session. It needs root, PAM and seat handling, and the physical display path is where an earlier one lost days.
  • GNOME Remote Desktop. RDP, with credentials of its own beside the account's grants, and no browser client.
  • A gst-launch child. A black box, whose watchdog guessed from silence.
  • Video on the ordered reliable pipe. One lost packet holds up every frame behind it.

Later — not promised yet

Each fits without changing the five concepts.

  • Publish a note: a button on a note or folder that grants its published version to a hosting node we run (D-34), md and mdx first.
  • Someone else's your.os: another account's devices, peer to peer, holding finely scoped grants on yours (a grantee whose chain starts at their account); how their keys reach your log, and how far you trust their account's revocations.
  • Levels: a symmetric profile for weak devices (PSK at enrolment, ratchet), fixed at enrolment; a delegation's minimum level.
  • Lossy links (serial, BLE) behind a reliability adapter; a device behind a plaintext hop as a resource of its gateway.
  • Mesh: a pipe that reaches many keys (zenoh with opaque key expressions), a hop limit; never dynamic routing in the core.
  • Resumption without the KEM after a session has ended.
  • Pub/sub with a content key per subtree wrapped to readers.
  • Zero copy across the service boundary (shared memory).
  • Bridges: zenoh/ROS 2, Matter, Meshtastic.
  • Onion pipes: a Tor onion service (arti, in Rust) as a pipe; no relay learns who talks to whom, at Tor's speed.
  • Someone else's relays: Nostr relays as a fallback carrying {to, ciphertext} when every your.os relay is down.
  • MoQ relays: WebTransport to the edge, once the spec settles (D-9).