env-sentinel is a lightweight, zero-dependency tool for analyzing .env files β offering both schema-based validation and powerful linting to ensure your environment variables are correct, consistent, and production-safe.
In addition to verifying variable types and required keys, env-sentinel can detect subtle and hard-to-spot issues such as malformed keys, unsafe characters, unescaped shell tokens, duplicate references, invalid syntax, and YAML pitfalls β problems that typical schema validation can't catch.
With fast execution and a human-readable schema format, env-sentinel eliminates the guesswork of .env.example files and bloated config validators β giving you clear, actionable feedback without writing custom validation logic.
π Read the full documentation at envsentinel.dev/docs β
Find detailed guides, examples, and API references on our official documentation site:
- Quickstart Guide - Get started in minutes
- Linting - Catch unused and inconsistent variables
- Validation - Validate against schema to prevent misconfigurations
- Documenting - Generate clear documentation for your team
- API Reference - Integration guides for programmatic use
- β Lint .env files to catch formatting issues, unsafe syntax, and common misconfigurations
- β Validate environment variables against a defined schema
- β Generate markdown documentation from annotated schemas
- β Simple schema format (e.g. VAR_NAME=required|number)
- β Smart type detection when generating from .env
- β Auto-generate schema with type inference from existing .env files
- β Zero dependencies and extremely fast
- β Ideal for local development, CI/CD, and team workflows
- β Fast fail with clear, colorized output
- β Available as both CLI tool and integrable library
The easiest way to run env-sentinel is with npx (no installation required):
npx env-sentinel lint --file .envAlternatively, you can install it globally or as a local project dependency:
# Global
npm install -g env-sentinel
# Local (in your project)
npm install --save-dev env-sentinel# Global
yarn global add env-sentinel
# Local
yarn add --dev env-sentinel# Global
pnpm add -g env-sentinel
# Local
pnpm add -D env-sentinel# Lint default .env file
npx env-sentinel lint
# Lint specific file
npx env-sentinel lint --file .env.production# Generate schema from .env
npx env-sentinel init
# Overwrite existing schema
npx env-sentinel init --force
# Generate from different file
npx env-sentinel init --file .env.local# Validate default files (.env against .env-sentinel)
npx env-sentinel validate
# Validate custom files
npx env-sentinel validate --file .env.production --schema config/prod.schema# Generate documentation from schema
npx env-sentinel docs
# Specify custom files
npx env-sentinel docs --schema .env-sentinel-example --output CONFIG.mdnpx env-sentinel lint [--file <path>]Options:
--file <path>- Path to .env file (default:.env)
What it checks:
- Invalid key characters
- Missing or malformed values
- Duplicate keys
- Unsafe shell characters
- YAML boolean literals
- And 20+ other formatting rules
npx env-sentinel validate [--file <env-file>] [--schema <schema-file>]Options:
--file <path>- Path to .env file (default:.env)--schema <path>- Path to schema file (default:.env-sentinel)
What it validates:
- Required variables
- Type checking (number, boolean, string)
- Value constraints (min, max, enum)
- Security checks
npx env-sentinel init [--file <env-file>] [--force]Options:
--file <path>- Source .env file (default:.env)--force- Overwrite existing schema file
Features:
- Auto-detects types (number, boolean, string)
- Infers required/optional based on usage
- Skips invalid entries and reports them
npx env-sentinel docs [--schema <schema-file>] [--output <output-file>]Options:
--schema <path>- Schema file to document (default:.env-sentinel)--output <path>- Output markdown file (default:CONFIGURATION.md)
Features:
- Generates markdown documentation from annotated schema
- Supports sections, descriptions, examples, and constraints
- Creates a table of contents for easy navigation
- Highlights sensitive variables (marked with
securevalidator) - See example schema and generated output
The .env-sentinel file defines validation rules for your environment variables. Think of it as a contract that your .env file must follow.
Quick example:
# Your .env file
DB_HOST=localhost
DB_PORT=5432
API_KEY=my-secret-key-12345# Your .env-sentinel schema file
DB_HOST=required
DB_PORT=required|number|min:1|max:65535
API_KEY=required|min:16When you run npx env-sentinel validate, it checks that:
- β
DB_HOSTexists (it does: "localhost") - β
DB_PORTexists AND is a number between 1-65535 (it is: 5432) - β
API_KEYexists AND is at least 16 characters long (it is)
Each line in .env-sentinel represents a variable and its validation rules. You can combine multiple rules using the pipe (|) character:
# Simple validation - just check if exists
DB_HOST=required
# Type validation - must be a number
DB_PORT=required|number
# With constraints - number between 1 and 65535
DB_PORT=required|number|min:1|max:65535
# Optional variables
DEBUG=boolean
NODE_ENV=enum:development,production,test
# String validation - minimum length
API_KEY=required|min:32
# Complex example - all together
DB_PASSWORD=required|secure|min:12|default:"changeme"Add documentation annotations using comment tags for markdown generation:
# @section Database
# @description Database connection settings
# @var Database server hostname
# @example localhost
DB_HOST=required
# @var Database server port
# @example 5432
DB_PORT=required|number|min:1|max:65535
# @var Database password (keep secure!)
DB_PASS=required|secure|min:8Available tags:
# @section <name>- Group variables into sections# @description <text>- Add description for section (supports multi-line)# @var <description>- Document a variable (supports multi-line)# @example <value>- Provide example value
Documentation features:
- Multi-line descriptions are supported
- Variables with
securevalidator are automatically highlighted with π - Default values from
default:"value"are shown in documentation - Type and constraints (min/max/enum) are automatically extracted
- Table of contents is generated for sections
See .env-sentinel-example for a full example.
Use these rules in your .env-sentinel schema file to validate environment variables. Multiple rules can be combined using the pipe (|) separator.
| Rule | What it does | Usage Example | Common use cases |
|---|---|---|---|
required |
Variable must exist and have a value | DB_HOST=required |
Critical variables like database connections, API endpoints |
number |
Value must be a valid number (integer or decimal) | PORT=required|number |
Ports, IDs, timeouts, counts, rate limits |
boolean |
Value must be exactly true or false |
DEBUG=boolean |
Feature flags, toggle switches, enable/disable settings |
string |
Explicitly marks variable as text (optional, default type) | APP_NAME=string |
Documentation purposes, explicit type declaration |
min:value |
For numbers: minimum value For strings: minimum length |
PORT=number|min:1API_KEY=min:32 |
Valid port ranges, minimum key/password length |
max:value |
For numbers: maximum value For strings: maximum length |
PORT=number|max:65535USERNAME=max:50 |
Port limits, username/input length restrictions |
enum:val1,val2 |
Value must be one of the listed options | NODE_ENV=enum:dev,staging,prod |
Environment modes, log levels, deployment targets |
secure |
Enforces strong passwords (uppercase + lowercase + special chars) | DB_PASS=required|secure|min:8 |
Passwords, API keys, sensitive credentials |
default:"value" |
Specifies default value (shown in docs, not used in validation) | PORT=number|default:"3000" |
Documenting fallback values, optional configuration |
How rules work together:
# Single rule - just check if it exists
DB_HOST=required
# Multiple rules - must be required AND a number
DB_PORT=required|number
# Complex validation - required, must be a number, and between 1-65535
DB_PORT=required|number|min:1|max:65535
# With metadata - required, secure password, min 8 chars, with default shown in docs
DB_PASS=required|secure|min:8|default:"ChangeMe123!"
# Enum - must be one of these exact values
NODE_ENV=required|enum:development,staging,productionImportant notes:
requiredmeans the variable must exist in your.envfile- If you don't specify
required, the variable is optional minandmaxare smart: they check numeric value for numbers, character length for stringssecurevalidator checks for: uppercase letter + lowercase letter + number + special characterdefaultis for documentation only - it doesn't set actual values in your app
.env:5 [error] no-missing-key β Variable name is missing
.env:8 [warning] no-unescaped-shell-chars β Unescaped shell characters in value
.env:12 [notice] no-empty-value β Variable "COMMENTED_OUT" has an empty value.env:3 [error] required β Missing required variable: DB_HOST
.env:5 [error] number β PORT must be a number (got: "abc")
.env:8 [warning] unknown-rule β Unknown rule 'invalid' for DEBUGenv-sentinel is also available as an integrable library for programmatic use:
npm install env-sentinelimport { lint, validate, parseEnvContent, parseSchemaContent } from 'env-sentinel';
// Lint .env content
const lintResult = lint(envContent);
if (!lintResult.isValid) {
console.log(`Found ${lintResult.summary.errors} errors`);
}
// Validate against schema
const envVars = parseEnvContent(envContent);
const schemaVars = parseSchemaContent(schemaContent);
const validateResult = validate(envVars, schemaVars, envContent);
// Handle results
validateResult.issues.forEach(issue => {
console.log(`${issue.severity}: ${issue.message}`);
});| Function | Description | Returns |
|---|---|---|
lint(envContent: string) |
Lint .env content | Result |
validate(envVars, schema, fileContent?) |
Validate against schema | Result |
parseEnvContent(content: string) |
Parse .env content | Record<string, string> |
parseSchemaContent(content: string) |
Parse schema content | Record<string, string> |
| Function | Description | Returns |
|---|---|---|
numberValidator(key, value, args) |
Validate number type | ValidationResult |
booleanValueValidator(key, value, args) |
Validate boolean type | ValidationResult |
minValueValidator(key, value, args) |
Validate minimum value | ValidationResult |
maxValueValidator(key, value, args) |
Validate maximum value | ValidationResult |
enumValueValidator(key, value, args) |
Validate enum values | ValidationResult |
secureValueValidator(key, value, args) |
Validate secure values | ValidationResult |
| Function | Description | Returns |
|---|---|---|
noLeadingSpacesCheck(lineNumber, lineContent) |
Check for leading spaces | LintResult | undefined |
noEmptyValueCheck(lineNumber, lineContent) |
Check for empty values | LintResult | undefined |
noMissingKeyCheck(lineNumber, lineContent) |
Check for missing keys | LintResult | undefined |
noDuplicateKeyCheck(lineNumber, lineContent) |
Check for duplicate keys | LintResult | undefined |
noInvalidKeyDelimiterCheck(lineNumber, lineContent) |
Check for invalid key delimiters | LintResult | undefined |
noInvalidKeyLeadingCharCheck(lineNumber, lineContent) |
Check for invalid leading chars | LintResult | undefined |
noInvalidKeyCharactersCheck(lineNumber, lineContent) |
Check for invalid key characters | LintResult | undefined |
noWhitespaceInKeyCheck(lineNumber, lineContent) |
Check for whitespace in keys | LintResult | undefined |
noLowercaseInKeyCheck(lineNumber, lineContent) |
Check for lowercase in keys | LintResult | undefined |
noUnsafeKeyCheck(lineNumber, lineContent) |
Check for unsafe keys | LintResult | undefined |
noQuotedKeyCheck(lineNumber, lineContent) |
Check for quoted keys | LintResult | undefined |
noSpaceBeforeEqualCheck(lineNumber, lineContent) |
Check for space before equals | LintResult | undefined |
noSpaceAfterEqualCheck(lineNumber, lineContent) |
Check for space after equals | LintResult | undefined |
noInvalidReferenceSyntaxCheck(lineNumber, lineContent) |
Check for invalid references | LintResult | undefined |
noUnquotedMultilineValueCheck(lineNumber, lineContent) |
Check for unquoted multiline values | LintResult | undefined |
noUnescapedShellCharsCheck(lineNumber, lineContent) |
Check for unescaped shell chars | LintResult | undefined |
noEmptyQuotesCheck(lineNumber, lineContent) |
Check for empty quotes | LintResult | undefined |
noUnquotedYAMLBooleanLiteralCheck(lineNumber, lineContent) |
Check for unquoted YAML booleans | LintResult | undefined |
noDuplicateReferenceCheck(lineNumber, lineContent) |
Check for duplicate references | LintResult | undefined |
noCommaSeparatedValueInScalarCheck(lineNumber, lineContent) |
Check for comma-separated values | LintResult | undefined |
type ValidatorFn = (key: string, value: string, args: string[]) => ValidationResult;
type ValidationResult = string | true; // Return true for success, string for error message
// Example custom validator
const customValidator: ValidatorFn = (key, value, args) => {
if (value.length < 8) {
return `${key} must be at least 8 characters long`;
}
return true;
};type LintCheckFn = (lineNumber: number, lineContent: string) => LintResult | undefined;
type LintResult = {
line: number;
issue: string;
severity?: 'warning' | 'error' | 'notice'
};
// Example custom lint check
const customCheck: LintCheckFn = (lineNumber, lineContent) => {
if (lineContent.includes('TODO')) {
return {
line: lineNumber,
issue: 'Found TODO comment in .env file',
severity: 'warning'
};
}
return undefined; // No issue found
};import { validatorRegistry, lintRegistry } from 'env-sentinel';
// Register custom validator
validatorRegistry.register('custom-min-length', customValidator);
// Register custom lint check
lintRegistry.register('no-todo-comments', {
name: 'no-todo-comments',
run: customCheck
});type Result = {
isValid: boolean;
issues: Issue[];
summary: Summary;
};
type Issue = {
line?: number;
key?: string;
message: string;
severity: 'error' | 'warning' | 'notice';
rule?: string;
value?: string;
};
type Summary = {
total: number;
errors: number;
warnings: number;
notices: number;
};- Zero dependencies β stays lightweight and fast
- Human-readable schema β more transparent than Joi/Zod configs
- Quick setup β works out of the box with npx
- CI/CD friendly β perfect for automated validation
- Extensible β custom validators and lint checks
- Type-safe β full TypeScript support
- Dual purpose β CLI tool and integrable library
|
@dartcdev |
MIT license β Free to use, modify, and contribute!
