HaloStack Docs
Open App
Config

Env

The validated, typed environment layer — the only sanctioned way to read environment variables in HaloStack.

@halostack/env is the single, sanctioned gateway to environment variables in HaloStack. It validates and types process.env exactly once at boot using envalid, then exports a frozen env object that the rest of the monorepo imports. Because the validation happens here, reading process.env directly is banned everywhere else — the one exception being the test tooling that bootstraps the environment before this package loads.

Importing

The package exposes three entry points through its exports map. Pick the subpath that matches where your code runs: server and library code gets the full schema, while the web and landing apps each get a narrower public subset so that bundling them never pulls in server-only variables.

In practice, most code reaches for the default entry point:

Structure

The package keeps schema definitions and composition separate. Each domain of configuration lives in its own schema file, and the apps/ folder assembles those schemas into the concrete env objects that consumers import.

  • src/schemas/ holds one file per domain — base, database, auth, email, jobs, notifications, observability, sentry, storage, and api. Each file exports a validator map describing its variables.
  • src/apps/ handles composition: _common.ts assembles the full server schema, packages.ts runs cleanEnv to produce the exported env, node-env-flags.ts derives the isProd / isDev helpers, web.ts defines the browser-safe subset, and landing.ts defines the narrow landing subset.

Adding a variable

Adding a new environment variable is a short, type-driven loop:

  1. Add it to the appropriate src/schemas/<domain>.ts validator, supplying a devDefault where it helps local development.
  2. If the web bundle needs it, surface it explicitly in src/apps/web.ts.
  3. Run bun run typecheck. Every consumer now sees the new key as a typed property.

Conventions

Never read process.env outside this package — that is the rule the whole layer exists to enforce. Note also that envalid is strictly for boot-time configuration; for untrusted application input, validate with Zod instead.