Reference

keychain(1)

Manager for ssh-agent and private keys, with native GnuPG support

NAME

keychain - Manager for ssh-agent and private keys, with native GnuPG support

SYNOPSIS

keychain [global-options] [action] [action-options] [keys...]

DESCRIPTION

Keychain orchestrates ssh-agent and gives you one coordinated, long-running SSH agent per user and host. It is a frontend to ssh-agent and ssh-add that lets you keep one long-running ssh-agent process per user per host -- rather than the default of one per login session -- so you only need to enter each key's passphrase once per boot.

The Keychain difference:

┌─────────────────────────────────────────────────────────────┐
│  Without Keychain:                                          │
│                                                             │
│  Terminal 1 → ssh-agent (prompt)                            │
│  Terminal 2 → ssh-agent (prompt again)                      │
│  Terminal 3 → ssh-agent (prompt again)                      │
│  Cron job → no agent (fails)                                │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  With Keychain:                                             │
│                                                             │
│  Terminal 1 ─┐                                              │
│  Terminal 2 ─┼→ single ssh-agent (prompt once)              │
│  Terminal 3 ─┤    ↓                                         │
│  Cron job ───┘    all terminals reconnect automatically     │
└─────────────────────────────────────────────────────────────┘

This dramatically reduces the number of times you need to enter your passphrase. With keychain, you only need to enter a passphrase once every time your local machine is rebooted. Keychain also makes it easy for remote cron jobs to securely "hook in" to a long running ssh-agent process, allowing your scripts to take advantage of key-based logins.

Keychain also supports the ability to auto-warm GnuPG signing and encryption keys so they are ready for use, and allows you to easily wipe cached GnuPG secrets using the keychain wipe --gpg command. GnuPG features require GnuPG 2.1 or later. When a GPG key is requested, Keychain asks GnuPG to prove the requested signing or decryption capability so any required passphrase is cached in memory. While Keychain manages the lifecycle of ssh-agent for you, GnuPG retains complete ownership of gpg-agent startup, configuration, and lifecycle.

Keychain 3.0.0_beta2+ introduces coordinated multi-terminal initialization, eliminating the could not acquire lock errors that occurred when multiple shells started simultaneously. When Visual Studio Code reconnects to WSL and restores several terminals at once, all terminals now coordinate automatically -- press Enter in any one terminal to initialize keys, and all others are notified when complete. See keychain man topic:coordination.

INTRODUCTION

Keychain 3, Requirements and Backward Compatibility (topic:requirements)

Keychain supports most UNIX-like operating systems and intentionally limits its scope to modern, widely deployed implementations of OpenSSH and GnuPG.

From shell script to Python zipapp. Keychain was conceived as a single POSIX Bourne shell script that grew steadily more sophisticated since its inception in 2002. In 2026, Keychain 3 moved to a new, portable technology: a single Python zipapp -- a single executable .pyz file that runs on any system with Python 3.9 or newer, without requiring external Python dependencies. This provides a modern runtime while maintaining the self-contained, script-based nature of Keychain.

Action-driven interface. Keychain 3 introduces a new action-driven interface (keychain add id_rsa, keychain agent stop --mine, keychain wipe --ssh, etc.) and is now the recommended way to drive Keychain. A full embedded documentation engine provides help via --help <action> or --explain for context-sensitive documentation. The complete man page is available via keychain man, with keychain man --list showing all available sections.

Backward compatibility. Maintaining keychain 2.x CLI compatibility was a primary goal. The legacy flat-flag command line (for example keychain --eval --quiet id_rsa) continues to work exactly as before via an explicit compatibility shim.

Shell support. Keychain 3 continues to be compatible with Bourne sh, csh/tcsh and fish shells. bash, ksh and zsh are compatible with the Bourne sh built-in to Keychain. Keychain also supports integration with systemd.

General Usage (topic:usage)

Typically, you configure Keychain to run from your shell startup files. Use the shell-specific recipes in keychain man topic:env so interactive terminals can load keys while non-interactive login shells establish the agent environment without prompting. To load a key from an interactive Bourne-compatible shell, run:

eval "$(keychain add id_rsa --eval)"

Keychain will start ssh-agent if one isn't already running, then checks to make sure your private keys (in this example, id_rsa) are loaded into the agent. If they are not, you are prompted for any passphrase necessary to decrypt them, so that they are cached in memory and available for use.

In addition to printing user-friendly output to your terminal, keychain outputs important ssh-agent environment variables. Command substitution captures this output, and the shell built-in eval evaluates it, causing these variables to persist in your current session.

These ssh-agent environment variables are also written to ~/.keychain/${HOSTNAME}-sh, so that subsequent logins and non-interactive shells such as cron jobs can source this file to access the running ssh-agent and make passwordless ssh connections using the cached private keys -- even when you are logged out. These files are collectively called pidfiles.

Multi-terminal coordination (3.0.0_beta2+). When multiple shells start simultaneously, all terminals coordinate automatically. Each displays Press Enter to run ssh-add in this terminal. Pressing Enter in any one terminal runs ssh-add; other terminals wait and are notified when complete. This eliminates the could not acquire lock errors from earlier versions. See keychain man topic:coordination.

The key files specified on the command-line will be searched for in the ~/.ssh/ directory, and keychain will expect to find the private key file with the same name, as well as a .pub public key. Keychain will also see if any GnuPG keys are specified and, if so, ask GnuPG to exercise the requested operation so any required passphrase is prompted for and cached.

Typically, private SSH key files are specified by filename only, without path, although it is possible to specify an absolute or relative path to the private key file as well. Private key files can be symlinks to the actual key. PKCS#11-backed SSH tokens can be requested explicitly via pkcs11:/path/to/provider.so; Keychain will compare the token's exported public keys against the current agent and, when needed, load the provider via ssh-add -s.

For GPG keys, Keychain exercises the requested capability so gpg-agent has the required passphrase cached and ready for use. Use gpgs:KEYID for signing-oriented workflows, gpge:KEYID for decrypt-oriented workflows such as pass, and gpga:KEYID when both capabilities should be warmed. The gpge: prefix performs an explicit encrypt-then-decrypt verification cycle -- a tiny temporary payload is encrypted to the key and immediately decrypted through gpg-agent. If this verification cannot be completed, add fails rather than reporting success. This is more reliable than relying on signing warm-up side effects, which may not populate the decryption passphrase cache on all GnuPG versions and key configurations. The legacy gpgk:KEYID alias is equivalent to gpgs:KEYID (signing warm-up only).

Coordinated key initialization (3.0.0_beta2+) (topic:coordination)

The problem. When multiple terminal shells start simultaneously, each shell may run keychain add --eval to initialize SSH keys. In Keychain 2.x and 3.0.0_beta1 and earlier, all terminals would race to acquire a single lock. One terminal would win and prompt for the passphrase; all others would wait for the lock timeout (default 5 seconds) and then fail with could not acquire lock. Users had to manually delete the lockfile or retry.

