Skip to content

fix(desktop): spawn agents with the configured relay URL, not the canonical form - #5200

Open
ZEDYB wants to merge 3 commits into
block:mainfrom
ZEDYB:fix/agent-spawn-configured-relay-url
Open

fix(desktop): spawn agents with the configured relay URL, not the canonical form#5200
ZEDYB wants to merge 3 commits into
block:mainfrom
ZEDYB:fix/agent-spawn-configured-relay-url

Conversation

@ZEDYB

@ZEDYB ZEDYB commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Locally spawned managed agents receive the canonical runtime-key relay URL in BUZZ_RELAY_URL instead of the configured workspace URL. buzz_core::relay::normalize_relay_url folds every loopback spelling to 127.0.0.1 — by its own contract, for "identity, receipts, status and deduplication", with "connection code may retain the configured URL". The spawn path violates that contract: spawn_agent_child copies runtime_key.relay_url into the child env.

On a per-host multi-tenant relay (community is resolved from the request Host, fail-closed, per docs/multi-tenant-relay.md), ws://localhost:3100 and ws://127.0.0.1:3100 are different communities. The result for a self-hoster whose workspace is ws://localhost:3100:

  • the desktop's own traffic (owner session, starter channels, profile sync, channel membership writes) lands in the localhost:3100 community;
  • every locally spawned agent harness connects to the 127.0.0.1:3100 community, logs discovered 0 channel(s) / no channel subscriptions resolved — agent will sit idle, and never sees the channels or memberships the user creates in the UI.

Observed live against a self-hosted relay: the agent registered (via its NIP-OA tag) in an empty parallel tenant while the UI kept adding it to channels in the configured tenant. Invisible against hosted relays, where the configured host is not loopback and the two forms agree.

Change

  • spawn_agent_child now hands the child the configured relay URL (env BUZZ_RELAY_URL and the credential.<relay>/git.helper scoping), and keeps the canonical runtime_key for the identity-side jobs: log path, receipts, and the spawn-config snapshot. The split is factored into spawn_relay_roles (new runtime/spawn_relay.rs submodule, with the rationale documented on the helper and the regression tests inline).
  • The SpawnConfigSnapshot relay facet stays keyed on the canonical relay on both sides (prospective_spawn_config_snapshot is already fed workspace_pair_key(...).relay_url by the summary builder), so no spurious restart badge is introduced for loopback-configured workspaces. Known boundary, documented at the stamp: a spelling-only workspace-relay edit (same canonical form, e.g. ws://localhost:3100ws://127.0.0.1:3100) does not badge even though the child's connection host now follows the configured spelling; making it badge would mean feeding the configured URL to both snapshot sides — left as a follow-up to keep this fix minimal.
  • All three spawn call sites (start_managed_agent_process, start_pair, launch restore) pass the configured URL through instead of key.relay_url.

Tests

  • spawn_relay_roles_keeps_configured_url_for_the_child — pins identity canonicalizing ws://localhost:3100ws://127.0.0.1:3100 while the child's connection URL stays ws://localhost:3100.
  • spawn_relay_roles_agree_for_non_loopback_hosts — pins that hosted-relay behavior is unchanged.

🤖 Generated with Claude Code

Michael Kennedy and others added 3 commits August 6, 2026 08:00
…onical form

normalize_relay_url folds every loopback spelling to 127.0.0.1 — by its
own contract for identity, receipts, and dedup only ("Connection code
may retain the configured URL"). The spawn path handed that canonical
form to the child as BUZZ_RELAY_URL, so on a per-host multi-tenant relay
a workspace configured as ws://localhost:3100 spawned every local agent
into the ws://127.0.0.1:3100 community — a different, empty tenant. The
harness logs "discovered 0 channel(s)" and idles while the UI writes
channel memberships to the configured tenant. Invisible against hosted
relays, where the configured host is not loopback.

The child now receives the configured URL verbatim (BUZZ_RELAY_URL and
the git credential scoping); the canonical runtime key keeps the
identity-side jobs: log path, receipts, and the spawn-config hash, which
must stay canonical to match build_managed_agent_summary's drift
recompute or every loopback-configured workspace would flag a permanent
spurious restart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Michael Kennedy <mike.kennedy12@gmail.com>
Re-anchors the fix onto the spawn_snapshot refactor (block#3637): the old
post-spawn spawn_config_hash block this branch edited was replaced
upstream by a pre-spawn SpawnConfigSnapshot stamp. The canonical-relay
decision carries over to the new API: the snapshot's relay facet stays
runtime_key.relay_url on the stamp side, matching the canonical
workspace_pair_key input the summary builder feeds
prospective_spawn_config_snapshot. The child's connection URL remains
the configured workspace URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Michael Kennedy <mike.kennedy12@gmail.com>
Move the identity/connection relay split and its two regression tests
into a new runtime submodule (matching metadata/path/stop/sweep, which
keep their tests inline), and fold the near-duplicate rationale comment
at the old effective_relay_url rebind into the helper's doc. Returns
runtime.rs and runtime/tests.rs under the desktop file-size ratchet;
no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Michael Kennedy <mike.kennedy12@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant