---
title: CLI Reference
description: Reference for the @envlock/cli command-line tool: check, example, diff, inspect, and init commands, flags, exit codes, and programmatic usage.
url: https://pr-1-b9e16090e83d.thally.app/envlock/cli
---

# CLI Reference

Reference for the @envlock/cli command-line tool: check, example, diff, inspect, and init commands, flags, exit codes, and programmatic usage.

# CLI Reference

The `@envlock/cli` package provides the `envlock` command for validating environment variables, rendering `.env.example` files, diffing dotenv files against a contract, and inspecting schemas from the terminal or CI.

## Installation

```sh
npm install -D @envlock/cli
```

Requires Node.js 22 or later. The package is ESM only. Its sole runtime dependency is `@envlock/core`.

After installing, the `envlock` binary is available via `npx envlock` or directly in npm scripts.

## Configuration Discovery

The CLI looks for a config file in the current working directory, probing in order:

1. `envlock.config.mjs`
2. `envlock.config.js`

You can override this with the `--schema` flag on any command.

The config file must use a default export or a named `schema` export:

```js
// envlock.config.mjs
import { defineEnv, env } from "@envlock/core";

export default defineEnv({
  PORT: env.port().default(3000),
  DATABASE_URL: env.url({ protocols: ["postgres:"] }).secret(),
});
```

Or with a named export:

```js
import { defineEnv, env } from "@envlock/core";

export const schema = defineEnv({
  PORT: env.port().default(3000),
});
```

The config is loaded via dynamic `import()`, so the file must be something Node.js can execute directly (`.mjs` or `.js` in an ESM package).

## Commands

### `envlock check`

Validates environment variables against the contract.

```sh
envlock check [flags]
```

| Flag | Type | Description |
|------|------|-------------|
| `--schema` | string | Path to the envlock config file. |
| `--env-file` | string | Validate this dotenv file instead of `process.env`. |
| `--merge-process-env` | boolean | When used with `--env-file`, layers the file over `process.env` (file values win). |
| `--strict` | boolean | With `--env-file`, reports undeclared keys. Ignored without `--env-file` (a note is printed to stderr). |
| `--json` | boolean | Output results as JSON (`{ ok, issues, source }`). |
| `--help` | boolean | Show command help. |

Without `--env-file`, the command validates `process.env` directly. With `--env-file`, it reads and parses the specified dotenv file.

The default output is a `KEY / CODE / MESSAGE` table. With `--json`, results are printed as a JSON object with `ok`, `issues`, and `source` fields.

Exits with code 0 when validation passes, code 1 when issues are found.

```sh
# Validate process.env
envlock check

# Validate a dotenv file in strict mode
envlock check --env-file .env.production --strict

# JSON output for CI
envlock check --env-file .env --json
```

### `envlock example`

Renders a `.env.example` file from the contract.

```sh
envlock example [flags]
```

| Flag | Type | Description |
|------|------|-------------|
| `--schema` | string | Path to the envlock config file. |
| `--out` | string | Write the rendered example to a file instead of stdout. |
| `--check` | boolean | Compare the rendered text against `--out` (defaults to `.env.example`). Exits with code 1 on drift or if the file does not exist. |
| `--help` | boolean | Show command help. |

Without `--out`, the rendered `.env.example` text is printed to stdout. With `--out`, it is written to the specified file path.

The `--check` flag is useful in CI to verify the `.env.example` file is up to date with the contract:

```sh
# Write .env.example
envlock example --out .env.example

# Verify .env.example matches the contract (CI)
envlock example --check
```

### `envlock diff`

Compares a dotenv file with the contract, reporting missing, unknown, and invalid variables. Always runs in strict mode.

```sh
envlock diff [flags]
```

| Flag | Type | Description |
|------|------|-------------|
| `--schema` | string | Path to the envlock config file. |
| `--env-file` | string | Path to the dotenv file to diff. Defaults to `.env`. |
| `--json` | boolean | Output as JSON (`{ missing, unknown, invalid, ok, source }`). |
| `--help` | boolean | Show command help. |

The default output groups results under Missing, Unknown, and Invalid headings. Exits with code 1 when any category is non-empty.

