镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(claude): run Claude with an Anthropic API key - #2553

Merged
baxen merged 11 commits into
mainfrom
mbot/claude-api-key
Nov 7, 2026
Merged

baxen merged 11 commits into
mainfrom
mbot/claude-api-key

Conversation

@baxen

@baxen baxen commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Claude agents now run with an Anthropic API key. They no longer use a Claude account sign-in from claude auth login. Security reviews used a claude setup-token sign-in; they now use the same key.

What

  • Setup: Claude asks for an Anthropic API key in Settings → Agent setup → Harness defaults → Claude. In Create agent, picking Claude without a key opens Set up Claude. Cancel returns to the harness the person left, and saving shows a "Claude is set up" toast linking to Settings. If a build's auth.tsx signs in interactively (ClaudeAuth.signIn), Set up Claude offers Sign in there, as Claude's Settings dialog does. Once a key is saved, the field becomes Replace API key. Saving closes the configuration dialog.
  • No key: agent cards and chat errors say "Claude is missing an API key. Head to Settings to add one." They link to a new buzz://settings?section=agents&provider=… link that opens Claude's configuration. Agents that run on an execution target don't need this computer's key, and they can be edited without one.
  • Codex first: Codex is now first in the Harness list and is the default for new agents. auth.tsx exports leadsHarnesses (false here), which src/bundled/index.ts reads, so a build whose Claude needs no key can keep Claude first.
  • Storage: native code (src-tauri/src/claude_key.rs) keeps the key in the OS credential store, the same way the TypeSafe key is kept. That's Keychain on macOS, Credential Manager on Windows and Secret Service on Linux. The macOS item is xyz.block.buzz.desktop.claude-api-key, plus .debug in debug builds. No command returns the key. Native code keeps a key once it has read or saved it. When it has none, it reads the store again, and every status check re-reads the store, so a key another Buzz saves or replaces is picked up.
  • Every claude asks for the key through plugin_host_process_spawn's secrets. Native code then sets ANTHROPIC_API_KEY itself, after the plugin's environment. That covers agents, the spare, model discovery, protected runs and security reviews. The key is set only for buzz.claude-code's claude-sh and claude-protected-* processes; any other process that asks is refused.
  • No silent fallback: the runtime refuses to start a turn's claude when no key is saved. Without that, a turn would quietly use an account sign-in in ~/.claude.
  • Replacing the key restarts idle sessions and the spare. Busy sessions restart when they next fall idle.
  • One seam: everything about how Claude authenticates lives in src/bundled/claude-code/auth.tsx (exports ClaudeAuth, Step, SignIn, SignedIn, Missing). A build can swap in another sign-in by replacing that one file. squareup/buzz-releases#133 does this to keep the login sign-in in Block builds.

What the key store does not guarantee

  • Claude's children inherit its environment: its Bash tool, hooks and MCP servers. Commands a Claude agent runs can read the key. Buzz doesn't set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, because it also strips SSH_AUTH_SOCK, GIT_* and every *TOKEN/*KEY variable. The docs and setup copy say so.
  • claude-sh is bash -c with a script the caller supplies, so plugin code can print the key back. Keeping the key out of the web view is auth.tsx's convention, not a native guarantee. Like the rest of the threat model, this trusts installed plugin code.

Tests

  • vitest run src/bundled/claude-code src/bundled/agents src/features/agents src/features/navigation: 2,495 passed.
  • cargo test --lib claude_key: the key-store tests pass. They cover a saved key used without another read, absence not kept, a check re-reading a key replaced or removed elsewhere, and an unreadable store treated as an error rather than a missing key. A related test checks that only Buzz's Claude processes get the key.
  • tabs.test.tsx: picking Claude, then cancelling or saving, plus the toast and the Codex-first order. Editing an agent on an execution target doesn't need a local key, and editing a local one still does.
  • Typecheck, biome, and the pre-push hooks (types, design system, clippy).
  • The full native cargo test --lib crashes with SIGSEGV in the window_state:: tests on macOS. A clean main checkout crashes the same way.

Deferred

  • A setup-token OAuth token saved for reviews by earlier builds stays at claude-code-review-home/token. This build doesn't read it, and older builds still do, so removing it needs a migration decision. Deleting the file also wouldn't revoke the token.
  • The key flow has been tried in a dev build, not yet in a signed release build.

🤖 Generated with Claude Code

@baxen
baxen force-pushed the mbot/claude-api-key branch from 4ae734e to 0c71552 Compare November 6, 2026 19:39
baxen and others added 6 commits November 6, 2026 17:20
Claude setup now asks for an Anthropic API key in place of a Claude
account sign-in. Buzz saves the key beside its copy of Claude through
claude.sh (stdin, 0600) and gives it to every claude process as
ANTHROPIC_API_KEY, protected runs and security reviews included.
Without a key no turn starts, so nothing falls back on an account
sign-in in ~/.claude. Saving a new key restarts idle sessions.

Everything about how Claude authenticates now lives in
src/bundled/claude-code/auth.tsx, so a build can replace that one file
with another way to sign in.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
The key form sat inside agent creation's and Settings' own forms, so
saving a key with Enter or the button also submitted them. It is no
longer a form; Enter saves the key.

On macOS the key file now lives in security reviews' folder, which Buzz
Security hides from contained agents, not in the download folder that
protected agents can read.

No key is no longer cached, so a key another Buzz saves is picked up,
and a stopped save still tells running sessions the key may have
changed.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
- Creation takes Model and Thinking's place with the key form, full width,
  saying the key is set up once, and waits for it (CreateConfiguration).
- An agent's library card no longer asks for the key: it says Claude is
  missing one and links to Claude's setup in Settings. Other Model rows
  open setup in a dialog that closes once the key is saved.
- Claude's configuration dialog: a missing key's form takes it over, and
  saving a key, new or replacement, closes it while the key is all it edits.
- A turn that fails for a missing or rejected key says so in its report,
  with a buzz://settings link to Claude's setup, a new presentation-only
  link form for Settings targets.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
The key was a 0600 file beside the review folder, which any process of the
user's, unprotected agents included, could read. Native code now keeps it in
the OS credential store (Keychain on macOS, readable without a prompt only by
the Buzz binary; Credential Manager or Secret Service elsewhere), as it does
the TypeSafe key, and never sends it back to the webview.

- claude_key.rs: claude_api_key_status / claude_api_key_set, with the same
  loose sk-ant- check claude.sh made.
- plugin_host_process_spawn takes `secrets`: native code sets the key as
  ANTHROPIC_API_KEY after the plugin's env, only for buzz.claude-code's
  claude-sh and protected releases, and refuses the secret to any other.
- auth.tsx asks for the secret where it used to pass the key in env, and
  reviews get it the same way; claude.sh drops save-key and key.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
The first-time key form split its copy around the field: why and once
above, where to get it below. One paragraph above now says both, and only
the replace form keeps a field description.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Codex now leads the Harness list and is new agents' default, since
Claude asks for an API key before its first agent. Picking Claude
without a key opens Set up Claude; closing it returns the person to
the harness and choices they left, and saving shows Model and Thinking
with a toast that links to Claude's setup in Settings. When Claude
starts selected, a row inside the Harness group opens the same dialog.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@baxen
baxen force-pushed the mbot/claude-api-key branch from 3f69002 to 7bc65b7 Compare November 7, 2026 01:29

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Combined review at 3f690022 (two reviewers). Not approved: the two P2s inline block approval.

Inline findings

  • [P2] Editing a remote Claude agent requires a local API key (tabs.tsx).
  • [P2] Native code never re-reads the key (claude_key.rs).
  • [P3] Clarify that Claude's child processes can read the key from the environment.
  • [P3] Clarify what the native secret gate guarantees.

Not tied to a line

  • [P3] The PR description still describes a 0600 api-key file written by claude.sh save-key, and tests for it. This head keeps the key in the OS credential store; please update the description.
  • Optional: a setup-token OAuth token saved for reviews stays at claude-code-review-home/token, and this build no longer reads it. That folder is shared across builds, and older builds still use the token, so removing it needs a migration decision. Deleting the file also does not revoke the token.

Validation

  • Targeted Vitest (auth, setup, runtime, buzz-links): 210/210 passed.
  • Full Vitest: 19,121 passed, 8 failed, 22 skipped, plus 1 unhandled error. The 8 failures were all outside the changed test files; whether this PR caused them is unconfirmed.
  • Native code compiled and the 4 new key tests passed, but the full native test run crashed with SIGSEGV.
  • Not checked: the app with a real key, and the rendered UI.

Comment thread src/bundled/claude-code/tabs.tsx Outdated
state.status?.state === "signed-out" && state.running !== "install";
// Only missing credentials hold creation: an agent made before Claude is
// installed waits for the download, as it did with the Model row alone.
const ready = !!state.status && !signedOut;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P2] Editing a remote Claude agent requires a local API key.

CreateConfiguration gates readiness on local signedOut and never looks at the agent's execution binding. UpdateReview edits through AgentCreateDialog, so an update to a remotely executed Claude agent, even a name-only one, can't be saved while this computer has no key, though the remote side supplies its own authentication. SettingsTab already resolves useRemote(agent) first (L1107-1109). Fix: resolve remote execution before applying the local-key gate, and gate only agents that run locally.

Comment thread src-tauri/src/claude_key.rs Outdated
.map_err(|_| "Secure storage is unavailable".to_string())??;
*key = Some(read);
}
Ok(key.as_ref().and_then(|k| k.clone()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P2] Native code never re-reads the key.

key() caches the first read for the life of the process, including Some(None). ClaudeAuth.has()/check() clear only the TypeScript cache (auth.tsx: "another Buzz on this computer may save one"), so the native layer keeps returning the stale value. When another instance or development worktree sharing the credential store saves or replaces the key, this one keeps reporting it missing, or keeps using the old (possibly revoked) key, until restart. Fix: don't cache absence, and re-read the store natively on an explicit check.

Comment thread src-tauri/src/claude_key.rs Outdated
//! the Claude processes `buzz.claude-code` starts that ask for it
//! (`plugin_host_process_spawn`'s `secrets`). On macOS the item is created by
//! the Buzz binary, so only that binary reads it without a prompt; an agent's
//! shell cannot.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P3] Clarify the boundary: Claude's child processes can read the key from the environment.

This comment covers direct Keychain reads, and docs/agent-protection.md already says the key is in each Claude agent's process environment. It doesn't say that Claude's Bash, hook and MCP children inherit it. In Buzz's current unprotected copy (2.1.319), Claude scrubs credentials from those children only when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set, and that flag also strips SSH_AUTH_SOCK, GIT_* and every *TOKEN/*KEY variable, so it isn't a usable fix. The protected release pinned here (2.1.305) hasn't been checked. Suggest stating explicitly in the docs and setup copy ("gives it only to Claude") that commands an agent runs can read the key, after confirming the behavior in the pinned version.

&& matches!(
process_id,
"claude-sh" | "claude-protected-arm64" | "claude-protected-x64"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P3] Clarify what this gate guarantees.

claude-sh is declared as bash -c with a script the caller supplies, so releasing the secret to it is equivalent to releasing it to any frontend caller that names this plugin. A caller can spawn it with printf %s "$ANTHROPIC_API_KEY" and read the key back through stdout. The documented threat model trusts installed plugins and runtime code, so this isn't a breach of a promised boundary. But "never sent back to the webview" is a convention in auth.tsx, not something native code enforces. Either release the key only to a declared process that can run nothing but claude, or reword the guarantee.

baxen and others added 3 commits November 6, 2026 17:38
An update to a Claude agent that runs on an execution target went through
CreateConfiguration's local-key gate, so even a rename could not be saved
while this computer had no key. Resolve where the agent runs first and ask
for the key only for agents that run here.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
…heck

Native code kept its first read for the life of the process, absence
included, so a key another Buzz saved or replaced was not seen until
restart. Keep only a key, and re-read the store on every status check.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Claude's children inherit ANTHROPIC_API_KEY, and keeping the key out of
the web view is the plugin's convention, not a native guarantee. Say both
in the docs, the setup copy and claude_key.rs.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@baxen

baxen commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Thanks, Kalvin. Addressed at 6f38a7ea8:

  • [P2] Editing a remote Claude agent requires a local key. Fixed in c6c88a1b0. CreateConfiguration now resolves where the agent runs (useRemote) before applying the key gate. Only agents that run here, or whose place can't be determined, need this computer's key. It stays not-ready while that's being resolved. New tests: a remote agent edits without a key, and a local one still asks for it.
  • [P2] Native code never re-reads the key. Fixed in b2f10d9c0. ClaudeKey keeps only a key, never absence, so with no key every ask reads the store again. configured() (the app's explicit status check) always re-reads, and updates the kept key, including clearing it. A failed read keeps the current key. Covered by new tests.
  • [P3] Child processes can read the key. Done in 6f38a7ea8. claude_key.rs, docs/agent-protection.md, docs/buzz-help.md and the Replace key copy now say that commands, hooks and MCP servers inherit ANTHROPIC_API_KEY, and why Buzz doesn't set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB. I checked 2.1.318/2.1.319, which have the flag. I couldn't check pinned 2.1.305 because it isn't downloaded here. Buzz doesn't set the flag in any version, so the inheritance claim holds either way.
  • [P3] What the gate guarantees. Reworded rather than restricted, in the same commit. The docs and claude_key.rs now say that claude-sh runs a caller-supplied script, and that "not in the web view" is auth.tsx's convention, not a native guarantee. What native code enforces is that no other plugin or process gets the key. Restricting it to a claude-only process is possible later, but it changes how claude.sh starts Claude.
  • PR description: rewritten for the credential store.
  • Review token file: left as is, and listed under Deferred in the description. It needs a decision about older builds.

On your validation: the full native suite also crashes with SIGSEGV for me, in window_state::, and a clean main checkout does the same. Vitest across src/bundled, agents, navigation and app passes here (8,463 at 7bc65b729, 2,495 for the touched areas at this head).

@baxen
baxen marked this pull request as ready for review November 7, 2026 01:49
@baxen
baxen requested a review from a team as a code owner November 7, 2026 01:49
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T01:52:59.269991Z 6f38a7e Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f38a7ea8b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +6 to +8
// Builds can replace this one file with another way to sign in, so the rest
// of the plugin reaches authentication only through its exports: keep their
// names and shapes when changing it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the private repository reference

The commit message names squareup/buzz-releases#133, exposing an internal repository and issue number in this public repository's permanent history. Remove that reference from the commit message before landing.

AGENTS.md reference: AGENTS.md:L234-L240

Useful? React with 👍 / 👎.

@@ -0,0 +1,207 @@
//! The Anthropic API key Buzz's Claude runs with, in OSS builds. Native code

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required commit attribution trailers

Commit 75eebec9d4f8c03a64ef7077904c51fdf92dfc74 has neither a Signed-off-by nor a Co-authored-by trailer. The hosted DCO check will reject the commit, and because it is authored by an agent, repository policy also requires crediting the human co-author.

AGENTS.md reference: AGENTS.md:L268-L274

Useful? React with 👍 / 👎.

baxen and others added 2 commits November 6, 2026 17:54
The create dialog's Set up Claude rendered only SignIn's full variant,
which leaves the sign-in action to the surface around it. A build whose
auth.tsx signs in interactively (Block's) opened an empty dialog. Offer
Sign in there when auth.signIn is set, as Claude's Settings dialog does,
and say "Claude is set up" for credentials of either kind.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Codex leads by default because Claude asks for a key first. A build that
swaps in an auth.tsx without that step can export leadsHarnesses = true
to keep Claude first.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Alignment check with the internal auth overlay at this head: the auth.tsx export surface, leadsHarnesses, and the create dialog's Sign in all line up; with the overlay applied, tsc --noEmit and vite build pass. One cleanup inline.

<Dialog
open={asking && signedOut}
title="Set up Claude"
onOpenChange={(next) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Closing this dialog mid sign-in leaves claude auth login running.

With an interactive auth.signIn, clicking Sign in here and then closing the dialog calls onDecline but never setup.cancel(). setup.step has no timeout, so running: "sign-in" stays set and blocks Install/Sign in elsewhere until the process ends or the person presses Cancel in Settings. ClaudeSignIn's dialog cancels on close; this one should too.

Reproduced in jsdom with a pending setup double: 1 start, 1 decline, 0 cancellations, still running after close and unmount.

Fix: call setup.cancel() here (and on unmount) when state.running === "sign-in", and add a test for closing during a pending sign-in — the new test covers only immediate success. API-key builds are unaffected; the key form starts no process.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Star Lord’s automated source review via Wes’s account.

Reviewed head 2167b4d646521ca257de914adc57bd0aba6419cf, target base 902644b25fec016b95689c6e2dc00c3853859282, feature merge-base f30cd0f52860813ff00e2894541ebbd10c77dab2. Three code findings are inline: one P2 affecting missing-key recovery, and two P3 storage-error recovery issues.

[P2] Remove the private distributor reference from the public PR description. The “One seam” bullet names a private distributor repository and its PR. This conflicts with the repository’s public-boundary rule and exposes internal coordination metadata. Keep the generic description of the replaceable authentication contract, but remove that private reference from the public description.

Coverage: native key storage and process boundaries, runtime/session replacement, setup/create/edit flows, and Settings navigation, with independent Rocket, Mantis and Groot review plus integration. The documented trusted-plugin and inherited-child-environment limitations are not reported as new vulnerabilities.

Source inspection only. No tests, builds, application execution, live credential operations or CI validation were performed; the PR’s reported checks and signed-release behavior were not independently reproduced.

// Buzz's copy, and the protected release (`claude-protected-*`).
if (id !== "claude" && !id.startsWith("claude-protected-"))
return spawn(id, options);
if (required && !(await this.has())) throw new Error(NO_KEY);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P2] Preserve this auth failure through session startup. An existing local Claude agent upgraded from account sign-in has no saved API key, so this throws NO_KEY. But AgentSessions.open() (sessions.ts:543–550) replaces every startup rejection with "Claude could not start". That generic result reaches runtime.report(), so the new explainTurn() wiring cannot recognize the missing key or publish its Settings link; every mention repeats the generic failure. Preserve the actionable auth error through the session-start result, or report it before that catch, while retaining the refusal to launch without a key. Cover the published runtime report: the new direct explainTurn() test bypasses the catch that loses the reason.

const signedIn = await auth.check();
if (!signedIn) return { state: "signed-out", version };
return { state: "ready", version, ...signedIn };
} catch {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P3] Keep credential-store failures distinct from a missing key. Once native code has cached a key, the preceding --version spawn can succeed without reading secure storage. auth.check() then calls configured(), which always rereads it; a newly locked/unavailable store or denied prompt throws here. This catch turns that failure into signed-out, hiding the native recovery message and asking the person to add a key that is already saved. Return an error status with the sanitized storage message, and cover a cached key followed by a failed status read.

) {}
readonly auth = new ClaudeAuth(spawn),
) {
this.spawn = spawn && auth.authorize(spawn);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P3] Do not cache a storage refusal as a bad Claude release. Wrapping all setup launches also makes the updater’s version/protocol probes request the native key. With no cached key and a locked or denied credential store, checkClaude() returns an error without a version; install() proceeds to the latest release, then testRelease() fails before its child starts. The catch adds that version to refused, so later automatic updates skip the same healthy release even after storage recovers, until a manually requested install clears the set. Keep credential-store/prelaunch failures separate from release-validation failures and retry after storage is available; add a locked-store → recovery regression.

@morgmart

morgmart commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Design review (critical path: agent setup / onboarding). No must-fix items.

Design preference (blocks merge):

  • New agents default to Codex instead of Claude in builds where Claude needs an API key. Codex also moves to the top of the Harness list. Option A: Codex is first and the default, so new people get an agent that works without a key. Option B: keep Claude first, and picking it opens Set up Claude right away. Both are reasonable, but this changes what everyone sees out of the box, so a designer should make the call.

Optional:

  • People whose Claude agents worked through a Claude sign-in will find them stopped after updating, until they add a key. They learn this from an error in the conversation. It does link to Settings, so it isn't a dead end, but a one-time notice on update would be kinder.
  • "console.anthropic.com" is plain text in both setup messages. Make it a link so people don't have to retype it.
  • The Replace API key description reads as a contradiction: "gives it only to Claude, so commands your Claude agents run can read it." Suggest: "…gives it only to Claude. Commands your Claude agents run can also read it."
  • The no-key message in security reviews spells out a five-step Settings path. Where a link to Claude's settings is possible, use the link, as agent cards already do.

Not checked: the running app (the PR has no screenshots), dark mode, narrow windows, the Block-build sign-in path.

Design preference outstanding with @klopez4212

@baxen
baxen merged commit f7290f0 into main Nov 7, 2026
60 of 62 checks passed
@baxen
baxen deleted the mbot/claude-api-key branch November 7, 2026 02:36
@quanganhhoang

Copy link
Copy Markdown

hi, could i please ask why this change was done? It abruptly affected my agents using claude harness, and it is not straightforward to get an API key from a company subscription account.

xarielx added a commit to xarielx/buzz-app that referenced this pull request Nov 7, 2026
Since block#2553 Claude ran only with an Anthropic API key, so agents on a
Claude subscription (`claude auth login`) stopped answering. Claude now
runs either way, as the owner chooses in Settings → Agent setup →
Harness defaults → Claude:

- Saving a key uses the key; Sign in runs `claude auth login` and uses
  the account. Settings shows which is in use and switches between them.
- Until the owner chooses, a saved key leads, and otherwise the account
  Claude is signed in to, so subscription-only setups work again after
  updating without any step.
- On the account, Buzz gives Claude no key and refuses no turn for want
  of one. Security reviews keep using the key: their own Claude folder
  holds no account sign-in.
- The choice lives in agent storage and is read as saved at each check
  and start, so another program's choice is honoured.

Fixes block#2993

Signed-off-by: Ariel Barboza <ariel@arielbarboza.com>
baxen added a commit that referenced this pull request Nov 7, 2026
## Why

After #2553, picking Claude in Create agent without a key popped **Set
up Claude** open on its own, and closing it switched the person back to
the harness they left. Builderlab handles its own missing sign-in more
quietly: a compact **Sign in** button beside the Harness picker
(`CreateAction`), with Model and Thinking left in place but disabled.
This makes Claude's key setup work the same way.

## What

While Claude has no credentials on this computer, in Create agent and
when editing a local Claude agent:

- **Button beside Harness:** Claude now supplies `CreateAction`, a
compact capsule button like Builderlab's. It reads **Add key**, or
**Sign in** in a build whose `auth.tsx` signs in interactively
(`auth.signIn`). Its accessible name is `auth.attentionLabel`. Clicking
it opens **Set up Claude**, with the same contents as before.
- **No automatic modal:** picking Claude no longer opens Set up Claude,
and closing it keeps Claude selected.
- **Model and Thinking stay**, disabled, instead of being replaced by a
"Claude needs an Anthropic API key" row. Create agent stays disabled
until Claude is set up, as before.
- **Credentials arriving** close the modal, remove the button, enable
the menus, and show the existing "Claude is set up" toast.
- **Returning during sign-in:** passive Back/unmount leaves provider
work running. Returning to creation restores the pending setup dialog
with Cancel rather than starting another sign-in.
- **Closing during sign-in** calls `setup.cancel()`, so `claude auth
login` doesn't keep running. This was the open review comment on #2553
([review](#2553 (review))).
- **Execution targets:** `CreateAction` now receives the agent being
edited, as `CreateConfiguration` does. An agent on an execution target
gets no button, so it can still be edited without a local key.
Builderlab's `CreateAction` ignores the new prop.
- **`onDecline` removed:** the creation dialog's `onDecline` seam (with
its `left` state) existed only for Claude's turn-down-returns-you
behaviour. Nothing else used it.

No change to `auth.tsx` or its exports, so the downstream authentication
overlay applies unchanged.

## Tests

- `tabs.test.tsx` (75 pass). Replaced the auto-open/decline tests with:
- picking Claude shows **Add key** and disabled Model/Thinking/Create;
closing the modal keeps Claude; saving a key closes it, enables Model
and shows the toast;
- a new agent without a key reports not-ready with Model and Thinking
disabled;
- an interactive-sign-in build labels the button **Sign in** and
finishes through the modal;
- closing the modal while sign-in is pending calls `cancel` once (new);
- editing an agent on an execution target shows no button; a local one
does.
- `vitest run src/bundled/claude-code src/bundled/agents
src/bundled/builderlab src/features/agents`: 2,817 pass. The pre-push
related-tests hook (37 files) passes.
- `tsc --noEmit` passes, also with the downstream authentication
overlay’s `auth.tsx` swapped in.
- Built ad-hoc signed macOS apps from this commit, OSS and with the
Block overlay. Both builds pass the auth-marker checks, and baxen has
tried them.

## Review follow-up

- `buzz_help` names the explicit build-dependent Add key / Sign in
action beside Harness.
- React regression exercises unmount/remount with a held modeled sign-in
child, then Cancel and retry through the real ClaudeSetup process-step
path. Failed on the prior production code (dialog not restored), passed
with the fix.
- At follow-up head `7f49bd53e4d3415a64794164b51319bb41c61a2c`, `pnpm
typecheck` passes and the full Claude plugin directory passes serially
(416 passed, 4 live tests skipped). An earlier two-worker run had three
failures in the transcript/shell-release tests; both whole files
subsequently passed (94 tests), followed by the full serial pass.
- New follow-up does not claim native authentication or routed
Back/Forward validation. Human verification of the remount/cancel path
remains deferred: start Sign in from creation, navigate Back, return,
confirm pending controls are restored, Cancel, and retry.

## Deferred

- A full `vitest run` on the dev machine hit 5s timeouts in unrelated
files (`App.profile`, `entity-navigation`, others), a different set each
run, under load average ~60. Those files pass when rerun under lower
load. CI is the clean run.

---------

Signed-off-by: Bradley Axen <baxen@squareup.com>
Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Co-authored-by: drone03 <3efda7d0089450ac8094dd25cfa734546dc0d6bad0ab87b64dacfff867bdc4d3@buzz.block.builderlab.xyz>
@cmd8

cmd8 commented Nov 8, 2026 •

Copy link
Copy Markdown

why was this merged? what was the rationale behind this decision? this literally makes the app unusable

xarielx added a commit to xarielx/buzz-app that referenced this pull request Nov 8, 2026
Since block#2553 Claude ran only with an Anthropic API key, so agents on a
Claude subscription (`claude auth login`) stopped answering. Claude now
runs either way, as the owner chooses in Settings → Agent setup →
Harness defaults → Claude:

- Saving a key uses the key; Sign in runs `claude auth login` and uses
  the account. Settings shows which is in use and switches between them.
- Until the owner chooses, a saved key leads, and otherwise the account
  Claude is signed in to, so subscription-only setups work again after
  updating without any step.
- On the account, Buzz gives Claude no key and refuses no turn for want
  of one. Security reviews keep using the key: their own Claude folder
  holds no account sign-in.
- The choice lives in agent storage and is read as saved at each check
  and start, so another program's choice is honoured.

Fixes block#2993

Signed-off-by: Ariel Barboza <ariel@arielbarboza.com>
@lukesbrave

Copy link
Copy Markdown

Can you please explain the reason behind removing Claude subscription sign-in? Was this required by Anthropic, or was it a Buzz product decision?

We’ve been building on Buzz, recommending it to clients with their Claude subscriptions. This update stopped those agents from working unless users add an API key with separate billing.

We haven’t seen a clear announcement explaining the change, its rationale, or a migration path. Finding out through agents suddenly failing is frustrating and makes it difficult to support the people we’ve encouraged to use Buzz.

Is subscription sign-in being permanently discontinued, or would you consider supporting both options as proposed in #2995? A clear answer would help us decide what to teach and recommend.

@kvncrw

kvncrw commented Nov 10, 2026

Copy link
Copy Markdown

@baxen can you explain this change per the above request? this has broken workflows for several of the guys on my team and it's not entirely clear if this was an accidental regression.

xarielx added a commit to xarielx/buzz-app that referenced this pull request Nov 11, 2026
Since block#2553 Claude ran only with an Anthropic API key, so agents on a
Claude subscription (`claude auth login`) stopped answering. Claude now
runs either way, as the owner chooses in Settings → Agent setup →
Harness defaults → Claude:

- Saving a key uses the key; Sign in runs `claude auth login` and uses
  the account. Settings shows which is in use and switches between them.
- Until the owner chooses, a saved key leads, and otherwise the account
  Claude is signed in to, so subscription-only setups work again after
  updating without any step.
- On the account, Buzz gives Claude no key and refuses no turn for want
  of one. Security reviews keep using the key: their own Claude folder
  holds no account sign-in.
- The choice lives in agent storage and is read as saved at each check
  and start, so another program's choice is honoured.

Fixes block#2993

Signed-off-by: Ariel Barboza <ariel@arielbarboza.com>
xarielx added a commit to xarielx/buzz-app that referenced this pull request Nov 11, 2026
Use Claude account always ran `claude auth login`, opening the browser
even when Claude Code was already signed in, as from a terminal. It now
uses that sign-in, as Buzz did before block#2553, and signs in only when
Claude Code isn't.

Signed-off-by: Ariel Barboza <ariel@arielbarboza.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants