Files
54f11219ef fix(acp): gate relay-signed workflow messages on their attributed author (#6129)
## Problem

Scheduled workflow `send_message` actions fire and land in the channel
with correct `p` tags for the mentioned agents — but the agents never
wake. The wake-up is silently dropped.

**Root cause:** workflow messages are signed by the **relay keypair**
(`workflow_sink.rs` signs with `state.relay_keypair`), so `event.pubkey`
is the relay's pubkey, not the workflow owner. In `buzz-acp`, the
inbound author gate (`author_allowed`) runs **before** the `p`-tag
mention check. Under the default `respond_to = owner-only`, the relay
pubkey is neither the owner nor a sibling, so every workflow wake-up
dies at the gate with a debug-level `"inbound author gate — dropping
event"`.

The relay-side comment even says the mention `p` tags exist *"so
mentioned agents are woken (wake is p-tag gated)"* — but wake is also
author-gated, and that path was missed.

## Fix

Gate relay-signed workflow messages on their **attributed author** — the
pubkey that created the workflow — instead of the relay pubkey:

- **Relay:** `workflow_sink.rs` now emits an explicit
`buzz:workflow-owner` tag carrying `workflow.owner_pubkey` (the workflow
creator, which the executor already passes as `author_pubkey` and whose
channel access the relay verifies before emitting). Ownership is never
inferred from `p`-tag order; mention `p` tags play no role in
attribution.
- **Harness:** at startup, `buzz-acp` fetches the relay's NIP-11 `self`
pubkey (new `RestClient::fetch_relay_self`, public `/info` endpoint).
Best-effort: fetch failure just logs a warning and preserves pre-fix
behavior.
- **Gate:** an event that is (a) authored by the relay `self` key, (b)
tagged `buzz:workflow`, and (c) carries a well-formed
`buzz:workflow-owner` pubkey is gated on that owner, through the exact
same owner/sibling/allowlist policy as a direct author.

## Security notes (all fail closed)

- No NIP-11 `self` pubkey → no exemption.
- `buzz:workflow` / `buzz:workflow-owner` tags on a non-relay-signed
event → ignored (a member cannot forge the exemption; the relay verifies
signatures on submission and only the relay holds its key).
- Relay-signed event without the tags, or with a malformed owner value
(not 64-hex) → plain author gate.
- Who is @mentioned in the message has no bearing on whose authority is
evaluated.
- A workflow owned by a random channel member still cannot wake an
owner-only agent — the owner's pubkey must pass the same policy.

## Testing

- 7 unit tests (`workflow_attributed_author_tests`) covering
attribution, fail-closed paths, p-tag independence, malformed owner
values, and the forgery case.
- Extended the PG-gated `workflow_send_message_p_tags_mentioned_member`
integration test to assert the `buzz:workflow-owner` tag.
- `cargo test -p buzz-acp`: 785 passed, 0 failed. `cargo test -p
buzz-relay --lib workflow_sink`: 17 passed. Clippy + fmt clean. (9
pre-existing `buzz-relay` failures in unrelated
`api::media`/`api::admin` tests fail identically on the base commit
without this change.)

Found while debugging scheduled automations in a Buzz review-pipeline
channel: two cron workflows fired daily @mentions at agents that never
responded, while direct human @mentions woke them instantly.

---------

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
Co-authored-by: Fizz <3a9f8a30fbb462abec1e2977b2280a7ae50c7ff794433790be15bd48bfd52d0b@buzz.block.builderlab.xyz>
2026-08-17 13:29:20 -04:00
..