README
¶
Locksmith 🔐
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.
Features
- 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.
Installation
Homebrew (macOS)
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 locksmith
Troubleshooting Permission denied (publickey)
If 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/locksmith
Library Usage (Go Module)
Locksmith 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 Deniederrors if not handled correctly.
When importing as a Go module, your build profile determines which compile-time guarded methods are included.
macOS Prerequisites
- 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 Prerequisites
- Windows 10/11 with Windows Hello support.
- Go 1.25.4.
Build from Source
git clone https://github.com/bonjoski/locksmith.git
cd locksmith
make build
make sign
# Binaries are now located in bin/
Usage
Storing a Secret
bin/locksmith add my-service my-password
To 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/github
Rotation uses in-process Go rotators, not shell scripts.
GitLab OAuth Auto-Rotation (Refresh Token Flow)
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 gitlab
Notes:
- 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.
Retrieving a Secret
bin/locksmith get my-service
Listing Keys
bin/locksmith list
Running Commands with Environment Injection (run)
Execute 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 devThis 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_keyRun with:
bin/locksmith run --env-file .env -- npm run dev
Running CLI Integrations with Vault-Backed Tokens (exec)
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 status
You 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/token
Because 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 powershell
Supported 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).
integrations migrate output reference
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.
GitHub App Rotation Quick Start
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-token
GitHub App Troubleshooting
401 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
AI & Model Context Protocol (MCP)
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.
Configuration
Add Locksmith to your claude_desktop_config.json or Cursor MCP settings:
{
"mcpServers": {
"locksmith": {
"command": "/absolute/path/to/locksmith/bin/locksmith",
"args": ["mcp"]
}
}
}
Supported Tools
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.
Summon Integration
Locksmith can be used as a Summon provider to inject biometric-protected secrets into processes:
Installation
make install-summon
Usage
Create a secrets.yml file:
AWS_ACCESS_KEY_ID: !var aws/access-key
AWS_SECRET_ACCESS_KEY: !var aws/secret-key
DATABASE_PASSWORD: !var db/password
Run 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 AWS
This provides Touch ID authentication for your DevOps workflows, ensuring secrets are never exposed in plaintext.
SSH & GPG Agent
Locksmith can act as a secure, biometric-protected SSH Agent and GPG Pinentry program to safeguard developer keys and passphrases.
SSH Agent
- Start the agent:
bin/locksmith agent start - Set your shell environment:
Add this export toexport 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
GPG Pinentry Integration
- 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 toexport 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/GPG Troubleshooting
- 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:
Configuration
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"
Expiration Notifications
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
AKIAIOSFODNN7EXAMPLE
JSON 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 ❌ Expired
Configuration 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 help
Security Features
Locksmith 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
Verifying Releases
All releases include cryptographic attestations. Verify a binary:
gh attestation verify locksmith-darwin-arm64 --repo bonjoski/locksmith
See RELEASING.md for details.
License
Distributed under the MIT License. See LICENSE for more information.
Security
For security-related issues, please refer to our Security Policy.
AI Ready 🤖
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.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
locksmith
command
|
|
|
summon-locksmith
command
|
|
|
pkg
|
|
|
scripts
|
|
|
dev/entropy-checker
command
|