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/@requiredto select by decorator#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="@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 key name/glob/tag-only 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. 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. @sensitive/@required selectors can’t be scoped this way (which items match isn’t knowable until the graph is already resolved), so a filter using either falls back to resolving and validating everything, same as no --filter at all.
Commands reference
Section titled “Commands reference”varlock init
Section titled “varlock init”Starts an interactive onboarding process to help you get started. Will help create your .env.schema and install varlock as a dependency if necessary.
varlock init [options]Options:
--agent: Run non-interactively for agent/automation workflows. Skips confirmation prompts and uses deterministic defaults for schema generation.
Examples:
# Interactive setup wizardvarlock init
# Non-interactive setup for AI agentsvarlock init --agentvarlock load
Section titled “varlock load”Loads and validates environment variables according to your .env files, and prints the results. Default prints a nicely formatted, colorized summary of the results, but can also print out machine-readable formats.
Useful for debugging locally, and in CI to print out a summary of env vars.
varlock load [options]Options:
--format: Format of output [pretty|json|env|shell|json-full]--agent: Agent-safe mode: defaults to JSON output and redacts sensitive values. Not compatible with--format envor--format shell.--compact: Compact output (json-full: no indentation, env/shell: skip undefined values)--show-all: Shows all items, not just failing ones, when validation is failing--include-internal: Include@internalitems in--format json-fulloutput. Excluded by default (json-fullis commonly consumed programmatically, e.g. by framework integrations, not just for local human inspection) - pass this for local debugging of a secret-zero credential.- Common options:
--env,--path/-p,--clear-cache,--skip-cache,--filter
Examples:
# Load and validate environment variablesvarlock load
# Load and validate for a specific environment (when not using @currentEnv in .env.schema)varlock load --env production
# Output validation results in JSON formatvarlock load --format json
# Output full serialized graph (including errors/configErrors fields)varlock load --format json-full
# Compact outputvarlock load --format json-full --compact
# Output as shell export statements (useful for direnv / eval)eval "$(varlock load --format shell)"
# When validation is failing, will show all items, rather than just failing onesvarlock load --show-all
# Load from a specific .env filevarlock load --path .env.prod
# Load from a specific directoryvarlock load --path ./config/
# Load from multiple directories (later paths take higher precedence)varlock load -p ./envs -p ./overrides
# Agent-safe JSON output with sensitive values redactedvarlock load --agent
# Only show STRIPE_* keys, excluding onevarlock load --filter="STRIPE_*,!STRIPE_DEBUG_KEY"
# Only show items marked @sensitivevarlock load --filter="@sensitive"
# Only show items tagged with @tag(billing)varlock load --filter="#billing"Exit codes:
0when all config is valid- non-zero when validation fails (missing required values, failed coercion, or resolver errors)
Output formats (for scripts & agents):
--format json: a flat{ "KEY": value }map of resolved values on stdout--agent: the same flat map, but@sensitivevalues are redacted (e.g."su▒▒▒▒▒"), so it is safe to print in logs or agent transcripts. Implies JSON output; not compatible with--format env/shell. Combine with--agent --format json-fullto get the redacted full graph.--format json-full: the full serialized graph: top-levelbasePath,sources,config(per-item metadata including resolved value, validation state, and sensitivity),settings. Use this when you need per-item validation/error detail rather than just values. ⚠️ This includes raw resolved secret values, so add--agent(--agent --format json-full) to redact them before logging or feeding to an agent.@internalitems are excluded unless you pass--include-internal.--format env/--format shell: dotenv lines / shellexportstatements with raw values. Never pipe these somewhere that gets logged when secrets are involved.
When emitting machine-readable output, add --summary-stderr (or --summary-file) to get a human-readable, redacted summary on stderr while keeping clean JSON on stdout. This is handy for agents and CI.
varlock run
Section titled “varlock run”Executes a command in a child process, injecting your resolved and validated environment variables from your .env files. This is useful when a code-level integration is not possible.
varlock run -- <command>Exit codes: varlock run exits with the child command’s exit code, so it is transparent in scripts and CI. If varlock itself fails before the command starts (e.g. invalid config or a failed resolver), it exits non-zero without running the command.
Options:
--redact-stdout/--no-redact-stdout: Override automatic stdout/stderr redaction.--redact-stdoutforces redaction of piped/redirected output (e.g., to override@redactLogs=false) and errors if output is attached to an interactive terminal, where redaction is not possible without breaking TTY behavior.--no-redact-stdoutdisables redaction entirely.--inject <mode>/-i: Control what gets injected into the child process environment:all(default: individual vars plus the__VARLOCK_ENVserialized config graph blob),vars(individual vars only, no blob), orblob(only the__VARLOCK_ENVblob, no individual vars)--include-internal: Pass@internalitems through to the child process. By default they are stripped from the child env, even if set in the ambient environment, so a secret-zero token never reaches your app. Use this for a nestedvarlock runwhose own resolution needs the internal value.- Common options:
--path/-p,--clear-cache,--skip-cache,--filter
Examples:
varlock run -- node app.js # Run a Node.js applicationvarlock run -- python script.py # Run a Python script
# Use a specific .env file as entry pointvarlock run --path .env.prod -- node app.js
# Use a specific directory as entry pointvarlock run --path ./config/ -- node app.js
# Use multiple directories as entry pointsvarlock run -p ./envs -p ./overrides -- node app.js
# Only inject STRIPE_* keys, excluding one (also strips them from the __VARLOCK_ENV blob)varlock run --filter="STRIPE_*,!STRIPE_DEBUG_KEY" -- node app.jsvarlock printenv
Section titled “varlock printenv”Resolves and prints the value of a single environment variable to stdout. Only the requested item and its transitive dependencies are resolved, making this faster than loading the full graph.
This is useful within larger shell commands where you need to embed a single resolved env var value.
Exit codes: 0 when the value resolves; non-zero when no variable name is given, the variable is not in the schema, or it fails to resolve.
varlock printenv <VAR_NAME> [options]Positional arguments:
<VAR_NAME>: The variable to print. Required; printenv errors if omitted.
Options:
- Common options:
--path/-p,--clear-cache,--skip-cache
Examples:
# Print the resolved value of MY_VARvarlock printenv MY_VAR
# Use a specific .env file as entry pointvarlock printenv --path .env.prod MY_VAR
# Use multiple directories as entry pointsvarlock printenv -p ./envs -p ./overrides MY_VAR
# Embed in a shell command using subshell expansionsh -c 'some-tool --token $(varlock printenv MY_TOKEN)'varlock explain
Section titled “varlock explain”Shows detailed information about how a single config item is resolved: all of its definitions, sources, overrides, and the final resolved value. This is the go-to command for debugging why a value is what it is (and an agent-friendly way to inspect resolution without dumping every value).
Sensitive values are redacted in the output.
Exit codes: 0 when the item is found and explained; non-zero when no key is given or the key is not in the schema.
varlock explain <VAR_NAME> [options]Positional arguments:
<VAR_NAME>: The config item to explain. Required; explain errors if omitted.
Options:
- Common options:
--env,--path/-p
Examples:
# Explain how a value is resolvedvarlock explain DATABASE_URL
# Explain in the context of a specific environmentvarlock explain --env production API_KEYvarlock scan
Section titled “varlock scan”Scans your project files for sensitive config values that should not appear in plaintext. Loads your varlock config, resolves all @sensitive values, then checks files for any occurrences of those values.
This is especially useful as a pre-commit git hook to prevent accidentally committing secrets into version control, and for scanning build output to ensure no secrets leaked into files that will be published or deployed.
Exit codes: 0 when no plaintext secrets are found; 1 when a leaked value is detected. This makes it usable directly as a pre-commit hook or CI gate.
varlock scan [paths...] [options]Positional arguments:
[paths...]: Optional list of file paths, directories, or glob patterns to scan. When provided, only these targets are scanned: git filtering (--staged,--include-ignored) is bypassed and build-output directories that are normally skipped (such asdist,.next,build) are included.
Options:
--staged: Only scan staged git files (ignored when explicit paths are provided)--include-ignored: Include git-ignored files in the scan (ignored when explicit paths are provided)--install-hook: Set upvarlock scanas a git pre-commit hook- Common options:
--path/-p(here it sets the schema entry point used to resolve sensitive values)
Examples:
# Scan all non-gitignored files in the current directoryvarlock scan
# Only scan staged git filesvarlock scan --staged
# Scan all files, including gitignored onesvarlock scan --include-ignored
# Scan a specific build output directory (e.g. to check for leaked secrets before publishing)varlock scan ./dist
# Scan multiple directoriesvarlock scan ./dist ./public
# Scan files matching a glob patternvarlock scan './dist/**/*.js'
# Use a specific .env file as the schema entry pointvarlock scan --path .env.prod
# Use multiple schema entry pointsvarlock scan -p ./envs -p ./overrides
# Set up as a git pre-commit hookvarlock scan --install-hookvarlock encrypt
Section titled “varlock encrypt”Encrypts sensitive values using device-local encryption. Encrypted values are stored in .env files using the varlock() resolver function and are automatically decrypted at load time.
On macOS, encryption is hardware-backed via the Secure Enclave (with Touch ID / biometric authentication). On Windows, keys are TPM-sealed when a TPM is available (Windows Hello gates interactive decrypts). On Linux, TPM2 and/or Secret Service is used. A pure-JavaScript file-based fallback is available on all platforms.
See the local encryption guide for platform details. On Windows, existing DPAPI keys are automatically upgraded to TPM sealing on the next decrypt.
varlock encrypt [options]Options:
--file: Path to a.envfile; encrypts all sensitive plaintext values in-place
Examples:
# Interactive mode: encrypt a single value (prompts with hidden input)varlock encrypt
# Pipe a value via stdin (keeps secrets out of shell history)printf '%s' "$SECRET" | varlock encryptvarlock encrypt < secret.txt
# Encrypt all sensitive plaintext values in a .env filevarlock encrypt --file .env.localIn single-value mode, you’ll either be prompted to enter a value (hidden input) or the value will be read from stdin when piped. The encrypted output is printed for you to copy into your .env.local file:
SOME_SENSITIVE_KEY=varlock("local:<encrypted>")In file mode, varlock loads the env graph, identifies @sensitive items with plaintext values, and lets you select which to encrypt in-place.
varlock reveal
Section titled “varlock reveal”Securely view or copy the value of a @sensitive environment variable. The value is displayed in an alternate terminal screen buffer so it doesn’t persist in your scrollback history.
🔒 Usually sensitive values are redacted, so this is needed to actually view the value without exposing it in plaintext on disk or in your terminal history.
varlock reveal [VAR_NAME] [options]Options:
--copy: Copy the value to clipboard instead of displaying (auto-clears after 10s)- Common options:
--path/-p,--env
Examples:
# Interactive picker to browse and reveal sensitive valuesvarlock reveal
# Reveal a specific variablevarlock reveal MY_SECRET
# Copy a value to clipboard (auto-clears after 10s)varlock reveal MY_SECRET --copyvarlock lock
Section titled “varlock lock”Locks the encryption daemon, requiring biometric authentication (e.g., Touch ID) for the next decrypt operation. This invalidates the current biometric session cache.
varlock lockThis command only has an effect when using a biometric-enabled encryption backend (macOS Secure Enclave, Windows Hello, or Linux with polkit/PAM biometric setup). On other backends, it will display a message and exit.
varlock audit
Section titled “varlock audit”Scans your source code for environment variable references and compares them against keys defined in your schema.
This command reports two drift categories:
- Missing in schema: key is used in code but not declared in schema
- Unused in schema: key is declared in schema but not referenced in code
Pure execution-environment plumbing, meaning variables that reflect where/how the process runs (e.g. PATH, HOME, SHELL, NODE_OPTIONS, npm_*), is read from process.env in normal code but is never part of your schema, so it is not reported as missing. Semantically meaningful variables your app or CI may depend on (e.g. NODE_ENV, CI, GitHub Actions vars) are still reported, so you can decide whether to declare them or suppress them with @auditIgnore.
Exit codes:
0when schema and code are in sync1when drift is detected
varlock audit [paths...] [options]Positional arguments:
[paths...]: Optional list of directories to scan. When provided, only these directories are scanned instead of the auto-detected scan root.
Options:
- Common options:
--path/-p(here it sets the schema entry point, single path only) --ignore/-i: Directory to exclude from code scanning (can be specified multiple times)
Examples:
# Audit current projectvarlock audit
# Audit using a specific .env file as schema entry pointvarlock audit --path .env.prod
# Audit using a directory as schema entry pointvarlock audit --path ./config
# Only scan specific directoriesvarlock audit ./src ./lib
# Exclude directories from scanningvarlock audit --ignore vendor
# Exclude multiple directoriesvarlock audit -i vendor -i generatedvarlock cache
Section titled “varlock cache”Manage the encrypted disk cache used by cache() and plugin authors when cache mode is set to disk. When run in a TTY, opens an interactive browser; otherwise prints a status summary.
varlock cache [status|clear] [options]Sub-commands:
status: print a non-interactive cache status summary (location, file size, entry counts by group)clear: remove cache entries. Requires--yesfor non-interactive use.
Options:
--plugin <name>: When clearing, only remove entries for a specific plugin--yes/-y: Skip confirmation prompts (required when clearing without a TTY)
Examples:
# Interactive cache browser (or status summary in CI)varlock cache
# Print a non-interactive status summaryvarlock cache status
# Clear all cache entries (prompts to confirm)varlock cache clear
# Clear all cache entries without confirming (e.g. CI)varlock cache clear --yes
# Clear cache for a specific plugin onlyvarlock cache clear --plugin 1password --yesSee the Caching guide for cache mode strategy and troubleshooting.
varlock codegen
Section titled “varlock codegen”Runs the code-generation decorators in your schema: typed env accessors like @generateTsTypes (type declarations) and @generatePythonEnv (a loadable module), plus any generators contributed by plugins. Uses only your schema definitions, so output is deterministic regardless of which environment is active. Keys (and decorators) that come only from value files like .env or .env.local are ignored. Only items declared in your .env.schema (or imported into it) are included. When run directly, the command reports any keys found only in a plain .env so you can move them into your schema if they belong there.
Add a per-language decorator for each output file you want: @generateTsTypes, @generatePythonEnv, @generateRustEnv, @generateGoEnv, or @generatePhpEnv. Each generates its own file. Each decorator can also take a filter= arg to scope its output to a subset of items, using the same selectors as the --filter CLI flag; see the code generation reference.
This command is particularly useful when you have set auto=false on a generator decorator to disable automatic generation during varlock load or varlock run.
varlock codegen [options]Options:
- Common options:
--path/-p
Examples:
# Generate using the default schemavarlock codegen
# Generate from a specific .env filevarlock codegen --path .env.prod
# Generate from multiple directoriesvarlock codegen -p ./envs -p ./overridesvarlock telemetry
Section titled “varlock telemetry”Opts in/out of anonymous usage analytics. This command creates/updates a configuration file at $XDG_CONFIG_HOME/varlock/config.json (defaults to ~/.config/varlock/config.json) saving your preference.
varlock telemetry disablevarlock telemetry enablevarlock generate-key
Section titled “varlock generate-key”Generates a random 256-bit encryption key for use with _VARLOCK_ENV_KEY. This key is used to encrypt the resolved env blob that gets baked into your build output on certain frameworks/platforms.
varlock generate-keySee the Next.js and Vite integration docs for setup instructions.
varlock install-plugin
Section titled “varlock install-plugin”Pre-downloads a plugin from npm into the local varlock plugin cache so it is available without an interactive confirmation prompt. This is mainly for the standalone binary in CI or other non-interactive environments. When varlock runs as a package.json dependency, plugins resolve through your normal node_modules instead.
The plugin must be specified with an exact version (name@version).
varlock install-plugin <name@version>Positional arguments:
<name@version>: The plugin to install, with an exact version (e.g.my-plugin@1.2.3).
Examples:
# Install a plugin at an exact versionvarlock install-plugin my-plugin@1.2.3
# Scoped packagevarlock install-plugin @my-scope/my-plugin@2.0.0varlock help
Section titled “varlock help”Displays general help information, alias for varlock --help
varlock helpFor help about specific commands, use:
varlock subcommand --help