Repository navigation
feat(identity): support a local debug restore helper - #1517
Conversation
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (systems/lifecycle), Mantis (correctness/security/concurrency), and Groot (product/error/retry) reviews.
No concrete correctness or security defects requiring changes found in the four-file PR diff. Source assessment: 9/10 for minimalness, elegance and correctness.
Reviewed head: 595ef41
Base: 5c314d1
Merge base: 44f38de
Checked macOS/debug gating, helper exit and timeout handling, bounded zeroizing output, retained-socket rejection, native key validation and create-only persistence, restore/retry/sign-out callers, CLI signing, tests, and the public description. No attached images or publication-surface disclosure findings.
Optional documentation improvement: docs/identity.md could mention that a debug build of the plugin CLI also calls read_human through sign_release_saved (crates/plugin-manager/src/lib.rs:508). If it inherits BUZZ_DEV_CREDENTIAL_HELPER, a helper restricted to the app’s signature can reject CLI signing; unset the variable for that CLI invocation. This is optional advice, not a required repair.
Validation limits: source-only; no tests, builds, app/helper execution or credential access performed. The hosted head-check snapshot showed CI required and all executed checks successful; Windows and flake lanes were skipped. The added macOS integration test uses synthetic helpers, and hosted success does not establish real helper audit-token/signature enforcement, hardened-runtime setup, attended authorization, rebuild/restart behavior, or keyboard/focus acceptance. Those remain outside this review’s verification; the PR’s reported attended checks were not independently reproduced. No browser journeys were added or removed. This COMMENT is not approval or merge authorization.
Co-authored-by: Codex <[email protected]> Signed-off-by: Daniel Rouleau <[email protected]>
Co-authored-by: Codex <[email protected]> Signed-off-by: Daniel Rouleau <[email protected]>
595ef41 to
cd6bb55
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating Rocket, Mantis and Groot. One migration integration issue needs repair; see the inline comment.
Head cd6bb550569c344776abaf31070f38a764aed6ac; base f0d5f229e836912ed5c7e01a05311dc930d2bffe. Source-only: no tests, builds, helper/app execution or credential access; native authorization and restore behavior were not independently exercised. This is a comment review, not approval.
Co-authored-by: Codex <[email protected]> Signed-off-by: Daniel Rouleau <[email protected]>
…ted-setup * origin/main: (24 commits) feat(agents): share agents to the community catalog and export snapshots (#1649) feat(navigation): dialogs never become Back/Forward destinations (#1696) fix(builderlab): sign in when finishing a hosted agent's setup (#1768) fix(messages): link #name prose to open channels the directory has named (#1706) feat(agents): keep the Mac awake while agents work (#1718) perf(panels): skip layout for covered workspace tabs (#1542) feat(composer): offer model and effort in a DM with an agent without a mention (#1621) test(workflows): wait for dialog initial focus before pasting YAML in WorkflowEditor test (#1705) fix(builderlab): show all captured activity without a conversation toggle (#1764) feat(agents): add a UI for Attention watches (#1663) Keep thread reply branches one level deep (#1751) fix(messages): keep table scrollbars inside webkit message rows (#1677) feat(agents): sync agent config through config channels (#1226) fix(agents): offer a new hosted agent in Solo chats right away (#1759) test(usage): make UsagePage read-window test DST-safe (#1668) fix(panels): scope split motion selectors to layout owners (#1681) feat(agents): simplify imports and clarify community scope (#1660) fix(pi): stop posting replies the agent did not send (#1758) feat(identity): support a local debug restore helper (#1517) test(perf): cover rich thread open close and reopen (#1683) ... Signed-off-by: Riley Crane <[email protected]>
…into rileycrane/bestie-hourly-wake * origin/rileycrane/bestie-hosted-setup: (25 commits) Give Bestie's test fixture the native keepAwake that main now requires feat(agents): share agents to the community catalog and export snapshots (#1649) feat(navigation): dialogs never become Back/Forward destinations (#1696) fix(builderlab): sign in when finishing a hosted agent's setup (#1768) fix(messages): link #name prose to open channels the directory has named (#1706) feat(agents): keep the Mac awake while agents work (#1718) perf(panels): skip layout for covered workspace tabs (#1542) feat(composer): offer model and effort in a DM with an agent without a mention (#1621) test(workflows): wait for dialog initial focus before pasting YAML in WorkflowEditor test (#1705) fix(builderlab): show all captured activity without a conversation toggle (#1764) feat(agents): add a UI for Attention watches (#1663) Keep thread reply branches one level deep (#1751) fix(messages): keep table scrollbars inside webkit message rows (#1677) feat(agents): sync agent config through config channels (#1226) fix(agents): offer a new hosted agent in Solo chats right away (#1759) test(usage): make UsagePage read-window test DST-safe (#1668) fix(panels): scope split motion selectors to layout owners (#1681) feat(agents): simplify imports and clarify community scope (#1660) fix(pi): stop posting replies the agent did not send (#1758) feat(identity): support a local debug restore helper (#1517) ... Signed-off-by: Riley Crane <[email protected]>
…#3002) ## 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 #1517 / #2174 / #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). --------- Signed-off-by: Bradley Axen <[email protected]> Signed-off-by: Tyler Longwell <[email protected]> Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <[email protected]> Co-authored-by: Quinn <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz>
Why
Local macOS debug builds can need a new Keychain approval after each rebuild. A fixed local helper can restore the saved human identity across builds.
What
Add an opt-in
BUZZ_DEV_CREDENTIAL_HELPERhook for human restore on macOS with Rust debug assertions. Release builds ignore it.How
Send the key through a fresh Unix socket into zeroizing memory. Bound the response and execution time. Keep helper failures distinct. The existing identity owner still validates the nsec. Normal reads and other credential operations keep their current behavior. Helper setup and signing keys stay outside this repository. See the helper contract.
Risk
The opt-in helper is trusted local code. It must verify the recipient audit token and live signature before it sends a key. The certificate check trusts access to the signing key. Another program with that access and the app identifier can satisfy the same requirement. This PR supplies the app hook, not helper provisioning.
Testing
A native probe linked to this crate restores the expected identity through the installed helper on restart, after a rebuild, and after a move. Ad hoc callers, pipe recipients, forged parents, and sockets created before exec receive no key. Hardened runtime blocks dylib injection. A release probe ignores the hook. Normal debug reads complete with the hook absent and Keychain dialogs disabled. The installed helper stays unchanged.
Attended macOS checks pass on
60517378b. The helper transport stays unchanged after the rebase onto9f60c3d1b. The hook runs before the normal service migration inread_human(). Focused checks cover this entry point oncd6bb5505. The full native app is not rebuilt after this rebase. The expected identity and community return without a Keychain prompt after restart and a native rebuild. The rebuilt window has a changed title. The signed binary hash changes and the fixed helper hash stays unchanged. The independent security review and clean-commit agent review find no blockers.buzz-review-completed
Generated with Codex