Security Model

Zero-knowledge .env encryption.

How share-env encrypts and transmits .env files without the relay server ever learning the decryption key — and what guarantees that provides.

Does the relay server ever see the decryption key?
Never. The 256-bit AES-256-GCM key is generated in local memory on the sender's machine and embedded in the share code that travels out-of-band (via Slack, Signal, etc.). The relay receives only ciphertext, IV, and auth tag. Even a fully compromised relay yields nothing without the key.

Encryption flow

What happens locally during push.

All of the following steps occur on the sender's machine before any network connection is made.

1
Generate random 256-bit key

crypto.randomBytes(32) generates 32 bytes (256 bits) of cryptographically secure randomness. This key never leaves the local process — it is embedded in the share code only.

node.js crypto
const key = crypto.randomBytes(32); // 256-bit AES key
2
Generate random 96-bit IV

crypto.randomBytes(12) generates 12 bytes (96 bits) — the NIST-recommended IV size for GCM mode. A fresh IV is generated for every push, even with the same key.

node.js crypto
const iv = crypto.randomBytes(12);   // 96-bit GCM IV
3
Encrypt with AES-256-GCM

The raw .env contents are encrypted. GCM mode produces both the ciphertext and a 128-bit authentication tag. The auth tag is used at decryption time to verify the ciphertext was not tampered with.

node.js crypto
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const authTag = cipher.getAuthTag(); // 128-bit auth tag
4
Upload ciphertext only

The relay server receives the ciphertext + IV + authTag via POST. The key is never sent. The relay cannot decrypt anything it stores.

node.js crypto
// What goes to the relay:
POST /push { ciphertext, iv, authTag }
// What NEVER goes to the relay:
// { key }
5
Build the share code

The relay returns a 3-word identifier (phrase). The CLI appends the 64-character hex key after a # separator to form the complete share code.

node.js crypto
const shareCode = `${phrase}#${key.toString("hex")}`;
// e.g.: apple-brave-cloud#a3f9b2c1d4e5...

Relay server

What the relay does and doesn't receive.

Relay receives
  • CiphertextEncrypted bytes. Unreadable without the key.
  • IV (nonce)Required for decryption, but harmless without the key.
  • Auth tagUsed to verify integrity. Cannot be reversed to reveal plaintext.
Relay NEVER receives
  • Encryption keyStays in local memory and the share code only.
  • Plaintext .env contentsNever transmitted in cleartext at any point.
  • Key derivation materialNo KDF, no passphrase — the full key is embedded in the share code.
Share code anatomy
apple-brave-cloud#a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1
Before the # — relay phrase
3-word identifier assigned by the relay server. Used to look up the encrypted payload. Does not reveal the key.
After the # — the AES-256 key
64 hex characters = 256-bit AES key. Travels only in this share code. Never transmitted to the relay. Must be copied in full.

Threat model

Known threats and mitigations.

ThreatMitigationStatus
Relay server is compromisedAttacker only obtains AES-256-GCM ciphertext. No key means no decryption — the ciphertext is computationally indistinguishable from random noise.Fully mitigated
Share code intercepted in transitThe share code contains the decryption key — treat it like a password. Send it over an encrypted channel (Slack, Signal, Teams). Use Signal or E2EE channels for highest assurance.User responsibility
Payload pulled by wrong personThe payload is deleted on first pull. If an attacker pulls first, the intended receiver gets a 404 error and should ask the sender to push again.Burn-after-reading
Payload never pulled (abandoned)The relay server automatically expires and permanently deletes all payloads after 10 minutes (600 seconds TTL), regardless of whether they were pulled.Auto-expiry
Accidental git add .envThe git guardrail check blocks both push and pull from running if .gitignore does not explicitly ignore .env and .env.* files. This is mandatory and cannot be disabled.Mandatory block
Ciphertext tampering in transitAES-256-GCM produces a 128-bit authentication tag. Any bit-level modification to the ciphertext causes decryption to throw an error — tampered data is always rejected.Auth tag verification
Malformed .env fileThe push command validates all lines in the .env file before encrypting. Lines not matching KEY=VALUE format cause the tool to exit with an error before any upload.Pre-upload validation

Security Q&A

Common security questions.

Does the relay server ever receive the encryption key?
No. The 256-bit AES-256-GCM encryption key is generated in local memory and is embedded in the share code that the sender transmits manually. The relay server receives only ciphertext, IV, and authentication tag — never the key.
What happens if the relay server is breached?
An attacker who fully compromises the relay server obtains only AES-256-GCM ciphertext. Without the 256-bit key (which never touched the relay), the ciphertext is computationally indistinguishable from random noise and cannot be decrypted.
Can a payload be pulled more than once?
No. The relay server permanently deletes the payload the moment it is pulled for the first time. A second pull attempt returns a 404 error. Nothing can be recovered after the first successful pull.