Skip to content
Go back

The Developer Stack Nobody Told You 1Password Covers

The default use case for 1Password is website logins, credit cards, and secure notes. That’s how it gets described, that’s how it gets recommended.

That framing undersells it by a lot.

I spent a day wiring 1Password into my development environment properly — SSH agent, git commit signing, AWS CLI credentials, secret injection for scripts, and migrating API keys out of AWS Secrets Manager. The result is that my private keys don’t exist as files anymore. My AWS credentials aren’t in ~/.aws/credentials. My .env files contain references, not values. Every credential operation goes through the vault, which means every credential operation requires my approval.

Here’s the full picture, layer by layer.

Layer 1: SSH agent

The default SSH setup puts your private key in ~/.ssh/id_ed25519. That file is readable by any process running as you, included in backups, and permanent until you delete it. You probably set it up once and forgot it was there.

1Password’s SSH agent replaces this. You store your SSH key as an item in your vault. 1Password runs a local agent at a Unix socket on your machine. You point SSH at that socket instead of the default agent:

# ~/.ssh/config
Host *
    IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"

When you ssh anywhere, the request goes to the 1Password agent. 1Password prompts for Touch ID. The signing happens inside the vault. The private key never appears as a file, never touches disk outside the encrypted vault.

You can verify it’s working:

SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock ssh-add -l

That should list your key. If it does, the chain is complete — GitHub, remote servers, anywhere you SSH now goes through 1Password.

The one rough edge: 1Password ships a config file at ~/.config/1Password/ssh/agent.toml that controls which vaults expose keys to the agent. The “Configure for SSH Agent” button in the UI tries to open this file, but macOS has no default handler for .toml files, so you get a dialog asking you to pick an application. Skip the button — just edit the file directly:

[[ssh-keys]]
vault = "Personal"

One line. Tells the agent to expose all keys in your Personal vault. Done.

Layer 2: Git commit signing

Git lets you cryptographically sign commits so they show as “Verified” on GitHub. 1Password handles this natively using the same SSH key you already set up for auth — no separate signing tool, no separate keyring. Three additions to ~/.gitconfig:

[user]
    signingkey = ssh-ed25519 AAAA...your public key here...

[gpg]
    format = ssh

[gpg "ssh"]
    program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"

[commit]
    gpgsign = true

The op-ssh-sign binary ships with 1Password and handles the signing operation through the vault. Every commit is signed. Commits show as verified on GitHub. The same key that authenticates your SSH connections signs your commits — two problems, one key, one vault entry, one Touch ID prompt.

Layer 3: AWS CLI credentials

The standard AWS CLI setup stores credentials in ~/.aws/credentials:

[personal]
aws_access_key_id = AKIA...
aws_secret_access_key = ...

That file is plaintext. It persists indefinitely. Any process running as you can read it.

1Password has a native AWS plugin that replaces this entirely. One command sets it up:

op plugin init aws

It walks you through selecting the vault item that holds your access key and secret, then writes the configuration and adds a shell alias so every aws command automatically runs through 1Password:

# Added to your shell config automatically
alias aws="op plugin run -- aws"

From that point, running any aws command triggers a Touch ID prompt. Credentials are retrieved from the vault, injected into the AWS CLI process, and discarded when it exits. Nothing lands in ~/.aws/credentials. Nothing sits on disk.

Your access key and secret live as fields in a 1Password item, stored once. Every credential retrieval goes through the vault.

Layer 4: The op CLI

The op CLI is the most general-purpose piece of this stack. You install it with Homebrew:

brew install 1password-cli

Once installed and linked to the 1Password app (biometric auth, no separate sign-in), you can retrieve any item from your vault in scripts:

op item get "My Item" --fields label=api_key

The more powerful use case is op run, which injects secrets into a subprocess without ever putting them in a file or shell variable:

op run --env-file=.env.template -- your-command

Where .env.template contains references instead of values:

STRIPE_SECRET_KEY=op://Personal/Stripe/secret_key
DATABASE_URL=op://Personal/Prod DB/connection_string

