为开发者提供安全、临时的秘密分享。
别再在 Slack 上粘贴秘密了。enseal 让安全路径比不安全路径更快——通过一条命令和零配置,即可通过加密的一次性通道分享 .env 文件和秘密。```bash
$ enseal share .env Share code: 7-guitarist-revenge Secrets: 14 variables (staging) Expires: on first receive
$ enseal receive 7-guitarist-revenge ok: 14 secrets written to .env
## 安装
### 从 crates.io```bash
cargo install enseal
git clone https://github.com/FlerAlex/enseal.git cd enseal cargo build --release
### 预编译二进制文件
从 [GitHub Releases](https://github.com/FlerAlex/enseal/releases) 下载适用于 Linux (x86_64, aarch64)、macOS (Intel, Apple Silicon) 和 Windows 的版本。
## 快速入门
### 匿名模式(零配置)
使用一次性代码共享机密。无需密钥,无需账户——立即可用。```bash
# terminal 1 (sender)
enseal share .env
Share code: 7-guitarist-revenge
Secrets: 14 variables
Expires: on first receive
# terminal 2 (recipient) — enter the code
enseal receive 7-guitarist-revenge
ok: 14 secrets written to .env
也适用于单个秘密:```bash
echo "my-api-token" | enseal share --label "API key" Share code: 4-orbital-hammock
enseal receive 4-orbital-hammock my-api-token
两个终端必须同时打开——发送方等待接收方连接。
### 身份模式(基于密钥,无需验证码)
适用于已建立密钥信任的团队。加密到名称,无需协调。```bash
# one-time setup
enseal keys init
enseal keys export > my-key.pub # share this with teammates
enseal keys import teammate-key.pub # import theirs
# sender encrypts to recipient by name
enseal share .env --to sarah
# or push through the public relay (no codes at all)
enseal share .env --to sarah --relay wss://relay.enseal.dev
# or produce an encrypted file (no network)
enseal share .env --to sarah --output ./drop/
enseal inject 7-guitarist-revenge -- npm start
enseal inject --listen --relay wss://relay.enseal.dev -- docker compose up
enseal inject ./staging.env.age -- python manage.py runserver
秘密仅存在于子进程的内存中。当子进程退出时,它们就消失了。
## 功能
### 三种共享模式
**匿名模式**(默认)——基于虫洞,零配置。只需一个人可读的代码即可。SPAKE2 相互认证可防止中间人攻击。```bash
enseal share .env # generates wormhole code
enseal receive 7-guitarist-revenge # uses code
With --relay, anonymous mode bypasses wormhole and uses the enseal relay transport instead. Both sides must use the same relay:```bash
enseal share .env --relay ws://relay.internal:4443 # generates channel code
enseal receive 3421-amber-frost --relay ws://relay.internal:4443
**异步上传 (`--upload`)** — 仅发送方使用。在本地加密并发布到 [burnurl.dev](https://burnurl.dev),返回一个自销毁的URL,接收方在浏览器中打开。接收方无需使用CLI。```bash
enseal share .env --upload
# Secret URL: https://burnurl.dev/s/a3f9c2e1...
# Expires: 2026-03-08 19:42:00 UTC (24h)
# Reads: 1 (self-destructs on first open)
接收者可在任意浏览器中打开该URL——无需安装enseal。添加 --passphrase 可在上传前在客户端加密(服务器仅能看到密文):```bash
enseal share .env --upload --passphrase # prompts for passphrase
enseal share .env --upload --ttl 4 # 4-hour TTL (max 24)
API访问需要burnurl.dev上的Pro或团队计划。将`BURNURL_API_KEY`设置为您自己的密钥。可以覆盖自托管实例的基本URL:`BURNURL_URL=https://burnurl.internal`。
**身份模式** — 为已知队友提供公钥加密。加密到名称。```bash
enseal keys init # one-time setup
enseal share .env --to sarah # encrypt to sarah's public key
身份模式支持三种传输选项:```bash
enseal share .env --to sarah
enseal share .env --to sarah --relay wss://relay.enseal.dev
enseal share .env --to sarah --output ./drop/
### 灵活输入
enseal 接受来自多个来源的机密:```bash
# .env file (default)
enseal share .env
enseal share staging.env
# environment profile
enseal share --env staging # resolves to .env.staging
# pipe from stdin
echo "sk_live_abc123" | enseal share
cat secrets.env | enseal share
pass show stripe/key | enseal share --to sarah
# inline (careful — visible in shell history)
enseal share --secret "API_KEY=sk_live_abc123"
# wrap raw string as KEY=VALUE
echo "sk_live_abc123" | enseal share --as STRIPE_KEY
${VAR} 引用在发送前会被解析,因此接收者能获得完全展开的值:```env
DB_HOST=postgres.internal
DB_PORT=5432
DATABASE_URL=postgres://user:pass@${DB_HOST}:${DB_PORT}/myapp
支持 `${VAR:-default}` 后备语法。循环引用和前置引用会被检测并拒绝。使用 `--no-interpolate` 发送原始 `${VAR}` 语法。
### 过滤
控制哪些变量被发送:```bash
# exclude public/non-secret vars
enseal share .env --exclude "^PUBLIC_|^NEXT_PUBLIC_"
# send only matching vars
enseal share .env --include "^DB_|^API_"
# skip .env parsing entirely (send raw file)
enseal share .env --no-filter
输出会根据发送内容自适应:```bash
enseal receive CODE ok: 14 secrets written to .env
enseal receive CODE --output staging.env
enseal receive CODE sk_live_abc123
enseal receive CODE --clipboard ok: copied to clipboard
enseal receive CODE --no-write
enseal receive ./staging.env.age ok: signature verified, file decrypted ok: 14 secrets written to .env
### 注入
接收秘密并将它们直接作为环境变量注入到子进程中。秘密永远不会触及文件系统。```bash
# anonymous mode: inject via wormhole code
enseal inject 7-guitarist-revenge -- npm start
# identity mode: listen for incoming transfer on relay
enseal inject --listen --relay wss://relay.enseal.dev -- docker compose up
# from encrypted file drop
enseal inject ./staging.env.age -- python manage.py runserver
使用 --listen 参数,接收方连接到中继并等待。发送方通过 enseal share .env --to alex --relay wss://relay.enseal.dev 推送——无需交换代码,无需协调。
除了共享,enseal 还是一个完整的 .env 安全工具包:```bash
enseal check error: missing from .env (present in .env.example): JWT_SECRET, REDIS_URL
enseal diff .env.development .env.staging
enseal redact .env DATABASE_URL= API_KEY= PORT=
enseal validate .env error: missing required: JWT_SECRET error: PORT value "abc" is not an integer ok: 11/14 variables passed validation
enseal template .env
### 静态加密
使用 age 加密对 `.env` 文件进行加密,以便安全地存储在 git 中:```bash
# whole-file encryption
enseal encrypt .env
ok: .env encrypted in-place (14 variables, age key)
enseal decrypt .env
# per-variable: keys visible for diffing, values encrypted
enseal encrypt .env --per-var
# DB_HOST=ENC[age:abc123...]
# DB_PORT=ENC[age:def456...]
# multi-recipient: anyone on the team can decrypt
enseal encrypt .env --to sarah --to alex
enseal keys init
enseal keys export > my-key.pub
enseal keys import sarah.pub
enseal keys list
enseal keys fingerprint
enseal keys remove [email protected]
enseal keys alias sarah [email protected]
enseal keys group create backend-team enseal keys group add backend-team sarah enseal keys group add backend-team alex enseal keys group list backend-team enseal share .env --to backend-team
enseal keys group delete backend-team
### 公共中继
一个免费的公共中继位于 `wss://relay.enseal.dev`。用于快速测试或当你不需要私有中继时。```bash
# check relay health
curl https://relay.enseal.dev/health
# use it for identity-mode transfers
enseal share .env --to sarah --relay wss://relay.enseal.dev
enseal inject --listen --relay wss://relay.enseal.dev -- npm start
# or set it globally
export ENSEAL_RELAY=wss://relay.enseal.dev
enseal keys init
enseal keys export > /tmp/mykey.pub enseal keys import /tmp/mykey.pub
enseal share --secret "TEST=works" --to mykey --output /tmp/ enseal receive /tmp/mykey.env.age
enseal inject --listen --relay wss://relay.enseal.dev -- env | grep TEST
enseal share --secret "TEST=relay_works" --to mykey --relay wss://relay.enseal.dev
### 自托管中继
将所有内容保留在您的网络内部。中继是无状态的 — 它只看到密文。```bash
# Docker (one command)
docker run -d -p 4443:4443 enseal/relay
# Or as a binary
enseal serve --port 4443
# Check relay health
curl http://localhost:4443/health
enseal serve 使用纯 WebSocket(ws://)。如需 TLS,请在前面放置反向代理(例如 Caddy、nginx),并使用 wss:// 连接。
设置 --relay 后,所有模式都会通过您的中继路由:```bash
enseal share .env --relay ws://relay.internal:4443
enseal receive 3421-amber-frost --relay ws://relay.internal:4443
export ENSEAL_RELAY=ws://relay.internal:4443 enseal share .env
使用自托管中继的身份模式完全无需编码:```bash
# receiver listens on the relay
enseal inject --listen --relay ws://relay.internal:4443 -- npm start
# sender pushes directly — no code generated
enseal share .env --to alex --relay ws://relay.internal:4443
ok: pushed to alex
在项目根目录的 .enseal.toml 中定义规则:```toml
[schema]
required = ["DATABASE_URL", "API_KEY", "JWT_SECRET"]
[schema.rules.DATABASE_URL] pattern = "^postgres://" description = "PostgreSQL connection string"
[schema.rules.PORT] type = "integer" range = [1024, 65535]
[schema.rules.API_KEY] min_length = 32
然后验证:```bash
enseal validate .env
当接收 .env 文件时,验证也会自动运行——在破损配置造成混乱之前将其捕获。
enseal share --env staging # shares .env.staging enseal validate --env production # validates .env.production enseal diff .env.development .env.production
## 工作原理
### 匿名模式
**Wormhole(默认,无 `--relay`):**
1. 发送方使用 `age` 加密有效载荷
2. 通过公共 wormhole 中继进行 SPAKE2 密钥交换,建立共享密钥
3. 加密后的有效载荷经由中继传输
4. 接收方使用协商密钥解密
5. 通道被销毁——一次性使用,限时有效
中继永远不会看到明文。wormhole 代码提供相互认证。
**Enseal 中继(`--relay`):**
1. 发送方使用 `age` 加密有效载荷,并将其发送到 enseal 中继,附带生成的通道代码
2. 接收方使用相同代码连接到同一中继,并接收有效载荷
3. 通道在首次接收后即被消耗
此模式中不涉及 SPAKE2——通道代码是唯一的凭证。
### 异步上传(`--upload`)
1. 发送方将有效载荷序列化为 `Envelope`(JSON,SHA-256 完整性检查)
2. 可选地使用 age scrypt 密码在客户端进行加密(`--passphrase`)
3. 通过 HTTPS 将有效载荷 POST 到 `burnurl.dev/api/secret`
4. burnurl.dev 使用服务器端 AES-256-GCM 进行静态存储,并返回一个自毁 URL
5. 该 URL 在配置的 TTL 内有效(免费层最多 24 小时),仅限一次读取
发送方分享该 URL。接收方在任何浏览器中打开即可——无需 enseal。使用 `--passphrase` 时,密码必须单独分享;服务器永远不会看到明文。
**层级:** API 访问需要 burnurl.dev 上的 Pro 或 Team 计划。将 `BURNURL_API_KEY` 设置为你的密钥——免费层没有 API 访问权限。
覆盖 `BURNURL_URL` 以指向自托管的 burnurl 实例。
### 身份模式(公钥)
1. 发送方使用接收方的 `age` 公钥加密
2. 发送方使用自己的 `ed25519` 密钥签名
3. 有效载荷通过中继、文件投放或 wormhole 传输
4. 接收方使用自己的私钥解密
5. 接收方验证发送方的签名
信任基于你已导入的密钥。
**身份模式中的传输选项:**
| 传输方式 | 标志 | 工作原理 |
|---|---|---|
| Wormhole(默认) | `--to sarah` | 生成一个代码,类似匿名模式但带有签名 |
| 中继推送 | `--to sarah --relay URL` | 推送到接收方的确定性通道,无需代码 |
| 文件投放 | `--to sarah --output ./dir/` | 生成加密的 `.env.age` 文件,无需网络 |
通过中继推送,接收方使用 `enseal inject --listen --relay URL -- cmd` 监听,或使用 `enseal receive ./file.env.age` 接收文件投放。
## 安全模型
**受保护的:**
- 传输中的秘密(加密通道)
- Slack/电子邮件历史中的秘密(临时,无持久化)
- 中间人攻击(SPAKE2 / 公钥认证)
- 恶意中继(端到端加密,中继仅见密文)
- 发送方冒充(身份模式:ed25519 签名)
- 磁盘上的秘密(注入模式:仅进程内存)
- Git 中的秘密(加密:静态加密)
**未受保护的:**
- 被攻破的端点(如果机器被控制,则无济于事)
- 密钥分发(你信任导入的密钥——无 PKI,无 CA)
## 配置
可选的 `.enseal.toml` 文件位于项目根目录:```toml
[defaults]
relay = "wss://relay.enseal.dev" # public relay (identity mode)
# relay = "ws://relay.internal:4443" # self-hosted without TLS
# relay = "wss://relay.internal:4443" # self-hosted with TLS reverse proxy
[filter]
exclude = ["^PUBLIC_", "^NEXT_PUBLIC_", "^REACT_APP_"]
[identity]
default_recipient = "devops-team"
[schema]
required = ["DATABASE_URL", "API_KEY", "JWT_SECRET"]
CORE
enseal share [] Send secrets (file, pipe, or --secret)
enseal receive [<code|file>] Receive secrets
enseal inject [] -- Inject secrets into a process
enseal keys Manage identity keys and aliases
enseal serve Run self-hosted relay server
.ENV TOOLKIT enseal check [file] Verify .env has all vars from .env.example enseal diff Compare .env files (keys only) enseal redact Replace values with enseal validate Validate against schema rules enseal template Generate .env.example with type hints
ENCRYPTION enseal encrypt Encrypt .env for git storage enseal decrypt Decrypt an encrypted .env
### `share` 标志```
--to <name> Identity mode: encrypt to recipient (alias, group, or identity)
--output <dir> File drop: write encrypted file (identity mode, no network)
--upload Post to burnurl.dev (async, browser-readable, no CLI on recipient side)
--ttl <hours> Secret TTL for --upload (1-24, default: 24)
--passphrase Encrypt client-side before --upload (prompts; server never sees plaintext)
--secret <value> Inline secret (raw string or KEY=VALUE)
--label <name> Human label for raw/piped secrets
--as <KEY> Wrap raw input as KEY=<value>
--relay <url> Route through relay server. Anonymous mode: uses enseal relay transport
(generates channel code, bypasses wormhole). Identity mode: push to
recipient's channel. Also: ENSEAL_RELAY env var.
--env <profile> Environment profile (resolves to .env.<profile>)
--exclude <pattern> Regex to exclude vars
--include <pattern> Regex to include only matching vars
--no-filter Send raw file, skip .env parsing
--no-interpolate Don't resolve ${VAR} references before sending
--words <n> Words in wormhole code (2-5, default: 2). Wormhole mode only (no --relay).
--quiet / -q Minimal output
receive 标志```--output Write to specific file --clipboard Copy to clipboard instead of stdout/file --no-write Print to stdout even for .env payloads --relay Use specific relay server --quiet / -q Minimal output
### `inject` 标志```
--listen Listen for incoming identity-mode transfer (requires --relay)
--relay <url> Use specific relay server (also: ENSEAL_RELAY)
--quiet / -q Minimal output
keys 子命令```enseal keys init Generate your keypair enseal keys export Print your public key bundle enseal keys import Import a colleague's public key enseal keys list Show all trusted keys and aliases enseal keys remove Remove a trusted key enseal keys fingerprint Show your key fingerprint enseal keys alias Map short name to identity enseal keys group create Create a named group enseal keys group add Add identity to group enseal keys group remove Remove identity from group enseal keys group list [name] List groups or group members enseal keys group delete Delete a group
### `serve` 标志```
--port <port> Listen port (default: 4443)
--bind <addr> Bind address (default: 0.0.0.0)
--max-mailboxes <n> Max concurrent channels (default: 100)
--channel-ttl <seconds> Idle channel lifetime (default: 300)
--max-payload <bytes> Max WebSocket message size (default: 1048576)
--rate-limit <n> Max connections per minute per IP (default: 10)
--health Print server health check and exit
encrypt / decrypt 标志```--per-var Per-variable encryption (keys visible, values encrypted) --to Encrypt to specific recipients (multi-key)
### 全局标志```
--verbose / -v Debug output (never prints secret values)
--quiet / -q Minimal output (for scripting)
| enseal | Slack DM | 1Password Share | dotenvx | croc | |
|---|---|---|---|---|---|
| 零配置 | 是 | 是 | 否 | 否 | 是 |
| 端到端加密 | 是 | 否 | 是 | 不适用 | 是 |
| 临时性(无历史记录) | 是 | 否 | 是 | 不适用 | 是 |
| 感知 .env | 是 | 否 | 否 | 是 | 否 |
| 进程注入 | 是 | 否 | 否 | 是 | 否 |
| 模式验证 | 是 | 否 | 否 | 否 | 否 |
| 静态加密 | 是 | 不适用 | 不适用 | 是 | 否 |
| 可自建中继 | 是 | 否 | 否 | 不适用 | 是 |
| 原始字符串/管道支持 | 是 | 是 | 否 | 否 | 是 |
--to 标志 (已完成)--upload)(当前)MIT