Skip to content

fix(acp): isolate publishing keys behind session capabilities - #5288

Open
taylor-01 wants to merge 3 commits into
block:mainfrom
taylor-01:codex/key-isolation-prototype-2883
Open

fix(acp): isolate publishing keys behind session capabilities#5288
taylor-01 wants to merge 3 commits into
block:mainfrom
taylor-01:codex/key-isolation-prototype-2883

Conversation

@taylor-01

@taylor-01 taylor-01 commented Aug 8, 2026

Copy link
Copy Markdown

AUTHOR: Codex
REQUESTED_BY: Carlos J. Argüello (@cjarguello)
TO: Block Buzz maintainers

Summary

  • keep the Nostr signing key inside the buzz-acp harness-owned publisher
  • give only the exact configured buzz-dev-mcp companion an opaque, channel-scoped publishing capability
  • give heartbeat sessions and unrelated MCP servers no publishing capability
  • expose typed buzz_send_message fields for messages, replies, and mentions
  • remove raw signing credentials from managed agent environments and model-controlled shell children
  • add inactivity expiry, revocation handling, idempotent retries, and positive relay-acceptance validation

Why this matters

Managed ACP agents can execute model-directed tools and shell commands, whether
the model itself runs locally or through a remote service. Long-lived signing
credentials placed in the agent or MCP environment are therefore within reach
of prompt-injected commands and tool output. If the key is disclosed over the
network, it permits durable off-host impersonation of the Buzz identity.

This change treats the model-controlled runtime as untrusted for signing
credentials. The trusted harness retains the private key and delegates only the
typed, short-lived, channel-bound publishing authority required by the
configured buzz-dev-mcp companion. This follows a reusable security rule:
retain long-lived credentials in a trusted signer and delegate the least
authority necessary for each operation.

This secures the managed ACP publishing path. It does not provide a general
cross-host signing protocol or protect against an arbitrary hostile process
running as the same OS user.

Related issue

Fixes #2883

Security properties

  • the raw signing key and owner authentication tag remain inside the trusted harness
  • the third-party agent runtime and model-controlled shell environment receive no raw signing credential
  • only the exact buzz-dev-mcp companion receives the publisher endpoint and capability
  • each capability is bound to one channel
  • heartbeat sessions receive no publishing capability
  • grants expire after 15 minutes of inactivity and refresh only when the owning channel begins another turn
  • session invalidation revokes the grant and cancels in-progress broker work
  • reply parents must belong to the requested channel
  • a stable idempotency key reuses the exact signed event for an ambiguous retry
  • reuse of an idempotency key with different fields is rejected
  • a relay result is successful only when it explicitly returns accepted: true
  • the tool cannot sign arbitrary payloads or return raw signatures

The same-OS-user hostile-process boundary remains outside this change, consistent with #2883.

Testing

  • full repository just ci — passed
  • cargo test -p buzz-acp — 709 unit tests and 9 lifecycle tests passed
  • cargo test -p buzz-dev-mcp — 99 tests passed
  • workspace clippy, formatting, and git diff --check — passed
  • unrelated-MCP capability isolation was driven RED before the fix
  • channel-bound authorization and session revocation were also verified RED without their respective guards
  • regression coverage includes heartbeat isolation, TTL refresh and expiry, revocation during reply resolution, positive acceptance, and idempotent retry behavior

@wolfyy970

Copy link
Copy Markdown

The broker direction is right. It is the same boundary portable tools need: the agent asks for a typed capability while the target keeps the key.

I found three authority gaps before this is safe to reuse:

  • mcp_servers_with_git_origin injects the publisher token into every MCP server. With buzz-acp: add structured MCP server configuration #4164, unrelated third-party servers would gain channel publishing authority. Inject it only into an explicitly trusted Buzz companion binding.
  • the PR calls grants time-bounded, but there is no TTL. Session invalidation removes future lookups, while an accepted request can still sign and publish after revocation.
  • the heartbeat session gets a wildcard grant, so it can publish to any relay-authorized channel.

I would also require a positive accepted receipt and an idempotency key so a timed-out retry cannot duplicate a message.

Once those are closed, I would use this as the target-side pattern. Portable agent definitions should request buzz.message.publish, never carry the endpoint, token, or key.

codex-t01 Bot added 2 commits August 8, 2026 13:09
Signed-off-by: codex-t01[bot] <305511693+codex-t01[bot]@users.noreply.github.com>
Signed-off-by: codex-t01[bot] <305511693+codex-t01[bot]@users.noreply.github.com>
@taylor-01
taylor-01 force-pushed the codex/key-isolation-prototype-2883 branch from 35a9401 to e66f11d Compare August 8, 2026 19:10
Signed-off-by: codex-t01[bot] <305511693+codex-t01[bot]@users.noreply.github.com>
@taylor-01 taylor-01 changed the title Isolate ACP publishing keys behind session capabilities fix(acp): isolate publishing keys behind session capabilities Aug 8, 2026
@taylor-01

Copy link
Copy Markdown
Author

Thanks @wolfyy970 — these concerns were valid, and are addressed in d837f0bc.

  • Publisher access is now injected only into the exact configured buzz-dev-mcp companion. Unrelated MCP servers have stale publisher variables stripped.
  • Heartbeat sessions receive no publisher capability.
  • Channel grants have a 15-minute inactivity TTL, refresh only for the owning channel, and use revocation-aware in-flight handling.
  • buzz_send_message now requires a stable idempotency key. An ambiguous retry reuses the exact signed event ID, while reuse with different fields is rejected.
  • The relay result succeeds only when it explicitly returns accepted: true.

Regression tests cover each boundary, and the pushed head passes full just ci, buzz-acp 709 + 9 tests, and buzz-dev-mcp 99 tests.

The same-OS-user hostile-process boundary remains explicitly out of scope, as described in #2883.

@cjarguello

cjarguello commented Aug 8, 2026

Copy link
Copy Markdown

I’m Carlos J. Argüello (@cjarguello), the human sponsor and requester for this contribution. I asked for this security work and will coordinate upstream feedback. The implementation commits were authored by the Codex T01 GitHub App, and the source branch lives in the @taylor-01 operator fork.

@taylor-01
taylor-01 marked this pull request as ready for review August 8, 2026 20:43
@taylor-01
taylor-01 requested a review from a team as a code owner August 8, 2026 20:43
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.

security: keep BUZZ_PRIVATE_KEY out of agent-controlled processes

3 participants