CLI Commands
Varlock provides a command-line interface for managing environment variables and secrets. This reference documents all available CLI commands.
See installation for instructions on how to install Varlock. You can also enable shell completion for tab completion of commands and flags.
Running commands in JS projects
Section titled “Running commands in JS projects”If you have installed varlock as a package.json dependency, rather than a standalone binary, the best way to invoke the CLI is via your package manager:
npm exec -- varlock ...pnpm exec -- varlock ...bunx varlock ...vlx -- varlock ...yarn exec -- varlock ...Also note that within package.json scripts, you can use it directly:
{ "scripts": { "start": "varlock run -- node app.js" }}package.json configuration
Section titled “package.json configuration”You can configure varlock’s default behavior by adding a varlock key to your package.json:
{ "varlock": { "loadPath": "./envs/" }}| Option | Description |
|---|---|
loadPath | Path (or array of paths) to a directory or specific .env file to use as the default entry point. Defaults to the current working directory if not set. Use a directory path (with trailing /) to automatically load all relevant files (.env.schema, .env, .env.local, etc.); a file path only loads that file and its explicit imports. When an array is provided, all paths are loaded and combined, with later entries taking higher precedence. Can be overridden by the --path CLI flag. Varlock looks for this config in the package.json in the current working directory only. |
Common options
Section titled “Common options”Several commands share these flags. They behave identically wherever they appear, except where a command’s own section calls out a deviation.
--path/-p<path>: Path to a specific.envfile or directory to use as the entry point (overridesvarlock.loadPathinpackage.json). Can be specified multiple times to load from multiple paths, where later paths take higher precedence. (Forscanandauditthis is the schema entry point used to resolve sensitive values;auditaccepts a single path only.)--env <env>: Resolve in the context of a specific environment (e.g.,--env production). Overridden by@currentEnvif it is set in your.env.schema.--clear-cache: Clear the active cache store before resolving values, then re-resolve all values (when combined with--skip-cache, the cache is cleared first, then reads and writes are skipped for the run).--skip-cache: Skip cache entirely for this invocation (no reads or writes). This overrides@cache=disk/@cache=memory.--filter(onloadandrun): Filter which items are shown/injected. See Filtering items below.
Filtering items
Section titled “Filtering items”--filter takes a comma-separated list of selectors:
- a key name or glob, e.g.
STRIPE_*(matches*and?) !selectorto negate any of the below, e.g.!STRIPE_DEBUG_KEY@sensitive/@required/@dynamicto select by decorator (negate for the opposite, e.g.!@dynamicselects static items)#tagnameto select items tagged via@tag(tagname)
How selectors combine: every non-negated selector is OR’d together into one inclusion set, regardless of kind: mixing a glob, a @decorator, and a #tag in the same filter just widens that set. Anything matching a negated (!) selector is then subtracted from that set, again regardless of kind. If a filter has no non-negated selectors at all, the inclusion set starts as “everything” before negations are subtracted.
varlock load --filter="KEY1,!NOT_THIS,STRIPE_*" # KEY1 and STRIPE_* keys, except NOT_THISvarlock load --filter="@sensitive" # only items marked @sensitivevarlock load --filter="@required" # only required itemsvarlock load --filter="#billing" # only items tagged @tag(billing)varlock load --filter="@dynamic" # only runtime-resolved (dynamic) itemsvarlock load --filter="!@dynamic,!@sensitive" # inlineable public items onlyvarlock load --filter="@sensitive,#billing" # sensitive items OR billing-tagged itemsvarlock load --filter="STRIPE_*,!@sensitive" # STRIPE_* keys, minus any that are sensitivevarlock load --filter="!#debug" # everything except items tagged @tag(debug)There’s no way to express an intersection (e.g. “STRIPE_* AND @sensitive”): only unions of non-negated selectors minus unions of negated ones. A negated selector always subtracts from the whole inclusion set; it isn’t scoped to only the positive selector(s) that happened to include a given item.
@internal items follow their usual visibility rules: --filter can only narrow a view further, never cause an internal item to appear somewhere it otherwise wouldn’t (even if a selector matches it by exact key name). On plain json, env, and shell output, and in run’s injected env / __VARLOCK_ENV blob, internal items are always excluded. The views that can show internal items keep doing so under a filter: the default pretty format always shows them, --agent shows them redacted, and --format json-full (on load) and run include them only with --include-internal. In every case, internal items still have to satisfy --filter to appear.
On run, --filter doesn’t just skip injection: excluded schema keys are also stripped from the __VARLOCK_ENV blob and removed from the child environment even when set in the ambient environment (same treatment as @internal items), so a filtered-out var can’t reach the child process at all.
Can also be set via the _VARLOCK_FILTER env var, for wrapper scripts, CI config, or anywhere else passing a CLI flag is inconvenient. An explicit --filter flag takes precedence.
A filter that matches no items (e.g. a typo’d key or tag) prints a warning to stderr. The command still succeeds, with empty output on load or no schema vars injected on run.
A --filter also scopes resolution and validation, not just output: only items it selects (plus their dependencies) are resolved, so an unrelated broken item outside the filter won’t block load/run, and excluded items’ value resolvers (exec commands, secrets managers, etc.) never run. This is useful for scoping validation differently across contexts, e.g. a build step that only needs --filter="#frontend" shouldn’t fail because an unrelated backend-only var is misconfigured, and --filter="!@dynamic" at build time skips runtime-only vars (e.g. platform-injected values that don’t exist yet at build time) including their @required checks.
Decorator selectors match on computed state, which can be value-dependent (e.g. @required=forEnv(prod)), so for those varlock resolves each candidate item’s decorator metadata first (cheap - no value resolvers run), then matches exactly and only resolves values for selected items. Values that decorator functions themselves reference (e.g. @required=eq($OTHER, x) needs OTHER) are true dependencies of evaluating the filter and do get resolved.
Commands
Section titled “Commands”Command docs are grouped by topic:
- Load and run:
load,run,printenv,explain - Project commands:
init,scan,install-plugin,flatten,telemetry,help - Encryption commands:
encrypt,reveal,lock,audit,generate-key - Cache and codegen:
cache,codegen - Proxy command:
proxy