Skip to content

Encrypt Secrets from an Existing .env

dotenv-fusion migrate secret selects secrets from a basic .env, writes their public references to .env-fuse, and stores their values in an age-encrypted bundle. It moves values into encrypted storage; it does not move inline contract definitions into a schema.

For that separate operation, see dotenv-fusion migrate schema.

Secret values stay inside the local process and are sent to age through stdin; they are never printed or written to a plaintext temporary file.

Review the selection

Use repeatable, case-sensitive globs. Always quote them for the shell:

dotenv-fusion migrate secret \
  --from .env \
  --to .env-fuse \
  --secret '*_TOKEN' \
  --secret '*_PWD' \
  --secret 'DATABASE_PASSWORD' \
  --public 'PUBLIC_*' \
  --age-output secrets/application.env.age \
  --dry-run

--public excludes matching names after secret selection. Every --secret pattern must match at least one variable, which catches misspelled patterns. Dry-run prints only selected names, preserved names, and output paths. It does not require a recipient and writes nothing.

Encrypt and write

Encryption needs public recipients, not decryption identities:

dotenv-fusion migrate secret \
  --from .env \
  --to .env-fuse \
  --secret '*_TOKEN' \
  --secret '*_PWD' \
  --public 'PUBLIC_*' \
  --age-output secrets/application.env.age \
  --age-recipient 'age1...'

Recipients are repeatable. Recipient files are also supported:

dotenv-fusion migrate secret \
  --secret '*_TOKEN' \
  --age-output secrets/application.env.age \
  --age-recipients-file config/age-recipients.txt

By default, the age store is the parent of --age-output, so the example emits references such as:

#@def API_TOKEN required=true source="age://store/application.env.age#API_TOKEN"

Use --age-store DIR when the ciphertext should be nested under a larger runtime store. The age output must remain below that store.

Safety contract

  • The source must be a basic .env; directives and duplicate assignments are rejected.
  • The original source is never overwritten or deleted.
  • Non-secret lines and ordinary comments are retained.
  • Selected values are resolved with dotenv-fusion's normal quote and expansion rules before encryption.
  • Plaintext is held only in memory and passed to age via stdin.
  • New .env-fuse and ciphertext outputs use mode 0600 on POSIX.
  • Existing outputs are refused. --force replaces only the exact targets; existing .env-fuse permissions are preserved and ciphertext becomes 0600.
  • Output and errors contain names and paths, never values.

When --age-output is beneath secrets/ beside the generated .env-fuse, the runtime store needs no configuration. Configure only the decryption identity:

export DOTENV_FUSION_AGE_IDENTITY=/home/user/.config/age/keys.txt
dotenv-fusion check -f .env-fuse
dotenv-fusion exec -f .env-fuse -- application

For another layout, set DOTENV_FUSION_AGE_STORE in .env-fuse or in the parent environment. Relative paths are based on the generated file's directory.

AI agent skill

The repository includes the skills/dotenv-fusion-migrate AI agent skill in SKILL.md format. It guides an agent through dry-run review and validation while explicitly forbidding direct reads of .env, identities, plaintext bundles, and ciphertexts. The deterministic CLI remains the only value-handling component.