自信地提交加密的.env文件
@asafarim/envag TS套件
安全、基于年龄的加密
.env横跨 monrepo 环境的文件.
envage 让您安全地执行加密任务 .env 保存到 Git 的文件
解密解密出版本控制. 它使用战斗测试
年龄 加密格式——与使用的工具相同
Go, Rust, and DevSecOps toolchains worldwide. 滚,鲁斯特,和DevSecOps 工具链全球.
特征
- 🔐 年龄加密 - 用途
age-encryption(纯类型脚本,无原生道克) - 🌍 多种环境 —— 说
dev, (中文(简体) ),staging, (中文(简体) ),prod,任何自定义名称,或root为平地.env文件 - 📁 单发觉 - 立即加密/解密所有应用程序
--all - 👥 团队接受者 ——独立身份解密共享密码;无私钥共享
- 🔑 密钥或密码句 ——支持 X25519 密钥派和基于密码口的加密
- 🛡 吉特安全系统 自动更新
.gitignore; 警告您是否设置已解密的文件 - ✅ 生产警卫 ——解密所需的交互确认
prod文件 - 🧩 程序化 API ——在您自己的脚本中用作库
- 📦 pnpm / 单重力 与 pnpm 工作空间无缝工作
兼容性
需要20号或更新的节点。 信封没有反应依赖性或对等版本 限制并工作在React 18和React 19项目工具. 其综合指数和 文件系统 API 在 Node.js 中运行,外部浏览器发送组件. 演示 使用React 18,在CI中有一个单独的React 19构建. 演示默认为黑暗 模式,记住您的主题选择,并包含 CLI/API 团队接收实例 和与当前里程碑相联系的路线图.
安装
# In your monorepo root
pnpm add -D @asafarim/envage
# Or globally
pnpm add -g @asafarim/envage
快速启动
1. 生成键盘
npx envage init-key
这就产生了:
.age/key.txt ← private key (mode 0600, gitignored automatically)
.age/key.pub ← public key (safe to share with teammates)
2. 创建您的配置
# envage.config.json (add it to the monorepo root)
# use relative paths from the monorepo root
# use 'root' for the .env files regardless of environment, 'dev' for development environment .env.dev, 'staging' for staging environment .env.staging, 'prod' for production environment .env.prod
# use '.' for the current directory (monorepo root): "apps": [".", "apps/web"],
{
"apps": [".", "apps/web", "apps/admin", "packages/api"],
"envs": ["root", "dev", "staging", "prod"],
"keyFile": ".age/key.txt"
}
3. 加密您的内存文件
# Encrypt a single app/env
npx envage encrypt apps/web --env dev
# Encrypt all apps at once
npx envage encrypt --all --env prod
# Encrypt plain .env files across all apps (one environment per invocation)
npx envage encrypt --all --env root
4. 检查情况
npx envage status
apps/web
dev 🔒 encrypted
staging 🔒 encrypted
prod 🔒 encrypted
apps/admin
dev 🔓 decrypted (not yet encrypted)
prod 🔒 encrypted
packages/api
dev 🔒 encrypted
prod 🔒 encrypted
9 entries — 8 encrypted 1 decrypted
5. 在新机器上解密
# Restore your own private identity from secure backup, or have an authorized
# teammate add your new public recipient (see Team encryption below).
npx envage decrypt apps/web --env dev
# Decrypt all at once
npx envage decrypt --all --env dev
CLI 参考
envage init-key
生成新时代的X25519 keypair.
envage init-key [--output <folder>]
| 选项 | 默认 | 说明 |
|---|---|---|
--output | .age | 输出文件夹 key.txt 财务报告和审定财务报表 key.pub |
envage encrypt
加密 .env.<env> → .env.<env>.age (或 .env → .env.age 何时 --env root) (中文(简体) ).
envage encrypt [path] [options]
| 选项 | 默认 | 说明 |
|---|---|---|
--env | dev | 环境名称 |
--key | 自动检测 | 年龄密钥文件路径 |
--passphrase | —— 说 | 密码句( 如果省略了 ) |
--all | —— 说 | 加密所有应用程序 envage.config.json |
实例:
envage encrypt apps/web --env dev
envage encrypt apps/web --env prod --key .age/key.txt
envage encrypt --all --env staging
envage encrypt apps/web --env dev --passphrase "my secret"
# Root env — targets the plain .env file (no suffix)
envage encrypt apps/portal --env root
envage encrypt --all --env root
envage decrypt
解密 .env.<env>.age → .env.<env> (或 .env.age → .env 何时 --env root) (中文(简体) ).
envage decrypt [path] [options]
| 选项 | 默认 | 说明 |
|---|---|---|
--env | dev | 环境名称 |
--key | 自动检测 | 年代私钥文件路径 |
--passphrase | —— 说 | 密码句 |
--all | —— 说 | 解密所有应用程序 envage.config.json |
解密
prod或者说production环境总是促使 手动确认.
实例:
envage decrypt apps/web --env dev
envage decrypt --all --env prod
# Root env — targets .env.age → .env
envage decrypt apps/portal --env root
envage decrypt --all --env root
envage recipient add/list/remove
管理团队成员并重新加密所有已配置的密码文本 .
add 财务报告和审定财务报表 remove 带一个完整的公共收件人 加上可选 --key <identity-file>。 。 。 .
见 团队加密 用于配置、恢复和撤销限制.
envage status
显示所有配置的应用程序和环境的加密状态 .
envage status [path]
| 选项 | 说明 |
|---|---|
--env | 过滤输出到单个环境 |
配置 - envage.config.json
将此文件放入您单曲的根 :
{
"apps": [
"apps/web",
"apps/admin",
"packages/api"
],
"envs": ["dev", "staging", "prod"],
"keyFile": ".age/key.txt",
"keyPubFile": ".age/key.pub"
}
根内-平面 .env 文件
使用 "root" 作为锁定赤裸的巢穴名称 .env 每个应用程序文件夹中的文件(无后缀).
这对已经使用单个的项目有用 .env 并且不想重命名它.
{
"apps": [".", "apps/web", "apps/admin", "packages/api"],
"envs": ["root", "dev", "staging", "prod"],
"keyFile": ".age/key.txt"
}
| 内存名称 | 纯文本文件 | 加密文件 |
|---|---|---|
root | .env | .env.age |
dev | .env.dev | .env.dev.age |
staging | .env.staging | .env.staging.age |
prod | .env.prod | .env.prod.age |
球形图案在 apps
App 条目可能使用 glob 模式, 相对于文件系统扩展 选择 目录 这是存在的。 这对单簧管有帮助 你不想逐个列出.
{
"apps": [".", "apps/*", "packages/*"],
"envs": ["root", "production"],
"keyFile": ".age/key.txt",
"keyPubFile": ".age/key.pub"
}
*匹配单个路径段(例如).apps/*→apps/web, (中文(简体) ),apps/admin) (中文(简体) ).**匹配任何深度的嵌入目录.- 只匹配已有目录; 文件被忽略 .
- 非 glob 条目( 如
.或者说apps/web) 保存为-s,即使文件夹 目前还不存在. - 结果是去重复的,所以重叠的模式是安全的.
配置字段
| 外地 | 类型 | 默认 | 说明 |
|---|---|---|---|
apps | string[] | [] | App文件夹路径( 相对 cwd) 。 支持球状图案(见下文) |
envs | string[] | ["dev","staging","prod"] | 管理的环境名称 |
keyFile | string | .age/key.txt | 私钥路径 |
keyPubFile | string | .age/key.pub | 公钥路径 |
recipients | string[] | 删除 | 公共年龄接受者; 省略保留遗留的单键模式 |
程序化 API
import {
encryptEnv,
decryptEnv,
generateKeyPair,
generateKeyPairToFolder,
getEnvStatus,
loadConfig,
resolveApps,
ensureGitignore,
updateRecipient,
} from "@asafarim/envage";
// Generate a keypair
const { identity, recipient } = await generateKeyPair();
// Write keypair to disk
await generateKeyPairToFolder(".age");
// Encrypt a named env file (.env.dev → .env.dev.age)
await encryptEnv({
folder: "apps/web",
env: "dev",
keyFile: ".age/key.txt",
});
// Encrypt the root env (.env → .env.age)
await encryptEnv({
folder: "apps/portal",
env: "root",
keyFile: ".age/key.txt",
});
// Passphrase-based encryption
await encryptEnv({
folder: "apps/web",
env: "dev",
passphrase: "my-secret-passphrase",
});
// Decrypt a named env file (.env.dev.age → .env.dev)
await decryptEnv({
folder: "apps/web",
env: "dev",
keyFile: ".age/key.txt",
});
// Decrypt the root env (.env.age → .env)
await decryptEnv({
folder: "apps/portal",
env: "root",
keyFile: ".age/key.txt",
});
// Expand app globs ("apps/*") to actual folder paths
const config = await loadConfig();
const folders = await resolveApps(config);
// → ["/repo/apps/web", "/repo/apps/admin", ...]
// Check status (globs in config are expanded automatically)
const statuses = await getEnvStatus(config);
statuses.forEach((s) => {
console.log(`${s.folder} [${s.env}]: encrypted=${s.encrypted} decrypted=${s.decrypted}`);
});
// Ensure .gitignore rules are present
await ensureGitignore();
团队加密(v0.5.0)
每个队友跑 envage init-key 当地份额 仅限 他们的公众
key.pub将私人身份保存在他们所有者的机器上或安全备份中.
现有获得授权的成员可登上另一个公共接收人:
envage recipient add age1... --key .age/key.txt
envage recipient list
envage recipient remove age1... --key .age/key.txt
替换 age1... 完全年龄的X25519公用钥匙。 命令从
项目根并需要已有 envage.config.json 无空 apps
财务报告和审定财务报表 envs添加/重新加密 所有已存在的密码文本 在那些配置中
插槽,包括被扩大的应用程序和 root 环境。 未配置的文件
不属于他们的范围。 缺少的密码文本被跳过 。 密码句加密或
不可解密的文件在替换开始前中止整个操作 .
公共收件人保存在版本配置中 :
{
"apps": [".", "apps/*"],
"envs": ["root", "dev", "prod"],
"keyFile": ".age/key.txt",
"recipients": ["age1...", "age1..."]
}
将更新的配置和密码文本一起提交。 普通 CLI 加密用途
配置收件人, 即使当 --key ,不需要私钥.
明确 --passphrase 选择密码句加密。 无配置
recipients 保持传统的单键行为。 第一个 recipient add
保留所提供身份的公用钥匙作为初始授权成员.
重复的,无效的,未知的,或最后接收的清除请求安全地失败.
方案API接受明确的公共接受者:
await encryptEnv({ folder: "apps/web", env: "dev", recipients: [alicePublic, bobPublic] });
await updateRecipient("add", bobPublic, { cwd: projectRoot, keyFile: ".age/key.txt" });
encryptEnv 不自动装入配置; 通过 config.recipients 你自己来.
收件人仅在内存中更改解密, 绝不更改为纯文本文件 。 替换
使用同名目录的临时文件和原子重命名. 写作失败时,
完成的替换被卷回。 多文件替换为 没有 一个
文件系统交易: 不运行并行加密/配置作者。 锁
防止同时接收者更新。 原件和清单保存在
私营 .envage-recovery-* 目录直到成功或回滚成功.
在进程中断或回滚失败后, 停止所有写入, 恢复 每个
该目录中的原始列表 manifest.json,验证解密,然后
删除回收目录 envage.config.json.lock从不执行这些
回收文物。 在更改前保持安全备份或清理 Git 检查站 .
仅删除新加密文件的收件人屏蔽访问 。 旧吉特 历史 密码副本 以及以前学到的秘密. 如果必须取消访问, 则单独旋转基本的 API 证书 .
Git 整合
.gitignore 规则
运行 envage init-key 规则自动添加到您 .gitignore数字 :
# --- envage managed ---
# Decrypted env files — never commit
.env*
!.env*.age
!.env.example
!.env.template
# Age private key
.age/key.txt
# --- end envage ---
已分阶段文件警告
如果你不小心的话 git add 加密前解密的内存文件
encrypt 命令将抓取并退出 :
✖ You are trying to commit a decrypted env file.
✖ Only encrypted .age files are allowed.
apps/web/.env.prod
可选: 承诺前钩
将此添加到 .git/hooks/pre-commit 完全防止意外犯罪:
#!/usr/bin/env bash
npx envage status 2>/dev/null | grep -q "decrypted" && \
echo "❌ You have decrypted env files. Run: npx envage encrypt --all" && \
exit 1
exit 0
Monorepo 结构
monorepo/
├── .age/
│ ├── key.txt ← private key (gitignored)
│ └── key.pub ← public key (commit this)
├── apps/
│ ├── web/
│ │ ├── .env.dev ← decrypted (gitignored)
│ │ ├── .env.dev.age ← encrypted (committed ✅)
│ │ ├── .env.prod ← decrypted (gitignored)
│ │ └── .env.prod.age ← encrypted (committed ✅)
│ ├── admin/
│ │ ├── .env.dev.age
│ │ └── .env.prod.age
│ └── portal/ ← app using root env (plain .env)
│ ├── .env ← decrypted (gitignored)
│ ├── .env.age ← encrypted (committed ✅)
│ └── .env.staging.age ← encrypted (committed ✅)
├── packages/
│ └── api/
│ ├── .env.dev.age
│ └── .env.prod.age
├── envage.config.json ← committed ✅
└── .gitignore ← auto-updated by envage
安全说明
- 永不承诺或共享
.age/key.txt- 这是你的私人身份. 给每个队友自己的身份,只增加他们的公收人. - 解密的数值从未被记录 ——不采用动词模式,不采用出错模式
- 生产解密设闸 - 总是要求明确
y确认 - 加密文件为 二进制 ——不能不小心读作正文
- 这个
age格式提供认证加密; 篡改可被检测到
发展
# Install dependencies
pnpm install
# Build
pnpm build
# Run tests
pnpm test
# Type-check only
pnpm lint
许可证
麻省理工学院 阿里·萨法里姆