Skip to content

fix: stop repeated Keychain password prompts - #8

Merged
ChrisOsegueda merged 2 commits into
mainfrom
fix/keychain-password-prompts
Nov 2, 2026
Merged

ChrisOsegueda merged 2 commits into
mainfrom
fix/keychain-password-prompts

Conversation

@ChrisOsegueda

@ChrisOsegueda ChrisOsegueda commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Stops the repeated macOS password prompt for Claude Code-credentials. InferenceMeter now reads the credential through /usr/bin/security, the same way Claude Code reads it, instead of calling SecItemCopyMatching.

securityd logged the cause on an affected machine:

ACL partition mismatch: client cdhash:<hash> ACL ("apple-tool:")
asking user about XARA partition for 'cdhash:<hash>'

The app was already on the item's access list, but Claude Code writes the item with security, so its partition list holds only apple-tool:. A direct read fails that check and macOS prompts, so Always Allow did not stop it.

The read has a five-second timeout. The old call blocked a worker thread for as long as the dialog stayed open.

Related issue

No linked issue; reported directly by the owner.

Verification

  • DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer make test (not run locally: the Xcode license is unaccepted on the dev Mac; covered by the required Test and build check)
  • DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer make build (same)
  • git diff --check
  • UI changes include a screenshot or an explanation of why one is unavailable (no UI change)

Run with the Command Line Tools toolchain instead:

  • The existing 93 tests pass in a scratch SwiftPM package over this source.
  • The real ClaudeProvider and Keychain, compiled into a binary whose signature was on no access list, read the real item and returned state=ok from the usage endpoint, with no securityd prompt events and no access-list change.
  • A dummy login-keychain item rewritten three times with security -i add-generic-password -U -X (the Claude Code write path) stayed at partition apple-tool:, and each read returned the new value with no prompt. The item was then deleted.
  • A missing item maps to itemNotFound (exit 44). Fake tools that hang, ignore SIGTERM, or suspend themselves were each stopped after about 5.3s and threw unhandled(9), with no leftover process.
  • An installed build completed two scheduled refreshes (HTTP 200) with no prompt events. Its binary has no SecItem symbols.

Not yet observed: a real Claude Code token renewal against the new build. The dummy-item rewrite stands in for it.

Security and privacy

  • The read-only OAuth model is preserved
  • Fixtures and diagnostics are sanitized
  • No tokens, credentials, authorization headers, prompts, or user session content are included

The credential travels over a pipe and only the service and account are arguments; stderr is discarded. This removes the consent dialog for this read. The item already trusts /usr/bin/security, so any process running as the user could already read it this way, and the README now says so.

https://claude.ai/code/session_01EFLAijFNmWssUbxYQDQR9N

Read the Claude credential through /usr/bin/security instead of
SecItemCopyMatching. Claude Code writes the item with that tool, so its
partition list holds only "apple-tool:". A direct read fails that check and
macOS asks for the login password even when the app is already on the access
list, so "Always Allow" never stopped the prompts.

The read is bounded by a five-second timeout, so a stuck read cannot stall
the refresh loop the way an open dialog did.

Claude-Session: https://claude.ai/code/session_01EFLAijFNmWssUbxYQDQR9N
SIGTERM only bounded the read when the child honoured it. A child ignoring
SIGTERM kept stdout open and blocked the read past the five-second limit.

Claude-Session: https://claude.ai/code/session_01EFLAijFNmWssUbxYQDQR9N
@ChrisOsegueda

Copy link
Copy Markdown
Contributor Author

Review ledger

Pass Reviewer Commit Result
1 (full) Astra, gpt-6-astra, medium, read-only abec67b 1 finding

P2, InferenceMeter/Core/Keychain.swift: the timeout does not bound a child that survives SIGTERM. Accepted in part.

  • Refuted: the reported trigger, a suspended child. A child confirmed stopped with WIFSTOPPED exited within 1.5s of SIGTERM on macOS 27.2, and a self-suspending fake tool timed out normally at 5.3s.
  • Accepted: the underlying gap. A fake tool that ignores SIGTERM was still blocking the read when a 12s safety alarm fired, which breaks "a read cannot block indefinitely".
  • Fixed in a4d204e with SIGKILL. Hanging, SIGTERM-ignoring, and self-suspending fake tools all stop at about 5.3s with no leftover process. The real provider still returns state=ok with no prompt events.

https://claude.ai/code/session_01EFLAijFNmWssUbxYQDQR9N

@ChrisOsegueda

Copy link
Copy Markdown
Contributor Author

Review ledger, pass 2: focused repair check on a4d204e by Astra (gpt-6-astra, low, read-only) returned NO FINDINGS. The required Test and build check passed on a4d204e, which covers make test and make build.

https://claude.ai/code/session_01EFLAijFNmWssUbxYQDQR9N

@ChrisOsegueda
ChrisOsegueda merged commit 78a85d6 into main Nov 2, 2026
1 check passed
@ChrisOsegueda
ChrisOsegueda deleted the fix/keychain-password-prompts branch November 2, 2026 18:08
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.

1 participant