Skip to content

feat(first-boot): keep login keychain unlocked on headless targets#30

Merged
smartwatermelon merged 1 commit intomainfrom
claude/keychain-no-timeout-20260413-1534
Apr 13, 2026
Merged

feat(first-boot): keep login keychain unlocked on headless targets#30
smartwatermelon merged 1 commit intomainfrom
claude/keychain-no-timeout-20260413-1534

Conversation

@smartwatermelon
Copy link
Copy Markdown
Owner

Summary

Follow-up to #27. Mac Mini dev servers can't use GUI auto-login (iCloud + TouchID accounts disallow it), so the login keychain starts locked every reboot. Without the idle-timeout setting, even after a manual unlock it would re-lock during idle and break `claude-wrapper`'s Keychain-backed token fetch.

Adds `security set-keychain-settings -l -u login.keychain-db` to the end of `import_external_keychain_credentials` so once the admin unlocks the keychain (console login or manual `security unlock-keychain`), it stays unlocked until sleep.

How this was discovered

During Phase 4 verification on MIMOLETTE after #27 merged, `claude-wrapper` appeared broken with `op: failed to DecodeSACredentials: unexpected end of JSON input`. Root cause was the login keychain being locked — `security find-generic-password` silently returned empty.

Test plan

  • `shellcheck -S info scripts/server/first-boot.sh` clean
  • Verified manually on MIMOLETTE (`security set-keychain-settings -l -u ...` → `show-keychain-info` reports "lock-on-sleep no-timeout")
  • Next fresh provisioning run: confirm the setting is applied and persists across reboot

🤖 Generated with Claude Code

Mac Mini dev servers can't use GUI auto-login (iCloud + TouchID accounts
disallow it), so the login keychain starts locked every reboot. Without
this setting, once unlocked it would re-lock after idle timeout, which
breaks claude-wrapper's Keychain-backed service account token fetch.

Set lock-on-sleep + no idle timeout so that once the admin unlocks the
keychain (console login or manual unlock), it stays unlocked until the
next sleep — matching typical headless build-server operation.

Discovered during Phase 4 verification on MIMOLETTE: claude-wrapper
appeared broken (op: failed to DecodeSACredentials) until the login
keychain was manually unlocked.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 13, 2026

PR Review - feat(first-boot): keep login keychain unlocked on headless targets

Scope: Single block added to import_external_keychain_credentials() in scripts/server/first-boot.sh (lines 485-495).


Summary

Clean, focused fix for a real operational problem (idle keychain re-locking breaking claude-wrapper). No blocking issues found.


Observations

-u flag semantics are non-obvious (maintenance note)

security set-keychain-settings -l -u: the -u flag in the security(1) man page is documented as 'lock keychain after timeout interval', which reads as enabling idle lock rather than disabling it. The intended behavior (no idle timeout) is actually achieved by omitting -t, so -u without -t effectively sets the lock-after-timeout mechanism with no timeout value, producing the 'no-timeout' result seen in show-keychain-info. The comment correctly describes the outcome but could confuse a future reader who looks up the flag. A brief inline note tying -u to the 'no -t means no timeout' behavior would help.

No functional concern. The manual verification on MIMOLETTE confirms the correct setting is applied.

Placement is appropriate

The block sits after unset op_service_token (line 480), so no credential is live in memory when the security call runs. Placing keychain configuration at the end of import_external_keychain_credentials is logical.

Error handling is appropriate

collect_warning on failure is correct. This is a configuration enhancement, not a critical gate. first-boot.sh continues normally if the call fails, which is the right tradeoff for a non-essential setting.

Idempotency

security set-keychain-settings is idempotent; repeated provisioning runs are safe.

Security tradeoff is documented

Removing idle timeout is a deliberate reduction in keychain security for operability on a headless build server. The comment explains why (no GUI auto-login, iCloud/TouchID accounts) and limits scope (lock-on-sleep is preserved). Adequate.


VERDICT: PASS

@smartwatermelon smartwatermelon merged commit 39bf05b into main Apr 13, 2026
1 check passed
@smartwatermelon smartwatermelon deleted the claude/keychain-no-timeout-20260413-1534 branch April 13, 2026 22:40
smartwatermelon pushed a commit that referenced this pull request Apr 13, 2026
- prep-airdrop.sh: only echo "Provisioning..." when token is present
  (previously printed before the nil-check, creating misleading output
  when the dev Keychain lacks the token) — closes #28
- first-boot.sh: clarify -u flag semantics for security set-keychain-settings
  — the no-timeout behavior comes from omitting -t, not from -u alone —
  closes #31
- CLAUDE.md: note that opp is a local shell function from dotfiles,
  not a standard macOS/Homebrew tool — closes #29

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
smartwatermelon added a commit that referenced this pull request Apr 13, 2026
- prep-airdrop.sh: only echo "Provisioning..." when token is present
  (previously printed before the nil-check, creating misleading output
  when the dev Keychain lacks the token) — closes #28
- first-boot.sh: clarify -u flag semantics for security set-keychain-settings
  — the no-timeout behavior comes from omitting -t, not from -u alone —
  closes #31
- CLAUDE.md: note that opp is a local shell function from dotfiles,
  not a standard macOS/Homebrew tool — closes #29

Co-authored-by: Claude Code Bot <claude-code@smartwatermelon.github>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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