---
title: CLI Commands
description: Reference documentation for Varlock CLI commands
---

import ExecCommandWidget from "@/components/ExecCommandWidget.astro";

Varlock provides a command-line interface for managing environment variables and secrets. This reference documents all available CLI commands.

See [installation](/getting-started/installation) for instructions on how to install Varlock. You can also enable [shell completion](/guides/shell-completion/) for tab completion of commands and flags.


### 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:

<ExecCommandWidget command="varlock ..." showBinary={false} />

Also note that within package.json scripts, you can use it directly:
```json title="package.json"
{
  "scripts": {
    "start": "varlock run -- node app.js"
  }
}
```

### `package.json` configuration

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

```json title="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 — later entries take 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. |


:::note[Plugins]
While plugins cannot add additional CLI commands, they can extend varlock with additional [resolver functions](/reference/functions/), [data types](/reference/data-types/), and [decorators](/reference/item-decorators/). See the [Plugins guide](/guides/plugins/) for more information.
:::

## Commands reference

<div class="reference-docs">

<div>
### `varlock init` ||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.

```bash
varlock init
```


</div>

<div>
### `varlock load` ||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.

```bash
varlock load [options]
```

**Options:**
- `--format`:     Format of output [pretty|json|env|shell|json-full]
- `--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
- `--env`:        Set the default environment flag (e.g., `--env production`), only useful if not using `@currentEnv` in `.env.schema`
- `--path` / `-p`: 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 — later paths take higher precedence.

**Examples:**
```bash
# 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
```

:::caution
Setting `@currentEnv` in your `.env.schema` will override the `--env` flag.
:::

</div>

<div>
### `varlock run` ||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.

```bash
varlock run -- <command>
```

**Options:**
- `--no-redact-stdout`: Disable stdout/stderr redaction to preserve TTY detection for interactive tools
- `--no-inject-graph`: Disable injection of `__VARLOCK_ENV` serialized config graph into the child process environment
- `--path` / `-p`: Path to a specific `.env` file or directory to use as the entry point. Can be specified multiple times to load from multiple paths — later paths take higher precedence.

**Examples:**
```bash
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
```

:::note[Shell expansion of env vars in commands]
Because of the way that shell expansion works, you may need to use use `sh -c` to properly expand environment variables in your command _after_ varlock has injected them.
```bash
varlock run -- echo $MY_VAR # ❌ will not work
varlock run -- sh -c 'echo $MY_VAR' # ✅ will work
```
:::

:::note[Interactive tools and TTY detection]
By default, `varlock run` pipes stdout/stderr through a redaction filter. To preserve color output, varlock automatically injects `FORCE_COLOR` into the child process environment when the parent terminal is a TTY. This means most CLI tools that use color libraries (chalk, kleur, etc.) will still display colors while redaction remains active.

For tools that require a fully connected TTY (e.g., interactive tools like `psql` or `claude` that use raw terminal features), use `--no-redact-stdout` to bypass piping entirely:
```bash
varlock run --no-redact-stdout -- claude --env development
varlock run --no-redact-stdout -- bash -c 'psql $DATABASE_URL'
```

Note: `--no-redact-stdout` disables stdout/stderr redaction entirely. Use it only when a tool needs true TTY pass-through.
:::

:::note[Preventing sensitive value exposure via env inspection]
By default, `varlock run` injects a `__VARLOCK_ENV` variable containing the full serialized config graph (including resolved sensitive values) into the child process environment. This is used by framework integrations to obtain sensitivity metadata without re-invoking the CLI.

For long-lived processes, interactive shells, or any workflow where subprocesses may inspect their environment (e.g., `env`, `printenv`, or an LLM-driven agent), use `--no-inject-graph` to omit this variable:
```bash
varlock run --no-inject-graph -- bash
varlock run --no-inject-graph -- claude
```

Note: When `--no-inject-graph` is set, framework integrations that rely on `__VARLOCK_ENV` will need to fall back to re-invoking the CLI. `__VARLOCK_RUN=1` is always set regardless of this flag.
:::

</div>

<div>
### `varlock printenv` ||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.

```bash
varlock printenv <VAR_NAME> [options]
```

**Options:**
- `--path` / `-p`: Path to a specific `.env` file or directory to use as the entry point. Can be specified multiple times to load from multiple paths — later paths take higher precedence.

**Examples:**
```bash
# 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)'
```

:::note[Why not use `varlock run -- echo $MY_VAR`?]
Shell expansion happens *before* varlock runs, so `$MY_VAR` is substituted by the shell with whatever value it already has (likely empty). `varlock printenv` avoids this by printing the value directly to stdout, letting you capture it with `$(...)` *after* varlock has resolved it.

```bash
varlock run -- echo $MY_VAR         # ❌ shell expands $MY_VAR before varlock runs
varlock printenv MY_VAR             # ✅ varlock resolves and prints the value
sh -c 'echo $(varlock printenv MY_VAR)'  # ✅ embed in a larger command
```
:::

</div>

<div>
### `varlock scan` ||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.

