Catch bad config before it ships
Schema-driven validation for environment variables and .env files — one portable schema, three native implementations.
Open source · MIT license · Rust · TypeScript · Kotlin
# envlint.toml — the SAME file works across all three implementations
[vars.DATABASE_URL]
type = "url"
required = true
[vars.LOG_LEVEL]
type = "enum"
values = ["debug", "info", "warn", "error"]
default = "info"
$ envlint check --env-file .env
envlint: validating .env
error: DATABASE_URL: required variable is not set
error: LOG_LEVEL: must be one of ["debug","info","warn","error"], got "verbose"
2 error(s), 0 warning(s) → exit 1envlint validates your environment against a single portable envlint.toml — in CI, in a container entrypoint, or at process boot. Catch missing or malformed config before it ships. CLI + library, native in Rust, TypeScript, and Kotlin.
Most production incidents that trace back to "config" are not subtle: a required variable was never set, a PORT held a hostname, a LOG_LEVEL of "verbose" silently fell back to a default, a timeout was 30 — seconds or milliseconds? These are caught trivially if something declares what the service expects.
envlint is that something. You describe your environment once in envlint.toml, then validate a .env file or the live process environment. Unlike dotenv, envalid, or zod — which make a running program read its config — envlint is a gate that runs before your program (or your deploy) does, with no code in the target service and no language lock-in. The same envlint.toml validates a Rust binary, a Node container, and a JVM service alike. It is a CI/CD check first, a library second.
Built to get out of your way
One portable schema
A single envlint.toml drives identical validation semantics across Rust, TypeScript, and Kotlin. Types: string, int, float, bool, url, port, enum, duration — plus required, default, pattern, min/max, and secret masking.
A gate, not a runtime read
Runs in CI, a container entrypoint, or at boot — before your program does. Exit codes are pipeline-ready: 0 clean, 1 validation errors, 2 usage/IO. Drop it into GitHub Actions in two lines.
Secrets stay masked
Mark a variable secret and it renders as ****** in every output — text and JSON, CLI and library. Validated values come back typed and default-filled, ready to hand to the rest of your config layer.
Add it in one line
Same library, three ecosystems. Pick yours.
cargo add envlint --git https://github.com/slothlabsorg/envlint
# CLI:
cargo install --git https://github.com/slothlabsorg/envlint envlintnpm i @slothlabs/envlintrepositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.github.slothlabsorg:envlint:v0.1.0")
}npm packages publish under the @slothlabs scope · JitPack builds from the git tag v0.1.0
One small example
Schema-driven validation for environment variables and .env files — one portable schema, three native implementations.
# envlint.toml — the SAME file works across all three implementations
[vars.DATABASE_URL]
type = "url"
required = true
[vars.LOG_LEVEL]
type = "enum"
values = ["debug", "info", "warn", "error"]
default = "info"
$ envlint check --env-file .env
envlint: validating .env
error: DATABASE_URL: required variable is not set
error: LOG_LEVEL: must be one of ["debug","info","warn","error"], got "verbose"
2 error(s), 0 warning(s) → exit 1Try envlint in your stack
Free and open source. Rust, TypeScript, or Kotlin — same semantics everywhere.