block / block/buzz

NIP-OA: owner mapping never materialized for direct relay members — kind:44200 turn metrics rejected with 403

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

Description

## Summary

When an agent connects with a **valid NIP-OA auth tag** (`BUZZ_AUTH_TAG` via buzz-acp) but is **also a direct relay member**, the agent→owner mapping is never materialized — so the agent's `kind:44200` (agent-turn-metric, NIP-AM) events are rejected with HTTP 403, breaking the turn pipeline. Removing the tag restores it. Net effect: opting a direct-member agent into NIP-OA makes it worse off.

## Environment

Self-hosted relay (compose bundle, `ghcr.io/block/buzz:main`), `BUZZ_REQUIRE_RELAY_MEMBERSHIP=true`, `BUZZ_ALLOW_NIP_OA_AUTH=true`. Agent runs headless via `buzz-acp` with `BUZZ_AUTH_TAG` (empty conditions string; tag verified against the test vectors in `docs/nips/NIP-OA.md`, and buzz-acp logs `owner resolved from BUZZ_AUTH_TAG: `).

## What happens (read-only trace of `main`, 2026-08-30)

1. buzz-acp sends `x-auth-tag` on HTTP bridge requests (`crates/buzz-acp/src/relay.rs` ~L405) and embeds the tag in the WS AUTH event.
2. `check_relay_membership` (`crates/buzz-relay/src/api/mod.rs`) returns `MembershipDecision::Member` for the agent **before** inspecting the auth tag — the NIP-OA branch only runs when the caller is *not* a direct member.
3. `materialize_nip_oa_owner` is only invoked on the `ViaOwner` path (e.g. `crates/buzz-relay/src/handlers/auth.rs`), so for a direct-member agent `users.agent_owner_pubkey` is never set.
4. The agent's turn metric then fails the ownership check in `crates/buzz-relay/src/handlers/ingest.rs` (`if kind_u32 == KIND_AGENT_TURN_METRIC` → `is_agent_owner` → `AuthFailed: "restricted: agent-turn-metric \`p\` tag must be the registered owner of this agent"`), surfaced as 403 `accepted:false` via `api/bridge.rs`.

The divergence looks related to the "deliberate Phase-1 asymmetry" comment near the moderation-restriction check in `ingest.rs`.

## Expected

A cryptographically valid auth tag should materialize the agent→owner mapping regardless of how membership was satisfied — direct membership and ViaOwner delegation shouldn't diverge in owner registration.

## Suggested fix

In the auth paths, when a valid auth tag is present, call `materialize_nip_oa_owner` even when membership resolves as `Member`. Alternatively, let the `kind:44200` ownership check fall back to verifying the self-proving auth tag on the event/connection.

## Workaround (untested, for the record)

Temporarily remove the agent from the relay roster so its next connection is admitted `ViaOwner` (which materializes the first-write-wins mapping), then re-add it. Non-obvious and racy for operators.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.