The op CLI resolves these against your vault at runtime, injects them into the subprocess environment, and discards them when the process exits. Your .env.template can be committed to git. The actual values never appear in the file, never appear in shell history, never appear in logs.

Layer 5: Migrating out of AWS Secrets Manager

Once you have the op CLI and AWS CLI working together, there’s a natural next step: auditing what you already have scattered across AWS Secrets Manager.

Secrets Manager is the right tool for infrastructure secrets — things that running services need at runtime, like a CloudFormation-managed encryption key or an RDS password. It’s the wrong tool for developer credentials: API keys, tokens, personal access tokens. Those end up in Secrets Manager because it’s convenient, not because it’s the right fit. The result is credentials split across two systems with no unified access model.

The migration is straightforward with both tools in hand:

# Retrieve a secret from Secrets Manager
VALUE=$(aws secretsmanager get-secret-value \
  --secret-id api-keys/openai \
  --profile my-account \
  --query SecretString --output text)

# Store it in 1Password
op item create --category "API Credential" \
  --title "OpenAI API Key" \
  --vault Personal \
  "api_key[concealed]=$VALUE"

# Delete the original
aws secretsmanager delete-secret \
  --secret-id api-keys/openai \
  --force-delete-without-recovery \
  --profile my-account

Fetch, create, delete. The op CLI handles the vault write. The AWS CLI handles the read and the cleanup. Touch ID gates both sides of the operation.

The rule I ended up with: Secrets Manager for secrets that services read. 1Password for secrets that humans use. The line is usually obvious once you ask which side of it a given credential falls on.

After the migration, the vault is the single answer to “where are my credentials?” — not “in Secrets Manager, except the ones in .env, except the ones in ~/.aws/credentials.”

The pattern

Looking across all five layers, the pattern is the same:

BeforeAfter
~/.ssh/id_ed25519 (plaintext file)SSH key in vault, accessed via socket
GPG keyring + gpg-agentop-ssh-sign binary, same key as SSH
~/.aws/credentials (plaintext file)credential_process → vault
.env with real values.env.template with vault references
API keys in AWS Secrets ManagerMigrated to vault, Secrets Manager for infra only

One vault. One biometric prompt per operation. One place to rotate credentials when you need to. No plaintext files accumulating across your filesystem, no credentials split across cloud services.

The op CLI is the connective tissue. It’s what makes the AWS layer work, what op run is built on, what the Secrets Manager migration runs through, and what you’d use to wire in any other credential that doesn’t have first-party 1Password support.

What’s not solved yet

Team workflows are more complex. 1Password Teams lets you share vault items, including SSH keys and API credentials, across members. But the operational model for shared infrastructure — who rotates, how rotation propagates, what happens when someone leaves — isn’t handled by the tooling. You can share the vault entry. You’re on your own for the process around it.

Key rotation is also still manual. Updating an SSH key means creating a new one, updating the vault, and updating every service that knows the public key. The vault doesn’t help with the propagation step.

The op plugin init aws setup adds a shell alias — it works in interactive terminal sessions but won’t automatically apply in non-interactive contexts like CI scripts or cron jobs. For those cases you need to invoke op run explicitly or use a different credential injection approach.

So what

The practical value isn’t security theater — it’s that your credential surface becomes auditable. Before this setup, I had no clear answer to “where are my credentials?” After: they’re in the vault. All of them. The question has one answer.

The secondary value is that biometric approval becomes the single checkpoint for all developer operations that touch credentials. SSH into a server: Touch ID. Sign a commit: Touch ID. Run an AWS CLI command: Touch ID. That’s not friction — it’s signal. You know exactly when credential material is being used because you’re approving it.

None of this is set up by default because 1Password doesn’t market itself as a developer tool. The SSH agent feature is buried in Settings → Developer. The op CLI is a separate install. The credential_process pattern requires reading AWS documentation. None of it is hard, but none of it is obvious either.

That’s the gap. The tooling exists. The integration is real. This post is what should have shipped with it.


Share this post on:


Previous Post
1Password Is Infrastructure, Not a Password Manager
Next Post
Failure Modes Are the Fingerprint