Locksmith is a secure, biometric-protected keychain vault for macOS, Windows, and Linux. It allows you to store keys, tokens, and passwords in the native system keychain, requiring biometric authentication (Touch ID on macOS, Windows Hello on Windows, Polkit/Secret Service on Linux) for access.
- Biometric Security: Leverages macOS
LocalAuthentication, Windows Hello, and Linux Polkit for biometric and interactive protection. - MCP Server: Built-in support for the Model Context Protocol, allowing AI agents (like Claude or Cursor) to securely access secrets via biometric gates.
- Keychain Integration: Stores secrets in the secure macOS Keychain Services, Windows Credential Manager, and the Linux Secret Service DBus.
- Binary Whitelisting: Restricts secret access to cryptographically verified or path-authorized binaries to prevent unauthorized exfiltration.
- Disk Caching: Optional encrypted disk cache for fast re-access with configurable TTL.
- CLI & Library: Use it as a standalone command-line tool or import it as a Go package.
- Auto-Provisioning: Built-in
Makefilethat automatically installs its own security and quality tools.
The preferred method for installing Locksmith on macOS. This ensures you receive a pre-signed binary that is compatible with macOS security policies:
brew tap bonjoski/locksmith
brew install locksmithIf you encounter a publickey error despite the repository being public, your local Git config is likely forcing SSH for all GitHub connections. You can bypass this for just this command without changing your global configuration:
GIT_CONFIG_GLOBAL=/dev/null brew tap bonjoski/locksmithLocksmith ships one fully functional release profile. Official release binaries include full secret lifecycle functionality (read, write, delete, rotate).
The locksmith_admin build tag is an internal compile-time mechanism used in source builds and tests. It is not a separate product edition.
Warning
While Locksmith can be used as a Go module, the Homebrew CLI is the recommended way to interact with the vault on macOS.
Using it as a library requires your host application to have specific signing entitlements to access the macOS Keychain, which can lead to Permission Denied errors if not handled correctly.
When importing as a Go module, your build profile determines which compile-time guarded methods are included.
- macOS with Touch ID or Apple Watch support.
- Go 1.25.4.
- Xcode Command Line Tools (
xcode-select --install). - An Apple Developer ID is recommended for persistent trust, but ad-hoc signing (
-) is supported for local development.
- Windows 10/11 with Windows Hello support.
- Go 1.25.4.
git clone https://github.com/bonjoski/locksmith.git
cd locksmith
make build
make sign
# Binaries are now located in bin/bin/locksmith add my-service my-passwordTo support rotator auto-loading, you can store secret context metadata:
bin/locksmith add github/token my-token \
--type oauth_token \
--owner-app github \
--source-url https://rotation.example.internal/githubRotation uses in-process Go rotators, not shell scripts.
For glab OAuth tokens, Locksmith can rotate the access token automatically when it has expired.
How it works:
- Store access token as
oauth_token(for example:gitlab/glab/token). - Store refresh token separately (for example:
gitlab/glab/oauth2_refresh_token). - Configure a rotation rule using
gitlab-oauth-refreshand pass client/refresh metadata. - On
locksmith get gitlab/glab/token, if the token is expired and a matching rule exists, Locksmith refreshes it and returns the new token.
Example rule:
rotation:
- secret: "gitlab/glab/token"
rotator: "gitlab-oauth-refresh"
secret_type: "oauth_token"
owner_application: "gitlab"
source_url: "https://gitlab.com/oauth/token"
metadata:
gitlab_refresh_token: "locksmith://gitlab/glab/oauth2_refresh_token"
# Optional for providers requiring confidential client auth:
# gitlab_client_id: "locksmith://gitlab/glab/client_id"
# gitlab_client_secret: "locksmith://gitlab/glab/client_secret"
ttl: "1h"Example secret setup for that rule:
# Access token used by locksmith exec glab -- ...
bin/locksmith add gitlab/glab/token "<access-token>" \
--type oauth_token \
--owner-app gitlab \
--source-url https://gitlab.com/oauth/token
# Refresh token used by the rotator
bin/locksmith add gitlab/glab/oauth2_refresh_token "<refresh-token>" \
--type oauth_token \
--owner-app gitlab
# Optional OAuth app credentials (only if your provider setup requires them)
# bin/locksmith add gitlab/glab/client_id "<client-id>" --type token --owner-app gitlab
# bin/locksmith add gitlab/glab/client_secret "<client-secret>" --type token --owner-app gitlabNotes:
- Automatic rotation on
getapplies to expiredoauth_tokensecrets with a matching rotation rule. - In non-admin compile profiles, rotation APIs are unavailable and Locksmith returns the stored value without auto-rotation.
bin/locksmith get my-servicebin/locksmith listExecute any command with biometric-protected secrets injected directly into its environment. Secrets can be specified as environment variables or in an env file (--env-file):
Environment syntax supported by run:
LOCKSMITH_SECRET_<ENV_NAME>=<secret_key>maps a secret key to an env var name.<ENV_NAME>=locksmith://<secret_key>resolves a secret URI into that env var.
-
Using
LOCKSMITH_SECRET_prefix:LOCKSMITH_SECRET_DATABASE_URL=db/password bin/locksmith run -- npm run dev
This will fetch the secret for
db/passwordand inject it asDATABASE_URLfor the child process. -
Using
locksmith://scheme:DATABASE_URL=locksmith://db/password bin/locksmith run -- npm run dev
-
Using an environment file: Given a
.envfile:DATABASE_URL=locksmith://db/password STRIPE_KEY=locksmith://stripe/api_key
Run with:
bin/locksmith run --env-file .env -- npm run dev
Use the exec subcommand for common CLI integrations where token env vars should always come from Locksmith.
Security posture: Locksmith treats plaintext token storage in third-party config files as a downgrade. The recommended workflow is vault-only tokens with runtime injection (locksmith exec), not writing tokens to gh/glab config files.
Built-in integrations:
acli: injectsATLASSIAN_API_TOKENfromlocksmith://atlassian/acli/tokengh: injectsGH_TOKENfromlocksmith://github/gh/tokenglab: injectsGITLAB_TOKENfromlocksmith://gitlab/glab/token
Examples:
# One-time setup (store tokens in vault)
bin/locksmith add atlassian/acli/token "<token>" --type oauth_token --owner-app atlassian
bin/locksmith add github/gh/token "<token>" --type token --owner-app github
bin/locksmith add gitlab/glab/token "<token>" --type token --owner-app gitlab
# Atlassian CLI
bin/locksmith exec acli -- auth status
# Run CLIs with tokens injected at runtime
bin/locksmith exec gh -- pr list
bin/locksmith exec glab -- auth statusYou can override built-ins or define additional profiles in ~/.locksmith/config.yml:
integrations:
acli:
command: acli
env:
ATLASSIAN_API_TOKEN: locksmith://atlassian/acli/token
gh:
command: gh
env:
GH_TOKEN: locksmith://github/gh/token
glab:
command: glab
env:
GITLAB_TOKEN: locksmith://gitlab/glab/tokenBecause tokens are read from the vault at process start, any token rotated by Locksmith is picked up automatically on the next command execution.
Integration Hardening and Migration (integrations doctor / integrations migrate / integrations scrub)
If integration config files contain historical plaintext credentials, Locksmith can detect, migrate, and remove them.
Examples:
# Scan for plaintext token fields
bin/locksmith integrations doctor all
# Scan default targets plus custom files/directories (repeat --path as needed)
bin/locksmith integrations doctor ai --path ~/work/project/.env --path ~/work/project/.cursor
# Import discovered plaintext integration secrets into Locksmith, then scrub them
bin/locksmith integrations migrate all
# Remove known plaintext token fields
bin/locksmith integrations scrub all
# Show optional shell aliases for Locksmith-backed exec profiles
bin/locksmith integrations aliases all
# Choose shell-specific alias output format
bin/locksmith integrations aliases all --shell powershellSupported targets:
ai(doctor/scrub)aclighglaball
doctor reports file paths and key paths for detected plaintext token fields.
migrate imports known fields into Locksmith first and only then runs scrub, blocking scrub if required Locksmith keys are still missing.
scrub removes known token keys from supported config files and leaves the vault-only exec flow as the token source of truth.
aliases supports --shell auto|bash|zsh|fish|powershell|cmd (auto defaults to PowerShell on Windows and bash elsewhere).
Successful migration + scrub example:
$ bin/locksmith integrations migrate all
Integration migration report:
- acli: stored 1 secret(s)
* atlassian/acli/token
- gh: stored 1 secret(s)
* github/gh/token
- glab: stored 2 secret(s)
* gitlab/glab/token
* gitlab/glab/oauth2_refresh_token
Removed 3 plaintext token field(s) across 2 file(s).
- [gh] /Users/alice/.config/gh/hosts.yml (github.com.oauth_token)
- [glab] /Users/alice/Library/Application Support/glab-cli/config.yml (line 12 (token))
- [glab] /Users/alice/Library/Application Support/glab-cli/config.yml (line 13 (oauth2_refresh_token))
Suggested aliases (optional, shell-aware):
- alias acli='locksmith exec acli --'
- alias gh='locksmith exec gh --'
- alias glab='locksmith exec glab --'
Blocked scrub example (migration could not source all required values):
$ bin/locksmith integrations migrate glab
Integration migration report:
- glab: stored 0 secret(s)
* missing source value for gitlab/glab/token
* missing source value for gitlab/glab/oauth2_refresh_token
Migration completed, but scrub is still blocked due to missing required Locksmith secret(s):
- glab token requires Locksmith key 'gitlab/glab/token' (import hint: bin/locksmith add gitlab/glab/token "$(glab auth token)" --type oauth_token --owner-app gitlab)
Error: migrate incomplete: missing required Locksmith secret(s)
When migration is blocked, add the missing key(s) shown in output, then rerun integrations migrate.
Use this pattern for GitHub token rotation to avoid long-lived PAT bootstrap credentials.
- Create and install a GitHub App.
- Organization Settings -> Developer settings -> GitHub Apps -> New GitHub App
- Grant only minimum required permissions.
- Install the app on required repositories.
- Generate an app private key in GitHub App settings and store it securely.
- Store app credentials directly in Locksmith:
bin/locksmith add github/app/clientid "Iv1.xxxxx" --type token --owner-app github
bin/locksmith add github/app/installation-id "987654" --type token --owner-app github
bin/locksmith add github/app/private-key "$(cat /secure/path/github-app-private-key.pem)" --type token --owner-app github- Add a rotation rule:
rotation:
- secret: "github/*"
rotator: "github-app-installation-token"
secret_type: "token"
owner_application: "github"
source_url: "https://api.github.com/app/installations/<installation_id>/access_tokens"
metadata:
github_app_client_id: "locksmith://github/app/clientid"
# Optional: explicit installation id. If omitted, Locksmith discovers it via /app/installations.
github_installation_id: "locksmith://github/app/installation-id"
# Optional when app has multiple installations:
# github_installation_account: "bonjoski"
github_app_private_key: "locksmith://github/app/private-key"
ttl: "24h"- Store token secrets with selector context so matching is deterministic:
bin/locksmith add github/ci-token placeholder \
--type token \
--owner-app github \
--source-url https://api.github.com/app/installations/987654/access_tokens- Rotate without exporting GitHub App credentials to shell env:
bin/locksmith rotate github/ci-token401 Unauthorizedor403 Forbiddenduring rotation:- Verify app client ID and installation ID are from the same app.
- Confirm the app is installed on the target repository/organization.
- Check app permissions match token usage.
- Multiple installations found error:
- Set
metadata.github_installation_idexplicitly, or - Set
metadata.github_installation_accountto the org/user login to select the right installation.
- Set
- Invalid private key or JWT signing errors:
- Ensure
github/app/private-keycontains the full PEM (including BEGIN/END lines).
- Ensure
- Rule not matching your secret:
- Confirm secret context matches rule selector (
--type token,--owner-app github, correct--source-url).
- Confirm secret context matches rule selector (
- Missing metadata reference secret:
- Confirm each
locksmith://...key exists in your vault and is readable by Locksmith.
- Confirm each
Locksmith includes a built-in MCP server that allows AI agents to securely interact with your keychain. All tool calls are protected by the same biometric gates as the CLI.
Add Locksmith to your claude_desktop_config.json or Cursor MCP settings:
{
"mcpServers": {
"locksmith": {
"command": "/absolute/path/to/locksmith/bin/locksmith",
"args": ["mcp"]
}
}
}locksmith_get_secret: Retrieve a secret (requires Touch ID/biometrics).locksmith_list_secrets: List names of stored secrets.
Important
When an AI agent requests a secret, you will be prompted for biometrics on your hardware. The AI cannot bypass this gate.
Locksmith can be used as a Summon provider to inject biometric-protected secrets into processes:
make install-summonCreate a secrets.yml file:
AWS_ACCESS_KEY_ID: !var aws/access-key
AWS_SECRET_ACCESS_KEY: !var aws/secret-key
DATABASE_PASSWORD: !var db/passwordRun your command with Summon:
summon --provider locksmith -f secrets.yml <command>Examples:
# Docker with biometric-protected secrets
summon --provider locksmith -f secrets.yml docker run --env-file @SUMMONENVFILE myapp
# Python script with AWS credentials
summon --provider locksmith -f secrets.yml python deploy.py
# Any command that reads environment variables
summon --provider locksmith -f secrets.yml env | grep AWSThis provides Touch ID authentication for your DevOps workflows, ensuring secrets are never exposed in plaintext.
Locksmith can act as a secure, biometric-protected SSH Agent and GPG Pinentry program to safeguard developer keys and passphrases.
- Start the agent:
bin/locksmith agent start
- Set your shell environment:
Add this export to
export SSH_AUTH_SOCK=~/.locksmith/ssh-agent.sock
~/.zshrcor~/.bashrcfor persistence. - Load a key into Locksmith:
bin/locksmith agent add id_ed25519 ~/.ssh/id_ed25519 - Use SSH/Git normally:
Auth and signature operations will prompt for Touch ID/Windows Hello/Polkit.
ssh -T git@github.com git fetch
- Store your GPG passphrase:
bin/locksmith add gpg/passphrase "<your-passphrase>" - Configure gpg-agent in
~/.gnupg/gpg-agent.conf:pinentry-program /absolute/path/to/locksmith/bin/pinentry-locksmith - Set terminal environment for GPG:
Add this export to
export GPG_TTY=$(tty)
~/.zshrcor~/.bashrcfor persistence. - Reload gpg-agent:
gpgconf --kill gpg-agent
- Test Git signing:
Pinentry prompts are routed through Locksmith.
git commit -S -m "signed commit"
- SSH auth still bypasses Locksmith:
- Run
echo "$SSH_AUTH_SOCK"and confirm it is~/.locksmith/ssh-agent.sock. - Ensure the agent is running:
bin/locksmith agent start.
- Run
- GPG pinentry does not use Locksmith:
- Verify
~/.gnupg/gpg-agent.confcontains exactly:pinentry-program /absolute/path/to/locksmith/bin/pinentry-locksmith - Reload agent:
gpgconf --kill gpg-agent.
- Verify
- Terminal signing prompts fail:
- Set terminal binding:
export GPG_TTY=$(tty). - Add it to your shell profile for persistence.
- Set terminal binding:
Locksmith supports optional configuration via ~/.locksmith/config.yml for customizing expiration notifications:
auth:
require_biometrics: true # Enforce Touch ID / Windows Hello
prompt_message: "Authenticate to Locksmith secret '%s'" # Optional custom prompt
notifications:
expiring_threshold: 10d # Warn when secrets expire within this duration
method: stderr # Options: stderr, macos, silent
show_on_get: true # Show warnings on 'get' command
show_on_list: true # Show status on 'list' command
access_control:
allow_binaries:
- "/usr/local/bin/allowed_app"
deny_binaries:
- "/usr/bin/forbidden_app"Locksmith can warn you about expiring or expired secrets:
Get command with warnings:
$ locksmith get aws/key
Warning: Secret 'aws/key' expires in 2 days
AKIAIOSFODNN7EXAMPLEJSON output:
$ locksmith get aws/key --json
{
"key": "aws/key",
"value": "AKIAIOSFODNN7EXAMPLE",
"created_at": "2026-01-01T12:00:00Z",
"expires_at": "2026-02-15T12:00:00Z",
"expires_in": "48h0m0s",
"is_expired": false,
"is_expiring": true
}List command with status:
$ locksmith list
KEY CREATED EXPIRES STATUS
------------------------------------------------------------------------------------
aws/access-key 2026-01-01 2026-02-15 ⚠️ Expiring
db/password 2026-01-15 2027-01-15 ✓ Valid
api/token 2025-12-01 2026-01-01 ❌ ExpiredConfiguration options:
expiring_threshold: Duration formats:10d(days),2w(weeks),1mo(months),1y(years),24h(hours)method:stderr(default): Print warnings to stderrmacos: Show macOS notification popupsilent: No notifications (useful for automation)
See config.example.yml for a complete example.
Locksmith includes a comprehensive suite of quality and security checks.
# Run all quality and security checks
make check
# Run manual biometric regression tests (macOS)
make test-manual
# View all available commands
make helpLocksmith implements defense-in-depth security:
- Hardware-backed security: Biometric authentication is enforced by the macOS Secure Enclave and Windows TPM.
- Biometric protection: Touch ID/Face ID, Apple Watch, and Windows Hello required for sensitive operations.
- Memory zeroing: Secrets cleared from memory immediately after use
- SLSA provenance: Releases include cryptographic attestations
- Continuous Fuzzing: Daily fuzz testing of critical parsing logic
- OpenSSF Scorecard: Automated security assessment
All releases include cryptographic attestations. Verify a binary:
gh attestation verify locksmith-darwin-arm64 --repo bonjoski/locksmithSee RELEASING.md for details.
Distributed under the MIT License. See LICENSE for more information.
For security-related issues, please refer to our Security Policy.
This repository is optimized for AI-assisted development.
- Guidelines: See .memory/guidelines.md.
- Architecture: See .memory/architecture.md for Mermaid diagrams and data flow.
- Tech Stack: Details on libraries and platform bridges in .memory/tech-stack.md.