---
title: "The Builder Stack Nobody Told You 1Password Covers"
description: "1Password gets described as a password manager. It also replaces SSH key files, GPG for git signing, .env files, AWS credential storage, and scattered Secrets Manager entries — all from one vault, all behind a single biometric prompt."
canonical_url: "https://artificialcuriositylabs.ai/posts/1password-builder-stack/"
md_url: "https://artificialcuriositylabs.ai/posts/1password-builder-stack.md"
published_at: "2026-05-16T00:00:00.000Z"
tags:
  - "infrastructure"
  - "build-log"
  - "patterns"
---

## TL;DR

- Five credential surfaces — SSH keys, git signing, AWS CLI, `.env` files, AWS Secrets Manager — all replaced by 1Password vault-backed alternatives with a single biometric prompt per operation.
- Private keys no longer exist as files; `~/.aws/credentials` goes away; `.env.template` commits vault references, not values.
- `op run` injects secrets into any subprocess at launch — the process sees normal env vars, never touches disk.
- The rule that emerged: Secrets Manager for secrets that services read; 1Password for secrets that humans use.
- If your answer to "where are my credentials?" isn't one word, set this up.

---

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:

```bash
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:

```toml
[[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`:

```ini
[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`:

```ini
[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:

```bash
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:

```bash
# 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:

```bash
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:

```bash
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:

```bash
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 builder 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:

```bash
# 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:

| Before | After |
|---|---|
| `~/.ssh/id_ed25519` (plaintext file) | SSH key in vault, accessed via socket |
| GPG keyring + `gpg-agent` | `op-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 Manager | Migrated 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 builder 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 builder 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.

---

*This is the first post in a six-post series on 1Password as infrastructure. The [second](https://artificialcuriositylabs.ai/posts/1password-builder-stack/1password-cli-ai-native-vault-organization) covers what an AI agent can do when it operates the vault directly. The [third](https://artificialcuriositylabs.ai/posts/1password-builder-stack/1password-infrastructure-not-password-manager) covers service accounts and runtime credential infrastructure. The [fourth](https://artificialcuriositylabs.ai/posts/1password-builder-stack/1password-trust-anchor-auth-chain) covers 1Password as the trust anchor in a live authentication chain. The [fifth](https://artificialcuriositylabs.ai/posts/1password-builder-stack/1password-same-pattern-solo-to-enterprise) covers how the same pattern scales from solo builder to enterprise. The [sixth](https://artificialcuriositylabs.ai/posts/1password-builder-stack/1password-op-run-tool-transparent) covers op run — making any tool vault-transparent.*
