---
title: @envlock/cli
description: Command-line tool for validating environment variables against an Envlock contract in local development and CI.
url: https://pr-1-ee19382a0710.thally.app/envlock/cli
---

# @envlock/cli

Command-line tool for validating environment variables against an Envlock contract in local development and CI.

## Installation

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

This adds the `envlock` binary to your project.

## Configuration discovery

When you run any command, the CLI looks for a configuration file in the current working directory, checking in order:

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

The configuration file must export a schema created with `defineEnv()` as either a **default export** or a **named `schema` export**.

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

export default defineEnv({
  NODE_ENV: env.enum(["development", "test", "production"]).default("development"),
  PORT: env.port().default(3000).describe("HTTP listen port"),
});
```

To use a configuration file at a different path, pass `--schema <path>` to any command that accepts it.

## Commands

### `envlock check`

Validates environment variables against the schema. By default it checks `process.env`. With `--env-file` it validates a dotenv file instead.

```sh
envlock check [--schema <path>] [--env-file <path>] [--merge-process-env] [--strict] [--json]
```

| Flag | Description |
|------|-------------|
| `--schema <path>` | Path to the config file. Overrides automatic discovery. |
| `--env-file <path>` | Validate this dotenv file instead of `process.env`. |
| `--merge-process-env` | Layer the dotenv file over `process.env` (file values win). Requires `--env-file`. |
| `--strict` | Report undeclared variables as issues. Only applies with `--env-file`; ignored with a note on stderr otherwise. |
| `--json` | Print output as JSON with `ok`, `issues`, and `source` fields. |

**Output** is a table with `KEY`, `CODE`, and `MESSAGE` columns. On success, prints `ok: <source> satisfies N declared variable(s)`. On failure, prints `error: N issue in <source>` (or `error: N issues in <source>` for plural).

### `envlock example`

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

```sh
envlock example [--schema <path>] [--out <path>] [--check]
```

| Flag | Description |
|------|-------------|
| `--schema <path>` | Path to the config file. |
| `--out <path>` | Write the rendered example to this file path. |
| `--check` | Compare the rendered output against the file at `--out` (defaults to `.env.example`). Exits with code 1 if the file is missing or out of date. |

Use `--check` in CI to ensure your `.env.example` stays in sync with the schema.

### `envlock diff`

Performs a strict diff of a dotenv file against the schema. Always reports missing, unknown, and invalid variables.

```sh
envlock diff [--schema <path>] [--env-file <path>] [--json]
```

| Flag | Description |
|------|-------------|
| `--schema <path>` | Path to the config file. |
| `--env-file <path>` | Path to the dotenv file. Defaults to `.env`. |
| `--json` | Print output as JSON with `missing`, `unknown`, `invalid`, `ok`, and `source` fields. |

Output is grouped into three sections: `Missing (N):`, `Unknown (N):`, and `Invalid (N):` with a table of details. A clean result prints `ok: <file> matches the contract exactly`. A mismatch prints `error: <file> drifts from the contract`.

### `envlock inspect`

Displays a table of all variables declared in the schema.

```sh
envlock inspect [--schema <path>] [--json]
```

| Flag | Description |
|------|-------------|
| `--schema <path>` | Path to the config file. |
| `--json` | Print the result of `describeSchema()` as a JSON array. |

The table shows columns for `KEY`, `TYPE`, `REQUIRED`, `DEFAULT`, `SECRET`, and `DESCRIPTION`. A header line shows `Contract: <path> (N variable(s))`.

### `envlock init`

Writes a starter `envlock.config.mjs` file in the current directory.

```sh
envlock init
```

If a configuration file already exists (`envlock.config.mjs` or `envlock.config.js`), the command refuses and exits with code 1, printing `error: <file> already exists; delete it first if you want a fresh starter`.

On success, prints `ok: wrote envlock.config.mjs; edit it, then run `​`envlock check`​``.

The starter config includes `NODE_ENV`, `PORT`, `DATABASE_URL`, and `LOG_LEVEL` as a starting point.

## Global flags

| Flag | Description |
|------|-------------|
| `--help`, `-h` | Print help text and exit. Also available as the `help` subcommand. |
| `--version`, `-v` | Print the version string and exit. |

Running `envlock` with no arguments prints the help text and exits with code 2.

## Exit codes

| Code | Meaning |
|------|---------|
| 0 | Validation passed, no drift, or informational output (help, version, inspect). |
| 1 | Validation failed, drift detected, or `init` found an existing config file. |
| 2 | Usage error: unknown command, unknown flag, missing flag value, or unexpected positional argument. |
| 3 | Configuration error: config file not found, import failed, no valid schema export, or env file unreadable. |

## Programmatic usage

The CLI can be embedded in other tools via the `runCli` function. It never calls `process.exit` directly; instead it returns an exit code.

```ts
import { runCli, type CliIo, type ExitCode } 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: ExitCode = await runCli(["check", "--strict", "--env-file", ".env"], io);
process.exit(code);
```

The `CliIo` interface has four properties:

- `stdout` -- function that receives text to write to standard output.
- `stderr` -- function that receives text to write to standard error.
- `cwd` -- the working directory used for config discovery and file resolution.
- `env` -- the environment record used when validating `process.env`.

### Exported types and constants

| Export | Description |
|--------|-------------|
| `runCli` | Entry point; accepts `argv` (without the `node`/script prefix) and a `CliIo` object. Returns a `Promise` that resolves to an `ExitCode`. |
| `EXIT_CODES` | Object mapping names to numeric codes: `ok` (0), `failure` (1), `usage` (2), `config` (3). |
| `CliIo` | Interface for injecting I/O and environment into the CLI. |
| `ExitCode` | Type alias for `0 | 1 | 2 | 3`. |
| `CONFIG_CANDIDATES` | Tuple of config file names tried during discovery. |
| `loadSchema` | Loads and validates a schema from a config file path or via discovery. |
| `resolveConfigPath` | Resolves the config file path given a working directory and optional explicit path. |
| `importSchema` | Imports a schema from a resolved config file path. |
| `LoadedSchema` | Type for a successfully loaded schema with its file path. |
| `SchemaLoadError` | Type for a failed schema load with an error message. |
| `STARTER_CONFIG` | The template string written by `envlock init`. |
| `parseArgs` | Low-level argument parser used internally. |
| `FlagSpec` | Type describing a single flag's shape and help text. |
| `ParsedArgs` | Type for the result of `parseArgs`. |