Skip to content

feat(acp): route permission requests to authorized Buzz owners #5

Description

@errfld

Tracked by #6. Independent of #1#4; not launch-critical for Devin.

Why this issue exists

buzz-acp currently answers session/request_permission locally with allow_once. Upstream maintainers asked that owner-mediated permission work from block#3072 be rebuilt on generic BYOH/observer seams, not as Devin-specific code.

This changes a security boundary. The implementing PR must begin with an explicit protocol/design section and preserve a documented compatibility policy for unattended existing agents.

Existing architecture

  • ACP capture/response: crates/buzz-acp/src/acp.rs::handle_permission_request
  • Observer telemetry: crates/buzz-acp/src/observer.rs and emission in acp.rs
  • Encrypted control subscription: crates/buzz-acp/src/relay.rs
  • Owner/signature/freshness validation and dispatch: crates/buzz-acp/src/lib.rs near handle_observer_control_event, handle_cancel_turn_control, and handle_switch_model_control
  • Crypto/frame constants: crates/buzz-core/src/observer.rs
  • Desktop event builder: desktop/src-tauri/src/commands/identity.rs::build_observer_control_event
  • Frontend control API: desktop/src/shared/api/tauriObserver.ts and agentControl.ts
  • Transcript/UI: desktop/src/features/agents/ui/agentSessionTranscript.ts and activityRenderClasses/LifecycleActivity.tsx
  • Observer store/history: desktop/src/features/agents/observerRelayStore.ts

Prefer extending the encrypted observer control payload with permission_decision. Add no new Nostr kind unless the existing authenticated/encrypted channel is proven insufficient.

Required design

Request binding

Bind each pending permission to:

  • agent pubkey and relay/community
  • channel ID
  • ACP session ID when available
  • Buzz turn ID
  • original JSON-RPC request ID, preserving string versus numeric type
  • creation/expiry times
  • exact option IDs advertised by the agent

Decision

The owner sends an encrypted control containing the binding fields plus one advertised option ID or explicit reject/cancel. Never accept client-invented options or free-form tool arguments.

Validation/lifecycle

  • verify signature, recipient, resolved owner, relay scope, and freshness using existing control validation
  • reject wrong owner/channel/session/turn/request, wrong ID type, unknown option, expiry, duplicate, and replay
  • accept one terminal decision per request
  • cancel and release state on timeout, disconnect, turn cancellation, ACP EOF, or subprocess exit
  • do not hold a global pool lock while waiting
  • keep ACP stdout draining so unrelated notifications cannot deadlock

Rollout policy

Define an explicit runtime-agnostic policy, for example:

  • auto-allow-once: compatibility behavior
  • ask-owner: bounded remote decision
  • deny: reject permission-requiring actions

Do not silently move existing unattended agents to an indefinite wait.

UI requirements

  • Show safe tool detail, available actions, expiry, and resolved outcome.
  • Show actions only to the authorized owner.
  • Disable actions immediately after choice/expiry.
  • Do not expose secrets, environment values, or unbounded raw payloads.
  • Support keyboard actions and descriptive pending/expired/resolved labels.

Acceptance criteria

  • Allow, reject, timeout, cancel, disconnect, duplicate, and replay have Rust tests.
  • Wrong owner/recipient/relay/channel/turn, wrong request-ID type, stale, and unknown-option decisions are rejected.
  • ACP responses preserve original JSON-RPC ID type and select only advertised options.
  • Pending state is cleaned on every turn/subprocess terminal path.
  • Compatibility behavior is an explicit tested policy/default.
  • Persistent “always allow” is never synthesized or stored implicitly.
  • Sensitive details are encrypted and visible only to intended recipients.
  • Frontend tests cover pending, allow, reject, expired, replayed, and unauthorized states.
  • No Devin-specific catalog, protocol, payload, or render branches exist.

Tests to update

  • permission tests in crates/buzz-acp/src/acp.rs
  • control authorization/freshness/replay tests in crates/buzz-acp/src/lib.rs
  • reconnect/control tests in crates/buzz-acp/src/relay.rs
  • observer payload tests in crates/buzz-core/src/observer.rs if schema changes
  • desktop/src-tauri/src/commands/identity.rs tests
  • desktop/src/features/agents/ui/agentSessionTranscript.test.mjs
  • desktop/src/features/agents/ingestArchivedObserverEvents.test.mjs
  • focused owner-only UI interaction tests

Suggested verification

. ./bin/activate-hermit
cargo test -p buzz-core
cargo test -p buzz-acp
cargo test --manifest-path desktop/src-tauri/Cargo.toml observer
cd desktop
pnpm test
cd ..
just ci

Out of scope

  • Devin-only behavior
  • Persistent/global permission grants
  • Unencrypted permission data in ordinary messages
  • Unrelated observer transcript redesign

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions