macOS SSH and Claude Code "Logged Out": The Keychain Unlock Fix
I recently had some long-running jobs running on the mac mini at home, so I started SSH’ing into it from my laptop to drive things — and as soon as I started Claude Code on the remote side, I got the /login prompt. The puzzling part: the night before I had logged into Claude Code from the mac mini’s local Terminal and was using it just fine. SSH’ing in and tmux attach-ing back to the original session occasionally hit the same “logged out” state too (less often than a fresh SSH, but it definitely happened). My first guess was a Claude Code bug, but a few rounds of testing showed that running security unlock-keychain once was enough to fix it. That makes it not a Claude Code problem but a property of the macOS login keychain — anything that stores tokens or credentials there can hit the same wall.
TL;DR — one command
After SSH’ing into the Mac, run the command below. It’ll prompt for your login password; once it succeeds, just restart Claude Code and it’ll work without forcing /login again.
# Unlock the current user's login keychain
security unlock-keychain ~/Library/Keychains/login.keychain-db
# The path is optional — login keychain is the default
security unlock-keychain
If you want to check the current state first, security show-keychain-info shows the idle auto-lock interval, and security list-keychains lists which keychains are currently mounted.
Why SSH triggers it
The macOS login keychain is an encrypted file — it stores all the passwords, tokens, and certificates that applications write into Keychain. Its encryption key is your account login password, which means it has to be unlocked before its contents can be read or written. The usual unlock moment: when you sit in front of the Mac and type your password into the login screen, loginwindow automatically uses that password to unlock the login keychain. After that, as long as you don’t log out and the auto-lock hasn’t fired, any process the same user starts can read keychain contents through the Keychain Services API.
The problem is that SSH doesn’t go through loginwindow. After sshd authenticates you (via PAM with a password or public key) it just hands you a shell — nothing in that path triggers a keychain unlock. So the SSH session has the right user identity, but when Claude Code tries to read its login token from the keychain, the keychain is locked and the read fails. The visible symptom is “please log in again.”
As for why tmux is occasionally affected: the tmux server is a long-running process, usually started the first time you opened a GUI Terminal — at that point the keychain was already unlocked, so any session attached afterwards inherits the unlocked state. But once the keychain locks at some point — idle longer than show-keychain-info’s timeout, manually locked, locked on sleep depending on settings, or the tmux server itself restarted — re-attaching from SSH behaves the same as a plain SSH session: the token can’t be read.
Why security unlock-keychain works
security is the built-in macOS CLI for talking to Keychain Services, and unlock-keychain unlocks the named keychain (defaulting to login keychain) using your password. The unlock is shared across processes — it’s not scoped to the current shell, but to the current user’s subsequent processes. So once we unlock from the SSH session, any Claude Code we start afterwards can read its token.
A few related commands worth knowing:
# Inspect auto-lock settings (idle seconds, lock-on-sleep)
security show-keychain-info ~/Library/Keychains/login.keychain-db
# Set idle auto-lock to 2 hours and disable lock-on-sleep
security set-keychain-settings -t 7200 ~/Library/Keychains/login.keychain-db
# Lock manually (the inverse)
security lock-keychain ~/Library/Keychains/login.keychain-db
# Pass the password inline with -p (lands in shell history — not recommended)
security unlock-keychain -p 'your-password' ~/Library/Keychains/login.keychain-db
The -p flag with a literal password is dangerous — it ends up in shell history and the process list. Stick to interactive entry, or wrap the command in a script that reads from a permission-protected file and pipes through stdin.
It’s not just Claude Code
Anything that stores tokens, passwords, or credentials in the login keychain can show the same symptom under SSH or in a tmux session whose keychain has been locked. Here’s a quick reference table — hopefully this helps people who hit the same problem with other tools find this post:
| Tool / scenario | Symptom when keychain is locked | Notes |
|---|---|---|
| Claude Code | /login prompt on launch, or 401 on agent API calls | Tokens stored in login keychain |
GitHub CLI (gh) | gh auth status shows not logged in, gh auth token returns nothing | When using --secure-storage or the macOS default |
| git + osxkeychain helper | HTTPS push/pull keeps prompting for password | git config --global credential.helper osxkeychain |
Docker Desktop / docker login | docker pull on private images returns 401 | Credentials store uses osxkeychain |
| AWS CLI / aws-vault | Profile credentials missing, MFA tokens unreadable | aws-vault uses keychain as the default backend |
| npm / yarn / pnpm | npm publish 401, registry auth tokens unreadable | Same applies to any Electron tool using keytar |
1Password CLI (op) | Session expired, biometric unlock unavailable | Biometric unlock requires GUI assistance |
| Xcode codesign / notarytool | Signing or notarization can’t find dev certs, errSecInternalComponent | Most common when CIs build over SSH |
| fastlane match | Certificate import fails or repeatedly prompts for passwords | Official docs recommend unlock-keychain first on CI |
| VS Code / Cursor Remote-SSH | GitHub, Copilot, sync state lost | The remote Electron can’t read the local keychain |
| Other Electron CLIs (keytar) | Various token failures | pnpm, Slack CLI, Firebase CLI, etc. |
Longer-term workarounds
Make a convenient alias
If you just want to type less, add this to ~/.zshrc and unlock manually whenever it bites:
alias unlock='security unlock-keychain ~/Library/Keychains/login.keychain-db'
Stretch the auto-lock timeout
If the mac mini is your own personal machine on a private network, lengthening the idle timeout or disabling lock-on-sleep cuts down on how often this fires. This trades security for convenience, so don’t do it on shared machines.
# -t seconds = idle timeout; omitting -l means don't lock on sleep
security set-keychain-settings -t 28800 ~/Library/Keychains/login.keychain-db
Keep the Mac in a GUI-logged-in state
If you’re using a Mac as a server, turn on auto-login in System Settings → Users — loginwindow will unlock the login keychain at boot, and every SSH session afterwards inherits the unlocked state. Screen lock and keychain lock are technically separate, but their idle timeouts and sleep settings interact, so combining auto-login with the set-keychain-settings command above tends to be the most stable setup.
Auto-unlock on SSH (be careful)
You can also put expect or a permission-protected file read into ~/.zprofile to auto-unlock. This is functionally equivalent to leaving your login password on disk — the risk is the same as whatever the keychain was supposed to protect. Realistically, only do this on tightly controlled single-user machines. The compromise I’ve used: store the password in 1Password on a different machine, then on SSH use op read to fetch it and pipe it into security unlock-keychain, so the password only ever lives in memory briefly.
Wrap-up
The one-liner to remember: the login keychain only auto-unlocks on GUI login, not on SSH. Any tool that stores tokens or credentials in macOS Keychain can show up as “logged out” or “credentials expired” inside an SSH session or a partially-affected tmux session. When it happens, run security unlock-keychain first; long-term, combine it with longer auto-lock intervals or auto-login to reduce the frequency. If you want to dig deeper into how tmux sessions actually work, take a look at our tmux multiplexer tutorial (繁體中文).