Skip to content

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.

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:

Terminal window
npm exec -- varlock ...

Also note that within package.json scripts, you can use it directly:

package.json
{
"scripts": {
"start": "varlock run -- node app.js"
}
}

You can configure varlock’s default behavior by adding a varlock key to your package.json:

package.json
{
"varlock": {
"loadPath": "./envs/"
}
}
OptionDescription
loadPathPath (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.

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 .env file or directory to use as the entry point (overrides varlock.loadPath in package.json). Can be specified multiple times to load from multiple paths, where later paths take higher precedence. (For scan and audit this is the schema entry point used to resolve sensitive values; audit accepts a single path only.)
  • --env <env>: Resolve in the context of a specific environment (e.g., --env production). Overridden by @currentEnv if 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 (on load and run): Filter which items are shown/injected. See Filtering items below.

--filter takes a comma-separated list of selectors:

  • a key name or glob, e.g. STRIPE_* (matches * and ?)
  • !selector to negate any of the below, e.g. !STRIPE_DEBUG_KEY
  • @sensitive / @required to select by decorator
  • #tagname to 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.

Terminal window
varlock load --filter="KEY1,!NOT_THIS,STRIPE_*" # KEY1 and STRIPE_* keys, except NOT_THIS
varlock load --filter="@sensitive" # only items marked @sensitive
varlock load --filter="@required" # only required items
varlock load --filter="#billing" # only items tagged @tag(billing)
varlock load --filter="@sensitive,#billing" # sensitive items OR billing-tagged items
varlock load --filter="STRIPE_*,!@sensitive" # STRIPE_* keys, minus any that are sensitive
varlock 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.

Starts an interactive onboarding process to help you get started. Will help create your .env.schema and install varlock as a dependency if necessary.

Terminal window
varlock init [options]

Options:

  • --agent: Run non-interactively for agent/automation workflows. Skips confirmation prompts and uses deterministic defaults for schema generation.

Examples:

Terminal window
# Interactive setup wizard
varlock init
# Non-interactive setup for AI agents
varlock init --agent

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.

Terminal window
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 env or --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 @internal items in --format json-full output. Excluded by default (json-full is 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:

Terminal window
# Load and validate environment variables
varlock load
# Load and validate for a specific environment (when not using @currentEnv in .env.schema)
varlock load --env production
# Output validation results in JSON format
varlock load --format json
# Output full serialized graph (including errors/configErrors fields)
varlock load --format json-full
# Compact output
varlock 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 ones
varlock load --show-all
# Load from a specific .env file
varlock load --path .env.prod
# Load from a specific directory
varlock 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 redacted
varlock load --agent
# Only show STRIPE_* keys, excluding one
varlock load --filter="STRIPE_*,!STRIPE_DEBUG_KEY"
# Only show items marked @sensitive
varlock load --filter="@sensitive"
# Only show items tagged with @tag(billing)
varlock load --filter="#billing"

Exit codes:

  • 0 when 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 @sensitive values 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-full to get the redacted full graph.
  • --format json-full: the full serialized graph: top-level basePath, 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. @internal items are excluded unless you pass --include-internal.
  • --format env / --format shell: dotenv lines / shell export statements 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.

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.

Terminal window
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-stdout forces 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-stdout disables redaction entirely.
  • --inject <mode> / -i: Control what gets injected into the child process environment: all (default: individual vars plus the __VARLOCK_ENV serialized config graph blob), vars (individual vars only, no blob), or blob (only the __VARLOCK_ENV blob, no individual vars)
  • --include-internal: Pass @internal items 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 nested varlock run whose own resolution needs the internal value.
  • Common options: --path / -p, --clear-cache, --skip-cache, --filter

Examples:

Terminal window
varlock run -- node app.js # Run a Node.js application
varlock run -- python script.py # Run a Python script
# Use a specific .env file as entry point
varlock run --path .env.prod -- node app.js
# Use a specific directory as entry point
varlock run --path ./config/ -- node app.js
# Use multiple directories as entry points
varlock 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.js

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.

Terminal window
varlock printenv <VAR_NAME> [options]

Positional arguments:

  • <VAR_NAME>: The variable to print. Required; printenv errors if omitted.

Options:

Examples:

Terminal window
# Print the resolved value of MY_VAR
varlock printenv MY_VAR
# Use a specific .env file as entry point
varlock printenv --path .env.prod MY_VAR
# Use multiple directories as entry points
varlock printenv -p ./envs -p ./overrides MY_VAR
# Embed in a shell command using subshell expansion
sh -c 'some-tool --token $(varlock printenv MY_TOKEN)'

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.

Terminal window
varlock explain <VAR_NAME> [options]

Positional arguments:

  • <VAR_NAME>: The config item to explain. Required; explain errors if omitted.

Options:

Examples:

Terminal window
# Explain how a value is resolved
varlock explain DATABASE_URL
# Explain in the context of a specific environment
varlock explain --env production API_KEY

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.

Terminal window
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 as dist, .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 up varlock scan as a git pre-commit hook
  • Common options: --path / -p (here it sets the schema entry point used to resolve sensitive values)

Examples:

Terminal window
# Scan all non-gitignored files in the current directory
varlock scan
# Only scan staged git files
varlock scan --staged
# Scan all files, including gitignored ones
varlock scan --include-ignored
# Scan a specific build output directory (e.g. to check for leaked secrets before publishing)
varlock scan ./dist
# Scan multiple directories
varlock scan ./dist ./public
# Scan files matching a glob pattern
varlock scan './dist/**/*.js'
# Use a specific .env file as the schema entry point
varlock scan --path .env.prod
# Use multiple schema entry points
varlock scan -p ./envs -p ./overrides
# Set up as a git pre-commit hook
varlock scan --install-hook

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.

Terminal window
varlock encrypt [options]

Options:

  • --file: Path to a .env file; encrypts all sensitive plaintext values in-place

Examples:

Terminal window
# 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 encrypt
varlock encrypt < secret.txt
# Encrypt all sensitive plaintext values in a .env file
varlock encrypt --file .env.local

In 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.

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.

Terminal window
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:

Terminal window
# Interactive picker to browse and reveal sensitive values
varlock reveal
# Reveal a specific variable
varlock reveal MY_SECRET
# Copy a value to clipboard (auto-clears after 10s)
varlock reveal MY_SECRET --copy

Locks the encryption daemon, requiring biometric authentication (e.g., Touch ID) for the next decrypt operation. This invalidates the current biometric session cache.

Terminal window
varlock lock

This 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.

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:

  • 0 when schema and code are in sync
  • 1 when drift is detected
Terminal window
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:

Terminal window
# Audit current project
varlock audit
# Audit using a specific .env file as schema entry point
varlock audit --path .env.prod
# Audit using a directory as schema entry point
varlock audit --path ./config
# Only scan specific directories
varlock audit ./src ./lib
# Exclude directories from scanning
varlock audit --ignore vendor
# Exclude multiple directories
varlock audit -i vendor -i generated

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.

Terminal window
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 --yes for 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:

Terminal window
# Interactive cache browser (or status summary in CI)
varlock cache
# Print a non-interactive status summary
varlock 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 only
varlock cache clear --plugin 1password --yes

See the Caching guide for cache mode strategy and troubleshooting.

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.

Terminal window
varlock codegen [options]

Options:

Examples:

Terminal window
# Generate using the default schema
varlock codegen
# Generate from a specific .env file
varlock codegen --path .env.prod
# Generate from multiple directories
varlock codegen -p ./envs -p ./overrides

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.

Terminal window
varlock telemetry disable
varlock telemetry enable

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.

Terminal window
varlock generate-key

See the Next.js and Vite integration docs for setup instructions.

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).

Terminal window
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:

Terminal window
# Install a plugin at an exact version
varlock install-plugin my-plugin@1.2.3
# Scoped package
varlock install-plugin @my-scope/my-plugin@2.0.0

Displays general help information, alias for varlock --help

Terminal window
varlock help

For help about specific commands, use:

Terminal window
varlock subcommand --help