The Problem

Every developer has done it. You're spinning up a local database, you need a password fast, and you type postgres / postgres into the init script without thinking. Then it gets committed. Then it gets shared over Slack. Then somehow — you're not sure how — it ends up in a production .env file because "it was only for dev."

Development environments are where security debt accumulates. Not because developers are careless, but because the friction of doing it right is too high and the consequences feel distant. Hardcoded passwords, committed secrets, and manually shared credentials are the path of least resistance. We decided to make the secure path the easy path instead.

The goal was simple: every password in our stack — from PostgreSQL credentials to JWT secrets — lives in 1Password vault. No exceptions, no shortcuts, no postgres / postgres anywhere.

The Architecture

There are three moving parts, and it helps to understand what each one does before we get into the flow.

1Password is the secret store. We use a service account so that automated processes can read and write vault items without requiring a human to authenticate interactively. Passwords are generated fresh per project, per environment, and stored in structured vault items — never in files, never in environment variables that outlive the process.

The infra gateway is an internal HTTP API that sits in front of PostgreSQL. It accepts provisioning requests — create a schema, create a user, grant privileges, install extensions — and executes them. Critically, it only accepts requests that carry a valid HMAC signature. That means only systems holding the shared signing key can ask it to do anything. No direct database access from developer machines, no manual SQL scripts, no SSH-and-paste.

The developer-facing tooling is the glue. It generates the password, writes it to the vault, signs the provisioning request, and calls the gateway. The developer just says "provision this project" — they never see the password, never touch SQL, and never need direct database access.

Developer
    |
    | provision request
    v
Tooling CLI
    |
    |-- generates random password
    |-- stores in 1Password vault item
    |-- builds HMAC-signed HTTP request
    v
Infra Gateway
    |
    |-- validates HMAC signature
    |-- rejects unsigned requests
    v
PostgreSQL
    |
    |-- CREATE SCHEMA
    |-- CREATE USER (with vault password)
    |-- GRANT privileges
    |-- CREATE EXTENSION

The developer never touches PostgreSQL directly. The gateway is the only thing that does.

How It Works

When you provision a new project, the tooling generates a cryptographically random password — something like 32 bytes of entropy, base64-encoded. It doesn't reuse passwords, it doesn't derive them from anything guessable, and it doesn't store them anywhere except the vault.

That password goes straight to 1Password via the op CLI. The vault item is structured: it has fields for the host, port, database name, username, and password — everything a connection string needs. Nothing is interpolated into a file at this point. The vault item is the source of truth.

Then the tooling constructs an HMAC-signed request to the gateway. The request body includes the project name, environment, and the vault reference — an op:// URI pointing to the credential. The gateway validates the HMAC, reads the password from the vault itself, and runs the SQL. By the time the developer sees a success message, the schema is ready and the password has never appeared in plaintext outside of the vault and the gateway's memory.

When an application container starts, it reads credentials from the vault at launch time using the op CLI or the 1Password Secrets Automation API. The Docker environment gets op://vault/project-dev/password — not the actual password. The vault resolves it. Rotate the password in the vault, restart the container, done. No code changes, no .env file edits, no deployment pipeline secrets to update.

What We Learned

The HMAC authentication on the gateway was the piece we almost skipped — and it would have been a mistake. Without it, any process on the network can send provisioning requests. With it, only systems holding the signing key can. It sounds obvious in retrospect, but the temptation when building internal tooling is to say "it's not exposed externally, so we don't need auth." That reasoning breaks down the moment a container gets compromised or a developer's machine gets on the wrong network.

The other insight was how much the same-flow principle simplifies operations. Provisioning locally and provisioning in production use identical logic — the only difference is which vault path and which gateway endpoint. There's no "dev mode" that skips the vault, no separate script for production, no manual SQL to run before deploying. The mental model is the same everywhere, which means there's nothing to forget and nothing to get wrong when you're tired at 2am.

Zero hardcoded passwords isn't just a security property. It's a forcing function that makes your infrastructure more reproducible, your onboarding faster, and your incident response less panicked. When a credential needs to rotate, you rotate it in one place. Everything else just works.