
Secrets that never touch a file: passwords for scripts and agents
Scripts and agents need secrets: a database password, an API token, the admin password of a service. The easy way, a .env file or a variable typed in a command, spreads them everywhere: backups, shell history, process lists, screenshots, the context of an AI agent. We use one rule instead.
1. Two kinds of secrets, two places
- A person's login passwords live in your password manager (Homelab 3). They are born there, never in a script.
- Machine secrets (database roles, API tokens, encryption keys) live in a secrets manager made for programs, such as Infisical, self-hosted.
2. Read through a pipe
Both have a command-line client. Unlock once per session (you type the master password yourself, in a terminal the script does not read), then pass the secret straight into the program:
bw unlock # you, once: it returns a session key
bw get password "database admin" | psql-admin-tool --password-stdin
The secret exists only in the pipe between the two programs. It is not written to disk, it is not in the command line (which ps shows to every user), and it is not on the screen.
3. With an AI agent
An agent can run the pipe without ever seeing the value: it asks for a secret by name and hands it to the program. Never let an agent read a secrets file, print a value to check it, or unlock the vault: unlocking is yours. A rule an agent can break by mistake should be enforced by a check on its commands, not only written in its instructions.
Traps we paid for
Store before you apply. When you create or change a secret, write it to the vault first, then apply it to the service. If the vault write fails, nothing has changed; the other way round, a failure leaves you locked out.
Copy a secret, never move it. When you move a secret to a new place, the old one stays where its program reads it until that program has been proven to start without it.
After reconnecting a password-manager client, sync it before reading: a fresh local copy may not know your folders yet and answer "not found" for an entry that exists.
Found this useful? Tell us what to measure next.