← Commit history

docs/mesh.md: the Adom Mesh, per-machine trust, and permission prompts that follow you across your boxes and into Hydrogen (2.1.74)

John Lauer ·86495c13be ·1mo ago ·parent 17ffd75
1 file changed +153
docs/mesh.mdadded+153
@@ -0,0 +1,153 @@+# The Adom Mesh: your machines find each other, and your permission prompts follow you++One person, several computers, one Adom account. The Adom Mesh is how every Adom Bridge you run+knows about the others, reaches them, and shows you a permission prompt raised on any of them,+wherever you happen to be sitting. This page is the user's view: what is on, what it costs you,+what a prompt from another box looks like (in Adom Bridge and in Hydrogen), and what a human+still has to decide.++## What the mesh is++Every Adom Bridge (2.1.40 and later) carries a durable node identity: an ed25519 key that lives+only on that machine (`~/.adom/mesh/node.key`, DPAPI-protected since 2.1.66). Machines signed into+the same Adom account learn about each other through the account directory in the Adom cloud+(each box publishes a small `bridge.runtime.<machine>` record: its name, node id, version, and the+relay it is homed on) and, on the same network, through mDNS. Nothing secret is in the directory;+the transport verifies the key, so a stale or forged record cannot impersonate a box.++The mesh has three modes (Settings, Mesh; or `settings_set {"mesh_mode": ...}`):++| Mode | What is open on your PC | How your boxes talk | When Windows asks anything |+|---|---|---|---|+| `off` | nothing | only through the cloud command relay, as always | never |+| `relay` (the default) | nothing: no port, no listener | each box holds one outbound connection to the Adom-hosted mesh relay; traffic goes box, relay, box, still end-to-end encrypted on the two node keys (the relay forwards ciphertext) | never |+| `direct` | one UDP endpoint | same as relay, plus direct QUIC between boxes on the same network or where a direct path exists | once, the ordinary per-app firewall question, with an on-screen caption explaining it |++Relay mode is the one networked feature that ships on, because it is outbound-only TCP, the same+class as the connection every Adom Bridge already makes to its command relay. Nothing on your+machine listens. Direct mode is opt-in; the first bind draws a caption next to the Windows+firewall dialog saying exactly what is being allowed (only machines signed into your own account+can connect; everything else stays on 127.0.0.1) and the same notice is mirrored to your other+boxes so nobody wonders what that dialog was.++## What it changes for the AI that drives you++- `--target <name>` (or `target` on the loopback API) tries the mesh first and falls back to the+  cloud command relay. A mesh answer carries `route: {via: "mesh", peer, nodeId, rttMs}`; the+  `via` is `direct` or `relay` depending on the path iroh selected.+- `targets` lists every box the relay knows and every box the mesh directory knows, in one list.+- `desktop_mesh_status` says what this box is (node id, mode, relay, endpoints, counters);+  `desktop_mesh_peers` lists the others with their reach, link, version, and trust state.+- Screenshots and `pull_file --target <peer>` stream over the mesh path with a sha256 check, so a+  file from another of your boxes lands on this one without a relay host holding it.++## Who is allowed in: a human trusts each new machine++The directory says which node ids belong to your account. Each box keeps its own answer to a+different question: which of those has a human let into this box's mesh (`~/.adom/mesh/trust.json`).++- **First start:** the fleet as it stands is trusted as a snapshot, so an upgrade or a fresh+  install joins an existing set of boxes without asking anything.+- **A new machine afterwards:** every box raises one card, "A machine wants to join your Adom+  Mesh", naming the machine, its Adom Bridge version and node id, with Trust this machine / Deny.+  Until someone clicks Trust, the new box is refused in both directions.+- **One click settles the fleet:** the decision is carried to your other boxes over the mesh+  (node-authenticated, never over the plain command relay), and their copies of the card go away.+- **Approval is human-only.** `desktop_mesh_trust` can `list`, `revoke`, `deny`, and (2.1.74)+  `raise`; it cannot approve. No verb, no AI, no remote caller can trust a machine into your mesh.++Since 2.1.74 an unanswered join card does not get lost. Hydrogen's copy of a card expires after+330 seconds; before 2.1.74 the node then sat "pending" forever while the status text claimed a+card was on your boxes. Now Adom Bridge re-raises a pending card every 10 minutes and whenever+`desktop_mesh_status` is read, an AI with eyes on the screen can bring it back on demand with+`desktop_mesh_trust {"nodeId": "...", "action": "raise"}` (or `{"all": true}`), and the pending+summary says when the card was last raised and whether it is still likely visible.++## Permission prompts follow you across your boxes++When one of your machines raises a human prompt, a shell approval, a grant request, a+web-control consent, a credential import, an input approval, or a toast with buttons, the same+card appears on every other machine signed into your account. Each mirrored card carries a bold+chip naming the box it belongs to ("ON CONFROOMROG"; local cards say THIS MACHINE), the AI thread+that asked, the command, and the reason the AI gave.++This does not need the mesh: since 2.1.59 the mirror also travels over the cloud command relay,+which every Adom Bridge is already connected to outbound, so a machine with the mesh `off` still+sees the other boxes' prompts. The mesh is the faster direct path and the fallback when the relay+is down; a card that arrives over both shows once.++Deciding a mirrored card is exactly as strong as deciding it on the box itself:++- A decision travels back to the origin and lands in the same place a local click does. Once,+  1 hour, 24 hours, Auto (persistent), or Deny mean the same thing there.+- Only a box that received the mirror can answer it: the origin stamps a one-shot nonce into the+  mirrored card and refuses a decision without it. A cloud AI is also a relay caller, and this is+  what keeps it from approving its own prompt by echoing an id.+- Every decision is attributed: the origin's Activity Log and the card's record say which machine+  the human decided on.++A native Windows toast announces every mirrored card once ("Permission request on winvm ..."),+so a box with Adom Bridge in the tray still hears about it. The `cross_box_toasts` setting+(`prompts`, `decisions` by default, `all`) says how much plain toast traffic crosses between your+boxes beyond the cards themselves.++## In Hydrogen: the cards show up where you are looking++When Adom Bridge runs embedded under Hydrogen, the human is looking at Hydrogen, not at Adom+Bridge's window and, if toasts are hidden or Do Not Disturb is on, not at a toast either. So every+card is forwarded to Hydrogen's permission dialog:++- **A local prompt** (an AI asking to run something on this machine) has always been forwarded.+- **A prompt from another of your boxes** is forwarded too since 2.1.74. Before that, a cross-box+  card stopped at Adom Bridge's own window and a Windows toast, and Hydrogen never showed it: you+  could see "Permission request on winvm, open Adom Bridge to decide it here" and find nothing in+  Hydrogen. Now the same card, with the machine chip, renders in Hydrogen on every box you have it+  open on, and deciding it there routes the decision back to the origin with the nonce, the same+  path a click in Adom Bridge takes.+- **Dismissals are forwarded as well.** When the origin decides, expires, or drops a prompt,+  Hydrogen's copy on every other box goes down with it. A mirrored card nobody answers is dropped+  from the receiving box after 330 seconds (the same cap Hydrogen applies), so a stack never+  fills with prompts that can no longer be answered.+- **The join card for a new mesh machine** is forwarded the same way, and re-raised as described+  above until a human decides.++The reliable surface is the dialog, in Hydrogen or in Adom Bridge's window. A Windows toast is a+courtesy: on a fresh Windows 11 install Do Not Disturb stays on until the one-time "Allow important+notifications from Adom Bridge" prompt is answered, and until then a toast folds into the+notification center unseen. `GET /status` on the loopback API reports this as+`notifications.mayBeSuppressed` (2.1.73), so an AI that raised a prompt can tell you to look at+the dialog instead of waiting for a toast, and `notification_settings_open` opens the Windows page+where the permission is granted. Adom Bridge never grants that permission for itself.++## The Mesh view++The Mesh button in Adom Bridge's header (or `desktop_ui_panel {"panel": "mesh", "action": "show"}`)+opens "Your mesh": this machine in the centre, the Adom mesh relay above it as its satellite, your+other machines fanning into that relay, the cloud command relay beside it, and each box's attached+Hydrogen container beneath its parent. Every line says what carries it (direct, via the Adom+relay, cloud command relay); hovering a line or a box shows the transport, the encryption in plain+words, both endpoints, round-trip time, bytes each way, and how long the link has been up.+Hovering a box also shows its trust record: who approved it, on which machine, when.++## What it cannot do yet++- **Direct paths across different networks.** Two boxes behind different NATs talk through the+  Adom mesh relay (still end-to-end encrypted). Hole punching needs a UDP-reachable relay host+  and is not on yet; a box on the same network as you connects directly in `direct` mode.+- **Other accounts.** The mesh is one account. Pairing a work account with a personal one is+  planned, not shipped.+- **Containers as full nodes.** Your Hydrogen containers are drawn under their parent box and are+  reachable through it; they do not yet hold their own node keys.++## Verbs and settings on this page++| Surface | What |+|---|---|+| `desktop_mesh_status` | this box: node id, mode, relay, endpoints, counters; also re-raises any pending join card older than 330 s |+| `desktop_mesh_peers` | every same-account box: reach, link (transport, encryption, RTT, bytes), version, trust |+| `desktop_mesh_trust` | `list`, `raise` (2.1.74), `revoke`, `deny`; never approve |+| `desktop_ui_panel {"panel": "mesh"}` | open the Mesh view |+| `settings_set {"mesh_mode": "off" \| "relay" \| "direct"}` | the mode; `mesh_enabled: true` means `direct` |+| `settings_set {"cross_box_toasts": "prompts" \| "decisions" \| "all"}` | how much plain toast traffic crosses boxes |+| `GET /status` -> `notifications.mayBeSuppressed` | whether a toast on this box may go unseen |+| `POST /permission/resolve {id, decision}` | how Hydrogen decides a card; a `remote:<peer>:<id>` id is routed back to the origin (2.1.74) |