This was especially problematic in Visual Studio Code: only one terminal is visible at a time, so the terminal running ssh-add might be hidden behind other terminal tabs. Users had to click through each terminal tab to find the one prompting for the passphrase, turning what should be a simple workflow into a frustrating scavenger hunt.

The solution (3.0.0_beta2+). Keychain now uses a coordinated unlock protocol designed specifically for multi-terminal scenarios:

1.

State lock (short-lived). A brief lock protects agent setup and coordination updates, including waiter registration. Agent queries and passphrase entry run outside this lock, so a slow or unanswered prompt does not block those updates.

2.

Loading and clearing. Each loading attempt has its own lock. Loading attempts share the activation lock, while SSH clearing requires exclusive access. This permits independent immediate prompts without allowing wipe to clear the agent during loading.

3.

FIFO notification. Waiting terminals create private named pipes (FIFOs) and sleep until keyboard input, a notification, or the end of a loading operation wakes them. A loading terminal also listens for cancellation and other terminals' results; it checks the agent before canceling its own redundant prompt.

4.

Moving the request. Pressing Enter in a waiting terminal asks existing loaders to cancel their requests. Keychain waits for those loading children to exit before starting a replacement and displays a short notice. The canceled terminals wait for the replacement operation.

User experience. All terminals that detect missing SSH keys display:

 ▸ Press Enter to run ssh-add in this terminal

Pressing Enter in any one terminal starts ssh-add. Other terminals show:

 ▸ Press Enter to move the passphrase request to this terminal

When initialization completes, waiting terminals wake automatically and print:

Keys initialized by another terminal.

Automatic activation. --immediate skips Keychain's initial Enter prompt. Several terminals can run ssh-add at the same time, but you only need to complete a prompt that loads your requested keys. Other invocations check the actual agent after completion notifications and cancel their own redundant prompts. A success message for another terminal's different key request is not enough: each invocation checks its own keys. Failed attempts do not cancel useful prompts elsewhere. The normal Enter prompt remains the default so users can choose where passphrase entry begins.

Implementation details. The latest completion record is stored in ~/.keychain/<host>.state.json; it is not proof that an operation is alive or that keys are loaded. Per-attempt locks, lifetime and cancellation FIFOs, and notification FIFOs live in ~/.keychain/<host>-waiters/. The shared loading/exclusive clearing lock is ~/.keychain/<host>.activation.lock. Runtime files are private to the current user and are checked before use.

Scope. The coordinated unlock flow applies to SSH key loading only. GPG keys use explicit warm-up paths (gpgs:, gpge:, gpga:) and do not participate in multi-terminal coordination.

Legacy behavior. Users upgrading from Keychain 2.x or 3.0.0_beta1 will notice the absence of Waiting N seconds for lock... messages during interactive key loading. This is intentional: internal coordination is quiet, and the state lock is held only briefly for metadata updates.

GLOBAL OPTIONS

--help, -h

Print the top-level help cheat sheet, or action-specific help when followed by an action path. -h is an alias.

--version, -V

Print the version banner and exit. -V is an alias.

--explain

This global instructs keychain to not actually act upon the the command-line but instead evaluate the command-line arguments (minus the --explain option) and print documentation boxes for the action, every recognised option and positional argument to standard output. Essentially, this tells Keychain to explain the command invocation it would have performed to you, by printing all relevant documentation snippets.

--allow-env, -E

By default, Keychain silently ignores all KEYCHAIN_* environment variables to prevent accidental or malicious injection in multi-user environments. Pass -E or --allow-env to allow $KEYCHAIN_CONFIG to override the config file path and $KEYCHAIN_SSH_AGENT_ARGS to inject extra flags into a spawned ssh-agent.

This flag is intended for trusted, single-user environments or CI pipelines where the shell environment is controlled. It is not needed for normal interactive use; all KEYCHAIN_* behavior can be achieved via ~/.keychainrc configuration.

--quiet, -q

Silence routine Keychain messages and successful ssh-add reports. Required interactive prompts, explicit results from actions such as list, warnings, and errors are still printed.

--debug, -D

Emit extra details about agent-discovery decision keychain makes: which pidfile candidates were considered, which sockets were probed, why one was selected, what ssh -G returned for each host: extkey, etc.

--debug-log

Use --debug-log FILE to collect Keychain diagnostics during shell or graphical-login startup without redirecting terminal output. The file receives diagnostic details even without --debug, including routine messages hidden by --quiet. Normal console output and the shell assignments produced by --eval are unchanged.

Records are plain text and include a UTC timestamp, process ID, and terminal name (none when unavailable). Existing records are preserved. The parent directory must already exist; the log must be a regular file owned by you, not a symlink or hard link. Keychain creates the file with private permissions and restricts an existing file to owner read/write access.

eval "$(keychain add --eval --debug-log ~/.keychain/debug.log id_ed25519)"

For shell startup, add --debug-log FILE to both branches of the Bash or fish recipe in keychain man topic:env, retaining --noask in the non-interactive branch.

The log records Keychain's decisions, not terminal input, passphrases, or private-key contents. It does not capture prompts that ssh-add writes directly to its terminal. Review paths and configuration details before sharing a log in a bug report. Remove the option when troubleshooting is complete; Keychain does not rotate the log.

--nocolor, --no-color

Disable ANSI color output. Useful when piping to a tool that doesn't strip escape codes, or in log capture where color bytes muddy the file. The conventional $NO_COLOR environment variable (https://no-color.org) is also honored; the flag wins when both are set. --no-color is an accepted alias.

--theme

Select the color / glyph theme. legacy is the keychain 2.x output style; modern is the 3.0 default. A once-forever lifestyle preference; usually set in ~/.keychainrc.

--no-gui, --nogui

Don't use GUI passphrase prompts (DISPLAY / SSH_ASKPASS). Force a TTY prompt instead. A once-forever lifestyle preference for headless or screen-shared environments.

--absolute

Treat --dir (or [paths] dir) as the literal pidfile directory; don't append /.keychain.

--dir

Override the per-user state directory. By default keychain uses ~/.keychain/ for pidfiles, env-files, and lock files. --absolute treats the value verbatim instead of appending /.keychain.

--host, --hostname

--host HOSTNAME (or the more explicit --hostname HOSTNAME) overrides the hostname used in pidfile names. Pidfiles are named HOST-sh / HOST-csh / HOST-fish etc. so multiple hosts sharing a homedir over NFS each get their own state. By default keychain probes hostname(1), $HOSTNAME, then uname -n.

ACTIONS

keychain add

Syntax: keychain add [OPTIONS] [KEYS...]

add is the workhorse of Keychain 3 -- in Keychain 2.x, this was the implied action when no other action was specified. It tells Keychain to first ensure that an ssh-agent is currently running (equivalent to an automatic keychain agent start). If not, one will be started. Then, for each KEY argument:

