fix(relay): execute a-tag workflow deletions against the a-tag owner, delete all name matches, surface no-op deletions#2242
Open
Orland00 wants to merge 1 commit into
Conversation
… delete all name matches, surface no-op deletions Issue block#1593: NIP-09 a-tag deletion for kind:30620 (workflow) parsed the a-tag's owner pubkey but never used it. Both the UUID and name-based delete paths scoped the DELETE by `actor_bytes` (the deletion signer) instead. `validate_standard_deletion_event` already authorizes the request against the a-tag pubkey, including the `is_agent_owner` allowance that lets a human owner delete their agent's workflows -- so a human owner's deletion of an agent-owned workflow passed validation, then silently deleted nothing, while the relay still replied `accepted:true`. Three fixes, one change: - handlers/side_effects.rs: `handle_a_tag_deletion`'s KIND_WORKFLOW_DEF branch now hex-decodes the a-tag pubkey and scopes both the UUID delete and the name-based delete by that owner, not the signer. - buzz-db/workflow.rs: add `delete_workflows_for_owner_by_name`, which removes every workflow matching (community, owner, name) in one statement. `find_by_owner_and_name`'s bare `LIMIT 1` (no unique constraint on the pair) meant name-based deletion previously removed at most one of N same-named rows; it has no remaining callers but stays in place as public API. - No-op deletions (no matching workflow) now surface as an `OK` message ("info: no matching workflow found for deletion") instead of a silent tracing::warn, following this codebase's existing convention of accepted:true + an informational message prefix (see the "duplicate:" and "info: you have left this relay" messages in ingest.rs). This required widening `handle_side_effects`'s return type to `Result<Option<String>>`, which ingest_event_inner now folds into the final IngestResult.message; every other kind is unaffected and always yields `Ok(None)`. Tests: - buzz-db: delete_workflows_for_owner_by_name_removes_all_same_named_rows, delete_workflows_for_owner_by_name_leaves_other_owners_untouched (Postgres-gated, existing repo pattern). - buzz-relay: a_tag_deletion_by_human_owner_deletes_agent_owned_workflow -- exact bug regression (agent-owned workflow, deletion signed by the human owner) using the same real-AppState-over-lazy-PgPool pattern as workflow_sink::integration_tests. All three verified red (fail without the owner-scope fix) and green (pass with it) against a local Postgres instance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Orlando Lopez <orlando.lf19@gmail.com>
Orland00
force-pushed
the
fix/1593-workflow-deletion-owner-scope
branch
from
July 21, 2026 17:55
d94cd43 to
103b368
Compare
4 tasks
chillerno1
added a commit
to chillerno1/buzz
that referenced
this pull request
Jul 24, 2026
delete_workflow built the NIP-09 a-tag coordinate with the caller's pubkey, but kind:30620 is addressable by (author, d-tag). For an agent-created workflow the coordinate named a record that doesn't exist — the relay accepted the kind:5 and deleted nothing while the UI reported success. Fetch the workflow's kind:30620 event first (the same #d query update_workflow already uses) and build the coordinate from the event's author. Deleting an unknown workflow id now surfaces "workflow not found" instead of silently publishing an unmatchable delete. Client half of the end-to-end fix; the relay half (execute a-tag deletions against the a-tag owner rather than the signer) is block#2242. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: chillerno1 <gh.chiller@pm.me>
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.
Partially addresses #1593 (the going-forward half; existing-orphan reconciliation is a separate discussion — see my comment on the issue).
Problem
Workflows whose owner can no longer delete them keep executing on their cron schedules indefinitely — recurring compute and API spend with no accountable owner and no off switch. Three execution bugs make that possible:
validate_standard_deletion_eventauthorizes an a-tag deletion against the a-tag's pubkey (including theis_agent_ownerallowance that lets a human owner delete their agent's content), buthandle_a_tag_deletionparses that pubkey and never uses it — both the UUID delete and the name fallback are scoped by the deletion signer. A human owner deleting an agent-owned workflow passes validation, then silently deletes nothing.find_by_owner_and_name(LIMIT 1, noORDER BY) + a single-row delete; with several same-named rows (there's no unique constraint on owner+name) an arbitrary one is removed and the rest keep firing.tracing::warn!server-side; the client seesaccepted: truewith an empty message — which misled the issue reporter into declaring victory twice.Implementation
handle_a_tag_deletion(KIND_WORKFLOW_DEF): hex-decode the a-tag pubkey and scope both the UUID delete and the name-based delete by it. Authorization is unchanged — it already ran invalidate_standard_deletion_event; this aligns execution with validation.delete_workflows_for_owner_by_nameinbuzz-db: singleDELETE … RETURNING id, channel_idremoving every match; each affected channel's workflows are invalidated (deduped).find_by_owner_and_nameis untouched (its only caller was this handler)."duplicate:"/"info:"OK-message convention iningest.rs: the kind:5 event is stored/accepted as before, but the OK message now carriesinfo: no matching workflow found for deletion. Plumbing:handle_side_effectsreturnsanyhow::Result<Option<String>>; kind:5 routes separately and every other kind keeps its exact previous behavior (Ok(None)).Behavioral note for reviewers: cross-owner deletions that previously passed validation and then silently no-op'd now actually execute — that is the intended fix, but worth a conscious look at the authz implications (validation already gated these on
is_agent_owner).Testing
Regression tests (Postgres-gated,
#[ignore = "requires Postgres"], following the existingsetup_pool/workflow_sink::integration_testspatterns):delete_workflows_for_owner_by_name_removes_all_same_named_rows(3 same-named rows → all deleted)delete_workflows_for_owner_by_name_leaves_other_owners_untoucheda_tag_deletion_by_human_owner_deletes_agent_owned_workflow— the exact reported bug: agent-owned workflow, deletion signed by the human owner, a-tag naming the agent → row actually deleted. Red/green verified (temporarily reintroducing the old actor scoping makes it fail).Ran locally:
cargo fmt --all -- --check·cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings·cargo test -p buzz-db -p buzz-relay(82 + 715 passed; PG-gated tests run against a real Postgres 17) ·cargo check --workspace. Relying on upstream CI for the full matrix (desktop/web/mobile untouched).Deferred
Reconciliation for already-orphaned rows (pre-Harden relay attack surfaces #1369 rows are unreachable by any client-signable event, per the issue's 2026-07-09 update) — awaiting maintainer preference on shape (migration vs startup sweep vs
scripts/maintenance/), see the issue thread.CLI
buzz workflows delete --namefollow-up (the relay path now supports it correctly).No-op surfacing is deliberately scoped to the workflow branch; the generic NIP-33 soft-delete no-match case still no-ops silently — happy to extend the same message there if you want the consistency.