Skip to content

feat(cli): add external-agent identity and listen primitives - #3964

Open
dspury wants to merge 1 commit into
block:mainfrom
Lunar-Park:pr/cli-external-agent-foundation
Open

feat(cli): add external-agent identity and listen primitives#3964
dspury wants to merge 1 commit into
block:mainfrom
Lunar-Park:pr/cli-external-agent-foundation

Conversation

@dspury

@dspury dspury commented Jul 31, 2026

Copy link
Copy Markdown

Summary

Adds the CLI primitives needed for a resident external agent to identify itself, publish a complete agent profile, and consume scoped Buzz events without being owned or supervised by Desktop.

What changed

  • Adds buzz users me, a local identity command that prints the configured public identity without making a relay request or exposing private key material.
  • Adds buzz listen, which streams channel-scoped and mention-scoped relay events as NDJSON with a versioned envelope suitable for long-running external consumers.
  • Adds buzz keys generate, a local-only identity generation command that writes secrets with owner-only permissions and refuses accidental overwrite.
  • Adds buzz agents profile get/set and routes channels set-add-policy through the same read-modify-write path so kind:10100 updates do not clobber existing profile fields.
  • Documents the external-agent CLI contract and adds fixture/subprocess tests for identity, listen, fixture normalization, and key generation behavior.

Why

Buzz already has the relay and profile model needed for agents that run outside Desktop, but the CLI was missing a small stable contract for those agents to bootstrap safely. External adapters need to prove local key custody, publish the agent's own directory record, and receive scoped events without relying on Desktop-owned process lifecycle or private key transfer.

The implementation keeps the boundary narrow: it adds generic CLI primitives and tests, without adding runtime-specific adapter code or new relay endpoints.

Compatibility / risk

  • Existing compact message consumers remain compatible; additional compact fields are additive.
  • buzz listen is opt-in and uses explicit channel/mention filters.
  • buzz keys generate does not require an existing Buzz identity or relay connection, and does not print the secret unless explicitly requested.
  • kind:10100 writes become safer by preserving existing and unknown profile fields instead of publishing partial replacement documents.

Validation

  • cargo fmt -p buzz-cli --check — passed
  • cargo clippy -p buzz-cli --all-targets -- -D warnings — passed
  • cargo test -p buzz-cli — passed

Add the CLI primitives needed for resident external agents to bootstrap and consume Buzz events without being owned or supervised by Desktop.

This includes local identity inspection, scoped realtime listening, local key generation, and safe read-modify-write updates for agent profile records. The changes are harness-neutral and avoid new relay endpoints.

The profile write path now preserves existing and unknown kind:10100 fields instead of publishing partial replacement documents, preventing channel_add_policy updates from clobbering visible agent metadata.

Signed-off-by: dspury <demitriusspury@gmail.com>
@murdawkmedia

Copy link
Copy Markdown

I exercised the CLI branch in a composed checkout (all Buzz CLI/unit and added external-agent tests passed), but found four production blockers for long-running Hermes/OpenClaw residents:

  1. Windows secret-file ACLs: keys generate --out applies/reasserts 0600 only under #[cfg(unix)]. On Windows the key inherits the parent DACL while the command/docs promise owner-only storage. Please install and verify a restrictive DACL or fail closed as unsupported; a transactional write would also avoid partial key files.

  2. Replaceable-profile lost updates: apply_profile_update performs fetch → merge → publish without optimistic concurrency or post-write head verification. Kind 10100 uses second-resolution timestamps and event-ID tie-breaking, while a dominated replaceable event can still produce an accepted/duplicate response and exit zero. Same-second or concurrent updates can therefore be lost while the adapter believes they succeeded. Please verify the submitted event became the current head and return conflict/retry otherwise.

  3. Lossy catch-up above 1,000 events: listen --since supplies no limit, pagination cursor, or overflow signal. The relay/DB ceiling defaults to the newest 1,000 rows and then emits EOSE, silently skipping older unseen events after a long outage. A resident bridge needs paginated catch-up or an explicit composite cursor/overflow recovery contract.

  4. Events cannot be independently verified: the WebSocket path deserializes relay events, then normalize_events removes sig before output. The adapter cannot verify the claimed ID/author afterward, yet owner/allowlist activation depends on those claims. Please verify ID/signature before emitting and preserve sig (or include an explicit trustworthy verification result), with tests for forged authors, invalid signatures, and filter mismatches.

The scoped #h/#p subscriptions, NIP-42/NIP-OA use, public-only users me, and documented at-least-once reconnect behavior are good foundations. These fixes would make the surface suitable for a durable independently hosted resident rather than only a happy-path adapter.

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.

2 participants