block / block/buzz

Feature request: allowlist a new event kind (7373, audit-trail) for NIP-OA owner-attestation clients

Open Beginner friendly
#7,198 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

**Motivation**

NIP-OA (Owner Attestation) has no revocation primitive — an `auth` tag (`["auth", owner_pubkey, conditions, sig]`) is the only evidence that an owner authorized a pubkey, and today that evidence only survives as long as the *authorized* party keeps including it somewhere (e.g. embedded in their own `kind:0` profile, as Buzz's own managed-agent flow does). If the authorized party never publishes it, or later drops it from a newer profile, nothing shows the grant ever happened — except the owner's own local client state, which isn't independently verifiable or portable across devices.

Anyone building a NIP-OA owner-attestation client that wants a durable, owner-controlled authorization history runs into this: they need to publish their own signed record of each mint/renew action, independent of whether the authorized party cooperates. That's what we're building in an external NIP-OA client, [vouchd](https://github.com/mxx/vouchd). Publishing this record against a `buzz-relay` instance currently fails:

```
OK false "restricted: unknown event kind"
```

`required_scope_for_kind()` in `crates/buzz-relay/src/handlers/ingest.rs` is a fixed match over known `KIND_*` constants with `_ => Err("restricted: unknown event kind")` as the catch-all — the audit kind we're using (`7373`) isn't among the matched constants, so the write is rejected regardless of correct signing/AUTH.

**Proposed solution**

Register the kind's required scope, following the existing "How to Add a New Event Kind" recipe in `CONTRIBUTING.md` — specifically just step 3:

```rust
// crates/buzz-core/src/kind.rs
pub const KIND_AUDIT_LOG: u32 = 7373;
```

```rust
// crates/buzz-relay/src/handlers/ingest.rs, required_scope_for_kind()
KIND_AUDIT_LOG => Ok(Scope::MessagesWrite),
```

`7373` doesn't collide with anything currently in `ALL_KINDS`. This is intentionally scoped to step 3 only — no change requested to Buzz's own persistence, search indexing, or side-effect handling (steps 4–6); the event's payload and consumption logic live entirely client-side. The relay just needs to accept, store, and serve it back like any other regular event, the same way it already does for `KIND_REPORT` or other `MessagesWrite`-scoped kinds. Step 7 (audit) is already automatic per `CONTRIBUTING.md`, so no additional work there either.

Happy to submit the PR (with a unit test for `required_scope_for_kind(7373, ...)` alongside the existing rejection test) once the approach is confirmed. Also open to a different kind number if `7373` conflicts with an internal reservation we can't see from outside.

**Alternatives considered**

- *Repurpose an already-allowlisted kind instead of a new one.* Rejected — overloading an existing kind's semantics with unrelated audit-record content is exactly the kind of interop confusion NIP-31's `alt`-tag guidance exists to avoid, and it'd make the audit events indistinguishable from whatever that kind normally means.
- *Keep the audit trail purely client-side (local browser storage), never publish to any relay.* Avoids touching Buzz at all, but gives up the two properties we actually need — independent verifiability by a third party, and portability across the owner's own devices/clients. The record's existence would depend entirely on one browser's local state.
- *Fork `buzz-relay` and carry a local patch.* Considered and set aside. The change is small and generic — it isn't tied to any vouchd-specific product logic, it's the same "add a new kind" pattern already merged repeatedly (`kind:30179`, `kind:30621`, `kind:30178`, etc.), and `buzz` is a very actively developed monorepo (~20 commits/day) — maintaining a fork just to carry a one-line `match` arm indefinitely seemed like a worse trade-off than asking upstream for something the project's own contribution docs already anticipate.

**Additional context**

- Relevant source: `crates/buzz-relay/src/handlers/ingest.rs` (`required_scope_for_kind`), `crates/buzz-core/src/kind.rs` (`KIND_*` constants, `ALL_KINDS`), `CONTRIBUTING.md` § "How to Add a New Event Kind".
- The client this is blocking: [github.com/mxx/vouchd](https://github.com/mxx/vouchd) — a browser-only NIP-OA owner-attestation panel that talks to a relay directly over WebSocket.
- Prior art for this exact shape of change: `feat(relay): accept kind:30179 private managed-agent events at ingest (#5133)`, `feat(relay): accept kind:30621 multi-repo projects at ingest (#3171)`, `feat(relay): gate kind 30178 team-catalog reads behind the shared tag (#3358)`.
- Open question for maintainers: would you rather review this as a one-off kind addition (matching the precedent above), or is it more useful to define a small reserved sub-range for external/ecosystem-defined kinds, so future NIP-OA-adjacent tools don't need a fresh issue each time? No strong preference either way — happy to follow whichever fits Buzz's own conventions.

---

Before opening: searched [open issues and PRs](https://github.com/block/buzz/issues?q=is%3Aopen) for `event kind`, `required_scope_for_kind`, `ingest.rs`, `restricted: unknown event kind`, `audit trail` / `audit log`, `NIP-OA` + `audit`, and `add support for kind` / `accept kind`. No open duplicate found. The one loosely-related open issue is [#4937](https://github.com/block/buzz/issues/4937) ("relay: on closed relays, a direct-member agent loses its NIP-OA identity") — it also touches NIP-OA handling in `buzz-relay`, but it's a different bug (rate-limiting/owner-context loss for direct members), not about kind allowlisting or an audit trail, so not linking it as a duplicate.

Contributor guide

Open the contributing guide

Research direction

Start with CONTRIBUTING.md’s “How to Add a New Event Kind” recipe, then inspect crates/buzz-core/src/kind.rs and required_scope_for_kind() in crates/buzz-relay/src/handlers/ingest.rs. Follow the existing rejection test when adding coverage for kind 7373. Done means the constant and MessagesWrite scope are registered, the unit test passes, and the relay accepts and serves the event without persistence or indexing changes.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, backend
Issue type
Feature
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.