•

For each SSH key, do nothing if ssh-agent already holds it; otherwise, prompt for its passphrase and load it.

•

For each GPG key, ask GnuPG to prove the requested signing or decryption capability. GnuPG owns any gpg-agent and pinentry interaction.

•

Write the SSH agent's environment to a per-user, per-host pidfile under ~/.keychain/ that scripts can source to gain access to the agent.

Coordinated multi-terminal initialization (3.0.0_beta2+). When multiple shells start simultaneously, Keychain 3.0.0_beta2+ uses a coordinated unlock protocol instead of the classic lock-timeout approach:

1.

All terminals that detect missing SSH keys display: Press Enter to run ssh-add in this terminal.

2.

Pressing Enter in any terminal designates it to run ssh-add.

3.

Other terminals wait automatically and are notified when initialization completes.

4.

When notified, waiting terminals print Keys initialized by another terminal. and configure their environment without prompting.

Moving a passphrase request. If the terminal that started ssh-add becomes inaccessible, press Enter at Keychain's waiting prompt in your visible terminal. Keychain cancels the existing request before prompting there and prints a short notice. The original terminal waits for the replacement operation to finish.

Transitioning from classic lock behavior. In earlier versions, simultaneous terminal startups would race for a lock, with losers timing out after 5 seconds (or the --lockwait value). The new coordination model is fundamentally different: terminals cooperate rather than compete, and all terminals succeed together once any one completes the passphrase entry.

Bare key names are looked up under ~/.ssh/ and as GPG key IDs; absolute paths are loaded directly. Symbolic links are supported. Extended-key syntax -- sshk:PATH, gpgk:KEYID, gpgs:KEYID, gpge:KEYID, gpga:KEYID, pkcs11:PATH, host:HOSTNAME -- is automatically detected and can be used to explicitly specify the lookup method.

When one or more KEY arguments are supplied and none of them resolve to a real SSH key or known GPG identity, add exits before starting or attaching to an ssh-agent. This is an intentional compatibility accommodation: keychain 2.x bare-token invocation is still accepted, but a mistyped modern action should not accidentally spawn a fresh SSH agent as a side effect. GnuPG may perform its normal gpg-agent autostart while resolving a bare token against local GPG key material. A token unknown to the local GPG keyring does not normally trigger agent autostart.

Note: The coordinated unlock flow applies to SSH key loading only. Due to differences in how GPG operates, GPG keys use Keychain's explicit GnuPG warm-up paths (gpgs:, gpge:, gpga:) and do not participate in the multi-terminal coordination protocol.

See also: topic:extkeys, topic:coordination.

keychain agent

Syntax: keychain agent <start|stop> [OPTIONS]

Domain command for managing agent processes without loading keys. agent start ensures an agent exists and can emit its environment; agent stop terminates agents recorded in the pidfile.

keychain agent start

Syntax: keychain agent start [OPTIONS]

Start an ssh-agent if none is running, then optionally emit its environment for the current shell. By default, Keychain pins a spawned ssh-agent to a host-specific socket path under ~/.keychain/ so the agent endpoint stays stable across WSL and other early startup environments that may clean /tmp. This action does not load any keys; use add when you want prompt-and-load semantics.

keychain agent stop

Syntax: keychain agent stop [--mine|--others]

Terminate the ssh-agents recorded in the pidfile. Bare agent stop targets every recorded agent; --mine and --others narrow the kill list by owner UID.

keychain env

Syntax: keychain env [--shell FORMAT] [--json]

Print SSH_AUTH_SOCK and SSH_AGENT_PID from the best available source (running pidfile, then inherited env) in the requested format. Output targets:

env       bare `KEY=value` lines (default)
sh        Bourne shell `KEY=value; export KEY`
csh       C-shell `setenv KEY value`
fish      `set -x KEY value`
json      JSON object on one line
eval      same as `add --eval`: shell-detected dialect
systemd   bare KEY=value (suitable for EnvironmentFile=)

--target is an alias for --shell; --json is shorthand for --shell json.

keychain forget

Syntax: keychain forget [KEYS...]

For each KEY argument, evict the matching SSH key from ssh-agent. Bare key names are looked up under ~/.ssh/; absolute paths are used directly. Extended-key syntax supports sshk:PATH and host:HOSTNAME. gpgk:KEYID and pkcs11:PATH are not supported by forget.

Without arguments, forget is a no-op (use wipe to remove all keys).

keychain help

Syntax: keychain help [<action>]

Print the top-level cheat sheet. With an action name, print the per-action cheat sheet (same as keychain <action> --help). Identical to keychain --help.

For full documentation, use keychain man, keychain man --list for a man index, and keychain man <doc-atom> for specific documentation on an action, option or topic. To display all documentation relevant to your current command-line, append --explain.

keychain inspect

Syntax: keychain inspect [KEYS...] [--json]

Run every state probe keychain would run during a real add (host detection, paths, perms, env-file, agent reachability, key list, extkey expansion, ...) and present the internal runtime in easy-to-read topical boxes. Runtime identity, the parsed ~/.keychainrc, effective preferences and their sources, and the presence of Keychain-relevant environment variables are included. Environment reporting is restricted to a diagnostic allowlist; unrelated variables and literal configuration-file contents are never exposed. Use with --json to generate a complete machine-readable state dump suitable for bug reports. The versioned JSON schema groups diagnostics under runtime, configuration, keychain_state, agent_state, and keys.

keychain list

Syntax: keychain list [--json]

Print the type, bit-length, fingerprint, and comment of every key held by the reachable ssh-agent, in the same shape as ssh-add -l. When the modern color theme is active, the output is rendered as a clean four-column table.

--json emits a JSON array suitable for scripting.

keychain man

Syntax: keychain man [<action>|<topic>] [--list] [--no-pager] [--width N]

Print the full embedded documentation in a pager-friendly form. Targets:

keychain man                  whole Keychain manual
keychain man <action>         one action (incl. all flags)
keychain man <topic>          named topical section
keychain man topic:<name>     same, with explicit prefix
keychain man --list           index every doc atom
keychain man --no-pager       skip $PAGER even on TTY
keychain man --width 100      explicit wrap width

On a terminal, Keychain uses $PAGER, then less, then more. Color is retained only when the selected executable is verified as less, including aliases such as macOS more; when $LESS is unset or empty, Keychain supplies LESS=-R to that child process without changing the caller's environment. Other pagers receive plain text.

keychain version

Syntax: keychain version [--json]

Print the version string, copyright, and license summary.

keychain wipe

Syntax: keychain wipe [--ssh] [--gpg]

