Default location: ~/.config/locksmith/config.yaml
Override with --config <path>.
defaults:
session_ttl: 3h # default session TTL (e.g. 1h, 30m)
socket_path: ~/.config/locksmith/locksmith.sock
logging:
level: info # debug | info | warn | error
format: text # text | json
file: ~/.config/locksmith/logs/daemon.log # optional log file
vaults:
<name>:
type: <plugin-type>
# ... plugin-specific fields
keys:
<alias>:
vault: <vault-name>
path: <secret-path>Optional. Log level for daemon output. Default: info.
debug- verbose output; session IDs are logged in plaintext (see security note below)info- standard operational messageswarn- warnings and errors onlyerror- errors only
Optional. Output format. Default: text.
text- human-readable plaintext logsjson- structured JSON for log aggregators
Optional. Path to the log file. If set, all log output is written to this
file instead of stdout. Supports ~ expansion. The parent directory is created
automatically with mode 0700 if it does not exist.
The file is rotated when it reaches 50 MB and files older than 3 days are deleted automatically.
Recommended when running as a background daemon via the shell hook.
Security note: If
logging.levelisdebug, session IDs are written to the log in plaintext. See Debug Logging Security Notice.
locksmith get --vault keychain --path my-account
locksmith get --vault my-gopass --path dev/keyRetrieves secrets from the macOS Keychain using the Security framework. Authorization is triggered by the OS Keychain on each access.
Plugin-specific setup, examples, and troubleshooting:
plugins/keychain/README.md.
Configuration:
vaults:
keychain:
type: keychain
service: com.example.myapp # optional: default Keychain service nameKey path format:
keys:
# Plain account - uses vault-level service (or "locksmith" if unset)
notion-token:
vault: keychain
path: notion
# service/account - overrides vault-level service for this key only
github-token:
vault: keychain
path: github/mytoken
# No service configured - falls back to "locksmith" for backward compatibility
legacy-key:
vault: keychain
path: my-old-accountService resolution order: path prefix service/account > vault service: > "locksmith" (backward-compatible default).
Full example:
vaults:
work:
type: keychain
service: com.acme.work # default service for all keys in this vault
keys:
slack:
vault: work
path: slack # service="com.acme.work", account="slack"
github:
vault: work
path: github/token # service="github", account="token" (overrides vault-level)
legacy:
vault: work
path: legacy-tool # service="locksmith" if vault has no service: setNotes:
- Only available on macOS (darwin/amd64 and darwin/arm64).
- Passwords are stored and retrieved using
SecItemCopyMatchingvia CGo. - Error messages come directly from
SecCopyErrorMessageStringfor readability. - A path with more than one
/is rejected at startup (e.g.a/b/cis invalid; usea/bfor service=a, account=b).
Retrieves secrets from a local 1Password account by shelling out to the
1Password CLI (op). Authorization is delegated to the 1Password 8 desktop
app or to an active op signin session.
Plugin-specific setup, examples, and troubleshooting:
plugins/onepassword/README.md.
Configuration:
vaults:
op:
type: 1password
vault: Personal # optional: default 1Password vault for non-qualified pathsNote: The
vault:key appears in two different contexts and means two different things. Undervaults:,vault: Personalnames the 1Password vault (the container inside your 1Password account). Underkeys:,vault: opnames the locksmith vault alias defined in thevaults:block. Same YAML key, different concepts.
Path forms:
keys:
# Form 1 - full op:// reference (vault-level vault: is ignored)
secret-a:
vault: op
path: "op://Personal/MyItem/password"
# 4-segment form also accepted: op://Personal/MyItem/Section/field
# Form 2 - item/field pair (vault-level vault: required)
secret-b:
vault: op
path: "MyItem/password" # resolves to op://Personal/MyItem/password
# Form 3 - item only, field defaults to password (vault-level vault: required)
secret-c:
vault: op
path: "MyItem" # resolves to op://Personal/MyItem/passwordResolution rules:
- Form 1: the path is passed to
op readas-is; accepts 3-segment (op://vault/item/field) or 4-segment (op://vault/item/section/field) references. - Forms 2 and 3: require
vault:to be set on the locksmith vault entry; the 1Password vault name is prepended automatically. - Empty path, malformed reference, or more than 4 segments returns
InvalidArgument.
Notes:
- Requires the
opCLI installed and on$PATH- seeplugins/onepassword/README.md. - For background daemons, enable "Integrate with 1Password CLI" in 1Password 8 -> Settings -> Developer; daemons run without a TTY and cannot prompt for a passphrase otherwise.
- Service-account tokens (
OP_SERVICE_ACCOUNT_TOKEN), 1Password Connect, and multi-account setups viaOP_ACCOUNTare not configured here - see the plugin README forOP_ACCOUNTdetails. - Windows is not yet supported by the locksmith bundle.
Retrieves secrets from a gopass password store.
Plugin-specific setup, examples, and troubleshooting:
plugins/gopass/README.md.
Configuration:
vaults:
secrets:
type: gopass
store: work # optional: gopass mount name (default: root store)
keys:
notion-token:
vault: secrets
path: personal/notion # gopass path within the storeFull example:
vaults:
personal:
type: gopass # uses root store
work:
type: gopass
store: work # uses "work" gopass mount
keys:
github-token:
vault: work
path: dev/github-api
anthropic-key:
vault: personal
path: ai/anthropicNotes:
- Requires
gopassinstalled and configured (gopass lsmust succeed). store:is passed as the gopass mount name; omit to use the default root store.- GPG passphrase prompts in background daemons require
locksmith-pinentry- see "GPG passphrase and background daemons" below.
agent:
pass_session_to_subagents: trueOptional. Controls whether agents should pass LOCKSMITH_SESSION to
sub-agents they spawn. Default: true.
When true, the agent passes LOCKSMITH_SESSION in the environment when
spawning child agents or tools, allowing them to reuse the parent session
without re-authorization.
When false, each agent obtains its own independent session.
See Agent Integration for the full protocol.
Configure named MCP servers under mcp.servers. Use
locksmith mcp run --server <name> to start them with secrets injected.
mcp:
servers:
github:
command: ["npx", "-y", "@github/mcp"]
env:
GITHUB_TOKEN: github-token # key alias from keys:
my-api:
url: https://api.example.com
transport: auto # sse | http | auto (default: auto)
headers:
Authorization: "Bearer {key:openai-key}"
X-Org-ID: "{vault:keychain path:org/id}"
ad-hoc:
command: ["my-tool"]
env:
API_KEY:
vault: gopass
path: work/api/keyThe first GetSecret call for any mcp.servers.<name> entry fires
on the first MCP request from the AI client, not at locksmith mcp run startup. If a configured MCP server is never invoked, its
secrets are never fetched and no vault prompt is shown. Locksmith
will also transparently start a fresh session and retry once if the
session held by mcp run expires between startup and the first
fetch.
Within proxy mode, locksmith also defers individual auth headers
(those whose value template references the vault via {key:...} or
{vault:... path:...}) until the remote server demands them. Static
header values - those without any { token - are sent from the very
first request. The auth-deferral is automatic and has no config
knob; servers that do not require auth on the MCP handshake therefore
never trigger a vault prompt for that connection.
The same one-shot resolution also fires on JSON-RPC body errors:
if the remote server returns HTTP 200 OK but the response carries a
JSON-RPC error field or a tool-level result.isError: true,
locksmith treats it as an auth-failure signal, resolves the templated
headers, and retries the failing request once. If the retry also
fails, the response is forwarded to the AI client unchanged. No
configuration knob - the behaviour is automatic.
Required (local mode). List of strings: executable followed by
arguments. Mutually exclusive with url.
Optional (local mode). Map of environment variable names to secret references.
Each value is either:
- A string: key alias from
keys:in this config file. - A struct with
vault:andpath:fields for direct vault access.
Required (proxy mode). Remote MCP server URL. Mutually exclusive
with command.
Optional (proxy mode). HTTP transport to use. Default: auto.
auto- try Streamable HTTP (MCP spec 2025-03-26); fall back to SSE on404/405http- Streamable HTTP onlysse- SSE only (legacy)
Optional (proxy mode). Map of HTTP header names to value templates. Template tokens:
| Token | Description |
|---|---|
{key:alias} |
Key alias from keys: |
{vault:name path:value} |
Direct vault + path |
| type | Description |
|---|---|
keychain |
macOS Keychain (CGo) |
1password |
1Password (shells out to the op CLI) |
gopass |
gopass password manager (shells out to gopass CLI) |
Default plugins are placed in ~/.config/locksmith/plugins/ automatically
by locksmith init from the embedded bundle. See
Plugins and PLUGINS.md.
Running locksmith init on a machine that already has a config file at the chosen
path will detect the file and validate it. You will be offered three options:
- Continue with existing config - skip rewriting the config file; proceed with agent and sandbox setup only.
- Overwrite with new config - run the full wizard and replace the file.
- Exit setup - cancel without any changes.
In --auto mode the choice is made automatically: a valid config is kept as-is;
an invalid config is silently replaced.
When running as a background daemon, GPG passphrase prompts require
locksmith-pinentry. See GPG Passphrase and Pinentry.
Sends SIGHUP to the running daemon, causing it to re-read
config.yaml. Active sessions and secret caches are preserved; an
invalid config is rejected and the previous config remains active.
Plugin processes are delta-synced (new types launched, removed ones
killed cleanly). Use reload for pure-config changes that do not
require a re-bind.
Stops the running daemon and starts a fresh one. Use this after
upgrading the locksmith binary or after changes that hot reload
cannot pick up (plugin re-extraction, socket-binding changes).
Hot-reload-eligible config changes are picked up by
locksmith reload; restart is the heavier hammer.
Flags:
--timeout <duration>(default5s) - how long to wait for the old daemon to release the socket before escalating from SIGTERM to SIGKILL.--no-start- stop the daemon but do not start a fresh one.
locksmith init calls the same logic at the end of its run, so a
manual restart is rarely necessary after init.
To start the locksmith daemon automatically when you open a terminal, add a
shell hook. The locksmith init wizard offers to do this for you.
To add it manually, append the following to your shell config file:
bash / zsh / ash (~/.bashrc, ~/.zshrc, or ~/.profile):
# locksmith daemon autostart
if command -v locksmith >/dev/null 2>&1; then locksmith _autostart 2>/dev/null; fifish (~/.config/fish/config.fish):
# locksmith daemon autostart
if command -v locksmith >/dev/null 2>&1; locksmith _autostart 2>/dev/null; endThe hook is idempotent: if the daemon is already running, _autostart exits
immediately without spawning a second process.