Skip to content

fix(credstore): stabilize macOS Keychain reads across rebuilds - #76

Merged
zzwong merged 2 commits into
mainfrom
fix/stable-keychain-reads
Sep 26, 2026
Merged

zzwong merged 2 commits into
mainfrom
fix/stable-keychain-reads

Conversation

@zzwong

@zzwong zzwong commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

macOS Keychain grants can be tied to the hash of an ad-hoc-signed CLI, so replacing a binary can prompt again for each stored credential.

Change

Read generic passwords through the fixed system-signed /usr/bin/security executable while preserving the existing service/account mapping. Keep native Keychain writes, metadata, listing, and deletion. No secrets are placed on command lines. Hermetic tests cover argument mapping, missing items, credential bytes, and the native-write boundary; the shared guidance is updated.

Verification

Check Result
GOTOOLCHAIN=go1.26.3 make check (tidy, lint, race tests, build) Pass
go test -tags keyring_no1password,keyring_nopassage ./credstore Pass
GitHub CI on this PR Lint and macOS/Linux/Windows build-test pass

Real macOS Keychain rebuild probe

On a local Mac, I created a disposable Keychain and one synthetic generic-password item under a unique service/account, with its ACL explicitly trusting /usr/bin/security. I put that Keychain first in the user search list, built two reader executables from this PR with different build markers, and used each executable to open the Keychain backend and call Store.Get for the same item. Neither execution prompted. The original search list was restored and the temporary Keychain deleted.

reader-one SHA-256 d229cb637f0e1a9ff240b9e3c87f367f0ba59628c5ec48ffd2a5ccfeb8781da1
reader-two SHA-256 148473f648bd455e144b1fb3526ac3d17ff3d24bad4672681045d9ebf5713335
reader-one designated requirement: cdhash H"22ac631e187fba1c8951f5beba2ca302e7833353"
reader-two designated requirement: cdhash H"d9b18b67e4297e515a99919270dc01e527e4bc63"
PASS: build one read expected synthetic credential
PASS: build two read expected synthetic credential
PASS: both distinct binaries read the same temporary Keychain item
PASS: temporary Keychain removed; original search list restored

This exercises the real macOS Keychain and the actual Store.Get path across two different binary identities. It does not test the interactive first-time Always Allow click or an installed CLI upgrade; those remain release verification steps. The local probe used synthetic data only.

Consumer verification boundary

The current codereview-cli release still embeds cli-common v0.4.1, so it does not contain this read path. Its signing identity alone is not evidence that every existing Keychain item grants access; a consumer upgrade must be tested separately.

Release follow-up

Build a consumer against this commit, grant Keychain access once through the normal user flow, rebuild or upgrade it, and verify a second read without a prompt. Coordinate consumer dependency bumps and releases after that check.

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

Automated PR Review

Reviewed commit: 8f62857b25bb
Profile: open-cli-collective-review - Posting as: monit-reviewer

Summary

Reviewer Findings
go:implementation-tests 0
policies:conventions 0
documentation:docs 1
documentation:docs (1 finding)

Minor - docs/working-with-secrets.md:54

Never ... use security -i for writes contradicts the repository's scripts/repair-macos-keychain-credentials.sh, which intentionally pipes generated input to security -i to repair existing Keychain items. Qualify this as a rule for normal CLI credential writes, and point readers needing ACL repair to the supported script; make the matching warning in docs/distribution.md consistent.

Reviewer Coverage

  • go:implementation-tests — complete (broad); inspected 3 assigned files (5 inspected across reviewers): credstore/osbackend_byteness.go, credstore/osbackend_keychain_stable_read.go, credstore/osbackend_keychain_stable_read_test.go; skipped: none; constraints: Review limited to assigned Go implementation and tests; read-only tooling could not execute macOS Keychain integration tests.
  • policies:conventions — complete (broad); inspected 4 assigned files (5 inspected across reviewers): credstore/osbackend_byteness.go, credstore/osbackend_keychain_stable_read.go, docs/distribution.md, docs/working-with-secrets.md; skipped: none; constraints: none
  • documentation:docs — complete (broad); inspected 2 assigned files (5 inspected across reviewers): docs/distribution.md, docs/working-with-secrets.md; skipped: none; constraints: none
Inspected files (5)
  • credstore/osbackend_byteness.go
  • credstore/osbackend_keychain_stable_read.go
  • credstore/osbackend_keychain_stable_read_test.go
  • docs/distribution.md
  • docs/working-with-secrets.md

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 1m 15s | $0.06 | openai-codex/gpt-5.6-terra | cr 0.10.316
Field Value
Model openai-codex/gpt-5.6-terra
Reviewers go:implementation-tests, policies:conventions, documentation:docs
Engine pi_rpc · openai-codex/gpt-5.6-terra
Reviewed by cr · monit-reviewer
Duration 1m 15s wall · 2m 13s compute
Cost $0.06
Tokens 15.7k in / 1.9k out

Per-workstream usage

  • orchestrator-selection — openai-codex/gpt-5.6-terra
    • In: 6.4k
    • Out: 463
    • Cache read: 0
    • Cache create: 0
    • Cost: $0.02
    • Duration: 10s
  • go:implementation-tests — openai-codex/gpt-5.6-terra
    • In: 2.9k
    • Out: 565
    • Cache read: 14.8k
    • Cache create: 0
    • Cost: $0.02
    • Duration: 53s
  • policies:conventions — openai-codex/gpt-5.6-terra
    • In: 821
    • Out: 107
    • Cache read: 12.8k
    • Cache create: 0
    • Cost: $0.01
    • Duration: 26s
  • documentation:docs — openai-codex/gpt-5.6-terra
    • In: 1.3k
    • Out: 679
    • Cache read: 8.7k
    • Cache create: 0
    • Cost: $0.01
    • Duration: 40s
  • orchestrator-rollup — openai-codex/gpt-5.6-terra
    • In: 4.3k
    • Out: 66
    • Cache read: 0
    • Cache create: 0
    • Cost: $0.01
    • Duration: 3s

Comment thread docs/working-with-secrets.md Resolved
@zzwong
zzwong marked this pull request as ready for review September 26, 2026 00:55
@zzwong
zzwong merged commit 86498ad into main Sep 26, 2026
4 checks passed
@zzwong
zzwong deleted the fix/stable-keychain-reads branch September 26, 2026 01:00
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