Clear cached credentials while leaving the agent processes running. By default, wipe removes identities from ssh-agent only (this behavior is new as of version 3.0.1 -- previous versions wiped GnuPG keys as well). GnuPG is affected only when --gpg is specified. Keychain then connects to an already-running gpg-agent and sends RELOADAGENT. GnuPG flushes the agent's entire in-memory secret cache, not just passphrases warmed by Keychain. This makes protected signing and decryption capabilities cold again and may clear other cache modes maintained by the same agent. No key files or other persistent key material are deleted.

wipe              SSH identities only
wipe --ssh        SSH identities only
wipe --gpg        gpg-agent's in-memory secret cache only
wipe --ssh --gpg  both

As part of RELOADAGENT, GnuPG also rereads its agent configuration and trust list. Clearing an absent gpg-agent is a successful no-op. If gpg-connect-agent is unavailable, times out, or cannot confirm the cache flush, Keychain reports an error.

ACTION OPTIONS

--eval

Syntax: keychain add --eval [KEYS...]

This is a foundational keychain feature. If you're using Keychain on a POSIX system, you'll likely want to use this flag in your shell startup files (e.g. ~/.bash_profile) to ensure that every new shell session can find and use ssh-agent without manual intervention.

Here's how it works. When you pass --eval, Keychain prints shell commands that set the environment variables needed by ssh and scp to find the running ssh-agent. Your shell's built-in eval executes those commands in the current shell. Keychain emits the appropriate environment syntax for the selected shell, but the surrounding command substitution must also use that shell's syntax. From an interactive Bash or Zsh terminal, run:

eval "$(keychain add --eval id_ed25519)"

The shell runs Keychain before eval processes its output, so --eval does not prevent a key-loading prompt. For automatic startup, use the guarded recipes in keychain man topic:env.

If you just want to retrieve the agent environment without adding any keys, use the keychain env --target eval action instead, or source the appropriate pidfile.

--systemd

After loading keys, push SSH_AUTH_SOCK and SSH_AGENT_PID into the calling user's systemd user environment block using systemctl --user set-environment. This makes the agent environment available to all child systemd units (services, timers) without requiring them to source a pidfile.

Use this option when your user session is managed by systemd and you need background services to access the SSH agent. From an interactive Bash or Zsh terminal, run:

eval "$(keychain add --eval --systemd id_ed25519)"

For automatic startup, add --systemd to both Keychain invocations in the Bash or fish recipe in keychain man topic:env. Keep --noask in the non-interactive branch so publishing the agent environment does not prompt for keys.

The --systemd flag is a side-effecting operation that runs systemctl directly; it does not print any shell code. If you only need to update the systemd environment without loading keys, use keychain env --target systemd instead, which prints the agent environment in systemd EnvironmentFile= format (bare KEY=value lines).

--quick, -Q

If a reachable ssh-agent already holds at least one key, exit immediately without prompting or loading anything new. Intended for shell-startup snippets where the agent is reused across many shells: the shell pays only the cost of a single ssh-add -l on subsequent invocations.

This is intentionally an SSH-only compatibility option. GPG key arguments are ignored and Keychain does not invoke GnuPG when --quick is present. Use normal keychain add for GPG workflows.

--immediate

When requested SSH keys are missing, start ssh-add without displaying Press Enter to run ssh-add in this terminal. Immediate invocations can prompt independently, so a hidden terminal's passphrase request does not prevent a visible terminal from loading keys. After another terminal completes loading, each invocation checks its own requested keys and cancels its unfinished ssh-add if those keys are now available.

This option does not suppress an ssh-add passphrase prompt, hide errors, disable coordination, or imply --quiet. For quiet automatic activation, add --quiet --immediate to the interactive branch of your startup recipe (see keychain man topic:env). The corresponding command in an interactive Bash or Zsh terminal is:

eval "$(keychain add --eval --quiet --immediate id_ed25519)"

A failed invocation does not cancel other attempts. Terminals requesting different keys can continue independently. Pressing Enter in a regular waiting terminal moves outstanding requests there; canceled invocations wait for that operation instead of immediately starting another prompt. Keychain reports the confirmation and lifetime settings used by a successful load. It does not reconcile differing settings: if the same identity is added again, OpenSSH applies that addition's settings. To make automatic activation persistent:

[agent]
activation = immediate

The --immediate flag overrides the configured activation mode for this invocation. See keychain man config:agent.activation for package defaults and the legacy immediate config key.

--no-passphrase, --noask

Complete SSH-agent setup and any requested environment output, then exit without inspecting, loading, or warming the specified keys. Keychain does not invoke GnuPG in this mode. The command can therefore succeed even when a requested key is not available; this option does not verify key availability.

For a modern agent-only invocation, prefer keychain agent start. The --noask spelling and its --no-passphrase alias preserve the traditional Keychain 2.x behavior.

--confirm

Pass -c to ssh-add: every subsequent use of the loaded key (for example, by ssh, scp, or Git over SSH) requires explicit user confirmation through OpenSSH's askpass mechanism. This is normally a persistent preference:

[agent]
confirm = true

On macOS, --confirm implicitly applies --no-inherit: Keychain does not adopt the $SSH_AUTH_SOCK / $SSH_AGENT_PID inherited from the parent shell. This ensures that, when no valid Keychain pidfile agent exists, Keychain starts an agent with the native confirmation environment described below. Other platforms retain normal inherited-agent discovery.

macOS. When Keychain starts a new managed ssh-agent with confirmation enabled, it installs a private helper at ~/.keychain/ssh-askpass-macos (or the selected Keychain directory) and starts the agent with the environment OpenSSH needs to invoke it. The helper accepts confirmation requests only and displays a native graphical Keychain SSH Confirmation dialog. Only choosing Allow approves the operation; choosing Deny, closing or cancelling the dialog, an unavailable desktop session, or any helper failure refuses it.

If $SSH_ASKPASS already names a custom helper when Keychain starts the managed agent, Keychain preserves it and enables OpenSSH's forced askpass mode. On other platforms, provide a suitable askpass helper in the agent's startup environment when the platform does not supply one.

An agent's askpass environment is fixed when the agent starts, and a valid pidfile agent is considered before inherited-agent discovery. After upgrading, run keychain agent stop once if Keychain's pidfile refers to an agent created without confirmation support; the next confirmed invocation will start it with the native helper. See issue #227.

--confirm cannot be combined with --no-gui. Keychain reports an error instead of silently weakening the request or loading a key whose confirmation constraint cannot be satisfied.

--timeout

Pass a timeout through to ssh-add: the key is forgotten by the agent after the requested number of minutes. Common settings include 480 (8h, a workday) or 60 (higher security). A common config-file preference.

--ignore-missing

For each KEY positional that doesn't resolve to an existing file (or known GPG ID), skip it silently instead of erroring. Useful in dotfiles shared across machines where not every key is present everywhere. If all requested keys are missing, add still exits successfully without spawning an agent.

--clear

Remove every identity from the selected ssh-agent, then process the requested keys normally. This is equivalent to calling:

keychain wipe && keychain add KEYS

--clear does not flush GnuPG's passphrase cache, even when GPG keys are also requested. Use keychain wipe --gpg when that separate operation is intended.

--confallhosts

Iterate every Host block in ~/.ssh/config (excluding pure-wildcard patterns and negations) and load its IdentityFile. Equivalent to --extended host:NAME for every host. Per-host expansion is delegated to ssh -G NAME so include files, Match blocks, and %d/%u/%h tokens work exactly as OpenSSH does.

--ssh-allow-forwarded

Adopt the inherited $SSH_AUTH_SOCK if it points to a forwarded agent (typical of ssh -A from a workstation into a server). Has security implications: the forwarded socket is controllable by the source machine. See keychain man topic:security.

--no-inherit, --noinherit

Don't trust $SSH_AUTH_SOCK / $SSH_AGENT_PID from the parent environment; always start fresh from the pidfile or by spawning. The inverse of --ssh-allow-forwarded.

--ssh-agent-socket

Use PATH as the ssh-agent socket explicitly. When this option is not supplied, Keychain now chooses a host-specific managed socket path under ~/.keychain/ instead of letting ssh-agent pick a transient location under /tmp. Override it only when integrating with another tool that expects a specific socket path.

--lockwait

Maximum time (in seconds) to wait for keychain's state lock when another keychain process is mutating shared coordination state. Default 5 seconds.

Historical note: In Keychain 2.x and 3.0.0_beta1, --lockwait controlled the timeout for the single global lock that protected the entire key-loading operation. This was the source of the could not acquire lock errors when multiple terminals started simultaneously. Starting in 3.0.0_beta2, the lock scope is much narrower (protecting only small coordination state updates) and the interactive unlock flow uses a separate activation lock with FIFO-based notification. As a result, --lockwait is now an internal tuning parameter that most users will never need to adjust. Set to 0 to fail immediately if the state lock is held (rare in normal operation).

--mine

Restrict the kill list to agents whose owner UID matches the calling user. Useful in shared accounts or root-cron contexts where you must not touch other users' agents.

--others

Inverse of --mine: terminate only agents whose owner UID does not match the calling user. Rare; typically a maintenance tool.

--all

Terminate every candidate agent recorded in the pidfile, regardless of owner. This is the default target for keychain agent stop.

--shell, --target

Specify the output format for keychain env. Supported targets:

•

env -- bare KEY=value lines (default, suitable for EnvironmentFile=)

•

sh -- Bourne shell KEY=value; export KEY

•

csh -- C-shell setenv KEY value

•

fish -- fish shell set -x KEY value

•

json -- JSON object on one line

•

eval -- shell-detected dialect (same as add --eval)

•

systemd -- bare KEY=value (alias for env, suitable for systemd)

Use --target systemd or --shell systemd when you want to redirect output to a file for use with systemd's EnvironmentFile= directive. The --systemd flag on add performs the same function but pushes directly to systemd via systemctl --user set-environment instead of printing to stdout.

--json

Equivalent to --shell json.

--json

Emit the inspection payload as JSON.

--json

Emit a JSON array of key records on stdout.

--list

Print slug -- one-liner for every action, global flag, config key, and topic. Useful as a discovery surface.

--no-pager

Skip the automatic $PAGER invocation even when stdout is a terminal.

--width

Explicit wrap width. Defaults to $COLUMNS then 80.

--roff

Write the manual as UTF-8 roff man(7) source, for installation as keychain.1. Never paged, and --width does not apply. $SOURCE_DATE_EPOCH pins the header date.

keychain man --roff > keychain.1

--json

Emit {"version": "X.Y.Z"}.

--ssh

Remove all identities held by ssh-agent using ssh-add -D. This does not delete the corresponding private key files.

--gpg

Send RELOADAGENT to an already-running gpg-agent. GnuPG flushes the agent's entire in-memory secret cache, not only entries warmed by Keychain, and rereads its configuration. Protected signing and decryption operations will need to unlock their keys again. No private keys or other persistent key material are deleted, and the agent process remains running.

REFERENCE TOPICS

Windows-specific notes (topic:windows)

Keychain runs on Windows Subsystem for Linux (WSL) and Git Bash on Windows via keychain.pyz plus the Python launcher (py.exe). What you get:

•

The full action surface (add, agent start, list, inspect, env, ...). The embedded documentation system (--help, man, --explain) works identically to the Linux build.

•

Coordinated multi-terminal initialization (3.0.0_beta2+) works on WSL and Git Bash. When multiple terminals start simultaneously (common when Visual Studio Code reconnects to WSL), they coordinate automatically to prevent could not acquire lock errors.

•

WSL requires the standard Linux OpenSSH tools (ssh-agent, ssh-add) installed in your WSL distribution.

•

Git Bash uses the MinGW environment and may require Git for Windows' SSH tools.

How keychain discovers, attaches, and spawns ssh-agent (topic:agents)

Discovery order. When add or agent start runs, keychain probes for an existing agent in this order:

1.

The pidfile in $KEYCHAIN_DIR/HOST-pid (its writer left SSH_AUTH_SOCK and SSH_AGENT_PID here). Socket presence on disk and reachability via ssh-add -l are both verified.

2.

Inherited $SSH_AUTH_SOCK / $SSH_AGENT_PID from the parent shell's environment, unless --no-inherit is set. Forwarded agents (ssh -A) are accepted only when --ssh-allow-forwarded is set. An inherited gpg-agent SSH socket is deliberately ignored.

3.

None of the above -- a new ssh-agent is spawned. By default, its socket is pinned to ~/.keychain/HOST-agent.sock instead of an ephemeral /tmp/ssh-* directory.

Pidfile is the source of truth. Per-host (so NFS-shared homes work) and per-user (mode 700). Older pidfiles are replaced atomically; no in-place edits. The default managed ssh-agent socket lives beside these files as HOST-agent.sock.

Coordinated key loading. A short-lived state lock protects metadata updates. Per-attempt locks identify live loading operations; the activation lock allows concurrent loading but excludes SSH clearing. Waiting terminals are notified through FIFO endpoints and verify the requested keys in the agent. See keychain man topic:coordination.

Agent responsibilities. Keychain owns ssh-agent discovery, startup, and coordination for SSH keys. For native GPG signing and decryption, Keychain invokes gpg and leaves gpg-agent startup, configuration, pinentry, and lifecycle entirely to GnuPG (this is new as of 3.0.1). It deliberately does not use GnuPG's SSH-agent emulation because adding an SSH key there imports the private key into GnuPG's persistent key store, whose behavior differs from the in-memory model users expect from ssh-agent.

Environment emission and shell integration (topic:env)

Keychain sets SSH_AUTH_SOCK and SSH_AGENT_PID so ssh, scp, and related tools can find the running agent. The --eval flag emits environment assignments for your shell to execute. Interactive terminals load your keys; non-interactive login shells establish the agent environment without prompting. This distinction lets graphical-session startup finish even when it runs a login shell whose terminal you cannot access.

Choose the recipe for your shell and replace the filenames in KEYCHAIN_KEYS with your keys. Keep one key per line and quote filenames containing spaces. The key list is a shell variable and does not need to be exported. Keep unrelated existing configuration, and replace any older Keychain startup invocation rather than adding a second one.

Bash startup. Add this to ~/.bash_profile, keeping any existing configuration:

KEYCHAIN_KEYS=(
    ~/.ssh/id_ed25519
    ~/.ssh/id_rsa
)
if [[ $- == *i* ]]; then
    eval "$(keychain add --eval "${KEYCHAIN_KEYS[@]}")"
else
    eval "$(keychain add --eval --noask)"
fi

Bash reads .bash_profile for login shells, including non-interactive ones. The i in $- identifies an interactive shell; --noask skips key loading in the other branch.

Zsh startup. Add this to ~/.zshrc, which is read only by interactive shells:

KEYCHAIN_KEYS=(
    ~/.ssh/id_ed25519
    ~/.ssh/id_rsa
)
eval "$(keychain add --eval "${KEYCHAIN_KEYS[@]}")"

If your graphical session also needs the agent environment, add eval "$(keychain add --eval --noask)" to ~/.zprofile. That login-shell file can run non-interactively; key loading belongs in .zshrc.

Fish startup. Add this to ~/.config/fish/config.fish, keeping any existing configuration:

set KEYCHAIN_KEYS \
    ~/.ssh/id_ed25519 \
    ~/.ssh/id_rsa
if status is-interactive
    eval "$(keychain add --eval $KEYCHAIN_KEYS)"
else if status is-login
    eval "$(keychain add --eval --noask)"
end

Fish reads config.fish for every shell. This recipe loads keys only in interactive shells, uses --noask for non-interactive login shells, and skips Keychain in other non-interactive shells. This avoids the inaccessible startup prompt reported with SDDM graphical login.

Bash alternative: load keys from .bashrc. Use this arrangement if you want both interactive login shells and interactive non-login terminals to run Keychain. In ~/.bash_profile, source .bashrc for interactive logins and establish the agent environment for non-interactive logins:

if [[ $- == *i* ]]; then
    if [[ -f ~/.bashrc ]]; then
        source ~/.bashrc
    fi
else
    eval "$(keychain add --eval --noask)"
fi

In ~/.bashrc, keep the key list and interactive key loading together:

KEYCHAIN_KEYS=(
    ~/.ssh/id_ed25519
    ~/.ssh/id_rsa
)
if [[ $- == *i* ]]; then
    eval "$(keychain add --eval "${KEYCHAIN_KEYS[@]}")"
fi

Place the .bashrc block after any existing early return for non-interactive shells. If .bash_profile already sources .bashrc, use the conditional source above in place of that existing source command. Keychain then runs once per interactive shell, while non-interactive logins still receive the agent environment without loading keys.

Alternate Option 1: Source the per-host pidfile directly. If you prefer not to invoke Keychain in every shell, source the pidfile that Keychain writes after the first run. For Bourne-style shells, run . ~/.keychain/$(hostname)-sh; for fish, run source ~/.keychain/$(hostname)-fish. This is faster because it avoids a Python invocation, but it requires that Keychain has already run at least once to create the pidfile.

Alternate Option 2: Push to systemd user environment. If your session uses systemd, you can push the agent environment to all child units via keychain add --systemd id_rsa. This runs systemctl --user set-environment to set SSH_AUTH_SOCK and SSH_AGENT_PID for all systemd services and timers. To update the systemd environment without loading keys, use keychain env --target systemd and redirect to an environment file.

Alternate Option 3: Emit bare KEY=value for tools. For non-shell tools that consume EnvironmentFile= or similar formats, use keychain env > /run/user/$UID/keychain.env to generate a bare definition file to be sourced by another tool. The default env target outputs bare KEY=value lines suitable for systemd's EnvironmentFile= directive or other configuration parsers.

Extended key syntax (topic:extkeys)

SSH and GPG keys can be specified as positional arguments for the add action. keychain add NAME will look for ~/.ssh/NAME if it exists and treat it as an SSH key; otherwise, it is looked up via gpg --list-secret-keys and if found the GnuPG signing key will be loaded and warmed. This may also warm an associated encryption key, but this is not guaranteed -- see below.

In addition to explicit passing of keys by name or path, positional key arguments may accept seven extended key prefixes to avoid ambiguity and provide more fine-grained control. The following prefixed formats can be mixed freely with regular positional key names:

sshk:PATH       an SSH key file path
sshk:NAME       an SSH key name under `~/.ssh/`
gpgk:KEYID      a GnuPG key ID (legacy alias for `gpgs:`)
gpgs:KEYID      warm and prove signing capability using GnuPG's signing path
gpge:KEYID      warm and prove encryption/decryption capability
gpga:KEYID      warm and prove signing + encryption/decryption capability
pkcs11:PATH     a PKCS#11 provider library loaded via `ssh-add -s`
host:HOSTNAME   every IdentityFile for HOSTNAME
                (resolved via `ssh -G HOSTNAME`)

Using the extended key syntax opens up several important features to help ensure that your specified GnuPG signing or encryption key is actually loaded and ready for use. This is important because GnuPG's keys and subkeys can be tricky -- OpenPGP encryption capability may live on the primary key or on a separate encryption subkey, and GnuPG's passphrase cache behavior can vary by version, platform, and key layout. A signing operation may happen to unlock later decryption on some systems, but Keychain does not rely on that as a portable guarantee.

Extended key syntax also covers PKCS#11-backed SSH tokens. pkcs11:PATH asks OpenSSH to enumerate public keys from the provider with ssh-keygen -D and compare their fingerprints with the identities already held by the running agent. If the token's identities are not already present, Keychain loads the provider with ssh-add -s PATH so the usual PIN or touch prompt can occur in the foreground.

For GPG keys, gpgk:KEYID is equivalent to gpgs:KEYID. Both perform a signing-oriented warm-up: Keychain asks GnuPG to perform a signing operation with the requested key, which causes gpg-agent to cache the passphrase needed for signing. This proves signing capability, but it does not prove that the same key can decrypt data.

Using gpge:KEYID performs a similar explicit check to ensure functionality. When used, Keychain will encrypt a tiny temporary payload with the requested key and then decrypt it to ensure that it is properly loaded and functional, and will fail if it does not succeed. If your workflow decrypts data -- for example, pass show ... -- use gpge:KEYID.

If your workflow needs both signing and decryption, use gpga:KEYID. Keychain will first warm the signing path, then require the same explicit decryption check performed by gpge:. If GnuPG cannot encrypt to and decrypt with the requested key, gpga:KEYID fails rather than treating signing warm-up as enough.

