Skip to content

feat(dev): just desktop me keeps Keychain approvals across rebuilds - #3002

Merged
baxen merged 8 commits into
mainfrom
mbot/dev-credential-helper
Nov 7, 2026
Merged

baxen merged 8 commits into
mainfrom
mbot/dev-credential-helper

Conversation

@baxen

@baxen baxen commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Why

just desktop me asks for Keychain access about six times on every restart. A debug Buzz binary is only ad-hoc signed, so Keychain ties each approval to that exact build: every rebuild, and every worktree, asks again, once per saved item.

The login keychain shows the build-up: jev-key.debug has 46 per-build entries, identity.debug 35, agents 30. A self-signed certificate alone doesn't fix it. A rebuilt binary signed with the same certificate is still refused, because code with no Apple team ID gets a per-build partition ID.

What

just desktop me now does the Keychain work through a small helper that's installed once and kept. Keychain approvals belong to the helper, so they last.

  • First me on a Mac:
    • Installs crates/dev-credential-helper at ~/.buzz-foundation/dev-credential-helper/.
    • Creates a self-signed Buzz Dev Signing certificate in the login keychain.
    • Records the code requirement the helper checks callers against.
    • You click Always Allow once per dialog: one for codesign, then one per saved item.
  • Every later launch, rebuild or worktree: no dialogs.
  • Not affected: plain just desktop, agents, CI and release builds, plus Linux and Windows.
  • No Apple account or Xcode needed.

How

  • Helper (crates/dev-credential-helper)
    • One request form: [--authorize] --item <read|add|replace|delete> <service> <account>, replacing the human, --agent-store and --session-store modes from feat(identity): support a local debug restore helper #1517 / Support agent credentials through the macOS debug helper #2174 / Route debug sessions through the credential helper #2541.
    • It serves only an exact list of items: the release human key (read only, for me), the debug human key (create-only), paired tools, agents, debug federated sessions, the Jev and Claude debug keys, and external-app pairings.
    • It checks the caller before touching the Keychain:
      • stdout must be a Unix socket;
      • the peer's audit token must be its parent process, with the same effective uid;
      • the peer's live code signature must meet the recorded requirement, identifier "xyz.block.buzz.desktop.dev" and certificate leaf = H"…".
    • Normal requests run with Keychain dialogs off. Only an item not yet approved, under --authorize, is retried once with a dialog allowed.
    • --version reports its protocol. me reinstalls the helper only when the checkout needs a newer protocol.
  • App (crates/credential-store)
    • In macOS debug builds with BUZZ_DEV_CREDENTIAL_HELPER set, every read / add / replace / delete, and the session functions, go through dev_item.
    • As before, a helper failure is returned and never retried against the Keychain.
    • jev.rs, host_mcp/pairing.rs and the macOS human add in identity.rs used to call SecKeychain directly. They now use credential-store, with the same semantics and messages.
    • The agent store's macOS add goes through the helper too.
  • Launcher (scripts/desktop-dev.mjs, scripts/dev-credential-helper.mjs, scripts/dev-sign-run.sh)
    • Only me on macOS turns this on. BUZZ_DEV_CREDENTIAL_HELPER* from .env.local or the shell is dropped, as BUZZ_DEV_RELEASE_KEY already is.
    • Tauri 2.12 dev starts the app with cargo run, verified with a logging --runner (run --no-default-features --color always --). So CARGO_TARGET_<triple>_RUNNER=scripts/dev-sign-run.sh signs the binary just before it starts. That takes about 1 s on the 420 MB debug binary.
    • If setup fails, it warns and launches as before.
  • Docs
    • docs/identity.md: the "Local restore helper" section is rewritten as "Local credential helper on macOS", including the trust trade-off.
    • docs/contributing.md, docs/buzz-help.md and AGENTS.md (agents never run me) are updated.

Risk

  • Trust: after approval, any program running as you that can sign code with the local certificate can read these items without a dialog, including your release key under me. The certificate's key lets codesign use it without asking. The docs say this, and say not to use me on a shared account. Before this change, the per-rebuild prompt was the only gate.
  • Existing helpers: a private helper written to the old human / agent / session modes stops working. The repository now ships the helper.
  • Approval timeout: under me, every helper call uses the 2-minute approval timeout, since any item may still need its one approval. A hung helper therefore takes 2 minutes to fail.
  • Old Buzz imports: bringing over old Buzz's items (legacy import) still reads the Keychain directly. It runs once.

