Skip to content

snapenv run

Runs a command with variables injected — never written to a .env file, and never appearing in anything you (or an AI coding agent driving the CLI on your behalf) actually wrote.

Usage

bash
snapenv run --env prod --only DATABASE_URL -- node server.js
snapenv run --env prod --all -- npm start
snapenv run --env prod --only API_TOKEN -- curl -H "Authorization: Bearer {{API_TOKEN}}" https://api.example.com
snapenv run --env staging --all --except DATABASE_URL -- ./migrate.sh

The -- separator is required — everything after it is the command to run, untouched by snapenv run's own flag parsing.

Flags

FlagDefaultDescription
--env$SNAPENV_ENVEnvironment to pull from
--project$SNAPENV_PROJECTProject UUID
--onlyComma-separated keys to expose (mutually exclusive with --all)
--allExpose every variable in the environment
--exceptComma-separated keys to exclude — only valid with --all

There's no default scope — running without --only or --all is a hard error. That's deliberate: a command that's supposed to see one database URL shouldn't silently gain access to every secret in the environment because a flag was forgotten.

Two ways to receive a value

As a real environment variable in the child process — the common case. Any program that reads process.env / os.Getenv / etc. just works, no code changes:

bash
snapenv run --env prod --only DATABASE_URL -- node server.js
# server.js reads process.env.DATABASE_URL normally

As a placeholder inside the command's own arguments, resolved immediately before exec — for tools like curl where the value has to be a literal argument, not something read from the environment:

bash
snapenv run --only API_TOKEN -- curl -H "Authorization: Bearer {{API_TOKEN}}" https://api.example.com

A placeholder referencing a key outside the current scope is an error, and the command never starts — this is what stops a typo (or an over-eager AI agent guessing at a variable name) from silently passing the literal string through instead of failing loudly.

Output scrubbing

Anything the command prints — stdout or stderr — is scanned as it streams, and any occurrence of an exposed value is replaced with *** before you (or an agent watching the command's output) ever see it. This is what protects against a secret leaking back out through a side channel the command wasn't asked to use: a verbose --debug flag, an accidental console.log, a stack trace on crash.

Values shorter than 6 characters are never scrubbed — a PORT of 3000 or a LOG_LEVEL of debug would otherwise collide with completely unrelated output far more often than they'd ever protect anything.

Scrubbing works correctly even when a value is split across two separate writes to the command's output (e.g. a slow process flushing one byte at a time) — the buffering that makes this reliable trades a small amount of latency (at most one secret's length) for correctness, so still effectively real-time for normal output volumes.

What this is not

snapenv run is not a sandbox. It reduces the chance of an honest mistake — a verbose log, a careless echo $DATABASE_URL, a crash that dumps the environment — not a guarantee against a program that's deliberately trying to exfiltrate its own environment. If you don't trust the command, snapenv run isn't what makes it trustworthy.

Exit codes

The command's own exit code is passed straight through — snapenv run exits with whatever the process it ran exited with. A scope/validation error (bad flags, an out-of-scope placeholder, missing --) exits 1 before the command ever starts.

Built with SnapEnv