devAlice
← Multi-OS

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

Item1PasswordBitwarden
PricePaid ($/mo)Free + paid tiers
Security modelE2E + Secret Key (device-authenticated)E2E + master password
CLI quality✅ Excellent (op CLI + biometric)Good (bw CLI)
SSH integration✅ Native SSH agentManual
Git signing✅ commit signing via ssh keyManual
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-cli

1.2 Install (Windows)

winget install --id AgileBits.1Password -e
winget install --id AgileBits.1Password.CLI -e

1.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 prompt

2. 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 true

Now 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.CLI

5.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:latest

Run 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 credential

Share .env.tmpl itself across both machines via Git/Syncthing (values live only in vault — templates are safe).

Verification

  1. op vault list or bw list folders — auth works
  2. ssh -T git@github.com — 1Password SSH agent authenticates with one Touch ID tap
  3. git commit -S -m "test"git log --show-signature → "Good signature"
  4. op run --env-file=.env.tmpl -- env | grep MY_KEY → real value injected
  5. 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

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