host:HOSTNAME can be used to refer to a specific hostname defined in your local OpenSSH config. host: expansion is delegated to ssh -G so the full OpenSSH semantics (Match, Include, %d/%u/%h substitution, quoted args) apply properly.

Pidfile location and contents (topic:pidfile)

Per user, per host, mode 700 directory:

~/.keychain/

(overridable by --dir / [paths] dir).

Traditional pidfiles. Files inside, where HOST is the resolved hostname:

HOST-sh          Bourne-style `KEY=value; export KEY`
HOST-csh         C-shell `setenv KEY value`
HOST-fish        fish `set -x KEY value`
HOST-envfile     bare `KEY=value` export for non-shell tools
HOST-agent.sock  managed default `ssh-agent` socket path

All files are owner-only (mode 600) and re-written atomically.

One pidfile per host means that homedirs shared over NFS across many machines each get their own agent state -- no ssh-agent confusion when you log in from multiple hosts.

Coordination state (3.0.0_beta2+). For coordinated multi-terminal initialization:

HOST.state.json      latest attempt identifier and recorded outcome
HOST.state.lock      protects state file and waiter registration
HOST.activation.lock shared for loading, exclusive for clearing
HOST-waiters/        per-attempt locks and private FIFO endpoints

The coordination state file is a result record, not a list of live processes or registered waiters. Held per-attempt locks identify live operations, and each terminal's notification FIFO is its registration. Coordination files are created with mode 0o600 inside the private keychain directory. Normal cleanup removes the attempt's temporary resources; files left by a terminated process are cleaned up after its lock is no longer held.

CONFIG KEYS (~/.keychainrc)

~/.keychainrc preference file (topic:config)

Keychain 3 introduces an optional configuration file, ~/.keychainrc, for setting persistent defaults. The config file allows users to customize Keychain's behavior across all invocations. Keychain reads ~/.keychainrc at startup. The file uses INI-style syntax with [section] headers and simple key/value pairs:

[agent]
timeout = 1h
[lock]
lock = true
[agent.env]
ssh_args = -a ~/.ssh/agent.sock

Evaluation order. Keychain evaluates settings in this order, with successive steps overriding previous steps:

1.

Built-in defaults provide the base settings, including any package-selected activation default.

2.

If --allow-env is specified, $KEYCHAIN_CONFIG can point to an alternate .keychainrc path.

3.

The selected .keychainrc file is parsed.

4.

If --allow-env is specified, supported KEYCHAIN_* variables from the environment are applied.

5.

Explicit command-line flags are applied last.

This means command-line flags always override environment and config file settings, giving you flexibility to customize individual invocations while maintaining sensible defaults.

Environment variables. Keychain does not support arbitrary KEYCHAIN_* environment variables. The supported environment surface is intentionally small:

•

KEYCHAIN_CONFIG -- alternate path to the config file (instead of ~/.keychainrc)

•

KEYCHAIN_SSH_AGENT_ARGS -- extra arguments for spawned ssh-agent

By default, Keychain ignores these variables for security reasons. To enable them, pass --allow-env (or -E) on the command line. This prevents untrusted shell environments from silently altering Keychain's behavior. Normal interactive users should prefer ~/.keychainrc for persistent settings.

Config key correspondence. Most config keys mirror command-line options. For example, --timeout 1h on the command line is equivalent to timeout = 1h in the [agent] section. When a config key corresponds to a CLI option, the documentation references that option for behavioral details.

Some boolean config keys use positive phrasing even when the historical CLI flag is negative for compatibility. For instance, [lock] lock = false produces the same behavior as --no-lock.

agent.activation

choose prompted or immediate SSH key activation

agent.clear

wipe SSH identities before adding

agent.confirm

ssh-add -c: prompt per use

agent.inherit

ignore inherited SSH_AUTH_SOCK / PID

agent.ssh_agent_socket

use PATH as the ssh-agent endpoint

agent.ssh_allow_forwarded

adopt a forwarded SSH agent

agent.systemd

push agent env to systemctl --user

agent.timeout

auto-expire keys after MIN minutes

agent.env.ssh_args

extra args passed to spawned ssh-agent

keys.confallhosts

load every IdentityFile in ~/.ssh/config

keys.ignore_missing

skip keys that don't exist

output.animate

animate the initial activation prompt

output.color

disable ANSI color output

output.gui

don't use GUI passphrase prompts

output.quiet

suppress non-error output on stderr

output.theme

color theme: legacy or modern

paths.absolute

treat --dir as the literal pidfile dir

paths.dir

pidfile directory (default: ~/.keychain)

paths.pid_formats

pidfile output formats

Detailed config entries

agent.activation

Config key: [agent] activation

--immediate selects activation = immediate for this invocation.

choose prompted or immediate SSH key activation

Set [agent] activation = prompt to wait for Enter before attempting coordinated SSH key initialization, or activation = immediate to begin automatically. This controls the initial activation step, not passphrase entry: ssh-add may still prompt, and immediate terminals may wait for another terminal to finish initialization.

Upstream builds default to prompt. A package may be built with immediate as its default; both behaviors remain available. Your ~/.keychainrc overrides the package default, and --immediate overrides the config for one invocation. To restore the Enter prompt on a package that defaults to immediate activation:

[agent]
activation = prompt

The legacy [agent] immediate = true and immediate = false settings remain accepted and mean activation = immediate and activation = prompt, respectively. If both keys are present, activation wins regardless of their order, and Keychain warns that immediate was ignored. Invalid activation values are errors. See keychain man --immediate for coordination and failure behavior.

agent.env.ssh_args

Config key: [agent.env] ssh_args

extra args passed to spawned ssh-agent

Append extra arguments when keychain spawns ssh-agent. This is an advanced escape hatch for cases Keychain does not model directly, such as pinning the agent socket to a specific path or enabling agent-level debugging. The value is split into shell-like tokens and appended after Keychain's own managed flags. For example, this pins the ssh-agent socket to a fixed location:

[agent.env]
ssh_args = -a ~/.ssh/agent.sock

If $KEYCHAIN_SSH_AGENT_ARGS is set in the environment and --allow-env is active, it overrides this config key.

output.animate

Config key: [output] animate

animate the initial activation prompt

The default is true. On a suitable color terminal, the complete activation prompt appears immediately, then changes from subdued text to its final colors from left to right over about half a second. This happens once; it does not repeat while Keychain waits. Enter works throughout, and the animation does not delay coordination events.

Set animate = false under [output] in ~/.keychainrc for a static prompt with the same final colors. There is no corresponding command-line option. Disabled color, redirected output, an unsuitable terminal, or insufficient or unknown terminal width also produce a static prompt. Required prompts remain visible with --quiet.

paths.pid_formats

Config key: [paths] pid_formats

pidfile output formats

