The envy CLI ships with @howells/envy.
npm install @howells/envy zod
npx envy --help
npx envy describeIt provides a working local preflight check for validating
process.env or dotenv files against an Envy schema before CI or deployment
continues.
envy.config.ts gives project defaults a typed home. The local check remains
explicit and accepts --schema for monorepos and one-off checks.
import { defineConfig } from "@envy/config";
export default defineConfig({
schema: "./src/env/schema.ts",
envDir: ".",
defaultTarget: "vercel",
});Use a schema path for monorepos and one-off checks.
envy check local --schema apps/web/src/env/schema.ts --from apps/web/.env.productionValidate a local env source against the schema.
npx envy check local --schema ./src/env/schema.ts --from .env.production
npx envy check local --schema ./src/env/schema.ts
npx envy check local --schema ./src/env/schema.ts --from .env --from .env.localThe schema module may export the schema as:
defaultenvSchemaschema
Use --export <name> for any other export:
npx envy check local --schema ./src/env/schema.ts --export appEnvModes map directly to parser methods:
--mode server:schema.parseServer(input), the default--mode client:schema.parseClient(input)--mode all:schema.parse(input)
npx envy check local --schema ./src/env/schema.ts --from .env.production --mode allJSON output is available for CI:
npx envy check local --schema ./src/env/schema.ts --from .env.production --json--json is an alias for --format json. Success writes a single-line JSON
envelope to stdout:
{ "ok": true, "data": {}, "metadata": {} }Errors write the same envelope shape to stderr:
{ "ok": false, "error": {}, "metadata": {} }Run envy describe or envy check local --describe to inspect supported
commands, flags, output shapes, and exit codes without scraping prose docs.
When --from is omitted, the CLI validates the current process environment.
When one or more --from files are provided, files are parsed and merged in
order without mutating process.env.
The dotenv parser supports common syntax: comments, blank lines, export KEY=,
single-quoted values, double-quoted values, inline comments after unquoted
values, and empty values. It does not append newlines to secrets.
Verify that schema-declared env keys are registered with Turborepo for a task.
npx envy check turbo --schema ./src/env/schema.ts
npx envy check turbo --schema ./src/env/schema.ts --turbo turbo.json --task build
npx envy check turbo --schema ./src/env/schema.ts --task test --mode server --jsonThe check reads the schema metadata and compares the selected keys with:
globalEnvglobal.envwhen Turborepo's global configuration shape is usedtasks.<task>.env
Exact key names and wildcard patterns such as NEXT_PUBLIC_* are accepted.
Negated patterns do not count as registrations. passThroughEnv and
globalPassThroughEnv are intentionally ignored because they make variables
available at runtime without including them in the task hash.
--task defaults to build, --turbo defaults to turbo.json, and --mode
defaults to all. Use --mode client to require only public variables, or
--mode server for the server-side parse surface.
Missing registrations fail with exit code 65 and include only key names:
{ "ok": false, "error": {}, "metadata": {} }Validate env first, then run a command with the checked dotenv files loaded into the child process environment.
npx envy run local --schema ./src/env/schema.ts --from .env --from .env.local -- node ./scripts/smoke.jsFiles passed with --from are parsed in order; later files override earlier
files. The current process environment wins over file values, so CI-injected
secrets are not replaced by local dotenv files. On success, Envy writes no
output of its own; stdout and stderr belong to the child process. If validation
fails, the command is not started.
Provider checks, safe provider pushes, Next codegen, dotenv loading, and lint configuration are available as package subpaths:
import { loadDotenv } from "@howells/envy/dotenv";
import { syncNextEnv } from "@howells/envy/next";
import { createLintIntegration } from "@howells/envy/lint";
import { vercel } from "@howells/envy/adapters/vercel";
import { railway } from "@howells/envy/adapters/railway";0: success64: usage error, unknown command, invalid flag, or invalid path65: env validation failed66: schema or env file could not be read70: internal error
CLI output:
- show key names
- never show secret values
- include enough context to fix the problem without opening provider dashboards
- support JSON output for CI
- reject paths containing NUL bytes or terminal control characters