enseal
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
)
# recipient
Installation
From crates.io
From source
# binary at ./target/release/enseal
Prebuilt binaries
Download from GitHub Releases for Linux (x86_64, aarch64), macOS (Intel, Apple Silicon), and Windows.
Quick Start
Share a .env file
Give the code to your teammate over any channel — Slack, phone, carrier pigeon. The code is useless without the encrypted channel, and it expires after one use or 5 minutes.
Receive secrets
Writes the .env file to disk. Done.
Share a single secret
# pipe from anywhere
|
# recipient gets raw string on stdout
# pipe to clipboard
|
Inject secrets into a process (never touch disk)
# via wormhole code
# via identity listen mode (zero codes, zero coordination)
Secrets exist only in the child process's memory. When it exits, they're gone.
Features
Two Sharing Modes
Anonymous mode (default) — wormhole-based, zero setup. A human-readable code is all you need. SPAKE2 mutual authentication prevents MITM attacks.
Identity mode — public-key encryption for known teammates. Encrypt to a name.
Identity mode supports three transport options:
# wormhole (default, no --relay): generates a code like anonymous mode
# relay push (with --relay): zero codes, pushes directly to recipient's channel
# file drop (with --output): no network, produces encrypted file
# produces ./drop/sarah@company.com.env.age
Flexible Input
enseal accepts secrets from multiple sources:
# .env file (default)
# environment profile
# pipe from stdin
|
|
|
# inline (careful — visible in shell history)
# wrap raw string as KEY=VALUE
|
Variable Interpolation
${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.
Filtering
Control which variables are sent:
# exclude public/non-secret vars
# send only matching vars
# skip .env parsing entirely (send raw file)
Smart Receive
Output adapts to what was sent:
# .env payload -> writes to file
# write to specific file
# raw string -> prints to stdout (pipe-friendly)
# force clipboard
# force stdout for any payload
# receive from encrypted file drop (identity mode)
Inject
Receive secrets and inject them directly as environment variables into a child process. Secrets never touch the filesystem.
# anonymous mode: inject via wormhole code
# identity mode: listen for incoming transfer on relay
# from encrypted file drop
With --listen, the receiver connects to the relay and waits. The sender pushes with enseal share .env --to alex --relay wss://relay.internal:4443 — no codes exchanged, zero coordination needed.
.env Toolkit
Beyond sharing, enseal is a complete .env security toolkit:
# check: verify your .env has all required vars
)
# diff: compare two .env files (keys only, never values)
)
)
# redact: strip values for safe sharing of structure
DATABASE_URL=<REDACTED>
API_KEY=<REDACTED>
PORT=<REDACTED>
# validate: check values against schema rules
# template: generate .env.example with type hints
# DATABASE_URL=<postgres connection string>
# API_KEY=<32+ character string>
# PORT=<integer, 1024-65535>
At-Rest Encryption
Encrypt .env files for safe git storage using age encryption:
# whole-file encryption
)
# per-variable: keys visible for diffing, values encrypted
# DB_HOST=ENC[age:abc123...]
# DB_PORT=ENC[age:def456...]
# multi-recipient: anyone on the team can decrypt
Identity & Key Management
# generate your keypair
# share your public key with teammates
# import a teammate's key (shows fingerprint, prompts for confirmation)
# list all trusted keys and aliases
# show your key fingerprint (for out-of-band verification)
# remove a trusted key
# create aliases for convenience
# create groups for multi-recipient sharing
# delete a group
Self-Hosted Relay
Keep everything inside your network. The relay is stateless — it sees only ciphertext.
# Docker (one command)
# Or as a binary
# Check relay health
# Clients point to your relay
# or set it globally
With identity mode and a self-hosted relay, sharing is fully codeless:
# receiver listens on the relay
# sender pushes directly — no code generated
Schema Validation
Define rules in .enseal.toml at the project root:
[]
= ["DATABASE_URL", "API_KEY", "JWT_SECRET"]
[]
= "^postgres://"
= "PostgreSQL connection string"
[]
= "integer"
= [1024, 65535]
[]
= 32
Then validate:
Validation also runs automatically when receiving .env files — catching broken configs before they cause confusion.
Environment Profiles
How It Works
Anonymous Mode (Wormhole)
- Sender encrypts the payload with
age - A SPAKE2 key exchange establishes a shared secret via the relay
- The encrypted payload transits through the relay
- Recipient decrypts with the negotiated key
- The channel is destroyed — single use, time-limited
The relay never sees plaintext. The wormhole code provides mutual authentication.
Identity Mode (Public Key)
- Sender encrypts with the recipient's
agepublic key - Sender signs with their own
ed25519key - Payload transits through relay, file drop, or wormhole
- Recipient decrypts with their private key
- Recipient verifies the sender's signature
Trust is based on which keys you've imported.
Transport options in identity mode:
| Transport | Flag | How it works |
|---|---|---|
| Wormhole (default) | --to sarah |
Generates a code, like anonymous mode but with signing |
| Relay push | --to sarah --relay URL |
Pushes to recipient's deterministic channel, no code |
| File drop | --to sarah --output ./dir/ |
Produces encrypted .env.age file, no network |
With relay push, the recipient listens with enseal inject --listen --relay URL -- cmd or receives the file drop with enseal receive ./file.env.age.
Security Model
Protected:
- Secrets in transit (encrypted channel)
- Secrets in Slack/email history (ephemeral, no persistence)
- MITM attacks (SPAKE2 / public key auth)
- Malicious relay (E2E encryption, relay sees ciphertext only)
- Sender impersonation (identity mode: ed25519 signatures)
- Secrets on disk (inject mode: process memory only)
- Secrets in git (encrypt: at-rest encryption)
Not protected:
- Compromised endpoints (if the machine is owned, nothing helps)
- Key distribution (you trust the keys you import — no PKI, no CA)
Configuration
Optional .enseal.toml in your project root:
[]
= "wss://relay.internal.company.com:4443"
= 600
[]
= ["^PUBLIC_", "^NEXT_PUBLIC_", "^REACT_APP_"]
[]
= "devops-team"
[]
= ["DATABASE_URL", "API_KEY", "JWT_SECRET"]
CLI Reference
CORE
enseal share [<file>] Send secrets (file, pipe, or --secret)
enseal receive [<code|file>] Receive secrets
enseal inject [<code>] -- <cmd> Inject secrets into a process
enseal keys <subcommand> 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 <file1> <file2> Compare .env files (keys only)
enseal redact <file> Replace values with <REDACTED>
enseal validate <file> Validate against schema rules
enseal template <file> Generate .env.example with type hints
ENCRYPTION
enseal encrypt <file> Encrypt .env for git storage
enseal decrypt <file> Decrypt an encrypted .env
share flags
--to <name> Identity mode: encrypt to recipient (alias, group, or identity)
--output <dir> File drop: write encrypted file (identity mode, no network)
--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> Use specific relay server (also: ENSEAL_RELAY)
--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> Number of words in wormhole code (default: 2)
--timeout <seconds> Channel expiry (default: 300)
--quiet / -q Minimal output
receive flags
--output <path> Write to specific file
--clipboard Copy to clipboard instead of stdout/file
--no-write Print to stdout even for .env payloads
--relay <url> Use specific relay server
--quiet / -q Minimal output
inject flags
--listen Listen for incoming identity-mode transfer (requires --relay)
--relay <url> Use specific relay server (also: ENSEAL_RELAY)
--quiet / -q Minimal output
keys subcommands
enseal keys init Generate your keypair
enseal keys export Print your public key bundle
enseal keys import <file> Import a colleague's public key
enseal keys list Show all trusted keys and aliases
enseal keys remove <identity> Remove a trusted key
enseal keys fingerprint Show your key fingerprint
enseal keys alias <name> <identity> Map short name to identity
enseal keys group create <name> Create a named group
enseal keys group add <group> <id> Add identity to group
enseal keys group remove <group> <id> Remove identity from group
enseal keys group list [name] List groups or group members
enseal keys group delete <name> Delete a group
serve flags
--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)
--health Print server health check and exit
encrypt / decrypt flags
--per-var Per-variable encryption (keys visible, values encrypted)
--to <name> Encrypt to specific recipients (multi-key)
Global flags
--verbose / -v Debug output (never prints secret values)
--quiet / -q Minimal output (for scripting)
--config <path> Path to .enseal.toml manifest
Comparison
| enseal | Slack DM | 1Password Share | dotenvx | croc | |
|---|---|---|---|---|---|
| Zero setup | Yes | Yes | No | No | Yes |
| End-to-end encrypted | Yes | No | Yes | N/A | Yes |
| Ephemeral (no history) | Yes | No | Yes | N/A | Yes |
| .env aware | Yes | No | No | Yes | No |
| Process injection | Yes | No | No | Yes | No |
| Schema validation | Yes | No | No | No | No |
| At-rest encryption | Yes | N/A | N/A | Yes | No |
| Self-hostable relay | Yes | No | No | N/A | Yes |
| Raw string/pipe support | Yes | Yes | No | No | Yes |
Roadmap
- v0.1 — Core: share/receive, pipe/stdin, .env toolkit (check, diff, redact)
- v0.2 — Identity mode: keys, aliases,
--toflag - v0.3 — Inject command, self-hosted relay
- v0.4 — Schema validation, templates, interpolation, profiles
- v0.5 — At-rest encryption (encrypt/decrypt)
- v1.0 — Groups, Helm chart, shell completions, docs
License
MIT