OS keychain vs vault vs brokerThree primitives. Three jobs. Pick the right one.
Most developers conflate the OS keychain with a secrets manager and call the broker layer a nice-to-have. Each one solves a different problem. Using the wrong one for the wrong job leaves gaps that stay invisible until a credential leaks.
-
01
Storage
OS keychain
Encrypted at rest, hardware-backed, per-machine
Keychain / libsecret -
02
Policy
Vault
Centralized ACL, rotation, audit log
Vault / 1Password -
03
Execution
Broker
Injects secret into process, then withdraws it
scoped to PID tree
TL;DR· the answer, in twenty seconds
What: The OS keychain stores credentials per-app, per-user, per-machine. A vault adds centralized policy, audit trails, and dynamic secrets. A broker sits at the execution boundary and injects credentials into specific processes at start time, then withdraws them.
Fix: Use the keychain for what it was built for: storing single-machine credentials like SSH keys and personal API tokens. Add a vault for team-scale policy and rotation. Add a broker when an AI coding agent, CI job, or script needs a credential but should not keep it.
Lesson: The layer you are missing is almost always the broker, not the vault. Most teams have storage. Few teams control delivery.
Most developers treat "secrets management" as one thing. It is three. The OS keychain protects credentials at rest on one machine. A vault manages policy and lifecycle across many machines and teams. A broker controls delivery: which process gets which credential, for how long, under what conditions. Each one fails differently when you use it for a job it was not designed for.
Getting this wrong is not hypothetical. GitGuardian's 2026 State of Secrets Sprawl found that AI-assisted commits leak credentials roughly twice as often as the baseline. The root cause, in most of those cases, is credentials sitting in ambient environment state where any subprocess, any tool, any persisted log can read them. That is a delivery-layer problem. A vault does not fix it. A keychain does not either.
Each primitive protects against different threats. Stack them in the right order and the gaps close.
What to know in 60 seconds
- The OS keychain is a single-machine encrypted store. It protects against disk reads and casual theft. It does not audit access, does not scope credentials to specific processes, and is hard to share across a team.
- A vault (HashiCorp Vault, 1Password Secrets Automation, AWS Secrets Manager) adds centralized policy, rotation, and an audit trail. It still delivers secrets into processes as environment variables or files, which means the credential is ambient once it arrives.
- A secret broker controls the moment of delivery. It injects a credential into a specific child process at exec time, keeps it out of the parent environment, and can redact the value from logs. It does not replace a vault. It consumes one.
- GNU pass and Linux secret-service (libsecret) are storage backends, not policy engines. They sit in the same tier as the OS keychain.
- For solo developers with no agents, no CI, and no team, the OS keychain is often enough. Once agents enter the picture, it is not.
What the OS keychain is good for
macOS Keychain Access, Windows Credential Manager, and the Linux secret-service protocol (implemented by GNOME Keyring and KDE Wallet, accessed via libsecret) solve one problem well: storing a credential on one machine so it survives logout and reboot without living in a plaintext file.
macOS Keychain uses the Secure Enclave on Apple Silicon machines. Keys marked non-exportable cannot be extracted by software, even by root. Biometric prompts tie high-sensitivity items to Touch ID or password confirmation. On Apple Silicon, that protection is hardware-backed. Your 1Password Master Password, your SSH private key in ~/.ssh/id_ed25519, your personal Stripe test key for local dev: these belong in the keychain.
Four things the keychain cannot do:
No process scope. Any application that can prompt for keychain access can get the credential. The prompt says "Terminal wants to use your password for X". Approving it once gives terminal access. Running claude code inside that terminal gives the agent access to the same keychain items the terminal can reach, depending on the item's access control list. The keychain does not know the difference between you typing a command and an agent spawning subprocesses.
No audit trail. You cannot query macOS Keychain for "which processes accessed item X between 2pm and 4pm on Tuesday". The access control log in the system log (log show --predicate 'subsystem == "com.apple.security"') is voluminous and process-level, not item-level. You will not find a clean per-secret access record.
Hard to share. iCloud Keychain syncs personal items across your Apple devices. Sharing a team API key via iCloud Keychain is possible but fragile. It has no RBAC, no rotation policy, no expiration, and no way to revoke access for a departing team member short of rotating the credential and re-adding it manually.
No redaction. When a secret from the keychain lands in an environment variable and that variable gets printed to a log, the keychain has no mechanism to detect or suppress the output. The value is in plaintext from the moment the keychain returns it.
GNU pass (Password Store) has the same scope. It is an encrypted store backed by GPG, organized as a directory tree, accessible via the pass command. It handles single-machine storage and, with git integration, manual syncing to a team repo. It does not manage rotation, audit, or delivery scope. libsecret on Linux provides a D-Bus API for the same single-machine keyring. Both are storage backends. Use them for storage.
What a vault adds
HashiCorp Vault, 1Password Secrets Automation, Doppler, Infisical, and AWS Secrets Manager/GCP Secret Manager/Azure Key Vault all operate at the policy layer. The keychain cannot do any of this:
Centralized access control. A vault has a concept of roles, policies, and namespaces. "This CI runner can read secret/prod/stripe_key. That developer can read secret/dev/stripe_key but not the prod version." You can express that. You can enforce it. You can audit it.
Dynamic secrets. HashiCorp Vault can generate short-lived database credentials on demand. The application requests a credential, gets a username and password that expire in one hour, and Vault revokes them automatically when the TTL expires. There is no long-lived static credential to steal. AWS IAM roles with STS work on the same principle.
Central audit log. Every read from Vault is a log entry. You can ask "did anything read secret/prod/stripe_key after the deployment at 3pm?" and get a precise answer. Vault's audit log is structured JSON, supports sinks, and integrates with Splunk, Datadog, and similar platforms.
The secret-zero problem. A vault does not eliminate credentials. It moves them upstream. Something still needs to authenticate to the vault. That first credential, the vault token or approle secret-id or AWS IAM binding, is the secret-zero. It has to live somewhere. Vault's answer is to use hardware attestation (AWS instance identity, GCP service account tokens) or short-lived tokens issued by another system. On a developer's laptop, the answer is often "the vault token lives in ~/.vault-token", which is a file. No different from a keychain entry in terms of theft surface.
Vault adds operational overhead. A self-hosted Vault cluster needs HA configuration, TLS, unsealing, snapshot backups, and someone to run it. HashiCorp Cloud Platform Vault (HCP Vault) reduces the ops burden but adds cost and a cloud dependency. For a solo developer, that overhead is not justified.
1Password Secrets Automation sits closer to the policy layer than the storage layer: service accounts, short-lived tokens, an API. Doppler and Infisical work the same way, syncing secrets to processes via their CLIs. None of them control delivery scope down to the individual process. That gap is what the broker closes.
What a broker adds on top
A vault tells you who is allowed to read a secret. A broker controls exactly when and how the secret appears in a running process, and what happens when that process exits.
The broker pattern is an exec wrapper. Instead of running claude code with secrets in the environment, you run broker exec -- claude code. The broker:
- Authenticates to the vault (or its own local encrypted store) and fetches the credentials for this project.
- Launches the target process with those credentials injected as environment variables or written to temp files with 0600 permissions.
- Keeps the credentials out of the parent shell environment.
echo $STRIPE_KEYin the parent terminal returns nothing. - Records the grant in an append-only audit log.
- When the child process exits, the temp files are removed and the injected environment disappears with the process.
Three gaps a vault leaves open, which a broker closes:
Ambient credential exposure. When you export STRIPE_KEY=... in your shell and run a coding agent, every subprocess the agent spawns inherits that variable. Context window, state files, and tool calls all see the same value. A broker breaks the ambient inheritance by scoping to a specific exec call.
Log exfiltration. If an agent prints environment variables to a log (for debugging, by accident, or via a prompt injection that executes env), a broker with a streaming redactor can suppress known secret values from the output stream. The Knostic disclosure of Claude Code's settings.local.json capturing environment variables in February 2026 is exactly the scenario this prevents. The Anthropic patch reduced the capture. A broker eliminates the ambient surface.
Audit at grant time, not access time. Vault logs every read. A broker logs every exec that received a credential, including the agent profile, the project, the timestamp, and the duration. You can answer "did Claude Code receive the prod database URL during Tuesday's 2pm session?" without parsing Vault audit logs and correlating them to process trees.
A broker has to pull credentials from somewhere: its own local encrypted store or a vault it authenticates to. The secret-zero problem still exists at the broker's authentication layer. Process-tree scoping limits lateral spread. It does not stop a malicious process from dumping its own environment.
Comparison table
| OS keychain | GNU pass / libsecret | HashiCorp Vault | 1Password / Doppler | Secret broker | |
|---|---|---|---|---|---|
| Encrypted at rest | Yes (hardware-backed on Apple Silicon) | Yes (GPG) | Yes | Yes | Yes |
| Multi-machine sharing | iCloud only, fragile | Manual git sync | Yes, native | Yes, native | Via underlying vault |
| Team RBAC | No | No | Yes | Yes | Via underlying vault |
| Audit trail | No | No | Yes | Yes (paid tiers) | Yes, per-exec |
| Dynamic secrets | No | No | Yes | No | No |
| Process-scoped delivery | No | No | No | No | Yes |
| Log redaction | No | No | No | No | Yes |
| Ops overhead | None | Low | High (self-host) / Low (cloud) | Low | Low |
| Secret-zero problem | N/A (local only) | N/A (local only) | Shifted upstream | Shifted upstream | Same |
Stacking them together
Pick one and you have gaps. Use all three in sequence and the gaps close.
Storage layer: The keychain or a local encrypted vault holds the credential that authenticates to your policy layer. On macOS, your 1Password account password or Vault AppRole secret-id lives in Keychain Access. It is hardware-protected, biometric-gated, and machine-local.
Policy layer: Vault or 1Password Secrets Automation holds the application credentials. The CI runner uses its cloud-platform IAM binding to authenticate. The developer's machine uses the token stored in the keychain. RBAC lives here. Rotation lives here. The audit log for reads lives here.
Execution layer: The broker authenticates to the policy layer at exec time, injects credentials into the specific agent or script that needs them, scopes them to that process tree, and records the grant. The agent never sees the vault token. The parent shell never sees the application credentials.
On a developer's laptop: Keychain holds the Vault token (or the broker's master password, or the 1Password service account key). When you start an agent session, the broker fetches the project's credentials and injects them into that process. They live in the process tree. Close the terminal, they're gone. Nothing lingers in your shell environment.
For CI: the runner's cloud IAM role authenticates to Vault via the cloud-native auth method (AWS IAM, GCP service account). The broker wraps the build steps. Each step gets only the credentials it needs, for the duration it runs.
When the OS keychain is enough
Not every developer needs all three layers. The keychain alone is reasonable if:
- You work solo, on one machine, with no team members who need access to the same credentials.
- You do not use AI coding agents. (Or you do, but the sessions never touch credentials that matter if leaked.)
- You do not run CI that produces persistent logs.
- Your threat model is opportunistic theft (stolen laptop, shoulder surfing) rather than targeted attack or insider risk.
For a developer building a personal project with a dev Stripe test key and a personal OpenAI key, macOS Keychain plus a careful .gitignore and .npmignore is a defensible position. The overhead of a vault or broker is not justified by the asset value.
Add any of these: agents that can access production credentials, a team of two or more, CI logs that persist for 90 days, compliance requirements. The keychain alone is no longer sufficient.
GitGuardian's 2026 State of Secrets Sprawl shows which credential categories are leaking fastest: AI service tokens and cloud provider keys, not passwords. Those are exactly the credentials that AI coding agents touch. If your agents can reach them, the delivery layer is where you need to invest.
A checklist you can paste into a PR
## Secrets layer audit
Storage layer
- [ ] Long-lived credentials stored in OS keychain or encrypted local vault, not in ~/.zshrc or ~/.bashrc exports
- [ ] No plaintext .env files committed to git
- [ ] .gitignore includes .env, .env.local, .env.*.local, *.pem, *.key
Policy layer
- [ ] Team credentials in a vault with RBAC (not shared via Slack or email)
- [ ] Rotation policy defined and documented for each credential type
- [ ] Audit log enabled and retained for >= 90 days
Execution layer
- [ ] AI coding agent sessions do not inherit ambient shell credentials
- [ ] CI steps receive only the credentials they need, not a global secrets bundle
- [ ] Log output checked for accidental credential printing (grep for known key prefixes)
- [ ] Temp credential files cleaned up on process exit
Agent-specific
- [ ] .claude/, .cursor/, .aider/, .codex/ in .gitignore and .npmignore
- [ ] No `export API_KEY=...` calls in shell rc files loaded by agent sessions
- [ ] Agent sessions audited: what did the agent receive, when, for how long
What this means for your stack
Audit which layers you have and which ones you are missing.
Most solo developers have storage (keychain) and no policy or execution layer. Most small teams have storage plus a cloud secrets manager, but still deliver credentials via environment variables that agents and CI jobs inherit from the parent process. The execution layer, the thing that controls delivery scope, is the most common gap.
A working execution layer: a local broker authenticates to your vault, injects credentials into a specific agent process at exec time, keeps the values out of ambient environment state, redacts them from log output, and records every grant in a tamper-evident audit log. This runs today on the same machine as your keychain and vault.
hasp is one working implementation. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, and the next agent session receives credentials scoped to its process tree rather than inherited from your shell. Source-available (FCL-1.0), local-first, macOS and Linux, no account.
Whatever you use, storage, policy, and delivery are three separate jobs. Treat them as one and you get credential leaks that no vault audit log catches, because the vault never saw the access.
Sources· cited above, in one place
- GitGuardian State of Secrets Sprawl report
- Knostic Research on AI code editor secret leakage (Claude Code, Cursor)
- HashiCorp Vault Documentation
- 1Password CLI op command-line tool
- Doppler Secrets management platform
- Infisical Open-source secrets management
- AWS Secrets Manager Documentation
- Google Cloud Secret Manager Documentation
- Azure Key Vault Documentation
- Functional Source License FCL-1.0 text
Stop handing the agent your real keys.
hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.
- Local, encrypted vault — no account, no cloud, no telemetry by default.
- Brokered run — agent gets a reference, the child process gets the value.
- Pre-commit + pre-push hooks catch managed values before they ship.
- Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.