UNPKG

envfortress

Version:

Turn your .env into a fortress—type-checked, secure, and effortless for React, Vite, and Node.

1,074 lines (809 loc) 32 kB
# 🛡️ envfortress > Turn your .env into a fortress—type-checked, secure, and effortless for React, Vite, and Node. > **Works with React, Vite, Node, CRA, Next.js, and any modern stack—fully typed and secure by default.** [![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://www.npmjs.com/package/envfortress) [![Test Coverage](https://img.shields.io/badge/tests-147%20passed-brightgreen)](https://www.npmjs.com/package/envfortress) [![npm version](https://img.shields.io/npm/v/envfortress.svg)](https://www.npmjs.com/package/envfortress) --- ## 🚨 The Problem envfortress Solves | Without envfortress | With envfortress | | ------------------------------- | ------------------------------------------------ | | App crashes with "undefined" | Clear error: "DATABASE_URL is required" | | Runtime type errors | Immediate validation: "PORT must be a number" | | Secrets leak to browser | Server-only variables stay secure | | "What variables do I need?" | `.env.example` shows exactly what's needed | | Manual validation everywhere | Automatic validation with helpful errors | ## 🚀 Get started in 30 seconds ```bash npm install envfortress zod npx envfortress init --framework vite --output src/env.ts ``` <details> <summary>📚 Table of Contents</summary> - [Why envfortress?](#-why-envfortress) - [Installation](#installation) - [Secrets Management (Encrypted, Git-Native)](#-secrets-management-encrypted-git-native) - [CLI Tool](#-cli-tool) - [Generate Schema from .env](#-generate-schema-from-env-interactive) - [Sync and Update Schema from .env (`--sync` mode)](#-sync-and-update-schema-from-env---sync-mode) - [Promote/Sync Environment Files](#-promotesync-environment-files) - [Quick Example](#-quick-example) - [envUtils: Common Schema Helpers](#-envutils-common-schema-helpers) - [Security](#-security) - [Getting Started (Step-by-Step Guide)](#-getting-started-step-by-step-guide) - [Advanced Usage](#-advanced-usage) - [Options](#-options) - [How it works](#-how-it-works) - [Testing](#-testing) - [TypeScript](#-typescript) - [FAQ](#-faq) - [Build-time Validation for Production](#-build-time-validation-for-production) - [Why not just use `process.env.X`?](#-why-not-just-use-processenvx) - [Roadmap](#-roadmap) - [License](#-license) - [Documentation](#-documentation) - [Contributing](#-contributing) </details> ## 🎯 Why envfortress? **envfortress is the all-in-one solution for environment variables and secrets:** - **🔒 End-to-End Encrypted Secrets Management** Secure, git-native, and team-friendly secrets storage with CLI. - **🛡️ Type Safety** Full TypeScript inference with autocomplete. - **⚡ Zero Runtime Errors** Catch missing/invalid variables before your app starts. - **🛠️ CLI Tool** Generate schemas, validate, sync `.env.example`, and manage secrets. - **🎯 Framework Smart** Works seamlessly with React, Vite, Next.js, Express, and more --- ## 🔒 Secrets Management (Encrypted, Git-Native) envfortress includes a powerful, end-to-end encrypted secrets manager designed for teams: - **Per-secret encryption**: Each secret is encrypted for all authorized users. - **Git-native workflow**: Secrets are stored in a merge-friendly, auditable format. - **Team management**: Add/remove users, rotate keys, and recover access with ease. - **Simple CLI**: Set, get, decrypt, and merge secrets with intuitive commands. **Example commands:** ```sh # Initialize secrets for your project envfortress secrets init # Add or update a secret (encrypted for all team members) envfortress secrets set API_KEY=supersecret # Get a secret (decrypts and prints to stdout) envfortress secrets get API_KEY # Decrypt all secrets to a .env file for local use envfortress secrets decrypt --output .env # Add a new team member (re-encrypts all secrets for new user) envfortress secrets add-user alice@example.com --pubkey AGE_PUBLIC_KEY # Remove a team member (re-encrypts secrets to exclude user) envfortress secrets remove-user bob@example.com # Merge encrypted secrets files (for resolving git conflicts) envfortress secrets merge --base base.secrets.enc --ours ours.secrets.enc --theirs theirs.secrets.enc --output merged.secrets.enc --identity AGE_PRIVATE_KEY --recipients AGE_PUBLIC_KEY ``` [See full secrets workflow and details below.](#-envfortress-secrets-secure-git-native-secret-storage-for-teams) --- ## 📦 Installation [![Zod v4+](https://img.shields.io/badge/zod-v4%2B-blue)](https://zod.dev) [![Node.js](https://img.shields.io/badge/node-%3E=16.0.0-green)](https://nodejs.org/) ```bash npm install envfortress zod # or pnpm add envfortress zod ``` > **⚠️ If you use TypeScript schema files (`.ts`), you must also install [`ts-node`](https://typestrong.org/ts-node/) as a devDependency!** > > ```bash > npm install --save-dev ts-node > # or > pnpm add -D ts-node > ``` > > `ts-node` is listed as a peer dependency and is required for TypeScript schema support. > **Requires [Zod v4+](https://zod.dev)** --- ## 🛠️ CLI Tool envfortress includes a powerful CLI tool for managing environment variables: ```bash # Get help npx envfortress --help # Initialize a new environment schema npx envfortress init --framework vite --output src/env.ts npx envfortress init --framework nextjs --output src/env.ts npx envfortress init --framework cra --output src/env.ts # Sync your .env.example file npx envfortress sync # Validate your environment variables npx envfortress validate # Validate in CI mode (strict, no debug output) npx envfortress validate --ci # Generate a TypeScript Zod schema from an existing .env file (interactive) npx envfortress generate-schema --env .env --output src/env.ts --guess-types # 🔄 Sync and update your schema from .env, preserving manual edits and comments npx envfortress generate-schema --env .env --output src/env.ts --guess-types --sync ``` --- ### 🧙‍♂️ Generate Schema from .env (Interactive) You can generate a TypeScript Zod schema from an existing `.env` file using: ```bash npx envfortress generate-schema --env .env --output src/env.ts --guess-types ``` - The CLI will prompt you for each variable, showing the inferred type and value. - You can pick from a list (string, number, boolean, url, enum). - For enums, if multiple values are detected, you can confirm or edit the enum values interactively. - Comments in your `.env` are preserved in the generated schema. **Example session:** ``` Variable: VITE_DEBUG Value: true Inferred type: z.boolean() Pick type: z.boolean() z.string() z.number() z.string().url() enum Variable: ENV Value: development Inferred type: z.string() Pick type: enum [development, production, test] z.string() z.number() z.boolean() z.string().url() Edit enum values for ENV (comma-separated): > development,production,test ``` The generated file will include sensible defaults, comments, and enum suggestions. --- ### 🔄 Sync and Update Schema from .env (`--sync` mode) You can keep your TypeScript schema in sync with your `.env` file, while preserving manual edits and comments, using the `--sync` flag: ```bash npx envfortress generate-schema --env .env --output src/env.ts --guess-types --sync --non-interactive ``` - **Preserves manual edits and comments:** Your custom comments and formatting are kept. - **Adds new variables:** Any new variables in `.env` are added to the schema. - **Updates types:** If a variable's type changes in `.env`, the schema is updated. - **Flags removed variables:** Variables no longer present in `.env` are commented out in the schema (not deleted). - **Non-interactive mode recommended:** Use `--non-interactive` for CI or automation. **Example:** Suppose your schema contains: ```ts // Custom header comment import { createEnv } from "envfortress"; import { z } from "zod"; export const env = createEnv({ client: { // API URL for frontend VITE_API_URL: z.string().url(), }, server: { // Database connection string DATABASE_URL: z.string(), // Secret key SECRET_KEY: z.string(), // This will be removed OLD_VAR: z.string(), }, }); export default env; ``` And your `.env` file is updated to: ```env VITE_API_URL=https://api.example.com DATABASE_URL=postgresql://user:pass@localhost:5432/db SECRET_KEY=supersecret NEW_SERVER_VAR=42 ``` Running: ```bash npx envfortress generate-schema --env .env --output src/env.ts --guess-types --sync --non-interactive ``` Will update your schema to: ```ts // Custom header comment import { createEnv } from "envfortress"; import { z } from "zod"; export const env = createEnv({ client: { // API URL for frontend VITE_API_URL: z.string().url(), }, server: { // Database connection string DATABASE_URL: z.string(), // Secret key SECRET_KEY: z.string(), NEW_SERVER_VAR: z.number(), // This will be removed // OLD_VAR: z.string(), }, }); export default env; ``` --- #### 🏭 Non-Interactive Mode (for CI and Automation) If you want to generate a schema without any prompts (e.g., in CI or scripts), use the `--non-interactive` flag: ```bash npx envfortress generate-schema --env .env --output src/env.ts --guess-types --non-interactive ``` - This will use the inferred types for all variables automatically. - Useful for automated workflows, testing, or when you want to skip manual review. --- ### 🔁 Promote/Sync Environment Files Easily promote or sync variables between env files (e.g., from development to production or to .env.example): ```bash npx envfortress promote-env --from .env.development --to .env.production --mode add npx envfortress promote-env --from .env --to .env.example --mode example ``` **Modes:** - `add` (default): Add missing variables from source to target - `remove`: Remove variables from target not in source - `comment`: Comment out variables in target not in source - `example`: Prepare a .env.example (add all, clear values, comment out extras) **Handy flags:** - `--dry-run`: Show what would change without writing to disk - `--backup`: Create a backup of the target file before writing - `--interactive`: Prompt before adding, removing, or commenting out variables **Examples:** ```bash # See what would change without writing npx envfortress promote-env --from .env --to .env.example --mode example --dry-run # Create a backup before writing npx envfortress promote-env --from .env --to .env.production --mode add --backup # Prompt before each change npx envfortress promote-env --from .env --to .env.production --mode add --interactive ``` **Example:** ```bash # Promote all variables from .env to .env.example, clearing values and commenting out extras npx envfortress promote-env --from .env --to .env.example --mode example ``` --- ## ⚡ Quick Example ```ts import { createEnv, envUtils } from "envfortress"; const env = createEnv({ client: { API_URL: envUtils.url(), DEBUG: envUtils.boolean(), }, server: { DATABASE_URL: envUtils.url(), PORT: envUtils.port(), }, }); console.log(env.API_URL, env.DATABASE_URL, env.PORT); ``` ## 🛠️ envUtils: Common Schema Helpers `envUtils` provides ready-to-use Zod schemas for common environment variable patterns: | Helper | Description | Example Usage | | -------------- | -------------------------------------- | --------------------------------- | | `boolean()` | Parses 'true', 'false', '1', '0', etc. | `DEBUG: envUtils.boolean()` | | `number()` | Parses numbers | `TIMEOUT: envUtils.number()` | | `port()` | Parses and validates port numbers | `PORT: envUtils.port()` | | `url()` | Validates URLs | `API_URL: envUtils.url()` | | `email()` | Validates email addresses | `EMAIL: envUtils.email()` | | `json(schema)` | Parses and validates JSON | `CONFIG: envUtils.json()` | | `list()` | Parses comma-separated lists | `FEATURES: envUtils.list()` | | `secret()` | Validates secret keys (min length) | `JWT_SECRET: envUtils.secret(32)` | --- ## 🔒 Security - **Server-only variables** are never exposed to the client bundle. - **Validation** ensures secrets and sensitive data are present and correct before your app runs. - **Best practices:** - Never commit secrets to version control. - Use `.env.example` to document required variables. - Use CI/CD secrets management for production deployments. --- ## 🚀 Getting Started (Step-by-Step Guide) ### Step 1: Install envfortress ```bash npm install envfortress zod # or pnpm add envfortress zod ``` ### Step 2: Create your environment schema Instead of manually creating files, use the CLI to generate a starter: ```bash # For a Vite project npx envfortress init --framework vite --output src/env.ts # For a Next.js project npx envfortress init --framework nextjs --output src/env.ts # For a Create React App project npx envfortress init --framework cra --output src/env.ts ``` This creates a file like `src/env.ts`: ```ts import { createEnv } from "envfortress"; import { z } from "zod"; export const env = createEnv({ client: { VITE_API_URL: z.string().url(), VITE_ENV: z.enum(["development", "production", "test"]), }, server: { DATABASE_URL: z.string().url(), SECRET_KEY: z.string().min(16), }, }); export default env; ``` ### Step 3: Generate your .env.example file ```bash npx envfortress sync ``` This creates a `.env.example` file showing exactly what variables you need: ```bash # Environment Variables # Copy this file to .env and fill in your values # Client-side variables (available in browser) VITE_API_URL= # URL (e.g., https://api.example.com) VITE_ENV= # enum (development | production | test) # Server-side variables (not available in browser) DATABASE_URL= # URL (e.g., https://api.example.com) SECRET_KEY= # string ``` ### Step 4: Create your .env file ```bash # Copy the example file cp .env.example .env # Edit it with your actual values ``` Your `.env` file should look like: ```bash VITE_API_URL=https://api.myapp.com VITE_ENV=development DATABASE_URL=postgres://localhost:5432/mydb SECRET_KEY=my-super-secret-key-that-is-long-enough ``` ### Step 5: Validate your setup ```bash npx envfortress validate ``` You should see: `✅ Environment validation passed!` ### Step 6: Use it in your code ```ts // In your React component import { env } from "./env"; function MyComponent() { return ( <div> <p>API URL: {env.VITE_API_URL}</p> <p>Environment: {env.VITE_ENV}</p> </div> ); } ``` ```ts // In your server code (API routes, etc.) import { env } from "./env"; export async function apiHandler() { // This will only work on the server const db = await connectToDatabase(env.DATABASE_URL); const secret = env.SECRET_KEY; // This stays on the server! } ``` ### 🎯 What just happened? 1. **You defined rules** - Your schema tells envfortress what variables you expect 2. **envfortress validated everything** - It checked that all variables exist and have the right format 3. **TypeScript knows everything** - You get autocomplete and type safety 4. **Security is automatic** - Server variables can't leak to the browser ### 🚨 Common mistakes (and how to avoid them) **❌ Don't do this:** ```ts // This could fail silently const apiUrl = process.env.API_URL; const port = process.env.PORT; ``` **✅ Do this instead:** ```ts // This will fail fast with clear errors const apiUrl = env.VITE_API_URL; const port = env.PORT; ``` ### 🔧 Customizing your schema You can add more variables to your schema: ```ts export const env = createEnv({ client: { VITE_API_URL: z.string().url(), VITE_ENV: z.enum(["development", "production", "test"]), // Add more client variables here VITE_FEATURE_FLAG: z.boolean().default(false), }, server: { DATABASE_URL: z.string().url(), SECRET_KEY: z.string().min(16), // Add more server variables here PORT: z .string() .transform((val) => parseInt(val, 10)) .default("3000"), NODE_ENV: z.enum(["development", "production", "test"]), }, }); ``` After changing your schema, run: ```bash npx envfortress sync --force # Update .env.example npx envfortress validate # Check your .env file ``` ### 🎉 You're all set! Now you have: - Type-safe environment variables - Clear error messages when something's wrong - Automatic validation - Security (server variables stay on server) - Team-friendly setup (everyone knows what variables are needed) --- ## 🏁 Getting Started ### Vite ```ts // src/env.ts import { createEnv, z } from "envfortress"; export const env = createEnv({ client: { VITE_API_URL: z.string().url(), VITE_ENV: z.enum(["development", "production", "test"]), }, server: { DATABASE_URL: z.string().url(), SECRET_KEY: z.string().min(16), }, }); ``` --- ### Create React App (CRA) ```ts // src/env.ts import { createEnv, z } from "envfortress"; export const env = createEnv({ client: { REACT_APP_API_URL: z.string().url(), REACT_APP_ENV: z.enum(["development", "production", "test"]), }, server: {}, }); ``` --- ### Node/Express ```ts // src/env.ts import { createEnv, z } from "envfortress"; export const env = createEnv({ client: {}, server: { DATABASE_URL: z.string().url(), PORT: z .string() .transform((val: string) => parseInt(val, 10)) .default("3000"), SECRET_KEY: z.string().min(16), NODE_ENV: z.enum(["development", "production", "test"]), }, }); ``` --- ### Public-Only (Client-Side Only) For cases where you only need client-side validation: ```ts // src/env.ts import { createPublicEnv, z } from "envfortress"; export const env = createPublicEnv({ VITE_API_URL: z.string().url(), VITE_ENV: z.enum(["development", "production"]), }); ``` --- ## 🧩 Usage ### 1. Define your env schema ```ts // src/env.ts import { createEnv } from "envfortress"; import { z } from "zod"; export const env = createEnv({ client: { NEXT_PUBLIC_API_URL: z.string().url(), NEXT_PUBLIC_ENV: z.enum(["dev", "prod"]), }, server: { DATABASE_URL: z.string().url(), INTERNAL_SECRET: z.string().min(10), }, }); ``` --- ### 2. Use it in your app ```ts // Client-safe usage console.log("Calling:", env.NEXT_PUBLIC_API_URL); // Server-only usage (SSR / API route / backend) if (typeof window === "undefined") { connectToDatabase(env.DATABASE_URL); } ``` --- ## 🚀 Advanced Usage You can use envfortress for much more than flat strings! It supports: - **Nested objects** (e.g., JSON configs for databases, AWS, etc.) - **Array transforms** (comma-separated lists) - **Union types** (enums, log levels, etc.) - **Complex validation** (refine, superRefine, regex, etc.) - **Optional and defaulted values** Example: ```ts import { createEnv, z } from "envfortress"; export const env = createEnv({ client: { VITE_ALLOWED_ORIGINS: z .string() .transform((val: string) => val.split(",").map((s) => s.trim())), VITE_DATABASE_CONFIG: z.string().transform((val: string) => { const parsed = JSON.parse(val); return z .object({ host: z.string(), port: z.number(), ssl: z.boolean(), }) .parse(parsed); }), VITE_API_KEY: z.string().refine((val: string) => val.startsWith("sk_"), { message: 'API key must start with "sk_"', }), VITE_LOG_LEVEL: z.union([ z.literal("debug"), z.literal("info"), z.literal("warn"), z.literal("error"), ]), }, server: { SECURITY_CONFIG: z.string().transform((val: string) => { const parsed = JSON.parse(val); return z .object({ jwt: z.object({ secret: z.string().min(32), expiresIn: z.string(), algorithm: z.enum(["HS256", "HS384", "HS512"]), }), cors: z.object({ origin: z.union([z.string(), z.array(z.string())]), credentials: z.boolean(), methods: z.array(z.string()), }), rateLimit: z.object({ windowMs: z.number(), max: z.number(), }), }) .parse(parsed); }), }, }); ``` --- ## ⚙️ Options You can configure additional options like: ```ts createEnv({ client: { ... }, server: { ... }, }, { exampleFile: '.env.example', // Sync validation allowMissingInProduction: false, strict: true, // Reject unknown environment variables debug: true, // Log parsed env in development onValidationError: (err) => { console.error('ENV ERROR', err) process.exit(1) } }) ``` ### Available Options | Option | Type | Default | Description | | -------------------------- | ----------------------------- | --------------------------------- | ----------------------------------------------- | | `exampleFile` | `string` | `'.env.example'` | Path to .env.example file for validation | | `allowMissingInProduction` | `boolean` | `false` | Allow missing variables in production | | `strict` | `boolean` | `false` | Reject unknown environment variables | | `debug` | `boolean` | `false` | Log parsed environment variables in development | | `onValidationError` | `(error: z.ZodError) => void` | `console.error + process.exit(1)` | Custom error handler | --- ## 🔍 How it works - At build-time or runtime, it validates `process.env` against your Zod schema - Only the `client` keys are included in browser bundles - Server-only keys are stripped from browser output - Throws in development if required vars are missing or invalid - Enhanced error messages show allowed values for enums and helpful hints --- ## 🧪 Testing You can safely override or mock the `env` object in your tests: ```ts vi.mock("./env", () => ({ env: { NEXT_PUBLIC_API_URL: "http://localhost:3000", DATABASE_URL: "postgres://test", }, })); ``` --- ## 📚 TypeScript The library exports useful types for advanced usage: ```ts import { createEnv, type InferClient, type InferServer, type EnvSchema, } from "envfortress"; // Use exported types in your own code type MyClientEnv = InferClient<typeof clientSchema>; type MyServerEnv = InferServer<typeof serverSchema>; ``` --- ## 🧠 FAQ **Q: What frameworks are supported?** A: Next.js, Vite, CRA, Express, and any Node.js runtime. **Q: Does it support `.env.example` syncing?** A: Yes! It can enforce required variables and validate your actual `.env`. **Q: Will this bloat my client bundle?** A: No server-only env vars are stripped out automatically. **Q: What's the difference between `createEnv` and `createPublicEnv`?** A: `createPublicEnv` is a convenience helper for client-side only validation, equivalent to `createEnv({ client: schema, server: {} })`. **Q: How do I enable strict mode?** A: Set `strict: true` in options to reject any environment variables not defined in your schema. **Q: Is there a CLI tool?** A: Yes! The CLI provides commands for initialization, validation, syncing, and type generation. See [CLI Documentation](./docs/cli.md). **Q: What Zod version is required?** A: `envfortress` requires Zod v4+. This is because `envfortress` uses Zod's `superRefine` for complex validation and `z.object({ ... })` for schema definition. **Q: Can I use `envfortress` in a monorepo?** A: Yes! `envfortress` is designed to be framework-agnostic and can be used across different projects. You can define your schema once and reuse it. **Q: How do I handle CI/CD secrets for production?** A: `envfortress` is designed to be secure. Server-only variables are stripped out automatically, and validation ensures secrets are present. For production deployments, use CI/CD secrets management to manage sensitive variables. --- ## 🏗️ Build-time Validation for Production You can catch environment variable issues before deploying by running validation in your CI or build scripts: ```bash npx envfortress validate --ci ``` - The `--ci` flag enables strict mode, disables debug output, and exits with an error code if validation fails. - Add this command to your CI pipeline or build step to prevent broken builds from being deployed. **Example (GitHub Actions):** ```yaml - name: Validate environment variables run: npx envfortress validate --ci ``` --- ## 🛑 Why not just use `process.env.X`? | Problem | Solution from `envfortress` | | -------------------------- | ------------------------------------- | | Silent failures | Runtime validation with Zod | | No autocomplete | Full TypeScript inference | | Secrets leaking to browser | Server/client segregation | | Missing `.env` variables | Optional `.env.example` syncing | | Confusing prefixes | Abstracted into simple API | | Unknown env vars | Strict mode to catch typos | | Poor error messages | Enhanced errors with hints and values | --- ## 🧱 Roadmap - [ ] ESLint rule for unknown `process.env.X` usage - [ ] Vite + Next.js plugins (optional) - [ ] VS Code extension for autocomplete - [ ] Build-time validation for production --- ## 📜 License MIT use freely and safely. --- ## 📚 Documentation - **[API Reference](https://github.com/martinmcdermid/envfortress/blob/main/docs/api.md)** - Complete API documentation - **[CLI Guide](https://github.com/martinmcdermid/envfortress/blob/main/docs/cli.md)** - Command-line tool usage - **[Migration Guide](https://github.com/martinmcdermid/envfortress/blob/main/docs/migration.md)** - Migrate from other libraries - **[Troubleshooting](https://github.com/martinmcdermid/envfortress/blob/main/docs/troubleshooting.md)** - Common issues and solutions ## 🤝 Contributing We welcome contributions! Please see our [Contributing Guide](https://github.com/martinmcdermid/envfortress/blob/main/CONTRIBUTING.md) for details. ## 📄 License MIT © [Martin McDermid](https://github.com/martinmcdermid) > **Note:** When using TypeScript schema files (`.ts`) with envfortress CLI, write your schema using CommonJS syntax (`require`/`exports`). > For example: > > ```ts > const { z } = require('zod'); > exports.env = { ... }; > ``` > > ESM syntax (`import`/`export`) is not supported for runtime-loaded schema files unless you use Node.js ESM loader and configure your project accordingly. ## 🔒 envfortress Secrets: Secure, Git-Native Secret Storage for Teams envfortress revolutionizes how you manage secrets in your codebase—making it secure, auditable, and effortless for teams. ### What envfortress Secrets Does - **Per-Secret End-to-End Encryption:** Every secret is individually encrypted using modern cryptography (age/libsodium). Only authorized users can decrypt—no plaintext secrets ever touch your repo, disk, or CI. - **Deterministic, Merge-Friendly Format:** Secrets are stored in a versioned YAML file designed for git. Track changes, review diffs, and resolve merge conflicts at the secret level—just like code. - **Easy CLI for Developers:** - `envfortress secrets set KEY=VALUE` Encrypt and store a secret. - `envfortress secrets get KEY` Decrypt and print a secret. - `envfortress secrets decrypt --output .env` Decrypt all secrets for local use. - **Schema Validation:** Built-in validation ensures your secrets are always in the right format, reducing misconfigurations and runtime errors. - **Auditability:** Every change to secrets is tracked, and the file format is designed for easy review and compliance. ### Why It’s Great - **No More .env Leaks:** Secrets are always encrypted—no more accidental commits of sensitive data. - **Works with Git, Not Against It:** Designed for version control, so you can branch, merge, and review secrets changes just like code. - **Zero Vendor Lock-In:** No servers, no subscriptions, no proprietary formats. Your secrets, your repo, your rules. - **Team-Ready:** Add or remove users at any time; secrets are re-encrypted for the current team, with no downtime or manual rework. - **Fast Onboarding:** New team members can get up and running with a single command—no more secret-sharing headaches. **envfortress secrets:** - Secure by default - Git-native - Team-friendly - Auditable - Simple CLI Lock down your secrets. Ship with confidence. --- ## envfortress Secrets: Typical Team Workflow ### 1. Project Initialization _One-time setup by a project owner:_ - Generates a cryptographic keypair for the owner. - Creates the encrypted secrets file (`.envfortress.secrets.enc`). ```sh envfortress secrets init ``` ### 2. Adding Secrets _Any authorized team member can add or update secrets:_ - Each secret is individually encrypted for all current team members. ```sh envfortress secrets set API_KEY=supersecret envfortress secrets set DATABASE_URL=postgres://... ``` ### 3. Sharing Access with the Team - **Add a new team member:** - All secrets are re-encrypted so the new member can decrypt them. ```sh envfortress secrets add-user alice@example.com --pubkey AGE_PUBLIC_KEY ``` - **Remove a team member (offboarding):** - Secrets are re-encrypted to exclude the removed user. - The action is logged for auditability. ```sh envfortress secrets remove-user bob@example.com ``` ### 4. Using Secrets Locally - **Decrypt all secrets for local development:** - Only users with a valid private key can decrypt. ```sh envfortress secrets decrypt --output .env ``` - **Get a single secret:** ```sh envfortress secrets get API_KEY ``` ### 5. Keeping Secrets Safe in Git - Secrets are always stored encrypted in `.envfortress.secrets.enc`. - The file is versioned and merge-friendly. - **Merge conflicts?** - Use the built-in merge helper to resolve conflicts at the secret level, not the file level. ```sh envfortress secrets merge --base base.secrets.enc --ours ours.secrets.enc --theirs theirs.secrets.enc --output merged.secrets.enc --identity AGE_PRIVATE_KEY --recipients AGE_PUBLIC_KEY ``` ### 6. Recovery & Audit - **Lost your key?** - Use the recovery admin key to regain access. - **Audit:** - All changes (add/remove users, secret changes) are logged for audit and compliance. ### Summary Table | Action | Command Example | | ------------------- | ------------------------------------------------- | | Init project | `envfortress secrets init` | | Add secret | `envfortress secrets set KEY=VALUE` | | Get secret | `envfortress secrets get KEY` | | Decrypt all secrets | `envfortress secrets decrypt --output .env` | | Add user | `envfortress secrets add-user EMAIL --pubkey KEY` | | Remove user | `envfortress secrets remove-user EMAIL` | | Merge conflicts | `envfortress secrets merge ...` | | Recover access | (Use recovery admin key) | --- envfortress secrets fits seamlessly into your git workflow, making secrets management secure, auditable, and team-friendly—without ever exposing sensitive data. ---