```sh
# Diff .env against the contract
envlock diff

# Diff a specific file with JSON output
envlock diff --env-file .env.staging --json
```

### `envlock inspect`

Prints the contract as a human-readable table or JSON.

```sh
envlock inspect [flags]
```

| Flag | Type | Description |
|------|------|-------------|
| `--schema` | string | Path to the envlock config file. |
| `--json` | boolean | Output the `describeSchema()` result as JSON. |
| `--help` | boolean | Show command help. |

The default output is a table with columns: `KEY / TYPE / REQUIRED / DEFAULT / SECRET / DESCRIPTION`.

```sh
# Human-readable table
envlock inspect

# JSON for tooling
envlock inspect --json
```

### `envlock init`

Creates a starter `envlock.config.mjs` in the current directory.

```sh
envlock init
```

The only accepted flag is `--help`.

The starter config contains a commented contract with `NODE_ENV`, `PORT`, `DATABASE_URL`, and `LOG_LEVEL` as examples to get you started.

If `envlock.config.mjs` or `envlock.config.js` already exists, the command refuses to overwrite it and exits with code 1.

## Global Flags

- `--help` / `-h` -- shows help. At the top level, prints the command list. On a specific command, prints that command's flags.
- `--version` / `-v` -- prints the package version.

## Exit Codes

| Code | Constant | Meaning |
|------|----------|---------|
| 0 | `ok` | Validation passed, no drift, operation succeeded. |
| 1 | `failure` | Validation failed, `.env.example` drift detected, or `init` found an existing config. |
| 2 | `usage` | Unknown command or flag, missing flag value, or unexpected argument. |
| 3 | `config` | Config file not found, import failed, no schema exported, or env file unreadable. |

## Programmatic Usage

The entire CLI is available as a function, making it straightforward to embed in scripts or tests.

### `runCli(argv, io)`

```ts
import { runCli, EXIT_CODES, type CliIo } from "@envlock/cli";

const io: CliIo = {
  stdout: (chunk) => process.stdout.write(chunk),
  stderr: (chunk) => process.stderr.write(chunk),
  cwd: process.cwd(),
  env: process.env,
};

const code = await runCli(["check", "--env-file", ".env"], io);
process.exit(code);
```

`argv` is the argument array without the `node` and script path -- equivalent to `process.argv.slice(2)`. The function returns a `Promise<ExitCode>`.

### `CliIo`

The I/O interface the CLI uses for all side effects:

```ts
interface CliIo {
  readonly stdout: (chunk: string) => void;
  readonly stderr: (chunk: string) => void;
  readonly cwd: string;
  readonly env: Readonly<Record<string, string | undefined>>;
}
```

By providing your own `CliIo`, you can capture output, set a custom working directory, or inject specific environment variables without modifying `process.env`.

### `ExitCode`

The union type `0 | 1 | 2 | 3`.

### `EXIT_CODES`

A frozen object mapping symbolic names to exit code numbers:

```ts
const EXIT_CODES = {
  ok: 0,
  failure: 1,
  usage: 2,
  config: 3,
} as const;
```

### `CONFIG_CANDIDATES`

The filenames probed during config discovery, in order:

```ts
const CONFIG_CANDIDATES = [
  "envlock.config.mjs",
  "envlock.config.js",
] as const;
```

### Config loading functions

The following functions are exported for advanced programmatic use. They underpin the CLI's config discovery and can be used in custom tooling.

`resolveConfigPath(cwd, explicit?)` — resolves the config file path from an explicit `--schema` value or by probing the candidates in `cwd`. Returns a path string or a `SchemaLoadError`.

`importSchema(path)` — imports the module at the given path and extracts the `default` or named `schema` export. Returns a `LoadedSchema` or a `SchemaLoadError`.

`loadSchema(cwd, explicit?)` — combines resolve and import in one call. Returns a `LoadedSchema` or a `SchemaLoadError`.

```ts
interface LoadedSchema {
  readonly schema: EnvSchema;
  readonly path: string;
}

interface SchemaLoadError {
  readonly error: string;
}
```