You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Buzz already treats a hand-launched agent as first-class at the protocol level: docs/remote-agents.md says a launcher that exports BUZZ_PRIVATE_KEY, BUZZ_RELAY_URL and BUZZ_AUTH_TAG and execs the harness "is a conforming launcher … today, with no code change". A systemd unit on a VPS, or a harness Buzz doesn't ship at all, qualifies.
What's missing is the owner's side. For an agent it did not create, the desktop cannot produce either of the two owner-signed things such an agent needs:
the NIP-OA attestation. The desktop mints one only inside the create-agent form, for a key the form generates (Keys::generate() in desktop/src-tauri/src/commands/agents.rs); there is no import. The CLI verifies tags but never mints them.
the kind:30177 policy. Without it, once an agent is attested, the mention picker hides it: getAgentMentionAdmission always admits a person but admits an agent only if a verified owner policy's respond_to includes the viewer. Adding an agent to a channel as bot goes through the same flow, so it can't be added either.
The form's "Where to run" choice can't express this case: it offers "This computer" and buzz-backend-* providers found on the owner's machine, all of which run the harness on the owner's behalf. An operator running the harness themselves — on a server, or a harness other than buzz-acp — has no door. Today the only way through is hand-computing the tag with compute_auth_tag, and hand-signing and publishing the kind:30177, which means handling the owner's nsec outside the keyring.
We hit this provisioning three agents as code (two headless buzz-acp agents under systemd, one agent running through another harness's Buzz channel plugin). Each showed as a person in the desktop, and once attested, could not be mentioned.
Proposed solution
A third "Where to run" option in the create-agent dialog: "Somewhere else — I run it myself".
It asks for the agent's public key (npub or hex) — not a keypair. An attestation is a signature over the public key, so the desktop never needs the agent's secret, and the operator's key never has to leave the host it runs on.
It skips the harness/runtime/provider fields, which aren't the desktop's to decide in this mode, and keeps name, respond_to / allowlist and optional persona.
On save, with the owner's keyring key, it computes the NIP-OA tag with empty conditions (as the form does today) and publishes the kind:30177 policy, using the existing build_agent_event shape. It omits system_prompt / model / provider, since the operator owns those.
It shows the BUZZ_AUTH_TAG value to install on the agent's host, single-quoted. Then, as it does for provider deploys, it can wait until the agent's latest kind:0 carries a valid tag — which is what the desktop itself uses to decide "agent".
These agents would then appear in My Agents with the existing "Not managed on this device" cloud marker, and their policy could be edited there like any other.
Permissions. We don't think this needs a relay-role gate, and it shouldn't be admin-only:
creating an agent isn't role-gated today;
on a closed relay, an owned agent is admitted through its owner's membership, the same delegation desktop-created agents use;
signing a tag over a key you don't control is inert — the relay records an owner only when the agent itself authenticates presenting the tag (needs its private key), the desktop reads ownership only from the agent-signed profile, and the directory accepts only policies from the owner that profile names.
A relay operator who wants to limit who may bring agents would be better served by a relay-side policy than a UI gate.
Alternatives considered
Importing a keypair into the form. Moves the agent's private key onto the owner's laptop, and the desktop would then consider itself the agent's manager. The public-key-only flow avoids both.
A CLI subcommand to mint tags and policies. Works — it's what we use today — but it means pasting the owner's nsec somewhere other than the keyring. A desktop action keeps the key where it is.
Additional context
Related, and in practice encountered in this order when doing this by hand:
Channel "Change role" offers admin/member/guest only, so an agent added as member before it was attested can't be switched to bot — it has to be removed and re-added. Possibly intentional; mentioning it because it's the last step of the same path.
Duplicates: none found for registering an externally-run agent. Closest are agent snapshot import (#4197, #5458), which imports a definition rather than an externally-run key, and draft-create (#3791).
Motivation
Buzz already treats a hand-launched agent as first-class at the protocol level:
docs/remote-agents.mdsays a launcher that exportsBUZZ_PRIVATE_KEY,BUZZ_RELAY_URLandBUZZ_AUTH_TAGand execs the harness "is a conforming launcher … today, with no code change". A systemd unit on a VPS, or a harness Buzz doesn't ship at all, qualifies.What's missing is the owner's side. For an agent it did not create, the desktop cannot produce either of the two owner-signed things such an agent needs:
Keys::generate()indesktop/src-tauri/src/commands/agents.rs); there is no import. The CLI verifies tags but never mints them.kind:30177policy. Without it, once an agent is attested, the mention picker hides it:getAgentMentionAdmissionalways admits a person but admits an agent only if a verified owner policy'srespond_toincludes the viewer. Adding an agent to a channel asbotgoes through the same flow, so it can't be added either.The form's "Where to run" choice can't express this case: it offers "This computer" and
buzz-backend-*providers found on the owner's machine, all of which run the harness on the owner's behalf. An operator running the harness themselves — on a server, or a harness other thanbuzz-acp— has no door. Today the only way through is hand-computing the tag withcompute_auth_tag, and hand-signing and publishing thekind:30177, which means handling the owner'snsecoutside the keyring.We hit this provisioning three agents as code (two headless
buzz-acpagents under systemd, one agent running through another harness's Buzz channel plugin). Each showed as a person in the desktop, and once attested, could not be mentioned.Proposed solution
A third "Where to run" option in the create-agent dialog: "Somewhere else — I run it myself".
respond_to/ allowlist and optional persona.kind:30177policy, using the existingbuild_agent_eventshape. It omitssystem_prompt/model/provider, since the operator owns those.BUZZ_AUTH_TAGvalue to install on the agent's host, single-quoted. Then, as it does for provider deploys, it can wait until the agent's latestkind:0carries a valid tag — which is what the desktop itself uses to decide "agent".These agents would then appear in My Agents with the existing "Not managed on this device" cloud marker, and their policy could be edited there like any other.
Permissions. We don't think this needs a relay-role gate, and it shouldn't be admin-only:
A relay operator who wants to limit who may bring agents would be better served by a relay-side policy than a UI gate.
Alternatives considered
buzz agents draft-create. It needsBUZZ_AUTH_TAGalready, so it can't bootstrap the first attestation. It's for an attested agent proposing a new agent (and see fix(desktop): buzz agents draft-create succeeds on relay but fails to trigger Desktop draft review form #3791).nsecsomewhere other than the keyring. A desktop action keeps the key where it is.Additional context
Related, and in practice encountered in this order when doing this by hand:
relay_members.buzz-acpnever republishes itskind:0withBUZZ_AUTH_TAG, and the desktop decides "agent" from that profile. (For comparison, another harness's Buzz channel plugin does refresh its profile with the tag on start.)set-profilewithoutBUZZ_AUTH_TAGsilently drops an existing attestation.memberbefore it was attested can't be switched tobot— it has to be removed and re-added. Possibly intentional; mentioning it because it's the last step of the same path.Duplicates: none found for registering an externally-run agent. Closest are agent snapshot import (#4197, #5458), which imports a definition rather than an externally-run key, and
draft-create(#3791).Tested against relay
40220d5(v0.2.1).