Skip to content

Route debug sessions through the credential helper - #2541

Merged
dr0uleau merged 6 commits into
mainfrom
drouleau/debug-session-helper
Nov 5, 2026
Merged

dr0uleau merged 6 commits into
mainfrom
drouleau/debug-session-helper

Conversation

@dr0uleau

@dr0uleau dr0uleau commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Why

A macOS debug app can restore its human key through a credential helper, but its saved organization session still calls Keychain directly. This can open another access prompt in a new test worktree.

What

Route debug session reads, replacements, and deletions through the selected helper. Preserve exact account checks, locks, and failure handling.

How

Use the existing socket protocol with a separate --session-store operation and a 1 MiB limit. Wait for socket readiness under the same deadline, including after the helper closes. Reconcile uncertain writes. Keep a candidate session durably refused until its write succeeds. An unsupported helper stops the operation without a direct Keychain retry.

Risk

This changes only macOS debug builds with a selected helper. The helper must support the session protocol and admit the exact signed app. Existing item access controls stay intact.

Testing

Two signed macOS debug apps from independent worktrees use the same helper, signer, and saved profile. Both restore the human identity and organization session with approval mode off. The second app also passes restart and a bounded signed read against the real relay. The human confirms that the tested app restores access after quit and relaunch without another dialog.

buzz-review-completed

Generated with Codex

dr0uleau and others added 3 commits November 4, 2026 16:17
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]>
@dr0uleau
dr0uleau requested review from gilgtm and kazemek21 November 5, 2026 00:40
@dr0uleau
dr0uleau marked this pull request as ready for review November 5, 2026 00:43
@dr0uleau
dr0uleau requested a review from a team as a code owner November 5, 2026 00:43
dr0uleau and others added 3 commits November 4, 2026 16:47
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]>

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

Approving. Kyle reviewed this PR alongside the Nexus review-tracker notes.

— Posted by Claude Code (claude-opus-5-5) on behalf of kylez

@dr0uleau
dr0uleau merged commit 406b4f9 into main Nov 5, 2026
40 checks passed
@dr0uleau
dr0uleau deleted the drouleau/debug-session-helper branch November 5, 2026 16:46
johnmatthewtennant added a commit that referenced this pull request Nov 5, 2026
Signed-off-by: John Tennant <[email protected]>

