Skip to content

Get Started with age

dotenv-fusion can resolve selected values from an age-encrypted dotenv bundle immediately before starting an application. The project configuration keeps public references and validation rules; the encrypted bundle keeps the values.

1. Install age support

Package installations made with pip, uv, or uvx include the official age backend. Install the age executable through the operating system.

The standalone dotenv-fusion script needs the matching backend sidecar described on the Installation page.

Backends are never selected from arbitrary Python module paths declared in .env-fuse. The core maps supported URI schemes to reviewed package modules and sidecar filenames, then rejects incompatible backend API versions.

2. Configure an identity

Recipient-encrypted files need the private age identity that can decrypt the bundle. In the usual case, configure one identity and then start the application:

export DOTENV_FUSION_AGE_IDENTITY=/home/user/.config/age/keys.txt
dotenv-fusion exec -- python app.py

The path belongs to the machine or execution environment, not .env-fuse. For a one-off command, pass the same identity explicitly:

dotenv-fusion exec --age-identity /home/user/.config/age/keys.txt -- python app.py

Everything before -- configures dotenv-fusion. Everything after -- is the application command. Repeat --age-identity only when the configuration uses bundles encrypted for different identities.

With no configured identity, age uses its normal interactive flow, including passphrase-protected files.

3. Create a bundle

dotenv-fusion secret edit secrets/application.env.age

Add ordinary dotenv assignments in the editor:

DATABASE_USERNAME=application
DATABASE_PASSWORD=correct-horse-battery-staple

See Create & Edit Secrets for recipients, editor selection, backups, and recovery behavior.

4. Declare selectors

Reference only the keys the application needs:

.env-fuse
#@def DATABASE_USERNAME source="age://store/application.env.age#DATABASE_USERNAME"
#@def DATABASE_PASSWORD required=true source="age://store/application.env.age#DATABASE_PASSWORD"

DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@database/app

The bundle is decrypted and parsed once per invocation, even when several definitions select from it. Omitting #SELECTOR selects the final variable name. Values derived from a secret are also treated as secret.

5. Validate and run

# Inspect the public contract without secret access
dotenv-fusion check --no-resolve-sources -f .env-fuse

# Resolve the bundle and validate selected values
dotenv-fusion check -f .env-fuse

# Resolve and start the application without an intermediate file
dotenv-fusion exec -f .env-fuse -- application --serve

Static commands such as list, docs, and template inspect source declarations without contacting age. Full check, load, exec, and fuse resolve the values when their behavior requires them.

check --no-resolve-sources validates source URI syntax, supported backends, option combinations, dependencies, and source-derived cycles. It cannot verify store access, selectors, or type and regex rules that depend on resolved values. Use a full check inside an authorized boundary for those checks.

load emits real resolved values because the shell or JSON consumer needs them. Prefer exec when starting one application, and review the distinction in Security & Limitations.

Store location

By default, age://store/... reads ciphertexts from secrets beside the main .env-fuse. Select another root in the file when the layout belongs to the project:

DOTENV_FUSION_AGE_STORE=../shared-secrets

The parent environment can override it:

export DOTENV_FUSION_AGE_STORE=/home/user/.local/share/dotenv-fusion/secrets

Relative paths from either source are resolved from the main .env-fuse, not the process working directory. Absolute paths and store roots outside the project are supported. References remain confined beneath the canonical store root.

Execution boundaries

Give each boundary only the identities it needs:

Boundary Recommended usage
Developer workstation Configure the developer's local identity
Pull request or static CI Provide no identity and run check --no-resolve-sources
Authorized CI job Inject an identity file and pass its path with --age-identity
Production application Provide the production identity and start with exec

Continue