Skip to content

feat: kind:0 NIP-OA as single source of truth for agent ownership (stacked on #1060)#1186

Closed
tellaho wants to merge 3 commits into
tho/activity-ingress-ownershipfrom
tho/activity-ownership-kind0
Closed

feat: kind:0 NIP-OA as single source of truth for agent ownership (stacked on #1060)#1186
tellaho wants to merge 3 commits into
tho/activity-ingress-ownershipfrom
tho/activity-ownership-kind0

Conversation

@tellaho

@tellaho tellaho commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1060 — base branch is tho/activity-ingress-ownership, not main. Review #1060 first.

What this does

Makes each agent's live kind:0 NIP-OA auth tag the single source of truth for agent-activity ownership, end-to-end — so the relay's observer-frame delivery and desktop's visibility gate agree on the same proof. This replaces the custom relay ownership endpoint #1060 introduces.

This is the direction Max & Eva converged on in the agent-observability room: the relay was gating delivery on the DB column agent_owner_pubkey (written from the NIP-42 AUTH event), while a UI gating on kind:0 could disagree with what the relay actually ships. This PR closes that gap.

The four moves

  1. Shared strict extractionbuzz-sdk::nip_oa gets exactly-one-auth-tag extraction (mirrors the relay's existing extract_single_auth_tag_json), so relay + desktop share one helper. +5 unit tests.
  2. resolveAgentOwnership → thin wrapper over the existing resolveOaOwner (live kind:0 path). Adapter shape unchanged — useCanViewAgentActivity and useChannelAgentSessions both keep working.
  3. Delete the custom ownership endpointGET /api/agents/:pubkey/ownership, its Tauri command + registration, and the e2e mock.
  4. Align the relay's observer-frame delivery gate to verify the same live kind:0 proof. Authority order: session NIP-OA fast-path → live kind:0 proof → DB cache fallback. A kind:0 owner mismatch DENIES (no DB fallthrough), so the DB can never contradict kind:0. agent_owner_pubkey stays as a cache/backfill fallback so BYO/CLI agents that AUTH without publishing a profile keep observer delivery. +3 DB-backed tests.

Policy note (open for tho)

Shipped with the BYO fallback kept — AUTH-only/CLI agents retain observer delivery without publishing a kind:0. If we'd rather require every owned agent publish a kind:0 (dropping the fallback for tidier authority), that's a one-line tightening of step 4.

Test status

  • Relay: cargo build clean + 332 tests pass (the 3 new DB-backed tests ran against live Postgres).
  • SDK: 179 tests pass.
  • Desktop: tsc --noEmit exit 0; no dangling refs to the deleted endpoint/command.

⚠️ One CI gap to confirm

Desktop tauri cargo build could not run locally (cmake missing for the audiopus native dep — environment limitation, unrelated to these changes). Please confirm the tauri crate builds in CI.


🤖 Stacked PR opened by Ned on behalf of @tellaho.

npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 3 commits June 22, 2026 16:42
Lift the relay's private extract_single_auth_tag_json into buzz_sdk::nip_oa
so relay and desktop enforce identical auth-tag structure (exactly one auth
tag, exactly four elements). The relay's identity-archive consent path now
calls the shared helper; the duplicated private copy is removed.

Adds unit tests for the happy path, ignoring non-auth tags, missing tag,
multiple auth tags, and wrong-arity auth tag.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…olveOaOwner

Drop the custom relay ownership endpoint call (resolve_agent_ownership)
in favor of the live kind:0 NIP-OA proof path (resolveOaOwner), the same
authority the relay now gates observer-frame delivery on. Adapter shape
(agentPubkey, ownerPubkey, isOwner) is preserved so both consumers --
useCanViewAgentActivity and useChannelAgentSessions -- keep reading
.isOwner unchanged. An agent with no kind:0, no auth tag, or a failing
tag resolves to { ownerPubkey: null, isOwner: false }.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
… custom ownership endpoint

Make the agent's live kind:0 NIP-OA auth tag the single source of truth
for agent-activity ownership end-to-end, so relay delivery and desktop
visibility agree.

Step 3 — remove the custom ownership endpoint:
- Delete GET /api/agents/:pubkey/ownership (relay route + api/agents.rs).
- Delete the resolve_agent_ownership Tauri command and its registration.
- Drop the resolve_agent_ownership e2e mock and agentOwnerIsMe config.

Step 4 — align the relay's observer-frame delivery gate:
- Add resolve_live_kind0_owner: reads the agent's latest global kind:0,
  extracts the single well-formed auth tag, and verifies it.
- Wire it into the observer gate. Authority order: session NIP-OA
  fast-path -> live kind:0 proof -> DB cache fallback.
- A kind:0 owner mismatch DENIES rather than falling through to the DB,
  so the DB column can never contradict kind:0.
- Keep agent_owner_pubkey (written from NIP-42 AUTH) as a cache/backfill
  fallback so BYO/CLI agents that AUTH without publishing a profile keep
  observer delivery.
- Add 3 DB-backed tests: owner match, owner flip, absent profile -> None.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho

tellaho commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #1229. This PR's kind:0 NIP-OA single-source-of-truth thrust is folded into #1229's consolidated, non-stacked approach (it was stacked on #1060, which is also being closed). Closing in favor of #1229. 🦝

@tellaho tellaho closed this Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant