Skip to content

RFC: Support keyless operation via a pluggable signer #6467

Description

@jrobotham-square

Summary

Decouple an agent's identity from its signing key so buzz can run an agent without the secret key ever residing on the host. Introduce a signer abstraction with two implementations — a local signer (holds the key, today's behavior) and a remote signer (delegates signing to an external service; the host holds only the public key).

Motivation

Today both the CLI and the agent harness hard-require the private key to be present locally. Identity and key custody are therefore inseparable: to run an agent, its secret must live on the host.

There are deployment and security scenarios where you want the opposite — running an agent in an ephemeral, shared, or less-trusted environment while the signing key stays under separate custody. Centralized custody also makes key rotation and revocation possible without touching every host. None of this is achievable while the key is a mandatory local input.

A remote signer also unlocks a stronger deployment: hosts with no direct route to the relay, able to reach it only through the signer. Beyond defense in depth, this is what makes keyless operation enforceable rather than merely available — if a host can reach the relay directly, nothing prevents falling back to a locally held key; remove that direct path and the signer becomes the only way through, leaving no local-key shortcut. The signer then doubles as a single mediated egress point for policy, auditing, and revocation.

Proposal

Introduce a signer interface that separates the two concerns:

  • Identity (public key) — needed pervasively (mention detection, thread self-detection, channel discovery, author filters). This should require only the agent's public key.
  • Signing (secret key) — needed at a small number of well-defined sites. Route these through the signer interface.

The abstraction belongs in the shared client layer that every consumer builds on, so the CLI, the agent harness, and any other programmatic user of the library inherit keyless support from a single change rather than each solving it independently. Covering only one consumer doesn't achieve the goal — if an agent's actions route through any key-holding component, it isn't keyless.

Two implementations:

  • Local — holds the keypair; current behavior and the default.
  • Remote — delegates every operation that needs the secret to an external signer. The host holds only the public key plus whatever credential authorizes calls to that signer.

Selection is a startup/config-time choice (an agent runs as one or the other for its lifetime), not runtime-dynamic. Local is the default, so existing users see no change. Configuring a remote signer is opt-in and takes precedence — any local credentials are ignored.

What this requires (high level)

  • Make the private key optional; require only the agent public key for identity operations.
  • Funnel the secret-dependent operations through the signer interface: event signing, relay auth (NIP-42), payload encrypt/decrypt (NIP-44), request auth (NIP-98), and encrypted-storage address derivation.
  • Treat relay authentication as a control-flow choice, not just a different signer — a remote signer may authenticate the relay leg itself, so the host should be able to skip local auth rather than perform it.
  • Do not assume direct relay connectivity. Every relay-touching operation — reads included — must be routable through the signer, so a deployment can remove a host's direct relay access entirely and make the signer its only path out.
  • Make non-essential signed housekeeping (presence, typing, reactions, etc.) skippable or offloadable, so an agent can run without a local key even if those are unavailable.
  • Ensure no secret can leak to child processes via inherited environment.

Scope / non-goals

  • This issue covers the interface and making the key optional within buzz.
  • The remote signer implementation (the service that holds the key and how it authenticates callers) is out of scope and deliberately left to implementers — the interface should not assume a particular backend.
  • The relay protocol is unchanged. Whether signing happens locally, via an inline proxy, or via an out-of-band signing service, the relay just sees a normal client — it never needs to know a remote signer exists.

Open questions

  • The exact shape of the signer abstraction (trait vs enum) and how far it threads through the CLI vs the harness.
  • Whether the remote path is best modeled per-operation or as a single "sign this" primitive plus a few protocol-specific helpers.
  • When a remote signer is configured, whether a local key that is still present in the environment should be tolerated, warned about, or refused — a present-but-unused key still contradicts the keyless intent.

Related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions