Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

watchkey

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.

Install

# Build and install to /usr/local/bin
make install

# Or with a custom prefix
make install PREFIX=~/.local

Requires Xcode Command Line Tools (xcode-select --install).

Usage

# 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_SECRET

All get, set, and delete operations require authentication.

If Touch ID and Apple Watch are unavailable, watchkey falls back to a system password prompt.

Optional WatchKey Companion

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:

  1. Sign in to your Companion web UI and select Pair machine.

  2. Run the enrollment command shown by the web UI:

    watchkey companion enroll https://watchkey.example.com ABCD-EFGH
  3. 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_command

Do 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.

Example: Doppler + Next.js

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"
}

How it works

  1. Secrets are stored in the login keychain as generic passwords, namespaced under the watchkey account
  2. On retrieval, watchkey authenticates via LAContext using deviceOwnerAuthenticationWithBiometricsOrCompanion (macOS 15+) or deviceOwnerAuthenticationWithBiometricsOrWatch (older versions) — the same API that pam-watchid uses for sudo
  3. If biometrics/watch aren't available, it falls back to deviceOwnerAuthentication (system password dialog)
  4. If you explicitly enroll and enable WatchKey Companion, remote approval and the native prompt are offered concurrently; the first decision wins

Requirements

  • macOS 13+
  • Apple Watch paired for unlock, or Touch ID

Uninstall

make uninstall

About

Access macOS Keychain secrets with Touch ID & Apple Watch

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages