Repository navigation
Authorize external application identities in Buzz - #2685
Merged
Merged
Conversation
Reuse the existing owner signer from Identity settings with exact-key confirmation and explicit disclosure of delegated agent control. Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Gil Birman <gil@squareup.com>
kazemek21
approved these changes
Nov 6, 2026
kazemek21
left a comment
Contributor
There was a problem hiding this comment.
Kyle reviewed this PR and the Nexus review-tracker notes. Approved.
Posted by Codex (gpt-6.1-sol) on behalf of kylez
tlongwell-block
added a commit
that referenced
this pull request
Nov 6, 2026
* eva/huddle-audio-fixes: (29 commits) fix(huddle): keep calls flowing under stalled sends and multiple devices fix(channels): wrap unread conversation stepping around the sidebar (#2682) fix(navigation): let bare buzz:// open Buzz without a broken-link notice (#2718) fix(inbox): open DM thread rows as the thread (#2587) test(channels): complete the context navigation fixture (#2783) fix(channels): open a thread's channel at its root message from the #channel link (#2377) fix(people): find people by a pasted npub or hex key (#2396) test(bestie): open the narrow long-history thread with clickRow (#2723) Account-first onboarding and sign-in recovery (#2579) fix(agents): scroll the avatar dock with a mouse wheel (#2567) fix(avatars): keep Done in view when the avatar popup is short (#2706) feat(messages): highlight reactions you've made (#2709) feat(usage): move Usage from the sidebar into Settings (#2742) fix(messages): keep a playing row mounted in the channel timeline (#2695) Fix plugin status declaration closure for author consumers (#2643) fix(navigation): keep empty new chats out of Back history (#1617) [buzz-app] Recover remote agent message outcomes (#2542) feat(messages): name background send destinations and attach the files that fit (#2406) feat(drafts): clear a draft from its row (#2725) Authorize external application identities in Buzz (#2685) ... Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@squareup.com> Signed-off-by: Tyler Longwell <tlongwell@squareup.com> # Conflicts: # docs/huddles.md # src-tauri/src/relay/huddle/audio.rs
kazemek21
added a commit
that referenced
this pull request
Nov 6, 2026
…uzz tools (#2707) ## TL;DR An approved external app (like Nexus) now gets the selected agent's whole Buzz MCP tool catalog and native dispatcher, including tools plugins add later, after one explicit "full access" approval. Before, it got a hand-picked list of six tools with rewritten arguments. Older narrow approvals still connect but grant no tools until the owner approves full access again, so no existing grant widens on its own. Initiative status: https://g2.sqprod.co/apps/kylez-nexus-buzz-plugins-status Fixes NEXUS-4262 ## What changes - **One scope, `agent`.** New links request `scope=agent`, and `agent` can't be combined with other scopes. Rust pairing accepts only `["agent"]` for new approvals (`valid_scopes`). Saved `read`/`post`/`create` approvals still load, renew and resume, but grant nothing. - **No adapter.** `respondExternal` now passes `tools/list` and `tools/call` to `respond` in `rpc.ts`, using the same `sessionTools(added)` catalog as native sessions. Plugin-added tools come from `addedTools.snapshot()` (the source native harnesses use) and are re-read on every message, so enabling or disabling a plugin changes the catalog live. The old policy map, argument rewriting and Bestie injection are removed. - **The agent's own authority.** The connection uses the agent's run handle (`runners.handle(key, undefined, true)`), so it gets device settings, plugin requests, Git access and handoff exactly as a native run does. Plugin installs still go through the owner's confirmation. Memory reads use the agent's `memoryListing`, like the native plugin path. Protected agents are still refused. `plugin_disable` acts at once, as it does natively, and the consent text says so. - **Handoff with true provenance.** `self_handoff` goes through the run's own handoff and `Agents.wakeIn`, but its `from` is `{ external: true }`. `wakeSection` shows it as an `<app-wake>` section that says an external app the owner approved sent it, and that it is not a message, a watch or timer, or another of the agent's conversations. - **Files where the agent reads them.** `send` with `path`/`files` calls a new optional `AgentType.readFile`. Claude, Codex, Pi and goose implement it with the reader their own tools use, in the agent's saved workspace on this device. Relative paths start there; absolute and `~/` paths reach any file the account can read, as they do natively, and the consent text says so. A Claude or Codex agent bound to an execution target refuses, because only a conversation's session opens that target. A type without `readFile` refuses with the same message its tools give natively. Closing the connection cancels a read in progress. - **No implicit conversation.** Calls run with an empty context, so every tool needs explicit channel or message IDs. - **Consent.** The dialog now states full access plainly: future plugin tools, Git access, waking the agent, Nexus workers, settings changes, posting any file the agent can read (including outside its workspace), and immediate plugin disabling. A legacy narrow link shows an update-required notice with Deny only, which sends the normal denial callback. `approve()` refuses non-`agent` requests as a second check. Agent settings flags a saved legacy approval as no longer working. ## Companion-client contract (for the Nexus consumer) | | Behavior | | --- | --- | | Request | `scope=agent` alone. Narrow scopes get the update-required notice and only a denial callback. | | `tools/list` | The full native catalog: 66 built-in tools with settings, plus plugin tools. Each tool is listed as natively, plus `_meta: {"buzz/requiredScopes": ["agent"]}`. Result `_meta`: `buzz/grantedScopes` (`["agent"]`, or `[]` for a legacy approval) and `buzz/supportedScopes: ["agent"]`. Cursors are rejected (single page). | | `tools/call` without the grant | `isError` result, nothing dispatched, `_meta["buzz/error"] = {code: "authorization_required", required_scopes: ["agent"]}`. | | `tools/call` with the grant | Native result and errors, unchanged. | | `self_handoff` | Native arguments. The wake reaches the agent as an `<app-wake>` from the app. | | `send` `path`/`files` | Read by the agent's type in its saved workspace. Agents on an execution target, and types without a reader, return their refusal. | | `channel_create` | Takes native arguments. A private Solo chat with the owner's Bestie must now pass `visibility: "private"`, `solo: true` and `members` explicitly. Buzz no longer fills them in. | An unupdated consumer that accepts only `read`/`post`/`create` will reject `supportedScopes: ["agent"]`. Its upgrade path is to accept `agent` and request `scope=agent`, which takes one owner approval. ## Overlap These open PRs touch nearby files. This PR neither depends on nor replaces any of them: - #2260 and #2685 cover external pairing and identity. - #2647 adds plugin skills to `session-tools`. - #2622 covers Bestie settings. - #1939, #1909, #1854, #2311 and #2070 change `service.ts`/`runner.ts`. - #2610 has no shared files. ## Validation Ran on a Linux Blox box against the current head: - `tsc --noEmit` and `tsc -p tsconfig.design.json`; - Biome on the touched files; - Vitest for `src/buzz-mcp`, the external connection, dialog and settings tests, `AgentsPage.test.tsx`, `service.test.ts`, `plugin-wake.test.ts` and the Claude, Codex, Pi and goose runtime tests. New tests cover the external handoff's trigger and `<app-wake>` wording, file reads through the type's reader (target workspace, cancellation on close, native refusal), the Claude and Codex workspace and execution-target cases, a saved narrow approval that a link asks to resume as `agent`, and memory read from the selected agent. The Rust pairing tests (`host_mcp::pairing`) need the Tauri system libraries, so they are left to PR CI. No browser journeys were added or removed. Independent security reviews traced authorize, approve, resume, renew and call, and found no path that grants tools without the `agent` scope. Not done: - The native desktop consent dialog and a live external connection have not been exercised against this build, including a live `<app-wake>` turn and a file read in a running Claude, Codex, Pi or goose agent. - No human testing yet. - The Nexus side of the contract is a separate change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Kyle Zemek <kylez@squareup.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
baxen
pushed a commit
that referenced
this pull request
Nov 9, 2026
…3032) ## Overview **Category:** improvement **User Impact:** Users can authorize an external application's identity in Settings → Buzz → Security, alongside paired command-line tools. **Problem:** External identity authorization sits among personal key and sign-in settings, making Identity busier and separating related security controls. **Solution:** Move the existing form into Security. Preserve its native-identity requirement, warning, explicit confirmation, signing behavior, and reset when the owner changes. **Context:** The form was introduced in #2685 to let applications such as Nexus get an explicit ownership proof. This move keeps that purpose and its safeguards and only changes where it lives. ## Changes <details> <summary>File changes</summary> **src/app/IdentitySettings.tsx** Remove the external authorization section. **src/app/SecuritySettings.tsx** Show the existing form below paired command-line tools and describe both security controls in the introduction. **src/app/Settings.tsx** Pass the current viewer to Security only when a native identity is available. **src/app/ExternalIdentityAuthorization.tsx** Point the identity-change recovery message to Security. **src/app/ExternalIdentityAuthorization.test.tsx** Exercise owner changes in Security and cancellation when leaving Security to sign out in Identity. **src/app/SecuritySettings.test.tsx** Assert that the authorization section is absent without a native viewer. **docs/buzz-help.md** Update the user-facing settings path. **docs/identity.md** Update the external identity authorization documentation. </details> ## Reproduction steps 1. Open Settings → Buzz → Identity in a native desktop build. External identity authorization is absent. 2. Open Security. Find “Authorize external identity” below “Paired command-line tools,” including when no tools are paired. 3. Enter an application's public key and choose “Authorize identity.” Check the exact identity and unchanged warning; Cancel must create no proof. 4. Choose “Authorize this key” to generate the ownership proof. Confirm copying and editing the application key still behave as before. ## Validation - At `57434713d`: pre-commit formatting/lint/icons and secret scan passed; pre-push TypeScript, related Vitest tests, and design checks passed. `git diff --check` passed on the clean committed tree. Native layout was checked in dark mode at 1280×795, with no paired tools. Signing and confirmation keyboard behavior were not exercised during capture. - Changed lines (additions + deletions): **22 tests; 27 non-test** (23 source, 4 documentation). Tests are **44.9%** of the diff. - No browser journeys added or removed. - Independent review passed at this head. Populated paired tools, confirmation focus/Escape, light/narrow/enlarged-text checks, and human acceptance remain pending. ## Screenshots Real native macOS app. Before uses the UI files from base `5b7df025f`; after uses `57434713d`. Account and workspace details are cropped out. The private key remains masked by the app. The PR source was restored and the working tree verified clean after capture. | Before — Identity, scrolled to the form | After — Security | | --- | --- | |  |  | --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
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.
Nexus keeps its own signing key, but signing in through the Block identity adapter also needs proof that the Buzz owner authorizes that key. Native desktop Identity settings now lets the owner enter an application's public key and create that proof with Buzz's existing signer.
Before signing, a confirmation shows the exact application identity and explains that its holder can direct the owner's Buzz coding agents to run commands, with no expiry or event-kind restrictions. Cancel signs nothing. The proof stays in the form until explicitly copied, and changing owner or leaving settings discards pending results. This adds no signing protocol, private-key export, relay calls, or persistent state.
Validation: 126 focused UI/host tests, TypeScript, Biome, native signer and production IPC signature tests passed at
95142e075eb6c17b4f780d5f1d7da4585e0271b6. Two independent review rounds have no remaining P0/P1 findings. The actual approved proof was accepted by Nexus's browser sign-in flow.Native macOS foreground verification at this head shows the exact Nexus identity and full warning. Cancel receives initial focus; pointer Cancel, Escape, and Return dismiss the confirmation, return focus to Authorize identity, and produce no proof. Earlier native captures were inconclusive; no source defect or root cause was established. Native checks cover dark and light themes, a 756-pixel compact window and 120% interface size. The human independently opened and cancelled the confirmation, then confirmed Nexus’s signed-in state. The approved proof was already accepted by real Nexus enrollment. Full Nexus task delivery remains tracked in squareup/nexus#6810; it is separate from this Buzz proof-authoring flow. Shared owners reused: AlertDialog, Field/Input/Textarea, InlineHeader and SettingsGroup. Theme and interface size were restored after testing. No new browser journey was added or removed.
WebKit diagnostic fixture with synthetic public keys; no approval submitted:
Actual native macOS confirmation; existing public identities, private key masked, no approval submitted during this check:
Native light theme at 120% interface size in a compact window; no signing performed:
buzz-review-completed