* origin/main: (47 commits)
  fix(messages): pass verified labels to the forward recipient picker (#2633)
  feat(messages): forward a message to a person (#2167)
  docs(agents): design relay-facing paths for the whole fleet at once (#2620)
  Route debug sessions through the credential helper (#2541)
  perf(search): skip no-op slow-search resets on each keystroke (#2605)
  perf(agents): dedupe channel members only for dm connection setup (#2603)
  perf(search): keep the search tab row across palette keystrokes (#2588)
  perf(sidebar): skip the hidden actions menu in solo and team headers (#2582)
  perf(search): rank each palette name once per render (#2606)
  perf(sidebar): skip re-rendering unchanged animated channel rows (#2572)
  feat(settings): follow the system clock and let people hide message times (#2440)
  feat(plugins): repository access rules for Git imports (#2484)
  feat(desktop): add Settings to the macOS app menu (#2594)
  Preserve and renew external app approval (#2464)
  fix(direct-messages): show verified identity in new message recipient search (#2585)
  Offer plugin updates from the Buzz shell (#2491)
  fix(messages): keep decoded images across virtualized remounts (#2474)
  Keep /leave commands out of channel history (#2476)
  fix(agents): ask before switching or closing drops unsaved edits (#2503)
  fix(agents): keep the Model settings row full width (#2577)
  ...
wpfleger96 added a commit that referenced this pull request Nov 5, 2026
…-service

* origin/main:
  fix(messages): pass verified labels to the forward recipient picker (#2633)
  feat(messages): forward a message to a person (#2167)
  docs(agents): design relay-facing paths for the whole fleet at once (#2620)
  Route debug sessions through the credential helper (#2541)
  perf(search): skip no-op slow-search resets on each keystroke (#2605)
  perf(agents): dedupe channel members only for dm connection setup (#2603)
  perf(search): keep the search tab row across palette keystrokes (#2588)
  perf(sidebar): skip the hidden actions menu in solo and team headers (#2582)
  perf(search): rank each palette name once per render (#2606)
  perf(sidebar): skip re-rendering unchanged animated channel rows (#2572)
cheng-block added a commit to cheng-block/buzz-app that referenced this pull request Nov 5, 2026
…ettings-shift-esc

* upstream/main: (40 commits)
  feat(conversation): let plugin rows manage messages without visiting a channel (block#2512)
  feat(github): make side-panel link opening optional (block#2629)
  feat(messages): show a blurred thumbnail until an image loads (block#2556)
  fix(relay): give large roster and read-state reads a 30s deadline (block#2627)
  fix(user-status): show a status emoji's :name: on hover where the full status shows (block#2384)
  fix(inbox): keep the list's place when returning in the same session (block#2134)
  fix(messages): pass verified labels to the forward recipient picker (block#2633)
  feat(messages): forward a message to a person (block#2167)
  docs(agents): design relay-facing paths for the whole fleet at once (block#2620)
  Route debug sessions through the credential helper (block#2541)
  perf(search): skip no-op slow-search resets on each keystroke (block#2605)
  perf(agents): dedupe channel members only for dm connection setup (block#2603)
  perf(search): keep the search tab row across palette keystrokes (block#2588)
  perf(sidebar): skip the hidden actions menu in solo and team headers (block#2582)
  perf(search): rank each palette name once per render (block#2606)
  perf(sidebar): skip re-rendering unchanged animated channel rows (block#2572)
  feat(settings): follow the system clock and let people hide message times (block#2440)
  feat(plugins): repository access rules for Git imports (block#2484)
  feat(desktop): add Settings to the macOS app menu (block#2594)
  Preserve and renew external app approval (block#2464)
  ...

Signed-off-by: Cheng Peng <[email protected]>
Co-authored-by: Cursor <[email protected]>
johnmatthewtennant added a commit that referenced this pull request Nov 5, 2026
…thumbnails

* origin/main: (120 commits)
  feat(conversation): let plugin rows manage messages without visiting a channel (#2512)
  feat(github): make side-panel link opening optional (#2629)
  feat(messages): show a blurred thumbnail until an image loads (#2556)
  fix(relay): give large roster and read-state reads a 30s deadline (#2627)
  fix(user-status): show a status emoji's :name: on hover where the full status shows (#2384)
  fix(inbox): keep the list's place when returning in the same session (#2134)
  fix(messages): pass verified labels to the forward recipient picker (#2633)
  feat(messages): forward a message to a person (#2167)
  docs(agents): design relay-facing paths for the whole fleet at once (#2620)
  Route debug sessions through the credential helper (#2541)
  perf(search): skip no-op slow-search resets on each keystroke (#2605)
  perf(agents): dedupe channel members only for dm connection setup (#2603)
  perf(search): keep the search tab row across palette keystrokes (#2588)
  perf(sidebar): skip the hidden actions menu in solo and team headers (#2582)
  perf(search): rank each palette name once per render (#2606)
  perf(sidebar): skip re-rendering unchanged animated channel rows (#2572)
  feat(settings): follow the system clock and let people hide message times (#2440)
  feat(plugins): repository access rules for Git imports (#2484)
  feat(desktop): add Settings to the macOS app menu (#2594)
  Preserve and renew external app approval (#2464)
  ...

Signed-off-by: John Tennant <[email protected]>

# Conflicts:
#	src-tauri/src/relay.rs
baxen added a commit that referenced this pull request Nov 7, 2026
…#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>
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.

2 participants