Skip to content

fix(desktop): spawn managed agents with the configured relay URL - #3484

Open
JHSeo-git wants to merge 1 commit into
block:mainfrom
JHSeo-git:fix/managed-agent-relay-url-tenancy
Open

fix(desktop): spawn managed agents with the configured relay URL#3484
JHSeo-git wants to merge 1 commit into
block:mainfrom
JHSeo-git:fix/managed-agent-relay-url-tenancy

Conversation

@JHSeo-git

Copy link
Copy Markdown

Summary

On a local relay, desktop-managed agents silently never respond: the relay derives the community boundary from the HTTP Host header, but every managed-agent spawn path handed the child ManagedAgentRuntimeKey.relay_url — the canonical identity form, whose normalization folds every loopback spelling to 127.0.0.1. With a community configured as ws://localhost:3000, the UI writes all channels, memberships, and messages into the localhost:3000 community while every agent connects to the empty 127.0.0.1:3000 community. Each harness logs discovered 0 channel(s) / no channel subscriptions resolved — agent will sit idle, the member list shows the agent stuck on "Waking", and mentions are never answered. No error surfaces anywhere.

This also violates the contract documented on buzz_core::relay::normalize_relay_url itself:

Connection code may retain the configured URL; this canonical form is for identity, receipts, status and deduplication.

Related issue

Fixes #2444. Same root cause family as #3283 (loopback tenant split on the seed side). Closest existing PR: #2446 — same diagnosis and direction; it now has merge conflicts against main and its description notes the missing automated regression test as an honest gap. This PR is rebased on current main, adds that regression coverage via a small testable seam, and additionally pins the spawn-config hash to the canonical form so restart-drift detection cannot false-positive. Happy to close this in favor of a revived #2446 if maintainers prefer — the test seam here grafts onto it cleanly.

Fix

Keep the canonical form for identity only (runtime-map keys, receipts, log paths, spawn-config hash). Dial the configured URL everywhere a child or probe actually connects:

  • child_connect_relay_url(configured, key) — new seam in runtime.rs that returns the configured URL for the child's BUZZ_RELAY_URL and git-credential scope, with a debug_assert that the configured URL canonicalizes to the runtime key's relay (callers cannot hand it a URL from a different pair).
  • The three spawn call sites (start_pair, launch restore, ensure-running) pass their already-resolved configured URL instead of key.relay_url.
  • The reconcile fan-out passes the requested community URL to start_pair instead of the canonical echo.
  • probe_agent_relay_access probes over the requested authority, so spawn eligibility is checked against the same community the child will join.
  • start_pair fills requested_relay_url in its status rows, so the frontend's existing requestedRelayUrl ?? relayUrl fallback re-dials the configured URL on restart.
  • The spawn-config hash deliberately stays on runtime_key.relay_url: status_for recomputes that hash from key.relay_url, so hashing the configured spelling would read as permanent drift and flag a spurious restart on every status pass.

