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.
Visit the official website for documentation, quickstart guides, and tutorials.
- β Lint .env files to catch formatting issues, unsafe syntax, and common misconfigurations
- β Validate environment variables against a defined schema
- β 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
- β NEW: 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 .env
Alternatively, 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
npx 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
Each line represents a variable and its validation rules:
DB_HOST=required
DB_PORT=required|number
DEBUG=optional|boolean
NODE_ENV=optional|enum:development,production,test
API_KEY=required|min:32
MAX_CONNECTIONS=optional|number|max:100
Rule | Description | Example |
---|---|---|
required | Must be defined | DB_HOST=required |
optional | Can be missed | DEBUG=optional |
number | Must be a number | PORT=required|number |
boolean | Must be true or false |
DEBUG=optional|boolean |
string | Can be anything | NAME=required|string |
min:value | Minimum value/length | API_KEY=required|min:32 |
max:value | Maximum value/length | PORT=required|number|max:65535 |
enum:values | Must match one of listed values | NODE_ENV=required|enum:dev,prod,test |
.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 DEBUG
env-sentinel is also available as an integrable library for programmatic use:
npm install env-sentinel
import { 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!