Skip to Content
CLI & ConfigEnvironments & Secrets

Environments & Secrets

glubean splits environment data into two files by design: public vars and private secrets.

File model

.env # Public vars — committed to git .env.secrets # Credentials — gitignored .env.staging # Staging vars — committed .env.staging.secrets # Staging credentials — gitignored

Add *.secrets to .gitignore.

Why two files

.env (vars).env.secrets (secrets)
APIctx.vars.require("KEY")ctx.secrets.require("KEY")
GitCommitted — shared baselineGitignored — never in repo
Cloud uploadVisible in dashboardRegistered for redaction by built-in rules

The SDK APIs are separate. vars resolves public variables and secrets resolves credentials. Keep credentials in .env.secrets so they stay gitignored and are treated as sensitive inputs during redaction.

Reading values in tests

const baseUrl = ctx.vars.require("BASE_URL"); // from .env const token = ctx.secrets.require("API_TOKEN"); // from .env.secrets

Template resolution in configure()

{{KEY}} in configure() searches secrets first, then vars:

export const { http: api } = configure({ http: { prefixUrl: "{{BASE_URL}}", headers: { Authorization: "Bearer {{API_TOKEN}}", }, }, });

That template fallback is convenient for shared HTTP options, but it is not a reason to put credentials in .env. .env is public project config.

Lookup order

Each key resolves file-first, then falls back to the system environment:

  • ctx.vars.*("KEY").env file, then process.env.KEY
  • ctx.secrets.*("KEY").env.secrets file, then process.env.KEY

If your shell variable names differ from test names, map them with ${...}:

# .env BASE_URL=${GLUBEAN_URL} # .env.secrets API_KEY=${GLUBEAN_API_KEY}

Switching environments

glubean run # Uses .env (default) glubean run --env-file .env.staging # Uses .env.staging + .env.staging.secrets glubean run --env-file .env.prod # Uses .env.prod + .env.prod.secrets

The CLI automatically pairs each env file with its matching .secrets file.

Next

Last updated on