{"_id":"@akashjavali/env-safe-guard","_rev":"2-3f81bd294a083b67e455800031ce0d20","name":"@akashjavali/env-safe-guard","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@akashjavali/env-safe-guard","version":"0.1.0","keywords":["env","environment","validation","security","dotenv","typescript","ai-safe","secrets"],"author":"","license":"MIT","_id":"@akashjavali/env-safe-guard@0.1.0","maintainers":[{"name":"akashjavali","email":"akashjavali@gmail.com"}],"bin":{"env-safe-guard":"dist/cli/index.js"},"dist":{"shasum":"1b424de89b476a354f0ae3e86152b379cdbd14a6","tarball":"https://registry.npmjs.org/@akashjavali/env-safe-guard/-/env-safe-guard-0.1.0.tgz","fileCount":8,"integrity":"sha512-6KIx9fiLuq6gynFzisLA6mklOi1NjTvtrmd9VyunEbSjSzTfMsJukPUgHLvNGZRRZT0nbN2pIDHhu6TzdTFwZg==","signatures":[{"sig":"MEUCIH3gi86Xib6LnfhlG22/keHmty/c+M0u1BXf/mlgRENyAiEAs/uAoohQXFbQEdtW7A59WyIk/R1P5a7vZ4c81atLmE8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47795},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"a77e884aee24b4e1bd39a1abcf8df92af2de3709","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"akashjavali","email":"akashjavali@gmail.com"},"_npmVersion":"10.8.2","description":"Type-safe environment validation with automatic secret redaction — built for the AI era.","directories":{},"_nodeVersion":"20.20.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^1.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/env-safe-guard_0.1.0_1774267984363_0.03831325271506136","host":"s3://npm-registry-packages-npm-production"},"deprecated":"This package has been renamed to envfort. Please use envfort instead: https://www.npmjs.com/package/envfort"}},"time":{"created":"2026-03-23T12:13:04.290Z","modified":"2026-03-24T11:55:30.779Z","0.1.0":"2026-03-23T12:13:04.508Z"},"license":"MIT","keywords":["env","environment","validation","security","dotenv","typescript","ai-safe","secrets"],"description":"Type-safe environment validation with automatic secret redaction — built for the AI era.","maintainers":[{"name":"akashjavali","email":"akashjavali@gmail.com"}],"readme":"# env-safe-guard\n\n> Type-safe environment validation with automatic secret redaction — built for the AI era.\n\n[![npm version](https://img.shields.io/npm/v/@akashjavali/env-safe-guard?style=flat-square)](https://www.npmjs.com/package/@akashjavali/env-safe-guard)\n[![npm downloads](https://img.shields.io/npm/dm/@akashjavali/env-safe-guard?style=flat-square)](https://www.npmjs.com/package/@akashjavali/env-safe-guard)\n[![license](https://img.shields.io/npm/l/@akashjavali/env-safe-guard?style=flat-square)](./LICENSE)\n[![CI](https://img.shields.io/github/actions/workflow/status/akashjavali/env-safe-guard/ci.yml?style=flat-square&label=CI)](https://github.com/akashjavali/env-safe-guard/actions)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/@akashjavali/env-safe-guard?style=flat-square)](https://bundlephobia.com/package/@akashjavali/env-safe-guard)\n\n---\n\n## Why env-safe-guard?\n\nAI coding assistants — Claude, Copilot, Cursor, ChatGPT — read your terminal output, error logs, and clipboard. Every time you `console.log(process.env)` or paste a stack trace into a chat window, you risk leaking database credentials, API keys, and tokens to a third-party model.\n\n`env-safe-guard` intercepts your environment object at the JavaScript layer using a `Proxy`. Secrets are **validated and typed at startup**, then **redacted everywhere they could accidentally escape** — logs, JSON serialization, template literals, error messages — while remaining fully accessible as raw values in your actual application code. Zero runtime overhead on hot paths. Zero extra dependencies for `.env` loading.\n\n---\n\n## Features\n\n- **Fail-fast validation** — throws a clear error at boot if required variables are missing or have the wrong type\n- **Full TypeScript inference** — `env.PORT` is typed as `number`, `env.DEBUG` as `boolean | undefined`, automatically\n- **Secret redaction via Proxy** — `console.log(env)`, `JSON.stringify(env)`, and template literals all produce `***REDACTED***` for marked fields\n- **Always-redacted secrets** — `secret: true` fields are redacted even on direct access (`env.API_KEY` returns `***REDACTED***`)\n- **Optional fields + defaults** — use `'number?'` with a `default` to express exactly what your schema means\n- **Zero-dependency `.env` loading** — built-in loader, no `dotenv` required\n- **CLI toolkit** — validate, scaffold, and lock down your env from the command line\n- **Git pre-commit hook** — blocks commits that introduce unprotected secrets\n- **Cross-runtime support** — Node 18+, Cloudflare Workers, Deno, Bun via `options.env`\n\n---\n\n## Installation\n\n```bash\n# npm\nnpm install @akashjavali/env-safe-guard\n\n# yarn\nyarn add @akashjavali/env-safe-guard\n\n# pnpm\npnpm add @akashjavali/env-safe-guard\n\n# bun\nbun add @akashjavali/env-safe-guard\n```\n\n---\n\n## Quick Start\n\n```ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nexport const env = createEnv({\n  DATABASE_URL: 'string',\n  API_KEY: { type: 'string', secret: true },\n  PORT: { type: 'number?', default: 3000 },\n  DEBUG: { type: 'boolean?', default: false },\n}, { redact: true })\n```\n\nThat's it. Import `env` anywhere in your app and get fully typed, validated, redaction-safe access to your environment.\n\n---\n\n## API Reference\n\n### `createEnv(schema, options?)`\n\n#### Schema Types\n\n| Syntax | TypeScript type | Behaviour |\n|---|---|---|\n| `'string'` | `string` | Required. Throws if absent. |\n| `'number'` | `number` | Required. Parsed with `Number()`. Throws if `NaN`. |\n| `'boolean'` | `boolean` | Required. `'true'`/`'1'` → `true`, `'false'`/`'0'` → `false`. |\n| `'string?'` | `string \\| undefined` | Optional. Returns `undefined` if absent. |\n| `'number?'` | `number \\| undefined` | Optional. Returns `undefined` if absent. |\n| `'boolean?'` | `boolean \\| undefined` | Optional. Returns `undefined` if absent. |\n| `{ type: 'string', secret: true }` | `'***REDACTED***'` | Always redacted, even on direct access. |\n| `{ type: 'number?', default: 3000 }` | `number` | Optional with fallback. Never `undefined`. |\n| `{ type: 'boolean?', default: false }` | `boolean` | Optional with fallback. Never `undefined`. |\n\n#### Options\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `redact` | `boolean` | `false` | Enable Proxy-based redaction for `console.log`, `JSON.stringify`, and string coercion. |\n| `loadDotEnv` | `boolean` | `false` | Parse and load a `.env` file before validation. Zero external dependencies. |\n| `dotEnvPath` | `string` | `'.env'` | Path to the `.env` file. Only used when `loadDotEnv: true`. |\n| `env` | `Record<string, string \\| undefined>` | `process.env` | Override the source environment. Useful for tests and non-Node runtimes. |\n\n#### TypeScript inference example\n\n```ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nexport const env = createEnv({\n  DATABASE_URL: 'string',\n  API_KEY: { type: 'string', secret: true },\n  PORT: { type: 'number?', default: 3000 },\n  DEBUG: { type: 'boolean?', default: false },\n}, { redact: true })\n\n// Inferred types:\nenv.DATABASE_URL  // string\nenv.API_KEY       // '***REDACTED***'  (always, by design)\nenv.PORT          // number            (never undefined — has a default)\nenv.DEBUG         // boolean           (never undefined — has a default)\n```\n\n---\n\n## Redaction Table\n\nWhen `redact: true` is set, `env` becomes a `Proxy`. Every access path that could cause a secret to escape is intercepted:\n\n| Access pattern | Result |\n|---|---|\n| `env.DATABASE_URL` | Real value (use freely in code) |\n| `env.API_KEY` where `secret: true` | `***REDACTED***` always |\n| `console.log(env)` | `{ DATABASE_URL: 'postgres://...', API_KEY: '***REDACTED***', PORT: 3000, ... }` |\n| `JSON.stringify(env)` | `{\"DATABASE_URL\":\"postgres://...\",\"API_KEY\":\"***REDACTED***\",\"PORT\":3000,...}` |\n| `JSON.stringify({ config: env })` | Safe — nested serialization is also intercepted |\n| `` `Config: ${env}` `` | `[redacted env — use env.KEY]` |\n| `String(env)` | `[redacted env — use env.KEY]` |\n| `console.log(env.API_KEY)` | `***REDACTED***` |\n| Error stack traces that include `env` | Redacted object representation |\n\n**The rule of thumb:** read individual keys (`env.DATABASE_URL`) in your business logic — they return real values for non-secret fields. Never spread or serialize the whole `env` object; the Proxy has you covered if you forget.\n\n---\n\n## CLI Reference\n\nAll commands are available via `npx` with no installation required.\n\n### `check` — Validate your environment\n\nReads your schema and current `.env`, reports missing or invalid variables.\n\n```bash\nnpx @akashjavali/env-safe-guard check\nnpx @akashjavali/env-safe-guard check --schema ./config/env.schema.ts\n```\n\n### `init` — Generate a schema file\n\nScaffolds a `env.ts` schema file from your existing `.env`.\n\n```bash\nnpx @akashjavali/env-safe-guard init\nnpx @akashjavali/env-safe-guard init --output ./src/env.ts\n```\n\n### `gen-example` — Generate `.env.example`\n\nProduces a `.env.example` with all keys present and secret values replaced by placeholders.\n\n```bash\nnpx @akashjavali/env-safe-guard gen-example\nnpx @akashjavali/env-safe-guard gen-example --schema ./src/env.ts --output .env.example\n```\n\n### `install-hook` — Install git pre-commit hook\n\nInstalls a pre-commit hook that runs `check` before every commit and ensures `.env` is in `.gitignore`.\n\n```bash\nnpx @akashjavali/env-safe-guard install-hook\nnpx @akashjavali/env-safe-guard install-hook --root ./packages/api\n```\n\nAfter installation, commits that would expose unprotected secrets are automatically blocked.\n\n---\n\n## Framework Examples\n\n### Next.js (App Router)\n\nCreate `src/env.ts` and import it in `next.config.ts` to validate at build time.\n\n```ts\n// src/env.ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nexport const env = createEnv({\n  DATABASE_URL: 'string',\n  NEXTAUTH_SECRET: { type: 'string', secret: true },\n  NEXT_PUBLIC_APP_URL: 'string',\n  NODE_ENV: 'string',\n}, { redact: true })\n```\n\n```ts\n// next.config.ts\nimport './src/env'  // validates at build time — bad config fails the build\nimport type { NextConfig } from 'next'\n\nconst config: NextConfig = {\n  // ...\n}\nexport default config\n```\n\n```ts\n// app/api/route.ts\nimport { env } from '@/env'\n\nexport async function GET() {\n  const db = await connect(env.DATABASE_URL)  // typed as string\n  // ...\n}\n```\n\n### Express\n\n```ts\n// src/env.ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nexport const env = createEnv({\n  DATABASE_URL: 'string',\n  JWT_SECRET: { type: 'string', secret: true },\n  PORT: { type: 'number?', default: 3000 },\n}, { redact: true, loadDotEnv: true })\n```\n\n```ts\n// src/index.ts\nimport express from 'express'\nimport { env } from './env'\n\nconst app = express()\n\napp.listen(env.PORT, () => {\n  console.log(`Server running on port ${env.PORT}`)\n  // If you accidentally log env here, secrets stay redacted\n})\n```\n\n### Plain Node.js with `loadDotEnv`\n\n```ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nconst env = createEnv({\n  STRIPE_SECRET_KEY: { type: 'string', secret: true },\n  WEBHOOK_URL: 'string',\n  RETRY_LIMIT: { type: 'number?', default: 3 },\n}, {\n  redact: true,\n  loadDotEnv: true,\n  dotEnvPath: '.env.local',\n})\n\nconsole.log(env.RETRY_LIMIT)       // 3 (number)\nconsole.log(env.STRIPE_SECRET_KEY) // ***REDACTED***\n```\n\n---\n\n## Cross-Environment Support\n\n`env-safe-guard` is not tied to Node's `process.env`. Pass any environment source via `options.env`.\n\n### Cloudflare Workers\n\n```ts\n// worker.ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nexport default {\n  async fetch(request: Request, cfEnv: Env) {\n    const env = createEnv({\n      API_KEY: { type: 'string', secret: true },\n      UPSTREAM_URL: 'string',\n    }, {\n      redact: true,\n      env: cfEnv as Record<string, string>,\n    })\n\n    // env is fully validated and redacted\n  }\n}\n```\n\n### Deno\n\n```ts\nimport { createEnv } from 'npm:@akashjavali/env-safe-guard'\n\nconst env = createEnv({\n  DATABASE_URL: 'string',\n  PORT: { type: 'number?', default: 8000 },\n}, {\n  redact: true,\n  env: Deno.env.toObject(),\n})\n```\n\n### Bun\n\n```ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nconst env = createEnv({\n  DATABASE_URL: 'string',\n  SECRET_KEY: { type: 'string', secret: true },\n}, {\n  redact: true,\n  env: Bun.env as Record<string, string | undefined>,\n})\n```\n\n### Testing\n\nInject a fake environment in your test suite without touching `process.env`:\n\n```ts\nimport { createEnv } from '@akashjavali/env-safe-guard'\n\nconst env = createEnv({\n  DATABASE_URL: 'string',\n  FEATURE_FLAG: { type: 'boolean?', default: false },\n}, {\n  env: {\n    DATABASE_URL: 'postgres://localhost/test',\n    FEATURE_FLAG: 'true',\n  },\n})\n```\n\n---\n\n## Git Safety\n\nInstall the pre-commit hook once per repository:\n\n```bash\nnpx @akashjavali/env-safe-guard install-hook\n```\n\nThis does two things:\n\n1. Adds a `.git/hooks/pre-commit` script that runs `env-safe-guard check` before every commit. If your environment schema is invalid or variables are missing, the commit is blocked with a clear message.\n2. Audits `.gitignore` and ensures `.env` (and common variants) are listed. If they are missing, it adds them automatically.\n\nFor monorepos, point it at the package root:\n\n```bash\nnpx @akashjavali/env-safe-guard install-hook --root ./packages/api\n```\n\n---\n\n## Comparison\n\n| Feature | dotenv | envalid | @t3-oss/env-nextjs | Doppler | **env-safe-guard** |\n|---|---|---|---|---|---|\n| Load `.env` | Yes | No | No | No | Yes (built-in, zero deps) |\n| Validate schema | No | Yes | Yes | No | Yes |\n| TypeScript inference | No | Partial | Yes | No | Yes |\n| Optional + defaults | No | Yes | Yes | No | Yes |\n| Secret redaction | No | No | No | No | **Yes** |\n| `console.log` safe | No | No | No | No | **Yes** |\n| `JSON.stringify` safe | No | No | No | No | **Yes** |\n| Template literal safe | No | No | No | No | **Yes** |\n| Always-redacted fields | No | No | No | No | **Yes** |\n| CLI toolkit | No | No | No | Yes | Yes |\n| Git hook | No | No | No | No | Yes |\n| Zero external deps | No | No | No | No | **Yes** |\n| Works outside Node | No | No | No | No | **Yes** |\n\n---\n\n## How It Works\n\n`createEnv` validates and coerces all environment values at call time. If validation passes, it returns a **JavaScript `Proxy`** wrapping a plain object of the parsed values.\n\nThe Proxy intercepts:\n\n- **Property access (`get`)** — returns `***REDACTED***` for `secret: true` fields; returns real values for everything else.\n- **`ownKeys` + `getOwnPropertyDescriptor`** — called by `JSON.stringify` and spread operators. The Proxy returns redacted representations for secret fields.\n- **`Symbol.toPrimitive` / `toString` / `valueOf`** — called when the object is coerced to a string (template literals, `String()`, `+` operator). Returns a safe sentinel message instead of exposing any values.\n\nThis means **you never need to call a helper function** to safely log your config. The object itself is safe by construction whenever `redact: true` is set.\n\nThe Proxy layer is allocated once at startup and adds no overhead to individual property access in hot code paths.\n\n---\n\n## Roadmap\n\n- **Secret leak scanner** — static analysis pass that finds raw `process.env` access in your codebase\n- **AI agent firewall** — intercept MCP tool calls and sanitize environment context before it reaches an AI model\n- **Team sync SaaS** — encrypted shared env for teams, with per-developer overrides and audit logs\n- **Schema export** — emit a JSON Schema or Zod schema from your `createEnv` definition\n- **CI integration** — GitHub Action that runs `check` on every pull request\n\n---\n\n## Contributing\n\nContributions are welcome. Please open an issue to discuss significant changes before submitting a pull request.\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feat/my-feature`\n3. Make your changes with tests\n4. Run the test suite: `npm test`\n5. Submit a pull request\n\nPlease follow the existing code style and keep commits focused.\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE) for details.\n\n---\n\n> If `env-safe-guard` has saved you from an accidental secret leak, consider giving it a star. It helps others find the project.\n","readmeFilename":"README.md"}