Testing

  • cargo test -p buzz-dev-credential-helper: parsing and the item list, including what it refuses.
  • cargo test -p buzz-credential-store: tests/dev_helper.rs covers --item routing, the me release read through the helper, no fallback on a denied release read, add/replace/delete with Occupied, sessions, limits, timeouts and a retained socket. closed_helper.rs updated.
  • cargo test -p buzz-agent-controller: dev_credentials.rs updated.
  • cargo test -p Buzz --lib: 1060 pass. window_state::tests::onboarding_snapshot_is_kept_until_next_launch_restore segfaults on this branch even when run alone; its file is not in this diff, and I have not checked it on main.
  • node --test tests/integration/dev-commands.test.mjs: 56 pass. The helper environment and runner are set only for me on macOS; values from .env.local or the shell are ignored; setup failure falls back.
  • Real helper against the real login keychain, with throwaway session items only and no dialogs shown:
    • a caller signed with the dev cert: absent → 10, add → 0, add again → 13, read → value, replace → 0, delete → 0, read → 10;
    • an ad-hoc caller, a caller with the wrong identifier, a non-listed service, and stdout that isn't a socket all → 11;
    • about 17 ms per call with a 420 MB caller.

Not yet done: a human run of just desktop me (first launch approves; restart, rebuild and a second worktree show no dialogs).

baxen and others added 5 commits November 7, 2026 13:00
Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
…elper request

Replace the helper's human, agent-store and session-store modes with one
--item <operation> <service> <account> request, used by every
credential-store read, add, replace and delete in macOS debug builds when
BUZZ_DEV_CREDENTIAL_HELPER is set. The release-key read for `me`, Jev,
paired tools and external-app pairings now go through credential-store
instead of calling the Keychain directly.

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
On macOS, `me` installs the helper outside the repository once (again only
when this checkout needs a newer protocol), creates a local code-signing
certificate, records the requirement the helper checks, and starts the
debug build through a Cargo runner that signs it first.

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

Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@baxen
baxen marked this pull request as ready for review November 7, 2026 21:06
@baxen
baxen requested a review from a team as a code owner November 7, 2026 21:06
@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-07T21:10:01.197035Z a8016e2 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: a8016e259c

ℹ️ 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 thread scripts/dev-credential-helper.mjs Resolved

@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.

🤖 Two actionable findings: non-macOS lint failure and inherited Cargo runner configuration.

Comment thread scripts/dev-sign-run.sh Resolved
baxen and others added 2 commits November 7, 2026 14:20
…helpers

- Allow dead code only off macOS for the policy items the macOS adapter alone
  uses, so Linux clippy -D warnings passes.
- dev-sign-run.sh drops the Cargo runner variables and the signing identity
  before starting the app, so agents it starts can still `cargo run`.
- Install one helper per protocol (buzz-dev-credential-helper-v<N>), so
  worktrees on different protocols never replace each other's helper.

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

Linux lint: the policy module's macOS-only members are dead code there.
dev-sign-run.sh unsets the runner and signing variables before exec, so
processes the app starts don't inherit a runner that fails without them.
The release human key is no longer served by the helper (protocol 2):
`me` reads it directly, so it keeps the release app's Keychain approval.
Docs state the same-user trust boundary, including the shared agent store,
and how to revoke a protocol-1 helper's release-key approval.

Co-authored-by: Quinn <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
assert!(read_human().is_ok());
std::env::remove_var("BUZZ_DEV_CREDENTIAL_HELPER_AUTHORIZE");

// `just desktop me` reads the release key straight from the Keychain, never

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] Distinguish fallback tests from backend-routing coverage

The fake-reader unit test covers release_or_own fallback ordering, but not the production macOS selection of read_keychain at src/lib.rs:315–318. That selection is excluded from test builds, so changing it back to the helper-backed reader would leave these tests green while release-key sign-in fails with Denied. Reword this comment to describe fallback-only coverage, or add a testable backend selector.

Comment thread scripts/dev-credential-helper.mjs Outdated
if (installedProtocol(helper) !== needed)
throw new Error("the installed helper did not start");
console.log(
"Installed the Keychain helper. Click Always Allow on each Keychain dialog this launch; later launches ask only for your installed Buzz's key.",

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] Align the release-key prompt guidance

This message describes later launches, while docs/contributing.md:113 says they ask only once and the identity/help documentation describes a release-key prompt after each rebuild. These descriptions leave developers with conflicting expectations about repeated prompts. Align the guidance around the documented after-each-rebuild behavior; actual native prompt persistence remains unverified.

…ychain helper"

This reverts commit 3a40477.

Co-authored-by: Quinn <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
@baxen
baxen merged commit 8f8f260 into main Nov 7, 2026
42 checks passed
@baxen
baxen deleted the mbot/dev-credential-helper branch November 7, 2026 23:07
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.

3 participants