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
Markdown
# 🛡️ 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.**
[](https://www.npmjs.com/package/envfortress)
[](https://www.npmjs.com/package/envfortress)
[](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
[](https://zod.dev)
[](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.