```bash
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
- `--path` / `-p`:      Path to a specific `.env` file or directory to use as the schema entry point. Can be specified multiple times to load from multiple paths — later paths take higher precedence.

**Examples:**
```bash
# 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
```

:::tip[Git pre-commit hook]
The easiest way to set up scanning as a pre-commit hook is to run:
```bash
varlock scan --install-hook
```
This will detect if you are using a hook manager (like husky or lefthook) and provide the appropriate setup instructions. Otherwise, it will create a `.git/hooks/pre-commit` hook for you automatically.

If varlock is installed as a project dependency, the hook command will be automatically prefixed with your package manager (e.g., `npx varlock scan`).

You can also set it up manually -- see the [Secrets guide](/guides/secrets/#scanning-for-leaked-secrets) for more details.
:::

</div>

<div>
### `varlock encrypt` ||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 and Linux, platform-specific secure storage is used. A pure-JavaScript file-based fallback is available on all platforms.

```bash
varlock encrypt [options]
```

**Options:**
- `--file`:    Path to a `.env` file — encrypts all sensitive plaintext values in-place

**Examples:**
```bash
# 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>")
```

:::tip
Piping via stdin avoids exposing secrets in your shell history. Prefer `printf '%s'` over `echo` to avoid a trailing newline (varlock strips a single trailing newline either way).
:::

In file mode, varlock loads the env graph, identifies `@sensitive` items with plaintext values, and lets you select which to encrypt in-place.

:::tip
Use `varlock encrypt --file .env.local` after adding new secrets to quickly encrypt them all at once.
:::

:::note[Alternative: prompt mode]
Instead of encrypting values ahead of time, you can use `varlock(prompt)` as a placeholder in your `.env` files. On first load, varlock will prompt you to enter the secret and automatically replace the placeholder with the encrypted value. See the [`varlock()` function reference](/reference/functions/#varlock) for details.
:::

</div>

<div>
### `varlock reveal` ||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.

```bash
varlock reveal [VAR_NAME] [options]
```

**Options:**
- `--copy`:        Copy the value to clipboard instead of displaying (auto-clears after 10s)
- `--path` / `-p`: Path to a specific `.env` file or directory to use as the entry point
- `--env`:         Set the environment (e.g., production, development, etc)

**Examples:**
```bash
# 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
```

:::note
Non-sensitive values are not shown by `varlock reveal`. Use [`varlock printenv`](#printenv) for non-sensitive values, or to inject a sensitive value into a command.
:::

:::note[Clipboard support on Linux]
`varlock reveal --copy` uses your system clipboard command (`xclip` or `xsel`) on Linux. Install one of them if copy mode is unavailable.
:::

</div>

<div>
### `varlock lock` ||lock||

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

```bash
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.

:::tip
Use `varlock lock` when stepping away from your machine to ensure the next person to decrypt a secret must authenticate biometrically.
:::

</div>

<div>
### `varlock audit` ||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

Exit codes:
- `0` when schema and code are in sync
- `1` when drift is detected

```bash
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:**
- `--path` / `-p`: Path to a specific `.env` file or directory to use as the schema entry point
- `--ignore` / `-i`: Directory to exclude from code scanning (can be specified multiple times)

**Examples:**
```bash
# 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
```

:::note
When `--path` points to a directory, code scanning is scoped to that directory tree. When it points to a file, scanning is scoped to that file's parent directory.
:::

:::tip[Suppressing false positives]
- Use [`@auditIgnore`](/reference/item-decorators/#auditignore) on individual schema items that are only consumed by external tools and won't appear in your application code.
- Use [`@auditIgnorePaths()`](/reference/root-decorators/#auditignorepaths) to exclude directories (e.g., vendored code, generated files) from the code scan.
:::

</div>

<div>
### `varlock typegen` ||typegen||

Generates type files according to [`@generateTypes`](/reference/root-decorators/#generatetypes) and your config schema. Uses only non-environment-specific schema info, so output is deterministic regardless of which environment is active.

This command is particularly useful when you have set `auto=false` on the [`@generateTypes`](/reference/root-decorators/#generatetypes) decorator to disable automatic type generation during `varlock load` or `varlock run`.

```bash
varlock typegen [options]
```

**Options:**
- `--path` / `-p`: Path to a specific `.env` file or directory to use as the entry point. Can be specified multiple times to load from multiple paths — later paths take higher precedence.

**Examples:**
```bash
# Generate types using the default schema
varlock typegen

# Generate types from a specific .env file
varlock typegen --path .env.prod

# Generate types from multiple directories
varlock typegen -p ./envs -p ./overrides
```

</div>

<div>
### `varlock telemetry` ||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.

```bash
varlock telemetry disable
varlock telemetry enable
```

:::note
You can also temporarily opt out by setting the `VARLOCK_TELEMETRY_DISABLED` environment variable. See the [Telemetry guide](/guides/telemetry/) for more information about our analytics and privacy practices.
:::
</div>

<div>
### `varlock help` ||help||

Displays general help information, alias for `varlock --help`

```bash
varlock help
```

For help about specific commands, use:

```bash
varlock subcommand --help
```

</div>
</div>