Comma-separated pidfile formats to write after agent setup. The default is sh; supported values are sh, csh, fish and envfile.

Advanced config settings. A few settings exist only in the config file, with no command-line equivalent. These include:

•

pid_formats in [paths] -- comma-separated pidfile formats to write after agent setup.

•

ssh_args in [agent.env] -- extra arguments passed when spawning ssh-agent. This is injected into Keychain's internal environment as $KEYCHAIN_SSH_AGENT_ARGS, which the agent startup code reads. If this environment variable is set in the outer environment and --allow-env is active, it takes precedence over the config file value.

•

Advanced tuning parameters for coordination and locking behavior

Value formats. Boolean values accept: true, false, yes, no, on, off, 1, 0. Integers are decimal. Strings are passed verbatim (quote values containing spaces, though unquoted usually works).

Editing and discovery. Edit ~/.keychainrc directly with your preferred text editor. Use keychain man --list to see all available config keys, or keychain man config:SECTION.KEY for detailed documentation on a specific key. The config key list is generated from Keychain's option metadata, so it stays synchronized with the implementation.

APPENDIX

keychain 2.x compatibility (topic:compat)

Every keychain 2.x flat-flag invocation is translated to the new-style action verbs transparently.

Old long-form spellings of options that have been renamed (--noask, --noinherit, --nogui, --nolock, --no-color) all remain accepted. No deprecation is planned for the legacy flag spellings.

Old options that are now hidden but still accepted for compatibility: --extended, --gpg2, --absolute, --clear, --theme, --ssh-allow-forwarded, --no-inherit, --ssh-agent-socket, --confirm. These are now lifestyle preferences set in ~/.keychainrc; the CLI flags work but no longer appear in --help. --clear follows Keychain 3's SSH-only default wipe policy; clearing GnuPG remains an explicit wipe --gpg operation.

Deprecated no-ops retained for command-line compatibility: --ssh-allow-gpg and --ssh-spawn-gpg. Each emits a warning and is ignored; Keychain uses ssh-agent exclusively for SSH keys.

Removed with migration guidance: --confhost; use host:HOSTNAME instead.

Other deprecated no-ops retained with a warning: --agents, --inherit, and --attempts.

Coordination model change (3.0.0_beta2+). The coordinated unlock protocol introduced in 3.0.0_beta2 fundamentally changes how multiple terminals initialize keys simultaneously. In Keychain 2.x and 3.0.0_beta1, terminals raced for a single lock with losers timing out after 5 seconds. In 3.0.0_beta2+, terminals cooperate via FIFO notification and all succeed together. Legacy invocations still work, but the behavior is now cooperative rather than competitive. Users will notice the absence of Waiting N seconds for lock... messages during interactive key loading.

Compatibility is therefore intentionally high but not perfect. In particular, bare tokens still default to add for 2.x compatibility, but if none of the requested keys resolve, keychain refuses to start an agent and exits with an error instead of preserving the old side effect.

Security model (topic:security)

Keychain runs with the authority of the invoking user. It assumes the operating system, the current user account, the user's executable search path, OpenSSH, and GnuPG are trusted. Within that boundary, Keychain protects its runtime state from other local users, validates agent endpoints before use, coordinates concurrent invocations, and quotes generated shell output.

Runtime storage. The default ~/.keychain/ directory is created mode 0700. Before runtime state is used on POSIX systems, Keychain checks the directory and existing pidfiles, state files, lock files, and waiter directory. It refuses paths owned by another user, paths with any group or other permissions, and paths that cannot be inspected. Pidfiles and coordination state are replaced through private temporary files.

Agent endpoints. An SSH endpoint must be a non-symlink UNIX-domain socket owned by the current user. Pidfile agents must also name a live process, and the selected endpoint is checked again immediately before ssh-add runs. Forwarded agents require the explicit --ssh-allow-forwarded opt-in. A socket identified as GnuPG's SSH endpoint is always rejected; Keychain uses ssh-agent exclusively for SSH identities.

Configuration and environment. On POSIX systems, .keychainrc must be owned by the current user and must not be writable by group or others. KEYCHAIN_* environment variables are ignored unless --allow-env (-E) is supplied. External programs receive argument vectors rather than shell command strings.

Shell output. Sourceable environment values are quoted for the selected shell. NUL and line-break characters are rejected rather than emitted into a pidfile or --eval output.

--ssh-allow-forwarded. Adopting a forwarded agent socket on a remote host means the source machine -- the laptop you forwarded from -- can sign authentication requests on this host's behalf. That is by design (it's the point of agent forwarding) but it widens the trust boundary. Don't enable on hosts you don't fully control.

Coordination security. The coordinated unlock protocol uses private FIFO endpoints for notification and operating-system advisory locks for ownership. Each attempt's held lock establishes that it is active; PID, hostname, heartbeat, and saved file contents do not establish ownership. The shared activation lock prevents SSH clearing during loading. A FIFO wakeup only signals a possible state change, so terminals check the agent before reporting that another attempt supplied their keys. --no-lock deliberately disables these guarantees.

What Keychain does not protect against. Root, the current user, or a process already able to act as that user can reach or replace user-owned resources. A malicious program selected through PATH, an intentionally unsafe command-line or preference-file setting, and a compromised agent are also outside the trust boundary. Any process that can reach an unlocked agent socket may ask it to sign; use agent timeouts, confirmation constraints, screen locking, and careful forwarding policy as appropriate.

History

--absolute until Debian 13 (Trixie); --agents until Debian 13 (Trixie); --attempts until Debian 13 (Trixie); --clear until Debian 13 (Trixie); --confhost until Debian 13 (Trixie); --confirm until Debian 13 (Trixie); --dir until Debian 13 (Trixie); --env until Debian 13 (Trixie); --eval until Debian 13 (Trixie); --gpg2 until Debian 13 (Trixie); --help until Debian 13 (Trixie); --host until Debian 13 (Trixie); --ignore-missing until Debian 13 (Trixie); --inherit until Debian 13 (Trixie); --list until Debian 13 (Trixie); --list-fp until Debian 13 (Trixie); --lockwait until Debian 13 (Trixie); --noask until Debian 13 (Trixie); --nocolor until Debian 13 (Trixie); --nogui until Debian 13 (Trixie); --noinherit until Debian 13 (Trixie); --nolock until Debian 13 (Trixie); --query until Debian 13 (Trixie); --quick until Debian 13 (Trixie); --quiet until Debian 13 (Trixie); --stop until Debian 13 (Trixie); --systemd until Debian 13 (Trixie); --timeout until Debian 13 (Trixie); --version until Debian 13 (Trixie); -L until Debian 13 (Trixie); -Q until Debian 13 (Trixie); -V until Debian 13 (Trixie); -h until Debian 13 (Trixie); -k until Debian 13 (Trixie); -l until Debian 13 (Trixie); -q until Debian 13 (Trixie).