Closed general

register_container adds a SECOND connection to a relay ab already reaches via Hydrogen: dedups on name not relay, no unregister_container, and it clobbers bridge.last_slug

John Lauer · 2mo ago ·closed by John Lauer

Filed by Claude (AI) after doing exactly the wrong thing during the #109 triage on ConfRoomROG. The AI could not tell that ab was already Hydrogen-managed, so it "helpfully" registered the workspace as a second connection. Nothing refused it, nothing warned, and the user spotted the problem himself in the Connections panel:

"i should NOT see that container entry in there cuz it now conflicts with the 'hydrogen / managed by hydrogen' setting. so that worries me cuz you could end up in a messaging loop."

What happened

While diagnosing "no desktop client connected" (which was really #109 — ab 2.0.91 crash-looping), the AI ran the documented preferred setup path:

adom-bridge register_container '{"name":"container","url":"ws://localhost:7380/proxy/8765/"}'
→ {"ok":true, "was_duplicate":false, "id":"0f5665b0-…"}

Once ab 2.0.92 came up, the Connections panel showed 4/4 connected:

galliaApril    [AdomLapper]   Disconnect
GrokBot                       Disconnect
hydrogen                      Managed by Hydrogen
container                     Disconnect      ← the AI's entry

hydrogen and container are the same relay in the same workspace, reached twice. was_duplicate:false is technically true (no name collision) and substantively false (that relay was already connected).

Why this is worse than untidy

  1. Two live WS connections to one relay. The user's own read is the concern to design against: a command path that can be delivered twice, or feed back on itself.
  2. It contradicts a UI state ab itself renders. The panel says Managed by Hydrogen for that relay — and then offers a second, unmanaged, user-Disconnect-able row for the same thing. Whoever is right, both cannot be.
  3. It is the recommended path. setup_desktop returns register_command as the "⭐ PREFERRED, ZERO-PASTE path", and the ab skill says to prefer it. An AI following the documentation correctly lands here. This is a guardrail gap, not user error.
  4. It is nearly invisible from the container. Nothing in register_container's response hints that the target already reaches this relay.

Asks

1. Refuse (or loudly warn) when the target already reaches that relay. Dedup on resolved relay identity, not entry name. If a connection to the same relay exists — especially a Hydrogen-managed one — return ok:false with errorCode:"already_connected_via" naming the existing entry, plus an explicit override (force:true) for the rare genuine case. was_duplicate should mean "you already have this connection", not "the name was taken".

2. Teach register_container about embedded mode. ab knows it is Hydrogen-managed (embedded.json, hd-managed, desktop_embedded_status). A cloud container registering the relay that Hydrogen already owns should be the specific thing this verb declines by default.

3. Ship unregister_container — the inverse does not exist. Reverting today means knowing that register_container writes three cloud keys, not one:

bridge.container.<id>     the ServerEntry
bridge.last_slug          overwritten with the new id
bridge.enabled            set true

…and that a local server_remove alone is undone by the next cloud sync (boot / sign-in / hourly). The actual revert was a raw Carbon PATCH per key plus server_remove — reverse-engineered from a _hint. That is too much standing on the caller for a verb this easy to invoke.

4. bridge.last_slug is clobbered with no way back. It is overwritten unconditionally and the prior value is unrecoverable, so even a perfect revert cannot restore the previous state — it can only null the key. Either preserve the old value for rollback, or stop writing it as a side effect of registration.

5. Make the danger legible in setup_desktop. It happily emitted the register_command for a machine already connected via Hydrogen. If it detects a reachable Hydrogen-managed ab, it should say so and recommend doing nothing.

  • #109 — the 2.0.91 crash loop that started this triage
  • #106 — cloud server sync (same KV surface)
  • #71 — Connections panel refresh (same panel)

Also being documented from the other side: hydrogen-adom-bridge-triage in adom/hydrogen-bootstrap (0.5.13) now tells agents not to call register_container when Hydrogen manages ab, and how to fully revert one. That is a workaround in a skill; the guardrail belongs in the verb.

2 Replies

John Lauer · 2mo ago

Confirmed and taking it — this is a real guardrail gap, and the diagnosis is right: register_container dedups by URL against the CLOUD keys (bridge.container.*), but the Hydrogen-managed connection is an ephemeral hd-spawn entry that is never written to the cloud KV, so the dedup is structurally blind to exactly the case that matters.

Plan (targeting 2.0.94, right after the in-flight 2.0.93 peer-panel fix ships):

  1. Dedup on relay identity, not name or cloud rows: before registering, register_container will query the target relay itself (get_clients on the relay's own API — the container can reach it, it is about to register that very URL). If any desktop client already reaches that relay, refuse with ok:false, errorCode:"already_connected_via" naming the connected client(s), with force:true as the explicit override. This catches the Hydrogen-managed case without needing to see the target's embedded state, and any other already-connected case for free.
  2. unregister_container — the inverse verb: nulls bridge.container.<id>, restores bridge.last_slug (see 3), and tells the caller the target drops the entry on its next cloud sync (plus best-effort immediate server_remove when the target is reachable).
  3. Stop destroying bridge.last_slug: the prior value gets preserved (bridge.last_slug_prev) before any overwrite and returned in the response, so a revert can actually restore state instead of only nulling it.
  4. setup_desktop legibility: when the relay it is about to recommend already has a connected desktop client, it will say so and recommend doing nothing instead of emitting the register_command.

Will post here when 2.0.94 is live.

John Lauer · 2mo ago

Shipped in 2.0.94, all five asks, with a live verification of the exact incident scenario.

Ask 1+2 — relay-identity dedup. register_container now asks the TARGET RELAY who is connected before touching the cloud. An already-reached relay refuses:

{"ok":false, "errorCode":"already_connected_via", "connectedClients":["ConfRoomROG","AdomLapper"],
 "error":"REFUSED: an Adom Bridge already reaches this relay ... a SECOND live connection ... risk loops",
 "_hint":"you almost certainly need to do NOTHING (verify with `adom-bridge targets`) ... force:true overrides"}

That JSON is from the live test: I ran the new binary's register_container against the galliaApril relay while two desktops were connected — refused in 165 ms, zero cloud writes. This catches the Hydrogen-managed case by construction (the relay's client list is ground truth; no embedded-state introspection needed), and any other already-connected case for free. An UNREACHABLE relay never blocks — the normal fresh-setup order (register first, serve later) still works; the guard only fires on a positive already-connected answer. force:true is the explicit override.

Ask 3 — unregister_container exists. Nulls bridge.container.<id> + the legacy desktop.container.<id> twin, restores bridge.last_slug from the preserved previous value (only when last_slug still points at the entry being removed — a later registration's slug is never clobbered), leaves bridge.enabled alone, and tells the caller how the target converges (next cloud sync, or an immediate server_remove while connected). Takes id or url.

Ask 4 — last_slug is no longer destroyed. The prior value is written to bridge.last_slug_prev before any overwrite and returned as previous_last_slug; register_container's revert hint now points at unregister_container instead of the raw-PATCH recipe you had to reverse-engineer.

Ask 5 — setup_desktop legibility. The already-connected branch now says explicitly: do NOT run register_container, it would create a duplicate (and the verb itself refuses anyway).

One transparency note: while testing I accidentally ran the OLD (pre-guard) CLI against the live relay first, and its dedup path did exactly what this issue describes — refreshed the real galliaApril entry's name/token and clobbered last_slug. Everything was restored and verified clean (entry name, last_slug, no stray keys; the relay runs token-less so the blanked token never affected connectivity). An unintentional but complete reproduction of the failure mode this release eliminates — the 2.0.94 guard refuses before any of those writes can occur.

John Lauer closed this · 2mo ago

Log in to reply.