Password manager — Mac + Windows unified setup with CLI automation
Set up 1Password / Bitwarden on both machines, with SSH · Git · dotfiles auto-fetch integration.
A password manager pays back at two boundaries — (1) browser auto-fill, (2) automated secret fetch from terminal / dotfiles / CI. If you only use (1), you're getting half the value. Set up (2) and you stop writing plaintext secrets to .env, and a new machine bootstraps with one command.
I believe what makes a developer's password manager usage different from a general user's is the CLI integration layer. Not because browser auto-fill isn't valuable — it is — but rather because developers deal with secrets that go beyond website passwords: SSH keys, API tokens, database credentials, signing certificates. Because those secrets need to be accessible in scripts, dotfiles, and CI pipelines, the op CLI or bw CLI is where the real productivity comes from.
This guide compares 1Password (recommended) · Bitwarden, then walks through unified Mac + Windows setup.
TL;DR
| Item | 1Password | Bitwarden |
|---|---|---|
| Price | Paid ($/mo) | Free + paid tiers |
| Security model | E2E + Secret Key (device-authenticated) | E2E + master password |
| CLI quality | ✅ Excellent (op CLI + biometric) | Good (bw CLI) |
| SSH integration | ✅ Native SSH agent | Manual |
| Git signing | ✅ commit signing via ssh key | Manual |
| Mobile / family share | ✅ | ✅ |
| Self-hosting | ❌ | ✅ (Vaultwarden) |
Recommendation: 1Password (better developer workflow integration). Bitwarden if cost-sensitive or self-hosting needed.
1. 1Password Setup
1.1 Install (Mac)
brew install --cask 1password 1password-cli1.2 Install (Windows)
winget install --id AgileBits.1Password -e
winget install --id AgileBits.1Password.CLI -e1.3 First Sign-In
Desktop app → sign in (Email + Secret Key + Master Password). The Secret Key came from your signup PDF or another device.
1.4 CLI ↔ Desktop Integration
Desktop → Settings → Developer → "Integrate with 1Password CLI" ✅. This is the key step — op commands authenticate via GUI biometric (Touch ID / Windows Hello).
Confirm:
op vault list
# Unlocks via Touch ID/Windows Hello, no password prompt2. SSH Key Integration (1Password)
Traditionally SSH keys are plaintext files at ~/.ssh/id_ed25519. 1Password ships a built-in SSH agent — keys live in the vault and biometric authorizes per use.
2.1 Generate the Key
1Password desktop → "+" → SSH Key:
- Name:
personal-ed25519 - Algorithm: Ed25519
- Key pair auto-generated
2.2 Enable the SSH Agent
Desktop → Settings → Developer → "Use the SSH agent" ✅
- macOS: auto-adds to
~/.ssh/config - Windows: auto-sets the env var
2.3 Register the Public Key with GitHub
1Password vault → that key item → "Public key" copy → GitHub → Settings → SSH and GPG keys → New SSH key.
2.4 Test
ssh -T git@github.com
# Hi yourname! You've successfully authenticated...
# (one Touch ID / Windows Hello tap)No plaintext key files in
~/.ssh/anymore. New machines just sign into 1Password and SSH works.
3. Git Commit Signing (with SSH Key)
Simpler than GPG, reuses the same key:
git config --global gpg.format ssh
git config --global user.signingkey "ssh-ed25519 AAAA... your-comment"
# Copy the public key from the 1Password item
git config --global commit.gpgsign trueNow git commit → 1Password biometric → auto-signed. Register the same public key on GitHub Settings → SSH and GPG keys → "Add new signing key".
4. .env / API Key Integration
Instead of plaintext .env, fetch from 1Password:
4.1 Add the Secret
1Password → "+" → API Credential:
- Title:
Anthropic API Key - Credential:
sk-ant-... - Tags:
dev,api
4.2 CLI Fetch
op item get "Anthropic API Key" --fields credential
# sk-ant-...4.3 op run — Auto-injection
.env.tmpl with placeholders only:
ANTHROPIC_API_KEY=op://Private/Anthropic API Key/credential
GITHUB_TOKEN=op://Private/GitHub/token
Run:
op run --env-file=.env.tmpl -- npm run dev
# The command runs with the real values injected as env vars.env.tmpl is safe to commit (references only). Actual secrets stay in the vault.
4.4 chezmoi Integration
From mac/dotfiles, a chezmoi template:
# dot_env.tmpl
export ANTHROPIC_API_KEY={{ onepasswordRead "op://Private/Anthropic/credential" }}
chezmoi apply auto-injects → produces a plaintext .env on the new machine.
5. Bitwarden — The Free Alternative
5.1 Install
# Mac
brew install --cask bitwarden bitwarden-cli
# Windows
winget install Bitwarden.Bitwarden
winget install Bitwarden.CLI5.2 Sign-in + Session
bw login email@example.com
# Master password → returns an API Key
# Export the session
export BW_SESSION="$(bw unlock --raw)"
# Or define a function in .zshrc:
function bwl { export BW_SESSION="$(bw unlock --raw)"; }5.3 Read Secrets
bw get item "Anthropic API"
bw get password "Anthropic API"
bw get notes "Some Long Note"JSON output, pairs well with jq:
bw get item "GitHub" | jq -r '.login.password'5.4 Weaknesses
- No SSH agent integration (you can store keys manually but they aren't auto-used)
- No automated Git commit signing
- Biometric integration depends on OS/app (less smooth than 1Password)
5.5 Self-Hosting (Vaultwarden)
# Mac/Linux docker
docker run -d \
--name vaultwarden \
-v ./vw-data:/data \
-p 8000:80 \
vaultwarden/server:latestRun on a home server and switch with bw config server http://your-server:8000. Bitwarden clients work as-is.
6. Two-Machine Workflow
Secrets added on Mac are immediately usable on Windows (vault sync is automatic):
# Add a new key on Mac
op item create --category="API Credential" \
--title="New Service" \
--vault="Private" \
credential="sk-..."
# Use it immediately in Windows pwsh
op item get "New Service" --fields credentialShare .env.tmpl itself across both machines via Git/Syncthing (values live only in vault — templates are safe).
Verification
op vault listorbw list folders— auth worksssh -T git@github.com— 1Password SSH agent authenticates with one Touch ID tapgit commit -S -m "test"→git log --show-signature→ "Good signature"op run --env-file=.env.tmpl -- env | grep MY_KEY→ real value injected- Sign into 1Password CLI on Windows → same vault accessible
Troubleshooting
op CLI doesn't pair with the GUI
- Desktop → Settings → Developer → "Integrate with 1Password CLI" missed
- Desktop must be running (signed in)
- macOS: System Settings → Security → grant biometric
Bitwarden session expires
Default 30 min idle lock. Run bw unlock --raw again. Don't put auto-unlock in .zshrc — the master password ends up in the environment.
Two SSH agents (1Password vs ssh-agent)
Pin in ~/.ssh/config:
Host github.com
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
On Windows, set SSH_AUTH_SOCK.
op run mangles quoting
Prefer op inject (clearer): op inject -i .env.tmpl -o .env.runtime && source .env.runtime. A plaintext file is created temporarily — delete after.
Family / team vault permission issues
1Password Business → per-vault permissions. Check which vaults the CLI can reach with op vault list. Commands fail if you lack permission.
References
- mac/dotfiles — chezmoi + 1Password integration
- multi-os/file-sync — sync non-secret files like
.env.tmplvia Syncthing - 1Password CLI (official)
- Bitwarden CLI
- Vaultwarden (self-host)
Changelog
- 2026-05-12: First draft. 1Password vs Bitwarden comparison + SSH integration · Git signing · env auto-injection · two-machine workflow + five troubleshooting cases.
Keep reading
- Dev Container — A unified Mac / Windows / Linux dev environment
Use VS Code Dev Containers to erase OS differences. A Docker-based, reproducible dev env defined by a single devcontainer.json the whole team shares.
- Mac ↔ Windows keyboard mapping — Karabiner + PowerToys Keyboard Manager
Smooth out the Cmd ↔ Ctrl divide between the two OSes. Karabiner-Elements (Mac) and PowerToys Keyboard Manager (Windows) setup plus five common remappings.
- Mac + Windows clipboard sync — Universal Clipboard · 1Clipboard · self-hosted
Three paths to sync text/images between Mac and Windows clipboards instantly. Free, paid, and self-hosted compared.