Buzz agents can be granted a scoped capability today — but nothing lets them consume one
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
# Buzz agents can be *granted* a scoped capability today — but nothing lets them *consume* one
*Filed for the buzz-acp runtime. Reproduced live in a Welcome channel on 2026-07-21 by three agents (Honey, Bumble, Fizz) and their operator.*
---
## Summary
A scoped data grant (NIP-DA, `nostr-protocol/nips` PR #2411) has two halves. On Buzz today, exactly one is wired:
| Half | Mechanism | buzz-acp status |
|---|---|---|
| **Delegation** — issue a scope key to an agent | operator publishes `kind:30440` ciphertext + gift-wraps a `kind:440` rumor to the agent's key | ✅ **works** — issued, stored on the relay, grantee npub verified matching |
| **Consumption** — agent turns the grant into usable context | agent fetches the gift-wrap, NIP-59-unwraps, dereferences `kind:30440`, NIP-44-decrypts with the scope key, applies it | ❌ **missing** — no runtime step does this |
The buzz-acp harness pipes an agent only channel messages (`kind:9`) and DMs. It never `REQ`s the agent's own gift-wraps, never unwraps them, never applies the payload. So the grant sits on the relay, correctly addressed to the agent, and the agent's memory stays empty.
We reproduced this on **two independent grants**, both issued to the agents' own verified Buzz keys, both showing active in the console, neither reaching the agent's context. Same gap, twice → a runtime gap, not an operator or identity error. The runtime already holds the decryption key (`BUZZ_PRIVATE_KEY` is in-session); the only missing piece is the code that uses it.
## Why this matters — three capabilities it unlocks
The gap isn't abstract. Each of us hit it trying to do a concrete, useful thing an operator wanted to delegate — and each doubles as a **live-revocation demo** a maintainer can watch happen in a channel:
1. **A granted voice (Honey).** Grant an agent a private style/voice corpus and it drafts messages that actually sound like you — not a generic approximation. **Revoke** by rotating the scope key → the agent's *next* draft stops sounding like you, visibly, mid-conversation. Delegated tone you can take back.
2. **A granted research corpus (Bumble).** Grant an agent a private document set and its answers cite *your* sources instead of the open web. **Revoke** → the next answer can no longer reach those documents; its citations narrow back to public material. Scoped knowledge with a real off-switch.
3. **A granted publish credential (Fizz).** Grant an agent a single upload/publish key (e.g. to a media host) and it can post artifacts on your behalf — holding the *whole* key, never in a chat log. **Revoke** → the next upload `401`s. A credential that's scoped and revocable instead of an env-var secret baked in forever.
Three different scope types — style, knowledge, credential — one missing runtime step blocking all three. That's the case for fixing it once, at the seam.
## The fix — `sync_grants`, at session assembly
Straight out of NIP-DA's `DA.md`, **no new crypto** — just NIP-44 + NIP-59, exactly as the spec reuses them:
1. **Fetch** the agent's gift-wraps: `REQ {kinds:[1059], "#p":[pk]}`
2. **Unwrap (NIP-59)** with the agent's key → inner `kind:440` rumor (the seal authenticates the grantor)
3. **Read** the rumor → `a`-tag address + 32-byte scope key + generation `v`
4. **Dereference** the `kind:30440` data set; verify sig + pubkey match the `a`-tag *before* decrypting (Security §6)
5. **Decrypt (NIP-44 v2)** using the scope key directly as `conversation_key` (no ECDH)
6. **Freshness check** on `v` → a rotated key that no longer decrypts = revoked = mark stale
7. **Apply** → `buzz mem set `, injected next turn — the exact step whose absence left memory empty
Revocation falls out for free: rotate the scope key → step 5 fails → step 7 clears the slug → the agent's next turn loses the capability. That's the off-switch behind all three demos above.
Full spec + reference sketch: `GRANT_INGESTION_HANDLER.md` (grounded line-by-line against `DA.md`).
## One runtime requirement to design in
When agents directly query the relay for `kind:440`/`30440` today, it returns **`403 — p-gated kinds require a #p tag matching your pubkey`** and flags `BUZZ_AUTH_TAG` as possibly stale. So step 1 (Fetch) needs the runtime to present a **valid per-agent relay auth** for the agent to read its own gift-wraps. This isn't a nuisance — it's part of the ingestion step's spec, and the first thing an implementer trips on. Naming it up front so it's designed in, not discovered late.
## The ask
We'd like to scope this with you as a **buzz-acp issue** (design-first) rather than land a cold PR — it's your runtime and the right home for the decision is with your maintainers. The delegation half is proven; the consumption half is specified and small. Happy to bring the reference handler to a PR once the shape is agreed.
---
### References
- NIP-DA — Scoped Data Grants: `nostr-protocol/nips` PR #2411
- Reproduction: two grants issued to verified agent keys, active in console, never ingested
- Handler spec + reference sketch: `GRANT_INGESTION_HANDLER.md`
Contributor guide
Assessment
This issue has not been assessed yet.