monkeys

Profile

A profile is a named set of secrets.

Every secret monkeys remembers sits under one, as <profile>/<KEY>, and a project's .monkeys file lists its keys under the profiles they belong to:

+foo
@test
DATABASE_URL
STRIPE_SECRET_KEY
OPENROUTER_API_KEY

The @ line names the profile and the + line the namespace it sits in, so these three are remembered as foo.test/DATABASE_URL and so on. The keys are the project's and the secrets are yours. Everyone who clones the repository gets the same profile and the same keys, and each of them fills their own vault. That is why the file can be committed and a vault never has to be.

The default profile

The first profile a .monkeys file mentions is the default, the one every command uses in that project when none is given. Inside the checkout, run takes only the command, remember writes under the profile, and preview with no keys shows the file's keys:

monkeys run ./hello.sh
monkeys remember STRIPE_SECRET_KEY        # remembers foo.test/STRIPE_SECRET_KEY
monkeys preview

list shows the whole vault as blocks by profile, so you can see what each one holds.

Several profiles

A profile line can name several profiles, and a file can hold several blocks:

+foo
@test,production
DATABASE_URL
STRIPE_SECRET_KEY
@production
SENTRY_DSN

A profile's keys are those of every block that lists it: both profiles here need DATABASE_URL and STRIPE_SECRET_KEY, and production also needs SENTRY_DSN. The default is still the first, test, so production is something you say.

A secret missing in one profile stops only that profile, and only when it is used: production can be half filled while test runs. doctor shows the whole picture.

The usual names

A name is yours to choose, and monkeys never checks one against a list. These four are what projects tend to mean, and a name the reader already knows needs no explanation:

+foo
@test,staging,production
DATABASE_URL
@production
SENTRY_DSN
@personal
OPENROUTER_API_KEY

test holds the keys that can leak without anyone being woken, production the real ones, and staging a deployment that is not production, for the projects that have one.

test goes at the top, and goes there even when it arrives after the others, unless whoever asked for it says where it belongs. The first profile is the one a command takes when none is given, so the default is the harmless one and production is something you say.

personal is a key each person holds their own copy of rather than one the team shares, a seat on an API they pay for themselves. It belongs in the file because the project needs it, so doctor names it in a fresh checkout, and pack asks before a bundle carries it away. A key that is yours in every project rather than in this one has no profile at all.

Write the names in full, not prod or dev. A prefix is enough to type, so @p already reaches @production, and the file stays readable for whoever did not write it.

Choosing a profile

Any command takes a leading @profile, which picks another declared one. A prefix that fits only one of them is enough, the way a short git hash is:

monkeys run @production ./deploy
monkeys remember @production SENTRY_DSN
monkeys run @prod ./deploy           # production, by its prefix

A profile the file does not declare is refused with the declared ones listed, and a prefix that fits several is refused with those, so a typo never becomes a new profile. A profile name is words of letters, digits, _ and -, joined by ., and the namespace supplies the part before the dot.

No profile

A key remembered outside any project has no profile and needs no @. That is where a secret that belongs to you rather than to a project lives, such as the one a tool you start from anywhere reads:

monkeys remember ANTHROPIC_API_KEY
monkeys run ANTHROPIC_API_KEY claude

A project profile never reads from the keys with no profile. A key missing in @production is missing there even when a copy with no profile exists, so a project cannot quietly pick up a secret meant for another.

Inside a project every command is scoped to that project's profile, so the keys with no profile are out of reach there: monkeys run reads foo.test/, and monkeys remember ANTHROPIC_API_KEY would write foo.test/ANTHROPIC_API_KEY. A bare @ means no profile. It sets the project file aside, so keys are given again, and reaches those keys without leaving the directory:

monkeys run @ ANTHROPIC_API_KEY claude
monkeys remember @ ANTHROPIC_API_KEY
monkeys preview @ ANTHROPIC_API_KEY

It is the same secret either way; the @ only says which profile to look in when a file would otherwise decide.

On this page