feat(relay): accept kind:30179 private managed-agent events at ingest - #5133
Merged
Conversation
Relay-only carve-out of the ingest half of #4999, so the shared relay can accept the private managed-agent head before the desktop half lands. Problem: kind:30179 (NIP-PMA private managed-agent config, owner-encrypted) is fully specified on main since #4593 -- constant, AUTHOR_ONLY_KINDS membership, result-gated reads, req/count/event/bridge author-only gates, parameterized-replaceable storage -- but generic EVENT ingest still rejects it as an unknown kind, so no client can publish one. Every end-to-end exercise of the desktop half currently requires a locally built relay. Change: two semantic lines in ingest.rs, byte-identical to the ingest hunk of #4999 at 6f486e8: - required_scope_for_kind: 30179 requires Scope::UsersWrite, same arm as its public sibling 30177 and the other owner-authored NIP-AP kinds; - is_global_only_kind: 30179 is owner-global, keyed (pubkey, kind, d-tag); a stray h tag must not channel-scope it. The remainder is import reflow plus replacing the guard test with positive assertions (UsersWrite scope, global-only, no h-scope requirement). Why the guard's precondition is met: the removed test pinned "must not enter generic EVENT ingest before privacy and aggregate CAS deploy". Both halves are resolved. Privacy: the author-only read gates for 30179 shipped to main with #4593 (req.rs pre-filter + result gates, count.rs, event.rs fanout, bridge pre-filter) -- only the author can read the event back. Aggregate CAS: #4999 settled generation as advisory -- the g tag is shape-validated, never relay-enforced; last-write-wins per coordinate is the contract of record, so no CAS mechanism is pending on the relay side. Why this is inert to existing relays and clients: no production desktop code on main authors kind:30179 (the codec has zero non-test callers), so this accepts a kind nobody can produce yet. Content is opaque NIP-44 ciphertext to the relay; reads remain author-only; storage semantics are the standard parameterized-replaceable path already exercised by 30175-30178. No schema, config, or migration changes. Contract of record for kind:30179 remains as documented in #4999; the desktop half rebases to pure-desktop scope once this lands. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
tlongwell-block
pushed a commit
that referenced
this pull request
Aug 7, 2026
Absorbs the relay half that shipped separately in #5133 (squash commit ad92335): the kind:30179 ingest acceptance hunk in crates/buzz-relay/src/handlers/ingest.rs was byte-identical on both sides, so this merge removes all relay-side changes from this PR's diff. #4999 now carries only the desktop + buzz-core codec half. No rebase, no force-push — history preserved per operator instruction. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> * origin/main: fix(bench): mention the orchestrator by pubkey when posting the task (#5136) feat(relay): accept kind:30179 private managed-agent events at ingest (#5133) fix(media): require authenticated reads (#4610) fix(desktop): preserve authoritative agent avatars (#4984) fix(desktop): next/back navigation during key creation onboarding (#4978) Alert community owners and admins when a new key joins (#4900) fix(desktop): prevent sidebar prefs from reverting on stale-localStorage boot (#5086) chore(hooks): run desktop typecheck in pre-push (#5110) feat(identity): recover desktop identity from a signed-in phone (#4845) fix(buzz-agent): classify read timeouts distinctly in LLM error messages (#4959) Refine agent runtime controls (#5026) test(desktop): await thread scroll anchor (#3174) Improve desktop mobile pairing flow (#5024) feat(desktop): show selected community in rail (#5000) fix(desktop): stop rate-limited reconnect backfill from tearing down the authenticated socket (#4990) fix(desktop): skip native notifications outside app bundles (#5004) ci: prove the relay-driven mesh lifecycle — discover, join, infer, deny — with real nodes (#3862) fix(desktop): virtualize channel member lists (#4991) Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
bradseiler
added a commit
that referenced
this pull request
Aug 7, 2026
…igration * origin/main: fix(bench): mention the orchestrator by pubkey when posting the task (#5136) feat(relay): accept kind:30179 private managed-agent events at ingest (#5133) fix(media): require authenticated reads (#4610) fix(desktop): preserve authoritative agent avatars (#4984) fix(desktop): next/back navigation during key creation onboarding (#4978) Alert community owners and admins when a new key joins (#4900) fix(desktop): prevent sidebar prefs from reverting on stale-localStorage boot (#5086) chore(hooks): run desktop typecheck in pre-push (#5110) feat(identity): recover desktop identity from a signed-in phone (#4845) fix(buzz-agent): classify read timeouts distinctly in LLM error messages (#4959) Refine agent runtime controls (#5026) test(desktop): await thread scroll anchor (#3174) Improve desktop mobile pairing flow (#5024) feat(desktop): show selected community in rail (#5000) fix(desktop): stop rate-limited reconnect backfill from tearing down the authenticated socket (#4990) fix(desktop): skip native notifications outside app bundles (#5004) ci: prove the relay-driven mesh lifecycle — discover, join, infer, deny — with real nodes (#3862) Signed-off-by: Brad Seiler <seiler@squareup.com> # Conflicts: # deploy/charts/buzz/templates/deployment.yaml # deploy/charts/buzz/tests/render_test.yaml # deploy/charts/buzz/values.schema.json # deploy/charts/buzz/values.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Relay-only carve-out of the ingest half of #4999: generic EVENT ingest now accepts kind:30179 (NIP-PMA private managed-agent config). One file,
crates/buzz-relay/src/handlers/ingest.rs, 16 insertions / 15 deletions; two semantic lines, byte-identical to the ingest hunk of #4999 at6f486e88:required_scope_for_kind: 30179 requiresScope::UsersWrite— same arm as its public sibling 30177 and the other owner-authored NIP-AP kinds.is_global_only_kind: 30179 is owner-global, keyed(pubkey, kind, d-tag); a strayhtag must not channel-scope it.The rest is import reflow plus replacing the guard test with a positive one (
private_managed_agent_kind_is_owner_scoped_global_user_data: asserts UsersWrite scope, global-only, no h-channel scope).Why the guard test can be retired
The removed test (
private_managed_agent_kind_remains_rejected_until_atomic_ingest_exists) pinned a stated precondition: "must not enter generic EVENT ingest before privacy and aggregate CAS deploy." Both halves are resolved:AUTHOR_ONLY_KINDSmembership,req.rspre-filter + result gates,count.rs,event.rsfanout, and the bridge pre-filter (bridge.rs:999-1000returnsrestricted: author-only kinds require authors=[self]/ 403). Only the author can read the event back.gtag is shape-validated, never relay-enforced. Last-write-wins per coordinate is the contract of record (see the kind:30179 contract blurb in Relay-primary private managed-agent config #4999), so no CAS mechanism is pending on the relay side.Why this is inert to existing relays and clients
private_managed_agent.rs) has zero non-test callers. This PR accepts a kind nobody can produce yet.Testing
buzz-relaypackage suite at this commit: 859 passed, 1 failed —api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo(504 vs 200), which reproduces identically on clean main769ac70bwith this change stashed; pre-existing/environmental, not introduced here.Relationship to #4999
#4999 (relay-primary agent config, desktop half) stays DO-NOT-MERGE pending live relay receipts + real CI; once this lands and deploys, its live test simplifies to plain
desktop-standaloneagainst the real relay, and #4999 rebases to drop its now-duplicate ingest hunk (identical bytes → trivial rebase).Originating thread: buzz://message?channel=06f13ed3-0557-4ac2-922c-1545dd00bf97&id=2a43b3b4933a2ea78b77088619251c061355f9b7b6dc29ea0d702193f2344149
Brownfield FTS note (review findings, operator-ruled non-blocking for this PR)
Max and Sami independently identified that the FTS privacy skip-set is regime-dependent: migration 0008 installs the positive allowlist (
kind IN (0, 9, 40002, 45001, 45003)) only on an empty events table; an already-populated database keeps the 0001/0005 negative skip-list (wrapped by 0014 to add 30350), which omits 30179 — so on such an installation this PR admits 30179 rows whose NIP-44 ciphertext gets indexed byto_tsvector. Sami measured both regimes against real Postgres (brownfield: 30179 INDEXED; fresh: NULL) and demonstrated the existing drift test only exercises the fresh regime.schema/schema.sql:222's canonical literal is also the negative list and omits 30179. Migration dates put any relay deployed with data before 0008 landed (2026-07-13) in the brownfield class.Scope of exposure (Sami's trace): not a content leak —
event_visible_to_reader/is_author_only_eventgates hold on both search surfaces (req.rs:725,bridge.rs:1770), so foreign readers receive nothing. Lost is the storage-level NULL-tsv backstop plus FTS page budget burned on post-filtered hits.Operator ruling (Tyler, events
1472e5b6,cbd368ed): ship this PR without an exclusion migration. Safety argument that makes this sound rather than merely accepted: main has zero non-test 30179 writers until #4999's desktop half deploys — no 30179 rows can exist, so nothing can be indexed in any regime while this PR is the only half live.Additional review characterizations (Sami, non-blocking, on the record):
required_scope_for_kind/is_global_only_kind/requires_h_channel_scope) compared for all 65,536 kinds at base769ac70bvs head77eeba6e— exactly one row differs (30179). No other kind or client changes behavior.AUTHOR_ONLY_KINDSpushdown clause exists inbuzz-db(onlySHARED_GATED_KINDShas one). Author-only kinds are protected by the pre-filter (author_only_filters_authorized) plus post-filter omission; mixed-kind filters can burn candidate-page budget on discarded rows. Pre-existing and identical for 30300/30350 — not introduced here; noted so the NIP's step-2 checkbox is not read as fully ticked.Bound follow-up (required before/with the #4999 desktop half): a 0014-shape additive migration (
pg_get_exprcapture +CASE WHEN kind = 30179 THEN NULL ELSE (<existing>) ENDwrap), add 30179 to theschema/schema.sql:221literal, and a brownfield-regime variant of the FTS drift test, per Sami's finding. Deploy-time spot check if ever wanted:SELECT pg_get_expr(d.adbin, d.adrelid) FROM pg_attrdef d JOIN pg_attribute a ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE d.adrelid = 'events'::regclass AND a.attname = 'search_tsv';