Back up and restore environment secrets
The unified runtime reads the motherrepo root .env. .env.example is a maintained minimal template, not a complete credential set. Setup adds calculated URLs, database connections, and missing generated auth/proxy/FormGen secrets to the root file.
For new configuration, follow environment setup. Never copy a template over existing settings. Google setup adds optional provider fields and separate files.
What an environment archive preserves
The root archive retains selectors and REMOTE_* inputs. On another machine, private addresses, Docker names, ports, or reviewed-source paths can be invalid. Check identity, endpoints, and code selection before startup. Do not regenerate working encryption keys or Kafka identity to repair a connection.
Use fulllocal/false for local placement, partialRemote/true for local simulation, and partialRemote/false for real remote services. Missing selectors mean full-local. Apply configuration changes through validation and ordinary restart. Quick start does not install new configuration. Generated ECS_EFFECTIVE_* outputs are not user backup inputs.
The archive does not back up database rows, S3 objects, Kafka events/offsets, Qdrant points, or named volumes. Office signing keys, Google grant keys, personal email-key backups, and other secret files need separate protection. A new login/key cannot decrypt old content.
Real remote placement still keeps PostgreSQL/S3/OpenSearch/Dragonfly local. Saving remote addresses does not transfer remote data or create Tailscale access. A source-root path does not back up its snapshot.
Password-protected ZIP
envzipper.sh resolves files from its own script directory. Run the copy in the clone that contains the intended configuration. Its ENV_FILES list covers 14 paths. Missing files are skipped.
| Path | Purpose |
|---|---|
.env | Unified root configuration |
mailGen/ECS_MovePST_To_TempSQL/.env | Onboarding/OpenSearch Compose |
mailGen/msmicroservices-architexture/.env | Standalone backend |
mailGen/ECS_MovePST_To_TempSQL/OneTimeUserOnboarder/.env | Legacy PST onboarding |
mailGen/ECS_MovePST_To_TempSQL/GraphOneTimeUserOnboarder/.env | Graph onboarding |
mailGen/msmicroservices-architexture/coolify/.env.coolify | Separate infrastructure deployment |
Architecture/GraphSyncExperimenter/graph-call-dashboard/.env | Separate Graph experimenter |
mailGen/ECS_MovePST_To_TempSQL/DataSearcher/.env | Standalone DataSearcher |
mailGen/bff/.env | Standalone BFF |
recGen/.env | RecGen (VoiceNextWeb) submodule |
mailGen/benchmarker/frontend/.env.demo, .env.development, .env.locsorting, .env.remotedev, .env.production | Five benchmark frontend profiles |
Install OpenSSL, zip, and unzip. On macOS: brew install openssl@3 zip unzip. On Debian/Ubuntu: sudo apt-get install openssl zip unzip. The script can select Homebrew OpenSSL when system LibreSSL lacks required options.
From the motherrepo root in Bash:
umask 077
read -r -s -p 'Archive password (at least 16 characters): ' archive_password
printf '\n'
bash envzipper.sh "$archive_password"
unset archive_password
chmod 600 secretvariables.zip
Encryption requires lowercase, uppercase, a digit, a special character, and at least 16 characters. For an empty/weak password, the script generates and prints a replacement. It also prints the password used after encryption. Protect terminal output and store the actual password in a password manager.
The script replaces an existing secretvariables.zip. Keep any required older archive before encrypting again. Transfer archive and password over separate secure channels.
ZIP encryption uses system zip -P, not modern age/AES archive protection. The password can also appear as a process argument. Use a trusted machine and do not rely on this ZIP alone for public distribution.
Restore missing files
Place the archive in the target motherrepo root. The decryptor uses unzip -n: it preserves existing files and restores only missing files. It neither merges fields nor refreshes existing credentials. It excludes obsolete .env.root.
Back up the target configuration and compare the intended versions with the operator. Do not delete working configuration merely to make extraction overwrite it.
read -r -s -p 'Archive password: ' archive_password
printf '\n'
bash envzipper.sh "$archive_password" --decrypt
unset archive_password
chmod 600 .env
Decryption accepts the supplied password unchanged. It does not impose a new strength requirement. Check this machine's identity, domains, and registered callbacks while preserving encryption keys.
Password tokens and L2 mode
The script also supports string encryption and password-token helpers:
bash envzipper.sh chip <passphrase> <ciphertext> --decrypt
bash envzipper.sh passenc <zip-password> <chip-passphrase>
bash envzipper.sh passdec <encrypted-password> <chip-passphrase>
bash envzipper.sh --l2 <zip-password> <chip-passphrase>
bash envzipper.sh -dl2 <encrypted-password> <chip-passphrase>
--l2 encrypts the ZIP and prints an encrypted password token. -dl2 accepts that token for restore. --decrypt --l2 is an alias. Token wrapping does not upgrade the ZIP's underlying encryption or make command-line secret arguments private.
Age-encrypted environment files
envsecreter/ contains envcrypter, which recursively encrypts .env* files. It needs Bun and an age key pair. Install age with brew install age on macOS or sudo apt-get install age on Debian/Ubuntu. Install Bun through its official distribution.
Keep the private key outside the repository, readable only by your user. For a new key:
umask 077
mkdir -p "$HOME/.config/envcrypter"
chmod 700 "$HOME/.config/envcrypter"
if [ ! -e "$HOME/.config/envcrypter/key.txt" ]; then
age-keygen -o "$HOME/.config/envcrypter/key.txt"
fi
age-keygen -y "$HOME/.config/envcrypter/key.txt"
The final command prints the public age1... recipient. Back up the private file separately. Never overwrite an existing key to restore old encrypted files.
Install dependencies once with bun install in envsecreter. Then run from the motherrepo root:
export AGE_PUBLIC_KEY='YOUR_PUBLIC_AGE_KEY'
bun run ./envsecreter/index.ts encrypt --dry-run
bun run ./envsecreter/index.ts encrypt
Restore from that same root:
export AGE_SECRET_KEY_FILE='/YOUR/PRIVATE/PATH/key.txt'
bun run ./envsecreter/index.ts decrypt --dry-run
bun run ./envsecreter/index.ts decrypt
The tool creates .encrypted copies. Plaintext files still need protection. .env.example is ignored by default. envcrypter.json configures folder/file/key exclusions. Existing targets use interactive merge by default. Equal values remain unchanged, missing keys are inserted, and conflicting values need a choice.
Inspect dry-run scope first. Do not select automatic replace on a working installation. See the tool's repository README for exact options.
Back up separate Google credentials
Neither envzipper nor envcrypter automatically backs up .docker-state/google/oauth-client-secret or .docker-state/google/mailbox-grant.key. Preserve both separately in an encrypted password manager or approved secret backup.
Restore them to the private directory with 700 directory and 600 file permissions. Regenerating the grant key makes old encrypted grants unreadable. The authenticated push service account does not need a JSON private key.
Keep .env, .docker-state, private keys, and secretvariables.zip outside commits. Standalone documentation needs none of these application secrets.