Access macOS Keychain secrets with Touch ID & Apple Watch — like pam-watchid, but for keychain items.
Instead of typing your password every time a script reads a secret from the keychain, authenticate with a tap on your Apple Watch or a finger on Touch ID.
# Build and install to /usr/local/bin
make install
# Or with a custom prefix
make install PREFIX=~/.localRequires Xcode Command Line Tools (xcode-select --install).
# Import an existing keychain secret (one-time)
watchkey set DOPPLER_TOKEN_DEV --import
# Retrieve with Touch ID / Apple Watch
watchkey get DOPPLER_TOKEN_DEV
# Store a new secret (reads from stdin)
watchkey set MY_SECRET
# Store via native macOS secure dialog (never touches terminal)
watchkey set MY_SECRET --gui
# Pipe a value in
echo "s3cret" | watchkey set MY_SECRET
# Delete a stored secret
watchkey delete MY_SECRETAll get, set, and delete operations require authentication.
If Touch ID and Apple Watch are unavailable, watchkey falls back to a system password prompt.
WatchKey Companion is an optional, self-hosted web UI that can send browser push notifications and show the machine, key name, working directory, and command requesting access. You can approve by entering this Mac's password in the browser, or enable button-only approval in Companion's security settings.
Companion is off by default. Pairing also leaves it off until you explicitly enable it:
-
Sign in to your Companion web UI and select Pair machine.
-
Run the enrollment command shown by the web UI:
watchkey companion enroll https://watchkey.example.com ABCD-EFGH
-
Enable remote prompts:
watchkey companion enable
Use watchkey companion disable to return to local-only authentication while
keeping the pairing, or watchkey companion unpair to delete the local
pairing. Revoke the machine in the Companion web UI as well if its token may
have been exposed.
When Companion is enabled, watchkey starts its existing local Touch ID, Apple Watch, or system-password prompt and the Companion request together. Approving either path is sufficient and closes the other prompt. Cancelling native authentication denies and closes the pending Companion request before watchkey exits. A remote denial or an invalid submitted machine password is terminal.
Companion always shows the actual WatchKey invocation. It also shows
WATCHKEY_FULL_COMMAND when set, explicitly labeled as a caller-reported,
unverified outer command. Otherwise, watchkey includes the best parent-process
information macOS exposes. A shell cannot always reconstruct the command
currently running inside another shell process, so set the variable before
execution when you need the exact outer command:
autoload -Uz add-zsh-hook
_watchkey_capture_command() {
export WATCHKEY_FULL_COMMAND="$1"
}
add-zsh-hook preexec _watchkey_capture_commandDo not put literal secrets on command lines: Companion displays and stores the command metadata so it can notify you. Machine-password approval is encrypted in the browser directly to an ephemeral key created by this watchkey process, then verified locally with OpenDirectory. The relay does not receive the plaintext password. TLS is still required, and a compromised self-hosted server could serve modified browser JavaScript that captures a password before it is encrypted.
Command context is advisory rather than OS-attested. Any program able to invoke watchkey can also choose its environment and arguments, so always verify the machine, operation, and key name shown by Companion.
Before:
{
"dev": "DOPPLER_TOKEN=\"$(security find-generic-password -w -s 'DOPPLER_TOKEN_DEV')\" doppler run -- next dev --turbopack"
}After:
{
"dev": "DOPPLER_TOKEN=\"$(watchkey get DOPPLER_TOKEN_DEV)\" doppler run -- next dev --turbopack"
}- Secrets are stored in the login keychain as generic passwords, namespaced under the
watchkeyaccount - On retrieval, watchkey authenticates via
LAContextusingdeviceOwnerAuthenticationWithBiometricsOrCompanion(macOS 15+) ordeviceOwnerAuthenticationWithBiometricsOrWatch(older versions) — the same API that pam-watchid uses for sudo - If biometrics/watch aren't available, it falls back to
deviceOwnerAuthentication(system password dialog) - If you explicitly enroll and enable WatchKey Companion, remote approval and the native prompt are offered concurrently; the first decision wins
- macOS 13+
- Apple Watch paired for unlock, or Touch ID
make uninstall