
Secure, ephemeral secret sharing for developers.
Secure, ephemeral secret sharing for developers.
Stop pasting secrets into Slack. enseal makes the secure path faster than the insecure one — share .env files and secrets through encrypted, single-use channels with one command and zero setup.
# sender
$ enseal share .env
Share code: 7-guitarist-revenge
Secrets: 14 variables (staging)
Expires: on first receive
# recipient
$ enseal receive 7-guitarist-revenge
ok: 14 secrets written to .env
cargo install enseal
git clone https://github.com/FlerAlex/enseal.git
cd enseal
cargo build --release
# binary at ./target/release/enseal
Download from GitHub Releases for Linux (x86_64, aarch64), macOS (Intel, Apple Silicon), and Windows.
Share secrets using a one-time code. No keys, no accounts — works immediately.
# 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
Works with single secrets too:
# terminal 1 — pipe a token
echo "my-api-token" | enseal share --label "API key"
Share code: 4-orbital-hammock
# terminal 2 — prints to stdout
enseal receive 4-orbital-hammock
my-api-token
Both terminals must be open at the same time — the sender waits until the recipient connects.
For teams with established key trust. Encrypt to a name, no coordination needed.
# 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/
# anonymous mode: sender gives you a code
enseal inject 7-guitarist-revenge -- npm start
# identity mode: listen on relay, sender pushes when ready
enseal inject --listen --relay wss://relay.enseal.dev -- docker compose up
# from an encrypted file drop
enseal inject ./staging.env.age -- python manage.py runserver
Secrets exist only in the child process's memory. When it exits, they're gone.
Anonymous mode (default) — wormhole-based, zero setup. A human-readable code is all you need. SPAKE2 mutual authentication prevents MITM attacks.
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:
enseal share .env --relay ws://relay.internal:4443 # generates channel code
enseal receive 3421-amber-frost --relay ws://relay.internal:4443
Async upload (--upload) — sender-only. Encrypts locally and posts to burnurl.dev, returning a self-destructing URL the recipient opens in a browser. No CLI required on the recipient side.
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)
Recipient opens the URL in any browser — no enseal install needed. Add --passphrase to encrypt client-side before upload (server sees only ciphertext):
enseal share .env --upload --passphrase # prompts for passphrase
enseal share .env --upload --ttl 4 # 4-hour TTL (max 24)
API access requires a Pro or Team plan on burnurl.dev. Set BURNURL_API_KEY to your key. Override the base URL for self-hosted instances: BURNURL_URL=https://burnurl.internal.
Identity mode — public-key encryption for known teammates. Encrypt to a name.
enseal keys init # one-time setup
enseal share .env --to sarah # encrypt to sarah's public key
Identity mode supports three transport options:
# wormhole (default, no --relay): generates a code like anonymous mode
enseal share .env --to sarah
# relay push (with --relay): zero codes, pushes directly to recipient's channel
enseal share .env --to sarah --relay wss://relay.enseal.dev
# file drop (with --output): no network, produces encrypted file
enseal share .env --to sarah --output ./drop/
# produces ./drop/[email protected]
enseal accepts secrets from multiple sources:
# .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} references are resolved before sending so recipients get fully expanded values:
DB_HOST=postgres.internal
DB_PORT=5432
DATABASE_URL=postgres://user:pass@${DB_HOST}:${DB_PORT}/myapp
Supports ${VAR:-default} fallback syntax. Circular and forward references are detected and rejected. Use --no-interpolate to send raw ${VAR} syntax.
Control which variables are sent:
# 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
Output adapts to what was sent:
# .env payload -> writes to file
enseal receive CODE
ok: 14 secrets written to .env
# write to specific file
enseal receive CODE --output staging.env
# raw string -> prints to stdout (pipe-friendly)
enseal receive CODE
sk_live_abc123
# force clipboard
enseal receive CODE --clipboard
ok: copied to clipboard
# force stdout for any payload
enseal receive CODE --no-write
# receive from encrypted file drop (identity mode)
enseal receive ./staging.env.age
ok: signature verified, file decrypted
ok: 14 secrets written to .env
Receive secrets and inject them directly as environment variables into a child process. Secrets never touch the filesystem.
# 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
With --listen, the receiver connects to the relay and waits. The sender pushes with enseal share .env --to alex --relay wss://relay.enseal.dev — no codes exchanged, zero coordination needed.
Beyond sharing, enseal is a complete .env security toolkit:
# check: verify your .env has all required vars
enseal check
error: missing from .env (present in .env.example):
JWT_SECRET, REDIS_URL