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: add the narrated demo video; rebuild the stills from the verified clips
5 files changed
+459−433
docs/mesh.md+385−364@@ -1,364 +1,385 @@-# 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 |-|---|---|-| `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, re-`raise` a-card a human has not got to yet, and 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:----## 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 cloud-vm, 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.--### 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 and settings--| 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.75), and `/permission/state` lists those ids as pending so Hydrogen keeps the card up |--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.---## 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.---## 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.+# 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. + +--- + +## Watch it + +Eighty-five seconds: the modes, what a mesh link actually is, and a permission +prompt raised on one machine being answered on another. + +<video src="docs/mesh/mesh-demo.mp4" controls muted loop playsinline width="820"></video> + +Recorded on a real fleet. The machine names, the account and the addresses are +substituted throughout; everything else, including the round-trip times and the +byte counters, is what the tool actually reported. + +--- + +## 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 | +|---|---| +| `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, re-`raise` a +card a human has not got to yet, and 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. + + + +That link goes **through the Adom relay**. `THIS END` reads `loopback-bound -> +relay (no port open)`, and `WHO CAN READ IT` says the relay forwards scrambled +packets. Nothing on that machine is listening on the network. + +Here is the same panel for a machine on the same network: + + + +`PATH` is now `Direct, machine to machine`, the transport is UDP straight +between the two, and both ends are ordinary addresses on the local network. The +encryption line is identical in both. Only the route changed. + +--- + +## 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. Two of these were raised on the other +machine and one on this one, and the chips say which is which: + + + +## 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 cloud-vm, 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. + +### 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 and settings + +| 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.75), and `/permission/state` lists those ids as pending so Hydrogen keeps the card up | + +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. + +--- + +## 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. + +--- + +## 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.
docs/mesh/cross-machine-card.png⋯ 1 unchanged line ⋯
docs/mesh/link-detail.png⋯ 1 unchanged line ⋯
docs/mesh/permission-stack.png⋯ 1 unchanged line ⋯
docs/mesh/topology.svg+74−69@@ -1,69 +1,74 @@-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 560" width="980" height="560" font-family="'Familjen Grotesk','Satoshi','Segoe UI',system-ui,sans-serif">- <defs>- <style>- .bg { fill: #0c1016; }- .card { fill: #161c24; stroke: #263040; stroke-width: 1.5; rx: 12; }- .self { fill: #12222a; stroke: #00b8b0; stroke-width: 2; }- .relay { fill: #141b22; stroke: #00b8b0; stroke-width: 1.5; stroke-dasharray: 5 4; }- .cloud { fill: #141b22; stroke: #3a4655; stroke-width: 1.5; stroke-dasharray: 5 4; }- .name { fill: #e8f4f3; font-size: 16px; font-weight: 600; }- .sub { fill: #8fa3ad; font-size: 12.5px; }- .mesh-l { stroke: #00b8b0; stroke-width: 2.5; fill: none; }- .cloud-l { stroke: #5a6675; stroke-width: 2; fill: none; stroke-dasharray: 6 5; }- .lbl { fill: #7f939d; font-size: 11.5px; }- .lbl-t { fill: #00b8b0; font-size: 11.5px; }- .h { fill: #e8f4f3; font-size: 19px; font-weight: 700; }- .legend { fill: #9bb0ba; font-size: 12.5px; }- </style>- </defs>-- <rect class="bg" width="980" height="560"/>- <text class="h" x="34" y="42">Relay mode: nothing listens, and the relay cannot read it</text>- <text class="sub" x="34" y="66">Every machine holds one outbound connection. Traffic goes machine, relay, machine, sealed the whole way.</text>-- <!-- Adom mesh relay, directly above self -->- <rect class="relay" x="395" y="108" width="190" height="56" rx="12"/>- <text class="name" x="490" y="132" text-anchor="middle">Adom mesh relay</text>- <text class="sub" x="490" y="150" text-anchor="middle">forwards sealed bytes</text>- <text x="601" y="140" font-size="15">🔒</text>-- <!-- peers -->- <rect class="card" x="60" y="236" width="180" height="62"/>- <text class="name" x="150" y="262" text-anchor="middle">StudioLaptop</text>- <text class="sub" x="150" y="281" text-anchor="middle">where you are sitting</text>-- <rect class="card" x="740" y="236" width="180" height="62"/>- <text class="name" x="830" y="262" text-anchor="middle">MeetingRoomPC</text>- <text class="sub" x="830" y="281" text-anchor="middle">another room</text>-- <rect class="card" x="395" y="236" width="190" height="62"/>- <text class="name" x="490" y="262" text-anchor="middle">cloud-vm</text>- <text class="sub" x="490" y="281" text-anchor="middle">a different network</text>-- <!-- self, centre -->- <rect class="self" x="380" y="382" width="220" height="72" rx="12"/>- <text class="name" x="490" y="410" text-anchor="middle">workshop-pc</text>- <text class="sub" x="490" y="430" text-anchor="middle">this PC, no port open</text>- <text class="sub" x="490" y="446" text-anchor="middle">loopback bound</text>-- <!-- every mesh link goes through the relay -->- <path class="mesh-l" d="M490 382 L490 164"/>- <path class="mesh-l" d="M150 236 L150 190 Q150 164 176 164 L395 140"/>- <path class="mesh-l" d="M830 236 L830 190 Q830 164 804 164 L585 140"/>- <path class="mesh-l" d="M490 236 L490 164"/>- <text class="lbl-t" x="612" y="206">every link goes through the relay</text>-- <!-- the cloud command relay is a different thing -->- <rect class="cloud" x="700" y="452" width="200" height="58" rx="12"/>- <text class="name" x="800" y="476" text-anchor="middle">cloud command relay</text>- <text class="sub" x="800" y="495" text-anchor="middle">your AI reaches you here</text>- <path class="cloud-l" d="M600 428 L700 474"/>- <text class="lbl" x="612" y="462">can read this one</text>-- <!-- legend -->- <line class="mesh-l" x1="40" y1="474" x2="86" y2="474"/>- <text class="legend" x="96" y="479">mesh link, end to end encrypted between the two machines' own keys</text>- <line class="cloud-l" x1="40" y1="502" x2="86" y2="502"/>- <text class="legend" x="96" y="507">cloud command relay, ordinary transport encryption to the host</text>- <text class="legend" x="40" y="535">In direct mode these mesh links become machine to machine on the local network, with the relay as fallback.</text>-</svg>+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 560" width="980" height="560" font-family="'Familjen Grotesk','Satoshi','Segoe UI',system-ui,sans-serif"> + <defs> + <style> + .bg { fill: #0c1016; } + .card { fill: #161c24; stroke: #263040; stroke-width: 1.5; rx: 12; } + .self { fill: #12222a; stroke: #00b8b0; stroke-width: 2; } + .relay { fill: #141b22; stroke: #00b8b0; stroke-width: 1.5; stroke-dasharray: 5 4; } + .cloud { fill: #141b22; stroke: #3a4655; stroke-width: 1.5; stroke-dasharray: 5 4; } + .name { fill: #e8f4f3; font-size: 16px; font-weight: 600; } + .sub { fill: #8fa3ad; font-size: 12.5px; } + .mesh-l { stroke: #00b8b0; stroke-width: 2.5; fill: none; } + .cloud-l { stroke: #5a6675; stroke-width: 2; fill: none; stroke-dasharray: 6 5; } + .lbl { fill: #7f939d; font-size: 11.5px; } + .lbl-t { fill: #00b8b0; font-size: 11.5px; } + .h { fill: #e8f4f3; font-size: 19px; font-weight: 700; } + .legend { fill: #9bb0ba; font-size: 12.5px; } + </style> + </defs> + + <rect class="bg" width="980" height="560"/> + <text class="h" x="34" y="42">Relay mode: nothing listens, and the relay cannot read it</text> + <text class="sub" x="34" y="66">Every machine holds one outbound connection. Traffic goes machine, relay, machine, sealed the whole way.</text> + + <!-- Adom mesh relay, directly above self --> + <rect class="relay" x="395" y="108" width="190" height="56" rx="12"/> + <text class="name" x="490" y="132" text-anchor="middle">Adom mesh relay</text> + <text class="sub" x="490" y="150" text-anchor="middle">forwards sealed bytes</text> + <!-- padlock, drawn rather than an emoji: the emoji renders as a blank box + wherever no colour-emoji font is installed, which is most render hosts --> + <g transform="translate(600,127)"> + <path d="M3 6 a4 4 0 0 1 8 0 v3" fill="none" stroke="#00b8b0" stroke-width="1.8"/> + <rect x="0.5" y="8.5" width="13" height="10" rx="2" fill="#0f1c21" stroke="#00b8b0" stroke-width="1.6"/> + <circle cx="7" cy="13.5" r="1.6" fill="#00b8b0"/> + </g> + + <!-- peers --> + <rect class="card" x="60" y="236" width="180" height="62"/> + <text class="name" x="150" y="262" text-anchor="middle">StudioLaptop</text> + <text class="sub" x="150" y="281" text-anchor="middle">where you are sitting</text> + + <rect class="card" x="740" y="236" width="180" height="62"/> + <text class="name" x="830" y="262" text-anchor="middle">MeetingRoomPC</text> + <text class="sub" x="830" y="281" text-anchor="middle">another room</text> + + <rect class="card" x="395" y="236" width="190" height="62"/> + <text class="name" x="490" y="262" text-anchor="middle">cloud-vm</text> + <text class="sub" x="490" y="281" text-anchor="middle">a different network</text> + + <!-- self, centre --> + <rect class="self" x="380" y="382" width="220" height="72" rx="12"/> + <text class="name" x="490" y="410" text-anchor="middle">workshop-pc</text> + <text class="sub" x="490" y="430" text-anchor="middle">this PC, no port open</text> + <text class="sub" x="490" y="446" text-anchor="middle">loopback bound</text> + + <!-- every mesh link goes through the relay --> + <path class="mesh-l" d="M490 382 L490 164"/> + <path class="mesh-l" d="M150 236 L150 190 Q150 164 176 164 L395 140"/> + <path class="mesh-l" d="M830 236 L830 190 Q830 164 804 164 L585 140"/> + <path class="mesh-l" d="M490 236 L490 164"/> + <text class="lbl-t" x="612" y="206">every link goes through the relay</text> + + <!-- the cloud command relay is a different thing --> + <rect class="cloud" x="700" y="452" width="200" height="58" rx="12"/> + <text class="name" x="800" y="476" text-anchor="middle">cloud command relay</text> + <text class="sub" x="800" y="495" text-anchor="middle">your AI reaches you here</text> + <path class="cloud-l" d="M600 428 L700 474"/> + + <!-- legend --> + <line class="mesh-l" x1="40" y1="474" x2="86" y2="474"/> + <text class="legend" x="96" y="479">mesh link, end to end encrypted between the two machines' own keys</text> + <line class="cloud-l" x1="40" y1="502" x2="86" y2="502"/> + <text class="legend" x="96" y="507">cloud command relay, ordinary transport encryption; the host CAN read this one</text> + <text class="legend" x="40" y="535">In direct mode these mesh links become machine to machine on the local network, with the relay as fallback.</text> +</svg>