Quick start
Store your first secret with age encryption, then make it available to a command. This walkthrough uses a local key file and needs no cloud account.
Already have a vault? Use 1Password with a local cache, or choose another provider.
1. Install fnox and age
With mise:
mise use -g fnox age
fnox --versionSee installation for other fnox installation methods. The age CLI supplies age-keygen; fnox handles encryption itself.
2. Create your encryption key
mkdir -p ~/.config/fnox
age-keygen -o ~/.config/fnox/age.txt
age-keygen -y ~/.config/fnox/age.txtThe last command prints your public recipient, beginning with age1. Copy it for the next step. If you already have age.txt, reuse it and run only the last command.
Keep age.txt private and back it up. The public recipient can go in git; the private key file cannot. fnox automatically reads age.txt from its configuration directory. If you use a different directory, set the provider's key_file.
3. Configure the project
From your project directory:
fnox init --skip-wizardEdit the new fnox.toml to contain the following, replacing age1... with the public recipient you copied:
#:schema https://fnox.jdx.dev/schema.json
default_provider = "age"
[providers.age]
type = "age"
recipients = ["age1..."] # Replace with your public recipient--skip-wizard creates an empty configuration. These provider settings are what enable encryption; initialization alone does not encrypt secrets.
4. Store and check a secret
fnox set DATABASE_URLAt the hidden prompt, enter a value such as postgresql://localhost/mydb. fnox uses default_provider = "age" and writes encrypted ciphertext into fnox.toml.
# Verify that configured secrets can be resolved
fnox check --all
# Inspect names and providers without displaying resolved values
fnox listUse fnox get DATABASE_URL when you need the value itself. It prints the decrypted secret to stdout.
5. Run a command
fnox exec -- npm start
# Or:
fnox exec -- python app.pyThe command receives DATABASE_URL as an environment variable. Your parent shell is unchanged. To verify injection without an application or printing the value:
fnox exec -- sh -c 'test -n "$DATABASE_URL" && printf "Database secret is available\n"'Put fnox options before --; everything after it belongs to the command.
6. Commit the configuration
Add these entries to your existing .gitignore:
fnox.local.toml
.fnox.local.toml
.envReview fnox.toml, then commit it with .gitignore. The age secret's value is ciphertext. Any default values remain plaintext, so reserve them for non-sensitive configuration.
Where to go next
- Shell integration: load secrets automatically when you enter a project.
- Profiles: use separate development, staging, and production values.
- Age team setup: add teammates and re-encrypt for their keys.
- Troubleshooting: diagnose configuration and authentication problems.