Testing

  • Two new regression tests pin the seam: child_connect_relay_url_preserves_configured_loopback_authority (canonical key is ws://127.0.0.1:3000 while the child still gets ws://localhost:3000) and child_connect_relay_url_is_identity_for_non_loopback_relays.
  • just ci passes locally; cargo test --lib on the Tauri crate: 1851 passed, 0 failed; clippy clean.
  • Manual end-to-end on macOS against a fresh local relay and a fresh desktop dev instance: before the fix, all three starter-team agents logged discovered 0 channel(s) and never replied (memberships verified present in the relay DB, all events under the localhost:3000 community while harnesses connected to 127.0.0.1:3000); after the fix, each harness logs discovered 1 channel(s) and a mention in the Welcome channel gets an agent reply within ~50s.

Follow-up candidates (out of scope here, discussed in #2444/#3283): collapsing loopback spellings into one community key on the relay side (#2619 explores this), and surfacing the agent-idle state in the UI instead of only in the harness log.

Relay tenancy is derived from the HTTP Host header, but the managed-agent
spawn path handed the child the canonical runtime-key relay URL, whose
normalization rewrites loopback spellings to 127.0.0.1. Against a local
relay joined as ws://localhost:3000, every harness therefore connected to
the empty 127.0.0.1:3000 community, discovered 0 channels, and never saw
a mention while the UI showed the agent stuck on Waking.

Spawn now hands the child the caller's configured relay URL (BUZZ_RELAY_URL
and the git-credential scope) through a small child_connect_relay_url seam,
and every path that reaches spawn passes the configured spelling through:
start_pair, launch restore, ensure-running, the reconcile fan-out, and the
relay-access probe (which now probes the requested authority). start_pair
also fills requested_relay_url in its status rows so the frontend's
requestedRelayUrl fallback re-dials the configured URL on restart.

The canonical form remains the identity for runtime keys, receipts, log
paths, and the spawn-config hash: status_for recomputes that hash from
key.relay_url, so hashing the configured spelling would read as permanent
drift and flag a spurious restart.

Signed-off-by: JHSeo <qnfqnfqnf@gmail.com>
@JHSeo-git
JHSeo-git force-pushed the fix/managed-agent-relay-url-tenancy branch from 556d776 to cb11965 Compare July 30, 2026 00:39
@jbold

jbold commented Aug 1, 2026

Copy link
Copy Markdown

Independent verification on Linux — this PR fixes #2444 for me.

Prior reports were macOS (0.4.23 DMG) and a main repro; adding a Linux data point plus one symptom I didn't see described in #2444.

Symptom was worse than "agents don't respond": onboarding never completes.

On a fresh install the app hangs permanently on the final onboarding step, "meet your starter team". There is no error, no failed request, and no timeout — the step simply never resolves, because it waits on starter-team agents that are connecting to a different community. The state is also persistent: quitting and relaunching returns straight to the frozen step, so the app is unusable until localStorage is cleared manually. #2444 describes agents going silent in an established workspace; this is the same root cause blocking first-run entirely.

Environment

  • Buzz: built from source, main @ 19d57b0, just relay + just desktop-standalone
  • Also reproduced with the released v0.5.3 .deb (sha256 61b3471776c4…)
  • Host: Fedora Silverblue 43, Wayland, NVIDIA
  • Build/run: Ubuntu 24.04 distrobox, WebKitGTK 2.52.3
  • Relay: local, Postgres/Redis/MinIO via Compose

Reproduction

  1. just relay with RELAY_URL=ws://localhost:3000
  2. just fresh=1 desktop-standalone, join the community as ws://localhost:3000
  3. Complete onboarding

Result: hangs at "meet your starter team" indefinitely.

Evidence it is the loopback tenant split

The dev database seeds a community row per loopback spelling, which makes the split directly observable. Before the patch, everything the UI wrote landed under localhost:3000 and no bot-role membership was ever created:

      host      |       name       | role  | members
----------------+------------------+-------+---------
 localhost:3000 | Welcome          | owner |       1
 localhost:3000 | general          | owner |       1
 localhost:3000 | welcome-everyone | owner |       1

After applying this PR (applies cleanly to 19d57b0) and joining as ws://127.0.0.1:3000:

      host      |       name       | role  | members
----------------+------------------+-------+---------
 127.0.0.1:3000 | Welcome          | owner |       1
 127.0.0.1:3000 | Welcome          | bot   |       3
 127.0.0.1:3000 | general          | owner |       1
 127.0.0.1:3000 | welcome-everyone | owner |       1

Onboarding completes and the starter team appears.

Why this took a long time to diagnose

Every check said the stack was healthy. The relay passed /_readiness, served a valid NIP-11 document, accepted a hand-signed NIP-42 AUTH over WebSocket (["OK",<id>,true,""]), and buzz-cli could list channels and post messages as a brand-new identity. The desktop app authenticated and polled POST /query200 continuously throughout. Nothing in the relay logs was above INFO at any RUST_LOG level.

The tenancy mismatch has no error path anywhere, so the only visible signal is a UI step that never finishes.

One measurable symptom, if it helps others identify this

While stuck, WebKitWebProcess sits at a sustained 100% of one core (405 jiffies / 4s). After this patch, on the same machine and the same WebKitGTK build, it drops to ~1%. So the frontend is busy-waiting rather than idle-waiting while the starter team never arrives — a quick ps/top check distinguishes this bug from a merely slow start.

Worth stating explicitly so nobody chases it as a rendering bug: it is not WebKitGTK. I initially suspected #3654 (NVIDIA + WEBKIT_DISABLE_DMABUF_RENDERER) and measured both workarounds from that issue — WEBKIT_DMABUF_RENDERER_FORCE_SHM=1 and WEBKIT_SKIA_ENABLE_CPU_RENDERING=1 — with no effect (100% / 111% / 101%). The spin resolves via this Rust-side patch, which touches no rendering code.

One adjacent note on relay URL entry

For a plaintext local relay you must type the scheme explicitly. A scheme-less entry (localhost:3000) is normalized to wss:// — intentional, and the code says so ("a scheme-less host is assumed to be a secure relay") — which then fails TLS against a plaintext port and surfaces as:

join policy request failed: error sending request for url (https://localhost:3000/api/join-policy)

relayProbe.ts maps an explicit http:// to ws:// correctly, so this only bites scheme-less input. Not a bug, but the error message never mentions that the scheme was defaulted, which cost me a while. Possibly worth a clearer message for loopback hosts.

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.

[Bug] Desktop 0.4.23 spawns managed agents on ws://127.0.0.1:3000 while the community uses ws://localhost:3000 - agents silently stop responding

2 participants