The short answer

Store the canonical copy of each key in an encrypted vault that locks itself, on a Mac with FileVault on and a short screen-lock delay, and never let the key reach your Git working tree.

That's four controls, and the order matters, because each one covers a threat the others don't:

LayerStopsDoes nothing about
FileVaultStolen or resold Mac, powered offAnything while you're logged in
Screen lockSomeone walking up to your deskCode already running as you
Encrypted vault with auto-lockCasual snooping, file copies, stale plaintextMalware active while the vault is unlocked
Git + backup hygieneThe most common real-world leakKeys pasted into chat by hand

"Safest" is not a single app. It's the combination — and knowing which threat each layer actually addresses is what stops you from over-trusting any one of them.


1. FileVault: the floor, not the ceiling

Modern Macs encrypt the internal SSD in hardware whether you ask or not. What FileVault adds is binding the decryption key to your credentials. Without it, the key is available to anything that can boot the machine — which means a stolen laptop is a stolen .env file.

# Should print "FileVault is On."
fdesetup status

If it prints anything else, stop reading and turn it on: System Settings → Privacy & Security → FileVault. (Apple: FileVault)

The critical limitation, and the one people misread: FileVault protects data at rest. Once you log in, the volume is decrypted for the whole session. Every process running as your user reads your files in plaintext. FileVault is why a thief who grabs your closed MacBook at a café gets nothing useful. It is not why a malicious postinstall script in an npm package fails.


2. Screen lock: the layer everyone leaves at 20 minutes

An unlocked, unattended Mac hands over every secret on it, and no encryption helps because the disk is already decrypted.

# 0 = require password immediately after screen saver / sleep
defaults read com.apple.screensaver askForPasswordDelay

Set the grace period to something small — immediately, or a few seconds — in System Settings → Lock Screen. Then make locking a reflex: Control-Command-Q locks instantly, and a Hot Corner set to "Lock Screen" costs nothing.

More detail: how to auto-lock sensitive data on macOS.


3. An encrypted vault that locks itself

Layers 1 and 2 both share a weakness: they protect you when you're away. Most of your day is spent logged in with the screen unlocked, and during those hours a plaintext .env file is simply a plaintext file.

This is the gap an application-level vault closes. Because it holds its own encryption key separately from your login session, it can be locked while you remain logged in.

Why not raw Keychain for everything?

The login keychain is unlocked automatically when you log in and stays unlocked for the session. That's excellent ergonomics and a real security ceiling: any process running as you can ask for an item, and for items your own tools created, it will often get it.

There's also a scaling problem. Ad-hoc security add-generic-password entries are write-once, understand-never:

# Fine for one secret. Unmanageable at forty.
security add-generic-password -a "$USER" -s STRIPE_TEST_KEY -w 'sk_test_...'
security find-generic-password -s STRIPE_TEST_KEY -w

Six months later you cannot tell which of those entries is current, which project it belongs to, or whether it was ever rotated. See macOS Keychain for developers for where Keychain genuinely fits.

What a developer vault adds

PassStore keeps vault data encrypted with AES-256-GCM, wrapping the key with Argon2id so a weak master password costs an attacker real time rather than milliseconds. It unlocks with Touch ID through the Secure Enclave or with your master password, and it re-locks on a timer — so the window where secrets are readable is minutes rather than your whole workday.

Three features matter more than the encryption details, because they change behaviour rather than math:

  • Auto-lock shrinks the exposure window without you thinking about it.
  • Clipboard auto-clear means a copied key doesn't sit in the pasteboard — readable by any process running as you, and persisted to disk by most clipboard-history apps — until the next thing you copy.
  • Vault health audit flags reused and weak values, which is how you discover that the same key is in four projects.

Full detail on the cryptography and threat model: Security. It's open source (MIT), so none of this requires taking our word for it: github.com/ilmakio/PassStore.


4. Keep keys out of Git and out of backups

This is where real leaks come from. Not cryptanalysis — a git add . at 1am.

# Ignore env files but keep a committed template
cat >> .gitignore <<'EOF'
.env
.env.*
!.env.example
EOF

# Stop tracking a file already committed (see the incident guide below)
git rm --cached .env

Add a scanner to your pre-commit hook so the rule is enforced rather than remembered:

brew install gitleaks
gitleaks protect --staged --redact

Full setup: keep secrets out of Git. If a key is already committed, go straight to I accidentally committed an API key — what now?.

Two backup paths people forget

Time Machine keeps deleted files. Deleting a .env from your project does not remove it from snapshots. A secret can live in backups for months after you think it's gone — which is one more reason revocation, not deletion, is what actually ends an exposure.

iCloud Drive syncs Desktop and Documents. If "Desktop & Documents Folders" is enabled and your projects live there, every .env file has been uploaded. Keeping code in ~/Developer or ~/code avoids this entirely. More: why your .env setup is probably leaking.


5. Reduce what a stolen key is worth

Storage is one half. The other half is making the secret on your laptop low-value in the first place:

  • Test keys locally, production keys never. A sk_test_ key on a laptop is an inconvenience if it leaks; a sk_live_ key is an incident.
  • Scope narrowly. A read-only token scoped to one resource beats an admin token you rotate diligently.
  • Prefer short-lived credentials where your stack allows it — aws sso login, OIDC in CI, GitHub App installation tokens. A credential that expires in an hour barely needs a storage strategy.
  • One key per project. Sharing one vendor key across five repos means one leak rotates all five.

See how to store API keys safely without committing them.


What local encryption does not promise

Being direct about the limits, because a security tool that oversells is worse than one you understand:

  • Malware running as you, while the vault is unlocked. No local vault survives this. It can read your memory, your clipboard, and anything you've decrypted. Auto-lock narrows the window; it doesn't close it.
  • You pasting a key into an LLM chat, a ticket, or Slack. The most common exfiltration path in practice, and no encryption addresses it.
  • Phishing and malicious dependencies. A postinstall script with your privileges is inside the boundary, not outside it.
  • A compromised backup destination. Your vault file is encrypted, so that's fine. The plaintext .env next to it is not.
  • Team distribution. A local-first vault is deliberately not a sharing mechanism. If several people need the same production credential, you want a cloud secret manager with audit logs — see local-first vs cloud secret managers.

The honest framing: local encryption raises the cost of the opportunistic attacks that actually happen — a stolen laptop, a synced folder, a committed file, a colleague at your unlocked desk. It does not defend against an attacker already executing code as you. Anyone claiming otherwise is selling something.


Is a password manager enough?

For passwords, yes. For API keys, it depends what you need beyond storage.

Browser-oriented password managers are built around login forms: a URL, a username, a password, autofill. API keys have none of that shape. They need project grouping, .env export, rotation dates, and fast terminal-adjacent copy. You can force them into a password manager — many people do — and the friction shows up as workarounds: keys copied into notes fields, or left in .env because the vault was too slow to reach.

Comparisons: 1Password vs a local secret manager · Bitwarden for developers · best macOS apps for API keys.


A 15-minute setup

  1. fdesetup status → turn FileVault on if it's off.
  2. Set screen lock to require a password immediately; bind a Hot Corner.
  3. Install a vault, set a strong master password, enable Touch ID, set auto-lock to 5–15 minutes.
  4. Move keys out of .env files into the vault, one project at a time; commit .env.example with blank values.
  5. brew install gitleaks and wire it into pre-commit.
  6. Check whether your projects sit inside an iCloud-synced Desktop or Documents folder — and move them if so.
  7. Downgrade every production key on the laptop to a test key.

Step 7 is the highest-leverage item on the list and the one most often skipped.