| name | arkenv |
| description | Answer questions about ArkEnv and help implement environment variable validation. Use when developers: (1) Ask about environment variable validation or typesafety, (2) Want to setup ArkEnv in a project, (3) Need to define or update schemas using ArkType or Standard Schema, (4) Are integrating with Vite, Bun, or other runtimes. Triggers on: 'ArkEnv', 'env validation', 'typesafe env', 'createEnv', 'env.ts', '@arkenv/cli'. |
ArkEnv
ArkEnv is a typesafe environment variable validator for modern JavaScript runtimes. It uses ArkType by default for schema definition but supports any Standard Schema validator (like Zod or Valibot).
Capabilities
Core usage
- Define typesafe schemas using
ArkType notation or any Standard Schema validator.
- Implement complex types, arrays, and unions.
- Configure automatic coercion and default values.
- Follow best practices for schema organization.
Framework integration
- Next.js: Build-time/runtime validation and
process.env type augmentation via @arkenv/nextjs. Supports automatic codegen (env.gen.ts) using the withArkEnv configuration wrapper in next.config.ts.
- Vite: Build-time validation and
import.meta.env type augmentation via @arkenv/vite-plugin.
- Bun: Build-time/Runtime validation and
process.env type augmentation via @arkenv/bun-plugin.
- Node.js: Standard
process.env validation and coercion.
CLI (setup & DevOps)
- Initialize ArkEnv in new or existing projects using
pnpm dlx @arkenv/cli@latest init.
- Scaffold schema files and detect framework-specific configurations (
Next.js, Vite, Bun, etc.).
- Support layout selection (
--strict for 3-file split vs --simple for a single file).
- Support option to skip codegen (
--no-codegen).
- Automatically configure
tsconfig.json and environment types for optimal typesafety.
Agent setup (machine-readable)
AI agents SHOULD always use the CLI for project initialization to ensure consistency and reliability. Use the --agent flag for a fully automated, machine-readable experience.
- Command:
pnpm dlx @arkenv/cli@latest init --agent
- Behavior: The
--agent flag automatically enables the following behaviors:
--yes: Bypasses all interactive prompts and uses recommended defaults.
--quiet: Suppresses spinners and ANSI formatting for cleaner terminal logs.
--json: Emits a structured JSON summary to stdout upon completion (all other output is sent to stderr).
- Success Verification: Parse the JSON output to verify
status: "success" and retrieve details like the scaffolded file path.
Handling refusals (status: "error")
--agent never implies --force. When a safety check trips, the CLI refuses and emits a machine-actionable JSON payload to stdout:
{
"status": "error",
"code": "GIT_TREE_DIRTY",
"message": "Git working tree is not clean.",
"retryWith": ["--force"]
}
code: a stable identifier you can branch on. Refusal codes: REQUIREMENTS_NOT_MET, GIT_TREE_DIRTY, NON_EMPTY_DIR. A code of INTERNAL means the CLI broke rather than refused - retrying with flags will not help.
retryWith: the flag(s) that would bypass the check (e.g. ["--force"]). Empty ([]) means the refusal is not bypassable.
Escalation pattern: always run init --agent without --force first. If you get status: "error", inspect code and retryWith. Only re-run with the flag(s) from retryWith (e.g. append --force) once you have deliberately decided the refusal is safe to bypass - do not add --force pre-emptively.
Operational logic
- Detection:
- Look for
env.ts (simple layout) or an env/ directory containing split files: env/client.ts, env/server.ts, and env/internal/shared.ts (strict layout).
- Check for framework config files (
next.config.ts, next.config.js, vite.config.ts, bunfig.toml, package.json scripts) to recommend appropriate plugins.
- Setup:
- If ArkEnv is not present or a fresh setup is requested, trigger the Setup Workflow.
- Prefer using the CLI for initialization:
pnpm dlx @arkenv/cli@latest init.
- If the CLI cannot be used or fails, fall back to manual configuration.
Setup workflow
When setting up ArkEnv, follow these steps:
- Initialize: Run
pnpm dlx @arkenv/cli@latest init --agent (optionally appending --strict or --simple based on layout preference). This will detect the environment, install dependencies, and scaffold schemas.
- Review & Refine Schemas:
- Simple Layout: Inspect and refine the generated
env.ts. Ensure it captures the required environment variables.
- Strict Layout: Inspect and refine the generated files under the
env/ directory: client.ts (client-only variables), server.ts (server-only variables), and internal/shared.ts (variables shared between client and server).
- Refine types (e.g., change
string to number.port or specific union types).
- Manual Plugin Configuration:
- The CLI installs plugins but might not update config files.
- Next.js: Wrap
next.config.ts (or next.config.js) using the withArkEnv configuration helper from @arkenv/nextjs/config. (Skip if scaffolded with --no-codegen).
- Vite: Update
vite.config.ts to import and include the @arkenv/vite-plugin plugin.
- Bun: Configure
bunfig.toml or add the plugin to the runtime if necessary.
- Typesafety & Augmentation:
- Next.js (Codegen): Import
createEnv from ./generated/env.gen instead of core @arkenv/nextjs. The codegen file automatically handles the runtime mapping and type definitions.
- Vite: Add type augmentation to
src/vite-env.d.ts or a new env.d.ts.
interface ImportMetaEnv extends import("@arkenv/vite-plugin").ImportMetaEnvAugmented<typeof import("./env").Env> {}
- Bun: Create a
bun-env.d.ts file (or update an existing one) with the following pattern:
type ProcessEnvAugmented = import("@arkenv/bun-plugin").ProcessEnvAugmented<typeof import("./src/env").default>;
declare namespace NodeJS {
interface ProcessEnv extends ProcessEnvAugmented {}
}
- Ensure
tsconfig.json has strict: true (the CLI tries to do this, but verify).
- Usage Update: Scan the codebase for existing environment variable usage (
process.env or import.meta.env) and ensure they are now typesafe via the augmentations.
- Validation: Run
pnpm check (or equivalent) or a build to confirm everything is typesafe and valid.
Core concepts
Defining a schema
Simple Layout
The best practice is to export a schema definition using type.
import { type } from 'arkenv';
export const Env = type({
NODE_ENV: "'development' | 'production' | 'test' = 'development'",
VITE_API_URL: "string",
PORT: "number.port = 3000"
});
Strict Layout (split-file)
Split files isolate environment variable definitions to prevent server secrets from leaking to client-side.
env/internal/shared.ts (Shared):
import { type } from "arkenv";
export const SharedSchema = type({
NODE_ENV: "'development' | 'production' | 'test' = 'development'",
});
env/client.ts (Client-side, prefixed with NEXT_PUBLIC_ for Next.js):
import arkenv from "./internal/shared";
export const env = arkenv({
NEXT_PUBLIC_API_URL: "string",
});
env/server.ts (Server-side):
import arkenv from "./client";
export const env = arkenv({
DATABASE_URL: "string",
});
export default env;
Usage: Next.js (with Codegen)
Wrap next.config.ts to enable automatic env.gen.ts generation:
import { withArkEnv } from "@arkenv/nextjs/config";
import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
export default withArkEnv(nextConfig);
Then import and use the generated env object:
import env from "./env/generated/env.gen";
Usage: Node.js (standard)
In Node.js, you validate the environment at runtime and export the result.
import arkenv from 'arkenv';
import { Env } from './env';
export const env = arkenv(Env);
const port = env.PORT;
Usage: Vite (frontend)
Vite requires build-time injection. Use the plugin in vite.config.ts and augment ImportMetaEnv.
import arkenv from '@arkenv/vite-plugin';
import { Env } from './env';
export default defineConfig({
plugins: [arkenv(Env)]
});
import type { ImportMetaEnvAugmented } from "@arkenv/vite-plugin";
import type { Env } from "../env";
interface ImportMetaEnv extends ImportMetaEnvAugmented<typeof Env> {}
Usage: Bun
Bun can use either runtime validation or a plugin for type augmentation.
import type { ProcessEnvAugmented } from "@arkenv/bun-plugin";
import type { Env } from "./env";
declare global {
namespace NodeJS {
interface ProcessEnv extends ProcessEnvAugmented<typeof Env> {}
}
}
CLI commands
init
Set up ArkEnv in your project. It detects your framework and configures the appropriate plugin and type augmentations.
pnpm dlx @arkenv/cli@latest init [options]
Options:
--strict: Use strict 3-file split layout.
--simple: Use simple 1-file layout (default).
--no-codegen: Disable Next.js codegen/withArkEnv configuration setup.
Best practices
- Prefer Native Primitives: To leverage the full power of ArkEnv plugins, you should access environment variables through the runtime's native primitives.
- Vite: Use
import.meta.env.
- Bun: Use
process.env.
- This ensures that build-time validation, static replacement (Vite), and runtime optimizations (Bun) work as intended while remaining fully typesafe via type augmentation.
- Avoid
import { env } in Plugin-managed Projects: In projects using @arkenv/vite-plugin or @arkenv/bun-plugin, you should generally avoid importing a runtime-validated env object. Using native primitives is the "cleanest" way to get typesafety and ensures consistency with framework-specific behavior.
- Use Codegen in Next.js: For Next.js projects, prefer using the
withArkEnv wrapper and importing createEnv / env from the generated generated/env.gen.ts file. This automates the destructuring of runtimeEnv to allow static inlining on the client side without leaking secrets.
- Commit Generated Code for CI/CD: Commit
generated/env.gen.ts to source control to ensure compatibility with CI/CD pipelines.
- Use Type Augmentation: This is the recommended way to make
import.meta.env or process.env typesafe. It connects your schema definition to the native primitives without adding runtime overhead to your application logic.
- Re-use Schema: Define your schema once and use it for both the plugin (build-time/config) and runtime validation if needed.
- Coercion: ArkEnv automatically coerces strings from
.env files (e.g., "3000" becomes 3000).