reader_authorized_for_event in filter.rs now gates KIND_AGENT_TURN_METRIC
alongside KIND_DM_VISIBILITY — reader must match the #p tag (owner).
This single function closes all kindless-ids retrieval paths: WS
historical pull (req.rs:330, req.rs:652), HTTP bridge (bridge.rs:608,
bridge.rs:863), and live fan-out (event.rs).
Live fan-out extended likewise: owner_only_kind now covers both 44200
and 30622, so kindless-ids subscriptions cannot receive 44200 events
for non-owners.
Tests added: reader_authorized_for_event_gates_agent_turn_metric_by_p
(owner allow, non-owner deny, authoring-agent deny).
Case-2 rationale in the existing req.rs test updated: pass-through at
the filter-authorization gate is correct because the result-level gate
is now the enforcement point for this path.
NIP-AM ref: docs/nips/NIP-AM.md at 19889ba0c (PR #1441).
Resolves blocking gap from PR #1445 review.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Add kind:44200 (KIND_AGENT_TURN_METRIC) as the durable, p-gated, owner-encrypted
per-turn token-usage event defined in NIP-AM (docs/nips/NIP-AM.md, PR #1441).
Changes:
- buzz-core/kind.rs: add KIND_AGENT_TURN_METRIC = 44200 to P_GATED_KINDS and
ALL_KINDS; compile-time asserts confirm regular (non-ephemeral, non-replaceable)
kind shape
- buzz-core/agent_turn_metric.rs (new): AgentTurnMetricPayload type matching
the NIP schema (harness+timestamp required; nullable token fields; turn/cumulative
objects; sessionId+turnSeq required when cumulative present; deltaReliable;
stopReason enum); encrypt_agent_turn_metric/decrypt_agent_turn_metric helpers
reusing encrypt_observer_payload/decrypt_observer_payload from observer.rs;
round-trip, wrong-key, null-field, and stop-reason tests
- buzz-relay/handlers/req.rs: extend p_gated_filters_authorized to deny the
ids-filter exemption for kind:44200 (same carve-out shape as KIND_DM_VISIBILITY);
new test covering both {kinds:[44200], ids:[...]} deny and the kindless-ids
pass-through path (with documented defense-in-depth note)
- buzz-relay/handlers/ingest.rs: validate_agent_turn_metric_envelope (p tag,
agent tag == event.pubkey, no h tag, NIP-44 content); async is_agent_owner
ownership check; required_scope_for_kind → MessagesWrite; is_global_only_kind
addition; envelope and ownership tests
- migrations/0001_initial_schema.sql + schema/schema.sql: add 44200 to the NULL
search_tsv CASE so the p_gated_persistent_kinds_have_storage_null_tsvector
drift test passes
No emit logic, no adapters — Task B (goose adapter, buzz-acp) is a separate PR.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>