tackle ↑ subaud

creel · masked secret capture into a local .env

Hand an agent a key without the key touching its context.

creel prompts for a secret in a masked field and writes it straight into a local .env. The value goes from your keyboard to the file; the agent that asked for it gets back only a status token saying the key was written — never the secret itself.

the problem

Secrets don't belong in a chat.

When a coding agent needs an API key, the obvious move — paste it into the conversation — is the wrong one: the secret lands in the transcript, the context window, and whatever logs sit behind them. creel is the small tool that closes that gap. The agent asks for a named key; you type it into creel; the agent never sees it.

two ways to run it

Type it yourself, or let a harness drive.

Interactive

Run creel bare (or from a tmux keybind) and it prompts for the name, the destination, and the value in a masked field. A name given on the command line is pre-filled.

$ creel                 # prompts for everything
$ creel STRIPE_SECRET   # name pre-filled, prompts for the value

Harness-driven

Given a name and a status file, creel runs non-interactively for a harness: it captures the value, upserts it into --dest (default .env), and writes a status token — added, updated, or an error — to --status-file. The value is never printed, returned, or logged.

$ creel OPENAI_API_KEY --dest .env --status-file /tmp/creel.status

This is how the pi harness's request_secret tool works: the agent names the key, creel opens for you to type it, and the agent reads only the status token back.

then use it

Hand the key to a command — not back to the agent.

The key is in .env, but a running agent shouldn't read it back: that puts the value straight into the context creel kept it out of. creel exec runs a command with the key in its environment only — the value goes from the file to the child process, never to stdout, argv, or the agent.

$ creel exec OPENAI_API_KEY -- node run.mjs
$ creel exec STRIPE_SECRET,OPENAI_API_KEY -- ./deploy.sh

Language-agnostic — it is just the process environment. Strict by default: a missing key is an error, not an empty value. The command's exit code, signals, and TTY pass straight through. Prior art: aws-vault exec, sops exec-env.

what it guarantees

Small on purpose.

value stays local

The secret lives only in creel's memory and the destination file. It is never echoed to the terminal, returned to a caller, or written to any log.

atomic upsert

creel rewrites the one NAME=value line — adding it or replacing it in place — and leaves the rest of your .env untouched.

confined destination

Driven by a harness, the destination is resolved against the working directory and rejected if it escapes it — an agent can't redirect your key somewhere unexpected.

valid names only

The name must be a legal environment variable ([A-Za-z_][A-Za-z0-9_]*), so the file it writes is always parseable.

install

Install creel, run it.

One command downloads creel, verifies its checksum, and puts it on your PATH (~/.local/bin). macOS and Linux, Apple silicon and x86-64.

$ curl -fsSL https://tackle.tools/install.sh | sh -s creel

# then
$ creel OPENAI_API_KEY

Run it again to upgrade; check your version with creel version. Building from source instead? go install github.com/subaud-tools/tackle/cmd/creel@latest.