Running Commands with Injected Secrets

The run command executes any command with your environment variables injected into it — without ever printing the secret values back to you. This lets you, a script, a CI job, or an AI coding agent use a secret without seeing it.

This is especially useful with AI agents. An agent often needs a real API key or database URL to run a command, but pasting that key into the agent's chat — or letting it read your .env — puts the plaintext secret into its context, its logs, and possibly a commit. With run, the agent writes the command using a placeholder or relies on the process environment, and EnvManager fills in the real value at the moment the command starts. The secret never lands anywhere the agent can read it.

Before You Begin

  • The CLI must be installed and authenticated. See Authentication if you have not run envmanager login yet.
  • You need a project and environment selected. Either run inside a folder with an envmanager.json (see the CLI Overview for envmanager init), or pass --project and --environment on the command line.

How It Works

When you run a command through envmanager run, three things happen together:

  1. Environment injection — the variables you select are added to the child command's environment. A program that reads process.env.DATABASE_URL (or $DATABASE_URL) sees the real value. The value lives only inside that short-lived process; it is never printed.
  2. Placeholder substitution — any {{VARIABLE_NAME}} token in the command is replaced with the real value inside EnvManager, just before the command starts.
  3. Output scrubbing — anything the command prints to its output is filtered, and any secret value that appears is replaced with ***. So an accidental echo, a verbose log, or a stack trace cannot leak the value back to you or your agent.

The command runs directly, not through a shell, so a secret value can never be misinterpreted as a shell instruction.

Choosing Which Variables to Expose

For safety, run will not expose your whole environment unless you ask it to. You must always say which variables the command may use. You can do this in three ways:

OptionMeaning
--only KEY1,KEY2Expose only these variables
--except KEY1Expose everything except these (use with --all)
--allExpose every variable this credential can access

You can also set a persistent allow-list in your envmanager.json so you do not have to repeat --only every time:

{
  "project_id": "your-project-id",
  "environment": "development",
  "agent": {
    "allowed_keys": ["DATABASE_URL", "API_TOKEN"]
  }
}

If you run a command without any of these, EnvManager stops with an error rather than silently exposing everything:

envmanager run -- printenv
# Error: No variable scope set. Specify --only KEY1,KEY2, --all,
# or add "agent.allowed_keys" to envmanager.json.

A --only key that does not exist in the environment is also an error, so a typo never silently does the wrong thing.

Two Ways to Use Your Variables

Environment injection — for programs that read the environment

Most tools (your app, npm, psql, the AWS CLI, and so on) read configuration from environment variables on their own. For these, just list the variables and run the command normally:

envmanager run --only DATABASE_URL,REDIS_URL -- npm start

npm start (and your app underneath it) sees DATABASE_URL and REDIS_URL as ordinary environment variables. You never see the values, and neither does anything reading your terminal.

Placeholders — for one-off commands

When you need to drop a value straight into a command — like a token in an HTTP header — use a {{VARIABLE_NAME}} placeholder:

envmanager run --only API_TOKEN -- \
  curl -H "Authorization: Bearer {{API_TOKEN}}" https://api.example.com/me

Two things matter here:

  • Use {{API_TOKEN}}, not $API_TOKEN. Your own shell would replace $API_TOKEN (with nothing) before EnvManager ever sees the command. The double-brace form passes through your shell untouched, and EnvManager fills it in.
  • Use -- to separate EnvManager's own options from the command you want to run. Everything after -- is treated as the command, so the command's own flags (like -H) are never mistaken for EnvManager options.

If a {{VAR}} refers to a variable that is not in scope, run stops with an error instead of substituting an empty value.

Hiding Values in Output

By default, run scrubs secret variables out of the command's output, replacing each occurrence with ***. Regular (non-secret) variables are left untouched, because they are not sensitive and masking them could mangle normal output.

envmanager run --only API_TOKEN -- sh -c 'echo "token is $API_TOKEN"'
# token is ***

If you want non-secret values masked as well, add --scrub-all:

envmanager run --all --scrub-all -- ./my-script.sh

One caveat: very short secret values (fewer than 6 characters) are not scrubbed, because such short strings appear too often in normal output and masking them would garble it. Avoid relying on run to hide very short secrets.

Security: What This Protects, and What It Does Not

run is designed to stop accidental leaks — a cooperative tool or agent surfacing a secret value into its context, logs, or output. It does this well.

It does not make a secret unreadable to a determined program running on your machine. Anything using your CLI credential could fetch the variables another way (for example with envmanager pull). So run reduces the chance of an honest mistake; it is not a sandbox around an untrusted program.

The practical advice that follows from this: scope tightly. Give a command only the variables it actually needs with --only, rather than --all, so that even in the worst case the exposure is limited to those few keys.

Options

FlagDescription
--only <keys>Comma-separated variable keys to expose
--except <keys>Comma-separated variable keys to exclude (use with --all)
--allExpose all variables this credential can access
--scrub-allAlso mask non-secret values in the output (default: secrets only)
-e, --environment <name>Environment name (default: from config or development)
-p, --project <id>Project ID (default: from config)
--org <name>Organization name (required if you belong to multiple)
--service <name>Limit to a specific service

The command exits with the same exit code as the command it ran, so it works correctly inside scripts and CI pipelines.

Get DevOps tips in your inbox

Security best practices and product updates. No spam.

No spam. Unsubscribe anytime.