app
Adom Bridge
Public Made by Adomby adom
Adom Bridge unleashes your AI onto your full PC: full power, full safety. The AI breaks out of the container it runs in and onto your real machine, managing and running your entire laptop as you: launch apps, move files, drive any window, control KiCad, Fusion 360 or your real signed-in browser. Works with any AI, cloud or local (Claude Desktop, Claude Code, Codex), no Hydrogen required. Bridge and its bridges are an operating system for AI, with managed Node and Python runtimes, and a human-onl
← Commit history
docs/mesh: how the Adom Mesh works, and how prompts cross machines
1 file changed
+315−156
docs/mesh.md+315−156@@ -1,157 +1,316 @@-# 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 (2.1.75) 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.-- **Plain toasts reach Hydrogen too (2.1.77).** A toast with nothing to decide ("export done", a- bridge's "library imported", "Hydrogen was restarted by ...") is shown to Hydrogen as one of its- own toasts, with the origin box named when it came from another of your machines (subject to- `cross_box_toasts`, exactly as the Windows toast is).--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 |+# The Adom Mesh++Your machines talk to each other directly, encrypted end to end, with keys that+never leave them. A permission prompt raised on the machine in the workshop+appears on the laptop in your hand, and you answer it there.++This page explains the whole thing: what it is, the three modes and why the+default one opens no ports, how machines prove who they are, how you decide+which machines are yours, and exactly how prompts and toasts travel between+them.++---++## The problem++An AI drives your desktop from the cloud. That works well until you have more+than one machine.++You are at your laptop. The AI is doing something on the workshop PC and it hits+a permission gate. The card appears on the workshop PC, two rooms away, and the+AI sits waiting for a click nobody is there to make. Meanwhile you cannot ask the+laptop to fetch a file from the workshop PC, because as far as the laptop is+concerned that machine is just a name on a list somewhere.++The relay that connects your machines to the cloud cannot fix this on its own. It+is a hub: everything goes up to it and back down. It is also a place your traffic+passes through.++The mesh fixes both. Your machines get a way to reach each other **directly and+privately**, and anything that needs a human gets shown to wherever the human+actually is.++---++## Three modes++++`mesh_mode` has three settings, and the difference between them is entirely about+what listens on your network.++| Mode | What it does | Does Windows ask about the firewall? |+|---|---|---|+| `off` | No mesh. Machines reach each other only through the cloud relay. | No |+| `relay` | **Default.** Full mesh, but every connection is carried over the Adom relay. Nothing listens on a network interface. | **No** |+| `direct` | Machines connect straight to each other on the local network, with the relay as fallback. | Once, the first time |++### Why `relay` is the default, and why it is still a mesh++Adom Bridge's standing rule is that it never opens a listening socket on a+network interface without being asked. Windows Firewall shows its "allow this app+on your network" dialog the first time unsolicited traffic reaches an app that is+listening, and a non-administrator cannot pre-create the rule. So a feature that+listens is a feature that interrupts you.++`relay` mode sidesteps this completely. The mesh binds **only to loopback** and+carries every peer connection through the Adom relay as an outbound connection.+No socket on a network interface, so the firewall has nothing to ask about.++It is still a real mesh, and this is the important part: **the relay carries+ciphertext it cannot read.** Connections are end to end encrypted between the two+machines' own keys. The relay forwards sealed bytes addressed to a node ID. It can+drop them, delay them, and see that two machines are talking. It cannot read them,+alter them, or pretend to be either end.++That makes `relay` mode the one networked feature that ships **on** by default. It+is outbound-only TCP, the same shape as the cloud connection Adom Bridge has always+had. That is a deliberate exception to the ships-off rule, not an oversight.++### When to choose `direct`++`direct` adds real peer-to-peer connections on your local network, with the relay+still there as a fallback. It is faster and takes your traffic off the relay+entirely when two machines are on the same network.++The cost is one Windows Firewall dialog, the first time. Adom Bridge does not+spring that on you: before it binds, it paints a caption on your screen explaining+what Windows is about to ask, that only machines signed into your own account can+connect, and that you should tick both network profiles. It also tells your other+machines, so a dialog appearing on a box you are not sitting at is explained rather+than mysterious. Clicking Cancel blocks only the mesh; nothing else changes.++---++## How a machine proves it is yours++Every machine generates one **ed25519 key pair** the first time the mesh starts.+The public half is the machine's node ID. The private half never leaves that+machine.++On Windows the private key is sealed with DPAPI, scoped to your Windows user on+that physical machine. Copying the file to another machine gets you nothing: it+will not unseal, so that machine mints a fresh identity instead, and your other+machines then ask you about the newcomer. Which is exactly what should happen.++Connections are QUIC with TLS 1.3, where **the certificate is the node key**, and+it is checked in both directions:++- The machine dialling looks up the node ID in its directory and connects to that+ ID specifically. Anyone without the matching private key fails the handshake.+- The machine accepting refuses any node ID that is not in its directory.++So impersonation requires the private key, which never leaves the machine that+made it.++---++## How machines find each other++Each machine publishes its **public** details to your account's cloud key-value+store: node ID, how to reach it, a version, and a short identity block used for+display. Every other machine signed into the same account reads that list.++Reads happen **when something needs them**, never on a timer:++- once when the mesh starts,+- when you address a machine the reader has not seen before,+- when a connection fails (the peer may have moved networks),+- when you open the Mesh view.++There is deliberately no polling loop. A 60 second poll across a large fleet is a+constant load forever to catch a case nobody is waiting on, so the directory is+read on the events that actually matter. Reads scale with use, not with installs.++---++## Trust: which machines are yours++The directory alone is not enough to let a machine in. It is written with your+account credentials, so anyone holding those, or a compromise of the store itself,+could insert a rogue entry. If the directory were the only gate, that entry would+be accepted everywhere.++So there is a second gate, and only a human passes it.++A node ID needs a **trust record** on a machine before that machine will dial it or+accept a connection from it. Records look like this:++| Field | Meaning | |---|---|-| `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.75), and `/permission/state` lists those ids as pending so Hydrogen keeps the card up |+| `state` | trusted, denied |+| `how` | `first-start` (the fleet that already existed when this machine first ran) or `human` (someone approved it) |+| `at` | when |+| `by` | who approved it, and on which machine |+| `on` | the machine holding this record |++The first time a machine starts the mesh, it trusts the fleet already in your+directory. That is the bootstrap. **After that, a node ID nobody has seen before+raises one card**, and only a person can answer it:++- clicking it in Adom Bridge's own window, or+- answering the mirrored copy that appeared on another of your machines.++There is no verb that approves a join. An AI can list trust records and it can+revoke or deny one, but approving is human-only, permanently. A decision made on+one machine is carried to your other trusted machines over the mesh itself, so one+click settles the fleet and the duplicate cards disappear.++The Mesh view shows each machine's trust record on hover, so "when did this box+join, and who let it in" is always answerable.++---++## Seeing it++Adom Bridge draws this honestly rather than reassuringly. Open the Mesh view and+hover any link, and it tells you what that link actually is: the path, the+encryption, and who can read it.++++The detail on its own:++++---++## The relay, and what it can see++Two different relays are involved, and they do different jobs.++| | Cloud command relay | Adom mesh relay |+|---|---|---|+| Carries | commands between your AI and your machines | mesh traffic between your own machines |+| Encryption | transport encryption to the relay | **end to end between the two machines** |+| Can the host read it? | yes | **no** |++That difference is drawn honestly in the Mesh view: a link carried by the mesh+relay is drawn in the accent colour with a padlock, and the plain cloud relay link+is not, because the relay host genuinely can read that one.++The mesh relay is enrolled: your account holds a token, each machine attaches it,+and a machine without it is refused and falls back to the cloud relay. The token is+never logged and never shown.++---++## Prompts and toasts across machines++This is the part people notice first: something needs a human, and it finds one.++### What crosses++| | Crosses machines |+|---|---|+| Permission cards (shell and system approvals, mesh joins, firewall notices) | **Always** |+| Toasts that are waiting on a button | By default |+| Plain informational toasts | Only if you ask for them |++Permission cards always cross, whatever your notification settings say. You can+turn off surfaces, never the decision.++### Where they show up++A mirrored prompt can appear in three places, and usually does in more than one:++1. **In Adom Bridge's own window**, in the permission stack, tagged with the+ machine it came from and that machine's colour and initials.+2. **As a Windows toast**, with buttons that decide it.+3. **Inside Adom Hydrogen**, if Hydrogen is running. Hydrogen renders it; Adom+ Bridge still owns the decision.++Here is one that crossed. The card was raised on a different machine, and it is+tagged with that machine, its colour and its initials, so you always know which+screen the request actually came from. The buttons decide it from here.++++In context, in Adom Bridge's own window, alongside a second request raised on this+machine:++++### How a decision is safe++A mirrored card is a grant path, so it is locked down.++The machine that raised the prompt mints a **one-shot nonce** and includes it in+the mirrored copy. A decision that does not echo the live nonce is refused. Only a+machine that actually received the mirror knows it, the fan-out reaches only+authenticated Adom Bridge desktops, and the nonce is consumed on use and expires.++The result: a human clicking a mirrored card is exactly as strong as a human+clicking the original. A cloud AI that can send commands cannot manufacture the+click, because it never sees the nonce.++Decisions are also **attributed**. When a card is dismissed, every copy learns the+decision and which machine made it, so the record says "allowed for 24 hours,+decided on the studio laptop" rather than just "allowed".++### Why a card sometimes goes quiet++Worth knowing, because it looks like a bug:++- Adom Bridge shows **one** prompt at a time per machine for the same grant, so a+ polling AI that gets refused every thirty seconds does not stack thirty cards.+- If the window that was rendering a mirrored card goes away (Hydrogen restarting,+ for instance), that copy can be lost while the request is still live on the+ machine that raised it.++---++## Settings++| Setting | Values | Default |+|---|---|---|+| `mesh_mode` | `off`, `relay`, `direct` | `relay` |+| `cross_box_prompts` | on, off | on |+| `cross_box_toasts` | `prompts`, `decisions`, `all` | `all` |+| `cross_box_windows_toasts` | on, off | on |+| `windows_toasts` | on, off | on |++Turning `windows_toasts` off removes the operating system toast only. Cards remain+decidable in Adom Bridge's window and in Hydrogen. No setting silences a permission+card everywhere.++---++## Verbs++| Verb | What it does |+|---|---|+| `desktop_mesh_status` | mode, running, node ID, relay, endpoints, phase |+| `desktop_mesh_peers` | the machines in your directory, with link path and trust |+| `desktop_mesh_trust` | `list`, `revoke`, `deny`. There is no approve: that is human-only |+| `desktop_ui_panel {panel:"mesh"}` | open the Mesh view |+| `settings_set {mesh_mode}` | change mode; the endpoint is rebound live |++Addressing a machine is the same as ever: `--target <name>`. When the mesh has a+route it is used, and the cloud relay is the fallback. `targets` tells you which+path each machine is on.++---++## If something is not working++- **A machine is missing from the Mesh view.** Its directory entry may be stale.+ Open the view, which forces a fresh read, or address the machine directly, which+ refreshes on failure and retries.+- **A machine is there but never connects.** Check its trust record. A node ID with+ no record is refused by design, and the card asking about it may be waiting on+ another of your machines.+- **`direct` mode connects to nothing.** The firewall dialog was probably declined.+ Cancelling creates a block rule; the mesh still works in `relay` mode.+- **A prompt never arrived.** Check `cross_box_prompts` on the receiving machine,+ and remember that one card at a time per grant is deliberate.++---++## The short version++Your machines hold their own keys, find each other through your account, refuse+anyone a human has not vouched for, and talk in sealed bytes that the relay+carrying them cannot read. When something needs you, it comes to the machine you+are actually sitting at, and your answer is trusted there exactly as much as it+would have been on the machine that asked.