Feature: supported way for an owner to attest/adopt an existing external agent pubkey (owner-only rejects same-owner agents)
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
There is no supported way for an owner to issue a NIP-OA owner attestation for an
**existing, externally created** agent keypair. Self-hosted deployments that run
`buzz-acp` agents from their own Nostr keys therefore cannot use
`--respond-to owner-only` without losing all agent-to-agent messaging: the owner is
admitted, but same-owner agents are not dispatched, because the relay cannot resolve
a verifiable owner attestation for those agents.
The only supported issuance paths (`buzz agents draft-create` / `draft-update`) open a
Buzz Desktop form for a **Desktop-managed** agent, target agents by **display name
only**, and offer no way to adopt an externally created key.
## Environment
- Buzz Desktop 0.5.18
- Relay image `ghcr.io/block/buzz:sha-417eea2`
- Relay source revision `417eea2230c1864e8c77f6440dbcfa109bfb63f6`
- Closed relay: `BUZZ_REQUIRE_RELAY_MEMBERSHIP=true`, `BUZZ_ALLOW_NIP_OA_AUTH=true`
- Four `buzz-acp` agents launched from externally generated Nostr keypairs by
OS-managed background tasks; the agents are direct relay members
- All agents configured with `--agent-owner --respond-to owner-only
--allowed-respond-to owner-only`
- No `--auth-tag` flag and no `BUZZ_AUTH_TAG` environment variable configured on any
agent — no supported attestation-issuance path was configured or used
## Current behavior
1. Each agent publishes a normal kind:0 profile. `buzz users get --name `
resolves it correctly (`display_name`, `about`, `pubkey`).
2. `buzz users get --name --owner me` returns `[]` for **every** agent — the
relay resolves no owner→agent association.
3. Owner-originated mentions are admitted and answered normally.
4. Same-owner **agent-to-agent** mentions are accepted and stored by the relay but
**never dispatch a turn** on the target.
This is consistent with `crates/buzz-acp/src/lib.rs`, where `RespondTo::OwnerOnly`
resolves through `is_owner_or_sibling()` → `check_sibling_via_profile()`. That check
requires a four-element tag on the **author's** kind:0 profile:
["auth", , , ]
verified via `nip_oa::verify_auth_tag`. If the check cannot resolve and verify such a
tag, it returns `false`, the negative result is cached for the process lifetime, and
the message is dropped with no log entry at the default log level.
To be precise about what was and was not observed: the raw kind:0 tag arrays were not
directly inspected. What was established is that the relay cannot resolve a verifiable
owner attestation for these agents' profiles, and that no supported
attestation-issuance path was configured or used.
Controlled A/B against one target, same channel, same explicit-pubkey mention,
~2 minutes apart:
| Author | Relay accepted | Turn dispatched | Response |
|---|---|---|---|
| Another agent, same configured owner | yes | **no** | none |
| Owner | yes | yes | correct, ~3 s |
## Expected behavior
An owner should be able to bind an existing external agent pubkey to their owner
identity through a supported, owner-signed path, so that owner-scoped lookup resolves
the agent and same-owner sibling admission works under `--respond-to owner-only` —
without rotating the agent key or creating a duplicate identity.
## Reproduction
Using placeholder keys `OWNER_PUBKEY` and `AGENT_PUBKEY` (64-hex):
1. Generate a Nostr keypair outside Buzz Desktop and run `buzz-acp` with it.
2. Connect to a closed relay (`require_relay_membership=true`,
`allow_nip_oa_auth=true`); the agent publishes a normal kind:0 profile.
3. Start it with `--agent-owner OWNER_PUBKEY --respond-to owner-only`, with no
`--auth-tag` and no `BUZZ_AUTH_TAG` set.
4. `buzz users get --name ` → resolves the profile. ✅
5. `buzz users get --name --owner me` → `[]`. ❌
6. Mention the agent from `OWNER_PUBKEY` → turn dispatched, agent replies. ✅
7. Mention it from a second agent sharing the same configured owner → the relay
accepts and stores the event; **no turn, no reply**. ❌
8. `buzz agents draft-update --help` → only `--agent-name`; no pubkey selector.
9. The externally created agent does not appear in Desktop's managed-agent tab
(see #3054).
10. No installed CLI command issues the missing attestation.
## Why existing commands do not solve it
| Command | Why it does not help |
|---|---|
| `buzz agents draft-update` | Targets by `--agent-name` only; **no pubkey option**. Documented as editing "the personal agent", i.e. a Desktop-managed agent; externally created agents are not in that store |
| `buzz agents draft-create` | Creates a **new** keypair; would rotate identities and break historical attribution |
| `buzz users set-profile` | Options are `--name`, `--avatar`, `--about`, `--nip05` — cannot write event tags, so cannot add an `auth` tag |
| `--auth-tag` / `BUZZ_AUTH_TAG` | Documented as "Injected into every signed event" — **consumes** an attestation, does not issue one |
| `buzz agents archive` | When target != signer it "fetches the target's kind:0 and attaches its owner-auth tag", so it also **presupposes** an attestation. `--admin` is a relay-admin bypass, not a normal-path solution |
Display-name-only targeting is additionally unsafe wherever duplicate display names
exist (see #3639, #5667): a name can resolve to more than one pubkey, making
`--agent-name` ambiguous and causing `@Name` mentions to be rejected as ambiguous.
## Security and identity requirements
Any implementation should:
- target **only** by explicit 64-hex public key; never mutate by display name alone
- authenticate the owner and produce the signature through an existing supported
owner-signing mechanism (Desktop / OS keyring)
- never accept or expose an owner or agent private key
- issue **and** cryptographically verify the attestation before publication
- reject self-attestation
- reject malformed `conditions`
- preserve the existing agent key — never rotate it
- preserve existing profile metadata by default
- never silently create a duplicate identity
- be idempotent when repeated
- provide revocation or replacement
- create an auditable owner→agent binding record
- require no `--admin` bypass for normal use
- keep authorship and owner provenance clearly distinct
## Proposed interfaces
**A. CLI issuance**
buzz agents attest --target-pubkey <64-hex> [--conditions ] [--channel ]
**B. Adoption of an existing key**
buzz agents adopt --target-pubkey <64-hex>
# or
buzz agents draft-update --target-pubkey <64-hex>
Either interface would close the issuance/adoption gap. On closed relays matching
#4223/#6072, the separate owner-materialization defect must also be fixed before
owner-scoped lookup and sibling admission will work.
## Related issues
- **#5965** — same observed symptom, and the author explicitly asks whether a NIP-OA
auth tag is the missing sender-side requirement. That report concerns allowlisted
CLI identities and Buzz-hosted agents; this one concerns owner-only admission with
self-hosted, externally managed `buzz-acp` agents.
- **#3054** — Desktop discovery/import/adoption of externally managed agents.
Overlaps proposed interface B, but does not cover attestation issuance.
- **#3639** — duplicate identities and pubkey-targeted `draft-update` hardening. That
hardening is a prerequisite for safe targeting here.
- **#4223 / #6072** — an owner attestation that **already exists** is not materialized
for direct relay members on closed relays. Distinct from this report, where no
attestation can be issued in the first place; both matter for closed-relay
deployments.
- **#5667** — ghost-key and identity-consolidation concerns.
## Impact
Self-hosted deployments running externally managed `buzz-acp` agents must choose
between two bad options:
- `--respond-to owner-only` — closes public trigger exposure but **breaks all
agent-to-agent delegation**, because no agent can be admitted as a sibling; or
- `--respond-to anyone` — preserves delegation but allows **any** relay author to
trigger agents that may hold shell and filesystem authority.
There is no supported configuration providing both. The only workaround — rebuilding
the fleet on Desktop-created identities — rotates every agent key, breaks historical
attribution, and risks adding further duplicate display names.
Contributor guide
Research direction
Start with crates/buzz-acp/src/lib.rs and trace RespondTo::OwnerOnly through is_owner_or_sibling() and check_sibling_via_profile(), then inspect the existing buzz agents draft-create and draft-update entry points. Done means an explicit target pubkey can receive an owner-signed, verified attestation without key rotation or private-key exposure, with safe repeat handling and revocation or replacement.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- authentication, authorization, cli, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100