Files
buzz/docs/channel-delete-authority.md
Carl 2208791c78 feat(relay): expose current channel delete eligibility
Read persisted, community-scoped Delete ownership through an authenticated query extension. Share the command predicate without changing mutation permissions and advertise support on mapped hosts.

Keep storage errors unavailable and validate viewer/channel/tenant boundaries, profile disagreement, replay, and archived state. Mark two byte-identical baseline test PEM fixtures with rule-specific scanner annotations.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
2026-09-28 21:44:15 -07:00

3.4 KiB

Channel Delete authority read

POST /query accepts an opt-in version-1 channel_delete_authority filter. This is a read-only UI eligibility probe, not a persisted Nostr event, delegated signing authority, ownership-transfer API or permission token. Kind 9008 remains the destructive command and performs its existing authorization checks at write time. A successful read does not protect against subsequent membership changes.

Discovery and exact request

Host-bound NIP-11 advertises:

{"channel_delete_authority":{"version":1,"community_id":"<host-community-uuid>"}}

Unresolved hosts do not advertise the capability. Clients must require version 1 and retain the host-derived community UUID for response validation. Older relays may ignore unknown filters and return ordinary event arrays; those are not eligibility evidence.

Send exactly one filter through the existing authenticated bridge:

[{"kinds":[9008],"#h":["<canonical-channel-uuid>"],"#p":["<authenticated-viewer-hex>"],"channel_delete_authority":1}]

The ordinary bridge Host binding, NIP-98 proof/body validation, admission, replay guard and relay-membership checks run before this extension. The same configured development-only auth fallback as other bridge reads still applies. Unknown fields, mixed filters, unsupported versions and mismatched viewers fail with 400. The viewer must be an active channel member; a nonmember or unknown channel returns a generic 403 without ownership or existence data.

Response and authority

{"channel_delete_authority":1,"community_id":"<host-community-uuid>","pubkey":"<authenticated-viewer-hex>","channel_id":"<canonical-channel-uuid>","can_delete":true}

The decision uses the writer's active roster and the same owner predicate as kind 9008: direct owner, or the human in the persisted community-scoped ownership mapping for any active owner-role agent. Profile claims are not consulted: replacing or removing a profile auth tag neither transfers nor revokes the first-write-wins users.agent_owner_pubkey mapping. Archived channels return false, matching the existing mutation validator's archive guard. The probe is intentionally narrower than the mutation's existing nonmember-owning-human path; this does not change that mutation permission.

Database failure or the five-second read deadline returns 503, never a fabricated false. Clients must bind version/community/viewer/channel, require a boolean, bound response bytes, and distinguish unavailable authority from denial. They should retain independently established actions when this optional read fails, provide retry, and recheck before signing and publishing. The bridge response is trusted through the configured relay transport; it is not a relay-signed event.

Deploy the relay capability before expecting clients to support owner-agent Delete. No schema migration or change to kind 9008's wire format is required.

Validation

channel_delete_authority_tests.rs exercises the production router with real NIP-98 proofs and Postgres/Redis, compares decisions with the actual Delete validator, and covers profile/mapping disagreement, coowners, demotion/removal, archived channels, tenant isolation, strict filters, replay and storage failure. It validates commands without executing deletion. With an isolated test database:

cargo test -p buzz-relay --lib api::bridge::channel_delete_authority -- --include-ignored