fix(desktop): enforce agent mention authorization at send boundaries - #5681
Conversation
Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com>
Require affirmative relay-directory admission before exposing remote agents, apply the internal same-owner boundary, and filter selected agent identities again when emitting mention tags. Cover directory errors, recovery, outgoing tags, and revocation before send. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Hide remote agents while directory policy is refetching and refresh both agent directories immediately before sending. Drop agent p tags and audience promotion when the fresh authorization is absent, revoked, or errors. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Revalidate edited-message agent tags immediately before save, including after deferred uploads. In owner-only builds, fetch fresh authoritative profile ownership for outgoing agent tags and fail closed on missing, changed, or unavailable ownership proof. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Combined review — two independent agent passes (Paul + Thufir), synthesized. Not approving yet: one authorization gap that both the verification trace and a full-suite run confirm should close before merge. Smaller items are inline.
Forum sends bypass the new revalidation boundary
desktop/src/features/forum/ui/ForumComposer.tsx:230-253 (file not in this diff, so noting here instead of inline): submitMessage calls the changed mentions.extractMentionPubkeys(trimmed) and passes that snapshot directly to submitter → ForumView.onSubmit → createPostMutation, which emits the p tags. It never calls the new mentions.revalidateMentionPubkeys, so forum posts/replies get only render-time filtering — a directory/allowlist/owner change between selecting an agent and submitting is not freshly checked, and the stale pubkey can still wake an agent current policy no longer authorizes. This is the same boundary this PR closes for chat sends and edit saves; the forum composer is a production consumer of the same mention API that misses it.
Suggested fix: await mentions.revalidateMentionPubkeys(mentions.extractMentionPubkeys(trimmed)) in ForumComposer.submitMessage before clearing/submitting, plus a regression that selects an agent, revokes authorization, submits, and asserts the signed forum event carries no agent p tag (the current forum e2e proves autocomplete visibility only, not emitted tags).
CI attribution (from the logs, not assumed)
The red checks are infra + pre-existing flake, not this diff: Desktop E2E Integration (2/2) died on docker compose (digest-mismatch, exit 101) before any test ran; Desktop Smoke E2E (4)'s failure is video-attachment.spec.ts:1242 with a virtualization.spec.ts flake — neither file is touched here; main's latest completed CI run failed the same Integration shards. The PR's own 12 new/changed mentions.spec.ts tests ran green in Smoke shard 3, and the full desktop unit suite (4,732) passes at this head.
What's solid
The tri-state admission model is the right shape. Chat sends and edit saves revalidate after deferred uploads and before signing/saving, owner-only mode fetches fresh verified ownership at send time, and error/missing/changed-owner all fail closed with unit coverage for each branch. Human mention tags survive revocation exactly as described. Most importantly the fix is tested at its seam: the e2e bridge now reads the signed event's actual p tags (pre-send and mid-send revocation), and deleting the production wiring turns the suite red — verified by mutation, not assumed from green.
| mentionableAgentPubkeys, | ||
| directoryAgentPubkeys, | ||
| directoryReady = true, | ||
| ownerOnly = false, |
There was a problem hiding this comment.
🤖 ownerOnly = false here swallows the undefined that useMentions passes while the owner-policy query is still loading (ownerOnly: agentAccessOwnerOnlyQuery.data), so the "unknown" branch getAgentMentionAdmission has for exactly that state is unreachable through this wrapper. During the window where the agent directories are ready but the owner policy isn't, an other-owned relay agent is admitted into autocomplete in an owner-only build. Send-time revalidation strips it on the chat/edit paths (it checks ownerOnly === undefined explicitly), which keeps this non-blocking — but it contradicts the fail-closed intent, and both reviews flagged it independently. Fix: drop the destructuring default and pass undefined through, or fold ownerPolicyReady into the directoryReady value this call site receives.
| return lookup; | ||
| }, [managedAgentsQuery.data, personasQuery.data]); | ||
| const knownAgentPubkeys = mentionableAgentPubkeys; | ||
| const knownAgentPubkeys = new Set([ |
There was a problem hiding this comment.
🤖 This Set is rebuilt every render (on main, knownAgentPubkeys aliased the memoized mentionableAgentPubkeys) and sits in the dep arrays of handleMentionSelect and isAgentPubkey, so both recreate every render and the churn propagates into useMentionSendFlow's callbacks. Same pattern in useAgentMentionRevalidation: getSelectedAgentPubkeys: () => selectedAgentMentionPubkeysRef.current is a fresh arrow per render listed in its useCallback deps, so revalidateMentionPubkeys never holds identity and MessageComposer's submitMessage churns with it. Correctness is unaffected — this is render hygiene on the hottest composer path. Wrap the Set in useMemo; pass the ref (or a stable callback) for the getter.
| const [selectedAgentMentionNames, setSelectedAgentMentionNames] = | ||
| React.useState<string[]>([]); | ||
| const selectedAgentMentionNamesRef = React.useRef<string[]>([]); | ||
| const selectedAgentMentionPubkeysRef = React.useRef<Set<string>>(new Set()); |
There was a problem hiding this comment.
🤖 clearMentions() clears both name states and both maps but never resets this set, so it accumulates for the hook lifetime. The effect is conservative (extra pubkeys get revalidated, never skipped), so no correctness issue — but reset it in clearMentions for symmetry. Both reviews flagged this independently.
Apply the same final authorization refresh used by chat sends and edits to forum posts and replies. Preserve the draft when refresh or submission fails, and guard against duplicate submits while revalidation is in flight. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
|
Reviewing on Wes's behalf at exact head The forum authorization bypass is closed, but the repair introduces a blocking draft-loss race. [P1] Forum edits made during final revalidation are silently discarded. Please either lock the forum editor/attachment controls for the full pending interval using rendered state, or re-read and atomically snapshot the current draft after revalidation before clearing. Add a deferred-revalidation regression: start submit, edit the draft while refresh is pending, release refresh, and prove the later input is neither silently cleared nor omitted. The final forum gate itself is correctly placed before signing, preserves the draft on refresh/submission failure, and has signed-event coverage for revoked-agent tag removal. Chat and edit boundaries remain sound. The earlier owner-policy default and callback/set churn comments are worthwhile follow-ups, but send-time admission remains fail-closed, so they are not additional merge blockers. |
|
Blocker: the new forum preflight can discard edits made while authorization refresh is in flight.
This is especially plausible here because the preflight does two directory refetches and, in owner-only mode, a fresh profile fetch. The ref prevents duplicate submits, but it does not prevent editing, and it is not represented in the toolbar/editor disabled props. Please make the snapshot/clear boundary coherent: either capture and clear the entire draft before the await and restore it on refresh/submission failure (while preserving any subsequently started draft), or explicitly disable editing/media for the whole pending interval and snapshot all fields consistently. Add a regression with a deferred revalidation promise: submit body A, edit to body B while deferred, release it, and prove B is neither cleared nor silently replaced by A. The authorization direction is otherwise strong: current-head forum sends now share the final revalidation boundary, chat and edit paths revalidate after deferred uploads, human mention tags survive agent revocation, and owner-only proofs fail closed. |
Keep the forum composer mutation surface disabled while final agent mention authorization runs so the submitted snapshot cannot race newer edits. Preserve the synchronous duplicate-submit guard, fail closed while owner policy loads, and clean up mention selection callback and cache stability. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Make the forum form inert while final mention authorization is pending, guard attachment callbacks against programmatic activation, and close already-open autocomplete, emoji, and link overlays. Extend the delayed preflight regression to prove an existing attachment cannot be removed and is included in the signed post. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
|
Release-safety follow-up reviewed by Carl on Wes’s behalf at exact head I re-traced the complete authorization boundary across autocomplete, chat send (including deferred upload), edit save, and forum post/reply. The two prior forum blockers are closed: forum sends now revalidate before signing, and the composer plus media/overlay mutation surfaces are locked for the full asynchronous preflight so the submitted snapshot cannot discard concurrent edits. The regression verifies both editor and attachment immutability and asserts the signed outgoing event omits the revoked agent while retaining the attachment. The policy remains fail-closed on directory loading/error/refetch, unknown owner policy, stale directory membership, and missing/changed/unavailable owner proof. Human mention tags are preserved. I found no remaining release blocker or compatibility/migration risk. GitHub CI is fully green on this exact head, including Desktop Core, all smoke shards, integration shards, builds, security, DCO, and cross-platform checks. |
|
Correction to my release verdict: do not merge this head yet. Princess Donut identified a real P1 gap I missed. At Required fix: revalidate before deriving any agent-preparation list and use only admitted pubkeys for channel preparation, start/attach, and Huddle enrollment; retain the existing final post-upload revalidation before signing. Add a regression asserting revocation causes zero preparation/enrollment side effects as well as no outgoing agent tag. I previously focused the release pass on signed-event enforcement and the repaired forum mutation race, and failed to trace authorization backward through all pre-sign side effects. That was my miss, not an ambiguity in the code. |
Refresh mention authorization before preparing channels, starting or attaching managed agents, or enrolling agents into Huddles. Keep the existing post-upload refresh before signing, and verify revoked relay agents cause no preparation side effects. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Refresh pending non-member mention authorization when Invite is clicked so a revoked relay agent cannot be added to channel membership before send-time preparation. Cover revocation after the dialog opens and assert no membership, agent lifecycle, Huddle, or outgoing mention side effects. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Re-reviewed at head 939bf536 (two independent passes, Paul + Thufir, synthesized).
All prior feedback is addressed:
- Forum send bypass (blocking) —
ForumComposer.submitMessagenow awaitsrevalidateMentionPubkeysbefore building/submitting, keeps the draft on refresh failure, guards duplicate submits, and locks the composer surface (inert, disabled toolbar/attachments, guarded insert callbacks) while revalidation is in flight so the submitted snapshot can't race edits. Signed-event regression covers revocation mid-preflight, including proving a queued attachment can't be removed. ownerOnly = falsefail-open window — the destructuring default is gone;ownerOnlyis now requiredboolean | undefined, so an unresolved owner-policy query reaches the tri-state"unknown"branch and fails closed, with a unit test for exactly that state.clearMentionsasymmetry —selectedAgentMentionPubkeysRefis now cleared.- Memoization churn —
knownAgentPubkeysisuseMemo'd and the revalidation getter is a stable ref, restoring the identity chain.
Beyond the asked fixes, the new commits also close the invite/preparation side doors: revalidation now runs before channel prep, managed-agent start/attach, and huddle sync in completeSend, and again on Invite click in handleInviteNonMembers — with e2e coverage asserting a revoked agent produces neither a p tag nor any membership/lifecycle/huddle commands. Traced all revalidation call sites at this head; no remaining consumer of the mention API skips the boundary.
CI: the only red (Smoke E2E shard 3, messaging.spec.ts:1311 compact link preview geometry) fails identically on main's latest run (45f4b91a, the commit that introduced that test) and neither the test nor link-preview-attachment.tsx is touched by this branch — pre-existing main breakage, not this PR.
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
…-projection * origin/main: test: add deterministic desktop release smoke (#5699) fix(channels): return complete member rosters (#5765) feat(desktop): add Inbox message delete action (#5779) fix(desktop): enforce agent mention authorization at send boundaries (#5681) Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com> * origin/main: Polish glass Huddle tray behavior (#5590) test: add deterministic desktop release smoke (#5699) fix(channels): return complete member rosters (#5765) feat(desktop): add Inbox message delete action (#5779) fix(desktop): enforce agent mention authorization at send boundaries (#5681) fix(desktop): route compact preview geometry fixture through media proxy (#5799) Make workflow run history authoritative in Desktop (#5780) fix(desktop): more compact "compact" link previews (#5629) Fix mobile composer input regressions (#5594) Add mobile community invites (#5641) Harden shared agent instruction review (#4220) chore(release): release Buzz Desktop version 0.5.11 (#5714) feat(acp): report standard adapter usage (#4950) fix(mobile): settle hydrated threads on latest reply (#4702) perf(desktop): persist channel snapshot hash (#5684) Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
* Make workflow run history authoritative in Desktop (block#5780) ## Summary - persist stable workflow run `error_code` values separately from human diagnostics - expose NIP-98 authenticated, channel-authorized run history and approval reads with stable keyset pagination - connect Desktop to those authoritative reads and return the relay-created run ID on trigger - show truthful loading, failure, and pending-trace states, and do not render approval actions from non-actionable stored hashes ## Validation - pre-push `branch-skew`, `desktop-typecheck`, `desktop-test`, `rust-tests`, `desktop-tauri-checks`, and `desktop-check` all passed on `a097dbe5f` - Desktop tests: 4,761 passed, 0 failed - `cargo check -p buzz-relay` - `git diff --check` ## Remaining gate This does not claim a relay-backed Playwright workflow journey. The browser relay bridge still routes workflow invokes through in-memory handlers; that production-shaped acceptance gate remains follow-up work before Workflows can leave preview. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz> * fix(desktop): route compact preview geometry fixture through media proxy (block#5799) **Category:** fix (CI) **User Impact:** None — test-only change that unblocks `main` and every open PR. **Problem:** `main` has been red since block#5629 landed on `45f4b91a3`: `Desktop Smoke E2E (3)` fails `compact link preview image geometry truncates long titles to one line` on every build (main run 31727837133, and e.g. block#5792, block#5790). Two independently-green PRs raced: block#5629 added the test stubbing its preview image at the raw relay origin (`http://localhost:3000/media/*.png`), while block#5627 rewrites sent snapshot media through the authenticated local media proxy (`http://127.0.0.1:54321` in the E2E mock bridge). Merged together, the image request goes to the proxy origin, the stub never matches, and `naturalWidth` stays `0`. **Solution:** Point the route stub at the mock proxy origin, matching the existing `sent link preview media uses the authenticated proxy in compact and rich cards` test in the same spec. **Testing:** Reproduced the failure locally on `45f4b91a3`, then with this fix: targeted test passes, and the full `messaging.spec.ts` smoke suite passes 58/58. Signed-off-by: Thomas Petersen <thomasp@squareup.com> Co-authored-by: Wintermute <3f1797424fd9ad6653a83665c660517777cd7f8c228c0d5907f49e01537f3ca5@buzz.block.builderlab.xyz> * fix(desktop): enforce agent mention authorization at send boundaries (block#5681) ## Summary - allow channel-member remote/headless agents only with current kind `10100` directory evidence, while stale member identities remain hidden - fail closed while managed/relay directories load, error, or background-refetch across channel, forum, and cached autocomplete surfaces - revalidate agent mention authorization immediately before normal sends and message-edit saves, including after deferred uploads - in owner-only builds, fetch fresh authoritative profile ownership at send time and deny missing, changed-owner, or unavailable proofs - preserve human mention tags when agent authorization is revoked or unknown Supersedes block#5536 because its contributor-fork head cannot be updated by maintainers. ## Validation Exact head: `7278cdd5fbcee676c7b858ea098503c62eeeff0d` - mandatory pre-push suites passed: desktop check/typecheck/tests, Rust tests, mobile tests, desktop Tauri checks, branch-skew - desktop unit tests: 4,732 passed - focused edit/ownership regressions: 8 passed - focused mention E2E: 5 passed (remote positive, stale-member negative, directory error, pre-send revocation, mid-send revocation) - file-size ratchet passed One first focused E2E batch had a timing-only miss where the send click did not emit; the isolated rerun passed. One separate pre-push attempt hit the existing randomized passphrase separator test; the successful exact-head push reran and passed the mandatory suite. --------- Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> * feat(desktop): add Inbox message delete action (block#5779) ### What changed? Inbox message action menus now show a standalone Delete action beside Edit for manageable messages. Delete reuses the existing confirmation and targets the message whose menu was opened, while the existing empty-edit deletion path remains unchanged. ### Why? Inbox users can delete a message directly without first entering edit mode. Thread context can contain multiple messages, so the action must preserve the active Inbox selection and delete only the chosen row. ### How is it tested? Desktop checks, typechecking, builds, and test suites pass. Added tests: - [Inbox edit and delete E2E coverage](https://github.com/block/buzz/tree/main/desktop/tests/e2e/inbox-edit.spec.ts) Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com> * fix(channels): return complete member rosters (block#5765) ## Summary - return complete channel rosters instead of truncating at 1,000 members - chunk `event_mentions` inserts inside one transaction so large kind `39002` snapshots remain discoverable by every `p` tag - add a targeted `buzz-admin reconcile-channels --channel <uuid>` force-republish path for stale discovery snapshots - cover a 1,501-member roster, 11,000-tag mention index, and kind `39002` tag construction past member 1,000 ## Why The relay builds NIP-29 discovery and several authorization decisions from `get_members()`, but that helper silently returned only the first 1,000 active members. Desktop then counted the truncated kind `39002` event, while late members could be rejected by roster-scanning member actions. Removing the roster cap exposes PostgreSQL's 65,535 bind-parameter ceiling in mention indexing, so the insert is chunked transactionally to preserve all-or-nothing indexing. The existing reconcilers only fill missing discovery events. The targeted admin option bypasses the separately known 1,000-channel reconciliation-list ceiling and replaces an existing channel snapshot using the configured production relay key. ## Attribution This supersedes and builds on block#3166 by @LordMelkor. Thank you for identifying the roster boundary and contributing the original complete-roster and mention-index patch. The production roster/query changes and the two PostgreSQL regressions retain that work's shape; this PR rebases it onto current `main`, adds relay coverage, and adds the targeted repair operation requested for rollout. ## Validation Exact pushed head: `24d02e4f3824150ed84913c9d230e675502e5b12` - `cargo check -p buzz-db -p buzz-admin` - `cargo test -p buzz-db channel::tests::get_members_returns_full_roster_beyond_1000 -- --ignored --exact --nocapture` - `cargo test -p buzz-db feed::tests::insert_mentions_indexes_rosters_past_bind_parameter_cap -- --ignored --exact --nocapture` - `cargo test -p buzz-relay --lib handlers::side_effects::tests::group_members_snapshot_keeps_members_past_one_thousand -- --exact` - `cargo run -q -p buzz-admin -- reconcile-channels --help` - mandatory pre-push hook: branch-skew, desktop checks/typecheck/tests, mobile tests, Rust tests, and desktop Tauri checks all passed on the pushed head ## Rollout 1. Deploy the relay/backend build. 2. Run `buzz-admin reconcile-channels --channel <general-channel-uuid>` with `BUZZ_RELAY_PRIVATE_KEY` configured. 3. Verify the replacement kind `39002` roster count matches the active database membership count. No schema migration or desktop release is required. Fixes block#3156 Supersedes block#3166 --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> * test: add deterministic desktop release smoke (block#5699) ## Summary - add `just desktop-release-smoke`, a deterministic desktop correctness/reachability smoke against an ephemeral real local relay - preserve existing DM history when the first live DM enters a pageless query window, the desktop-v0.5.10 disappearing-DM regression - enforce foreground JS ordering: a frame and actionable sidebar input must dispatch before mounted stale queries begin resume refetches, while separately requiring the navigation to commit promptly - seed a 10,000-event dense-second fixture and verify exact event-ID reachability, SHA-256 identity, ordering, duplicate absence, bounded mounted rows, and drained render work - isolate Postgres per run, serialize the shared Redis DB, retain phase/relay/Playwright diagnostics, and gate desktop release manifest assembly on the smoke This is deliberately **not a performance-regression gate**. CDP and action timing fields are informational only. There is no candidate/baseline comparison or threshold. A future performance lane needs repeated equivalent fixtures, discrete interaction samples, and an explicit comparator/noise policy. The diagnostics record the fixture version, row count, wall-clock base timestamp (`fixtureSecond`), expected event-ID hash, observed state, and measurements. Because the created-at floor requires a current timestamp, paired comparison remains disabled. The release job runs on an isolated GitHub-hosted runner. The script also guards automatic local runs with a Redis allocation lock. Its remaining direct-PID cleanup and free-port selection race mean it should not be repurposed onto a persistent concurrent shared runner without first hardening process-group cleanup and port reservation. ### Related issue N/A ### Testing - `pnpm --dir desktop typecheck` - focused real-local-relay release smoke passed after adversarial review fixes - identical DM witness passed current and failed `desktop-v0.5.10` with the history-loss signature - identical foreground witness bytes (`2c1e97df04c9b8ca0304b66bbbe9bdb4d08924ad8ce0f68a9c490458fcc3aca8`) failed `desktop-v0.5.10` structurally: the first resume fetch was marker 1, before first frame/sidebar dispatch at marker 8 - with PR block#5696 (`59f613c40`) merged, the witness showed focus at 951.3 ms, first frame at 951.6 ms, click dispatch at 952.1 ms, first resume fetch at 968.9 ms, and route commit at 992.4 ms - the gate therefore protects first paint and actionable input dispatch; route commit is a bounded responsiveness witness, not a prerequisite for resume work - the corrected focused foreground scenario passed at `6d9b5be40da58bbee92a856b04c3558946d0a950`; the prior merged-tree full run passed DM retention and 10k reachability before exposing this contract mismatch - pre-push passed on exact pushed head `6d9b5be40da58bbee92a856b04c3558946d0a950`, including desktop checks, typecheck, desktop tests, Rust tests, mobile tests, and Tauri checks - full 10,000-event scenario reached 10,000/10,000 exact IDs with matching SHA-256, 199 continuation requests, and 95 mounted rows in about 4.4 minutes - reduced-row review run passed in 18.4 seconds ### Foreground witness boundary The Chromium test is a deterministic JS policy gate. Headless Chromium does not expose an honest blur/focus transition in this fixture, so the test drives the production focus listener and `document.hasFocus()` predicate together and records that simulation explicitly. It proves refetch fan-out ordering, not AppKit activation, WKWebView paint, or an activating physical click. A packaged macOS native lane is still required before claiming the actual desktop activation experience is certified. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> * Polish glass Huddle tray behavior (block#5590) ## Summary - inset the in-app Huddle tray with four rounded corners and even 8px spacing when Glass background is enabled - keep the popped-out Huddle dock full-width - hide and suppress Glass background on Linux ## Why The in-app tray reused the opaque backing needed by non-glass windows, which covered the native vibrancy around it. Linux does not support this window treatment. ## Testing - `pnpm -C desktop build:e2e` - focused Appearance and Huddle Playwright smoke tests - pre-push desktop checks, typecheck, and 4,666 unit tests --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> * Speed up initial direct messages (block#5658) ## Summary - avoid blocking first-DM navigation on a full channel-list refresh - publish the initial message through the acknowledged HTTP path instead of waiting on a missing WebSocket acknowledgement ## Validation - 4,715 desktop unit tests - desktop typecheck and checks - focused new-DM Playwright coverage --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz> * Preserve member admission during send-time mention revalidation getAgentMentionAdmission's lenient-member rule requires isMember and compares against directoryAgentPubkeys, but revalidateAgentMentionPubkeys called it without either — a channel-member agent with no kind:10100 directory record that the picker correctly admits was then denied by the mandatory pre-send revalidation pass, silently dropping its mention tag (no wake, no audience promotion, no Huddle enrollment) while the visible @name stayed in the message text. Thread channel-membership pubkeys through revalidateAgentMentionPubkeys and useAgentMentionRevalidation, and derive directoryAgentPubkeys from the freshly refetched relay directory so revalidation applies the same admission rule the picker uses. Reported-by: Alex <alex@buzz> Signed-off-by: Junchao Yan <yjc801@gmail.com> * Refetch channel roster during send-time mention revalidation The prior fix passed the picker's cached membership set into revalidation while the managed-agent and relay directories were freshly refetched. That left a stale-membership window: if another admin removed a directory-less agent after the picker/draft loaded, the cached member set still marked it a member, so isLenientMember kept admitting it and the send emitted its mention/wake tag for an agent no longer in the channel. The membership query's 30s staleTime and user-scoped invalidation subscription don't fence against another member's removal. Refetch the channel roster in the same Promise.all as the managed/relay directory refetches and derive membership from that fresh result. Fail closed (deny) when the roster refetch errors or returns no data, matching the existing fail-closed behavior for the other directory fetches. Reported-by: Alex <alex@buzz> Signed-off-by: Junchao Yan <yjc801@gmail.com> * Only require fresh channel roster for channel-scoped mention admission Round-2's fail-closed roster refetch ran unconditionally, so a new-DM composer (MessageComposer with channelId=null, eligibilityScope "managed-only", before onPrepareSendChannel creates the channel) had no roster to fetch, failed closed, and stripped a valid managed-agent mention before the DM was ever created. Roster proof is only relevant to the lenient channel-member admission branch. Fetch it only when eligibilityScope.type === "channel"; other scopes use an empty member set and are admitted on the managed/relay directory checks alone, same as before the roster refetch existed. Reported-by: Alex <alex@buzz> Signed-off-by: Junchao Yan <yjc801@gmail.com> --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Signed-off-by: Tom Brow <tomb@block.xyz> Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Junchao Yan <yjc801@gmail.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz> Co-authored-by: thomaspblock <thomasp@squareup.com> Co-authored-by: Wintermute <3f1797424fd9ad6653a83665c660517777cd7f8c228c0d5907f49e01537f3ca5@buzz.block.builderlab.xyz> Co-authored-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Co-authored-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: klopez4212 <klopez4212@gmail.com> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz>
* fix(desktop): enforce agent mention authorization at send boundaries (block#5681) - allow channel-member remote/headless agents only with current kind `10100` directory evidence, while stale member identities remain hidden - fail closed while managed/relay directories load, error, or background-refetch across channel, forum, and cached autocomplete surfaces - revalidate agent mention authorization immediately before normal sends and message-edit saves, including after deferred uploads - in owner-only builds, fetch fresh authoritative profile ownership at send time and deny missing, changed-owner, or unavailable proofs - preserve human mention tags when agent authorization is revoked or unknown Supersedes block#5536 because its contributor-fork head cannot be updated by maintainers. Exact head: `7278cdd5fbcee676c7b858ea098503c62eeeff0d` - mandatory pre-push suites passed: desktop check/typecheck/tests, Rust tests, mobile tests, desktop Tauri checks, branch-skew - desktop unit tests: 4,732 passed - focused edit/ownership regressions: 8 passed - focused mention E2E: 5 passed (remote positive, stale-member negative, directory error, pre-send revocation, mid-send revocation) - file-size ratchet passed One first focused E2E batch had a timing-only miss where the send click did not emit; the isolated rerun passed. One separate pre-push attempt hit the existing randomized passphrase separator test; the successful exact-head push reran and passed the mandatory suite. --------- Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> (cherry picked from commit bcf353c) * feat(mobile): require device authentication for identity export (block#5116) **Category:** new-feature **User Impact:** Mobile users must confirm with Face ID, biometrics, or their device passcode before sending their Buzz identity to Desktop. **Problem:** A signed-in phone could send its full identity, including the `nsec`, to a desktop without fresh local verification. **Solution:** Require OS device authentication before opening the identity-recovery scanner, retain that authorization only for the active pairing session and short pairing window, and require fresh authentication again if it expires before the identity payload is sent. Normal app opening, identity import, and community removal remain unchanged. ## Screencasts | Enable Face ID | Use Face ID | | --- | --- | |  |  | <details> <summary>File changes</summary> **Android and iOS integration** - `mobile/android/app/build.gradle.kts` declares the AppCompat dependency required by the biometric activity theme. - `mobile/android/app/src/main/kotlin/xyz/block/buzz/mobile/MainActivity.kt` uses the activity type required by the system authentication prompt. - `mobile/android/app/src/main/res/values/styles.xml` and `mobile/android/app/src/main/res/values-night/styles.xml` use the compatible launch theme. - `mobile/ios/Podfile.lock` records the native local-authentication dependency. - `mobile/ios/Runner/Info.plist` explains why Buzz requests Face ID access. **Identity policy and pairing flow** - `mobile/lib/shared/security/sensitive_action_authorizer.dart` wraps OS authentication and maps platform errors to stable app-level outcomes. - `mobile/lib/shared/community/community.dart` and `mobile/lib/shared/community/community_storage.dart` persist the sensitive-action policy. - `mobile/lib/features/invites/invite_join_provider.dart` assigns the explicit policy for invite-created communities. - `mobile/lib/features/pairing/pairing_provider.dart` gates export, binds grants to the active community/session, reauthenticates expired grants, and clears grants on every terminal path. - `mobile/lib/features/pairing/pairing_page.dart` lets users choose biometric protection while importing an identity. - `mobile/lib/features/settings/settings_page.dart` wires pairing into settings. - `mobile/lib/features/settings/settings_page/connection_section.dart` authenticates before opening export recovery and bounds the foreground-resume wait. - `mobile/pubspec.yaml` and `mobile/pubspec.lock` add and lock `local_auth`. **Coverage** - `mobile/test/shared/security/sensitive_action_authorizer_test.dart` covers native result mapping, unsupported devices, and single-flight behavior. - `mobile/test/shared/community/community_test.dart` and `mobile/test/shared/community/community_storage_test.dart` cover policy defaults and persistence. - `mobile/test/features/invites/invite_join_provider_test.dart` covers the invite policy. - `mobile/test/features/pairing/pairing_page_test.dart` covers import protection controls. - `mobile/test/features/pairing/pairing_provider_test.dart` covers export/import authorization, stale/reset/concurrent guards, malformed payload cleanup, and no-export failure paths. - `mobile/test/features/settings/connection_section_test.dart` covers the tap gate, lifecycle resume, and timeout behavior. </details> ## Reproduction steps 1. Pair an identity into the mobile app. 2. Open Settings and choose “Send identity to desktop.” 3. Verify Face ID, biometrics, or the device passcode is required before the recovery scanner opens. 4. Cancel device authentication and verify the scanner does not open and no identity transfer begins. 5. Authenticate, scan a Desktop recovery code, confirm the SAS, and verify the identity transfer completes. ## Validation At `be5620f5f10aa6cc16e86a4f01f102f3d9aeef9b`: - `cd mobile && ../bin/flutter analyze` — no issues - `cd mobile && ../bin/flutter test` — 1,368 tests passed - `cd mobile/android && JAVA_HOME=$(/usr/libexec/java_home -v 21) ./gradlew app:assembleDebug` — debug APK assembled successfully --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> (cherry picked from commit d8281b9) * Adapt security ports to Namleh baseline Signed-off-by: shelman09 <shelman09@outlook.com> --------- Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: shelman09 <shelman09@outlook.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
…lock#5681) ## Summary - allow channel-member remote/headless agents only with current kind `10100` directory evidence, while stale member identities remain hidden - fail closed while managed/relay directories load, error, or background-refetch across channel, forum, and cached autocomplete surfaces - revalidate agent mention authorization immediately before normal sends and message-edit saves, including after deferred uploads - in owner-only builds, fetch fresh authoritative profile ownership at send time and deny missing, changed-owner, or unavailable proofs - preserve human mention tags when agent authorization is revoked or unknown Supersedes block#5536 because its contributor-fork head cannot be updated by maintainers. ## Validation Exact head: `7278cdd5fbcee676c7b858ea098503c62eeeff0d` - mandatory pre-push suites passed: desktop check/typecheck/tests, Rust tests, mobile tests, desktop Tauri checks, branch-skew - desktop unit tests: 4,732 passed - focused edit/ownership regressions: 8 passed - focused mention E2E: 5 passed (remote positive, stale-member negative, directory error, pre-send revocation, mid-send revocation) - file-size ratchet passed One first focused E2E batch had a timing-only miss where the send click did not emit; the isolated rerun passed. One separate pre-push attempt hit the existing randomized passphrase separator test; the successful exact-head push reran and passed the mandatory suite. --------- Signed-off-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: JDiz00 <174381550+JDiz00@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: bhargavms <bhargav.m@ewa-services.com>
Summary
10100directory evidence, while stale member identities remain hiddenSupersedes #5536 because its contributor-fork head cannot be updated by maintainers.
Validation
Exact head:
7278cdd5fbcee676c7b858ea098503c62eeeff0dOne first focused E2E batch had a timing-only miss where the send click did not emit; the isolated rerun passed. One separate pre-push attempt hit the existing randomized passphrase separator test; the successful exact-head push reran and passed the mandatory suite.