Repository navigation
feat(relay): agents cannot write in DMs except with their owner - #9371
Conversation
A key with a stored agent owner (users.agent_owner_pubkey) is refused when it writes into a DM channel, opens a DM, or adds DM members, unless the DM's participants are exactly the agent and its owner. Humans and non-DM channels are untouched. The check sits in the shared channel write gate, so messages, reactions, edits, typing and artifact moves all hit it, and it fails closed on a lookup error. Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Joah Gerstenberg <[email protected]>
kazemek21
left a comment
There was a problem hiding this comment.
Posted by Codex (gpt-6.1-sol) on behalf of kylez
A missing owner link can be written later, possibly by another relay process, so caching its absence let a newly linked agent pass as human for up to five minutes. Only a confirmed owner, which never changes once set, is cached now. Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Joah Gerstenberg <[email protected]>
🔐 Codex Security Review
|
A workflow's send_message posts as the workflow owner and checks may_post directly, skipping the shared channel gate, so an agent could save a workflow that posts into a DM its direct writes are refused in. The sink now applies the same owner-only DM rule before persisting. Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Joah Gerstenberg <[email protected]>
|
🤖 Re the Codex Security Review finding (workflow |
Kind 9000 (put-user) and kind 9002 (edit metadata) skip the generic Post gate. An agent could use 9000 to add a third person to its owner-only DM, or 9002 to put text into a DM it may not post in through the system message. In a DM, an agent may now never put users, and may edit metadata only in its owner-only DM. Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Joah Gerstenberg <[email protected]>
|
🤖 Re the Codex Security Review findings on 1ce727c, both fixed in d990020:
|
Huddle admission checks may_post directly, so an agent could join a DM's huddle and talk there even though its posts are refused. Admission now applies the owner-only DM rule to the huddle channel and its lifecycle parent. Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Joah Gerstenberg <[email protected]>
|
🤖 Re the Codex Security Review finding on d990020 (audio admission): fixed in ea3585e. |
|
@buzz-security-review ea3585e |
kazemek21
left a comment
There was a problem hiding this comment.
[P2] Recheck the owner-only DM rule when committing huddle admission
The admission check now covers both the huddle channel and its lifecycle parent. Non-blocking remaining portion of my earlier concern: could the commit-time validation also recheck the owner-only DM rule alongside the role checks? That would close the documented window where a human widens the DM after admission but before the join commits.
Kyle reviewed this PR and the Nexus review-tracker notes and approves.
Posted by Codex (gpt-6.1-sol) on behalf of kylez
…small * origin/main: fix(db): seek replaceable heads by channel (#9147) buzz-db: check events created_at floor in app writers (phase 1) (#8325) fix(mobile): keep a thread tail behind the keyboard unread (#9295) fix(mobile): restore header cues and per-channel identity check for #9369 (#9370) fix(relay): treat empty filter sets as match-nothing in SQL (#9342) buzz-cli: add --before and --before-id to messages search (#9382) feat(relay): agents cannot write in DMs except with their owner (#9371) fix(relay): bound each cross-node delivery attempt (#9305) feat(mobile): add iOS Home Screen shortcuts for New chat, New DM and the Bestie (#9360) perf(mobile): stop downloading whole videos for posterless stills (#9367) Enforce single-community relay mode (#9149) fix(relay): match #a tag filters in SQL before LIMIT (#9339) Add participant avatars to mobile conversation headers (#9369) fix(mobile): stop asking for an API token when the channel list is refused (#9327) chore(release): release Buzz Relay version 0.3.0 (#9362) Fix gallery video selection, thumbnails, and swipe dismissal (#9356) Signed-off-by: dessert <7d616e7e143e5d408159614dccff1371350a1c1bd36c8de838a45ece5aca0aca@buzz.block.builderlab.xyz>
…04 (#9418) ## Summary Two separate CI breaks are hitting every PR right now. Each one blocks a required check, and neither fix can go green on its own, so this PR fixes both. **1. Rust test targets stopped compiling on main at fc8c802.** #9227 added a required `RunCause` argument to `create_workflow_run` and a `may_direct_agents` flag to `ActionSink::send_message`. Two tests that merged around the same time still used the old signatures: - `attach_rejects_run_from_another_workflow` in `crates/buzz-db/src/store/workflow.rs` (from #8317) - the agent-DM refusal test in `crates/buzz-relay/src/workflow_sink.rs` (from #9371) Each PR passed CI on its own base. Together they break Rust Lint, Unit Tests, Windows Rust and the Desktop E2E Relay artifact build (main CI run 43291564834). The fix passes `RunCause::Schedule` and `true`, matching the neighbouring tests. Neither test is about run cause or agent mentions, so neither assertion changes. **2. Desktop E2E Integration fails on GitHub's new Ubuntu 26.04 runners.** `ubuntu-latest` is rolling to 26.04, and Playwright 1.60 (our lockfile) fails with `Playwright does not support chromium on ubuntu26.04-x64`. Playwright 1.61 is the first release that knows ubuntu26.04. This PR bumps `@playwright/test` to `^1.65.0` in `desktop/`, `web/` and `admin-web/` and regenerates `pnpm-lock.yaml`. It also pins every `ubuntu-latest` job in `ci.yml` and `_ci-*.yml` to `ubuntu-26.04`, so the next runner image change arrives in a reviewed diff. macOS and Windows jobs are unchanged. The newer Chromium bundled with Playwright 1.65 changed two smoke specs. It reports Tailwind's `items-end` as `align-items: flow-end` (`doctor-states` now accepts `flex-end` or `flow-end`), and it draws the textarea resize grip differently, so the `workflow-template-variable-autocomplete` baseline is refreshed from the ubuntu-26.04 CI output. It also exposed a timing race in `profile.spec.ts` "owned agent profile stays in parity": when the Message click lands while the profile panel is still settling, Playwright's scroll-into-view retry scrolls the profile body, which hides the owned-agent action row (aria-hidden + inert) for good. The test now pins the body to the top and waits for the row before clicking (12/12 repeats plus the full profile spec pass on Linux). ## Verification - `cargo clippy -p buzz-db -p buzz-relay --tests -- -D warnings` is clean on fc8c802 with commit 1 applied. - `pnpm install --frozen-lockfile` succeeds with the new lockfile, and `playwright --version` reports 1.65.0. - `tsc --noEmit` passes for `desktop`, `web` and `admin-web`. - This PR's CI is the E2E check on 26.04. ## Notes - Workflows that don't run on PRs (release, helm charts, sprig, image builds, codex-security-review) still use `ubuntu-latest`. This PR can't exercise them, so they're left for a follow-up. - The hermit-pinned actionlint (1.7.12) doesn't know the `ubuntu-26.04` label yet. CI only actionlints `codex-security-review.yml`, which this PR doesn't touch. ## Simpler No production code changes. The runner pin swaps an implicit moving target for an explicit one. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Luke Tornquist <[email protected]> Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <[email protected]>
#9500) …er (#9371)" This reverts commit a09b84d, resolved against current main (#9227 changed the neighboring workflow_sink tests). Supersedes #9393 Signed-off-by: Joah Gerstenberg <[email protected]> Co-authored-by: Joah Gerstenberg <[email protected]> Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <[email protected]>
Summary
Agents stop writing in direct messages, except a DM with only their owner. This is the relay-side backstop for block/buzz-app#3261, which takes away the app agents' DM tools. An agent holding its own key could still sign DM events directly, and only the relay can stop that.
A key counts as an agent when it has a stored owner (
users.agent_owner_pubkey, written when the agent authenticates with a NIP-OA owner attestation). For those keys:restricted: agents cannot post in direct messages; start a Solo chat that includes your owner instead. The check sits incheck_channel_membershipforChannelWrite::Post, so it covers stream messages, reactions, deletions, typing and other channel ephemerals, artifact moves and edit validation. Non-DM channels skip it at no extra cost.send_message(which posts as the workflow owner and checksmay_postdirectly) applies the same rule before persisting, so an agent can't route a DM post through a workflow.may_postdirectly, so it applies the same rule to the huddle channel and its lifecycle parent.agent_owner_cache, 5 min TTL). The link is first-write-wins and never changes once set. A missing link is never cached, because another relay process may write it later.link_agent_ownerprimes the cache.Humans are unaffected: they can still open DMs that include an agent and post in them. The agent answers elsewhere.
Not covered:
Postgate.Related issue
Companion to block/buzz-app#3261. AGNTOPS-780.
Testing
New Postgres ingest tests (
handlers::ingest::postgres_tests::agent_*), run locally:send_messageis refused in a DM with a stranger and allowed in its owner-only DM.cargo clippy -p buzz-relay --all-targets -D warningsis clean. The other ingest and command-executor tests pass locally (214), except two that fail only because of my local database: one needs the desired-state FTS schema, and the other fails creating its restricted role in setup. Neither reaches DM code. I pushed without the local pre-pushjust test-unitlane because abuzz-cliunit test hung there for about 30 minutes; CI runs that lane.🤖 Generated with Claude Code