Import and export
fnox import encrypts values from an existing file into fnox.toml. fnox export writes resolved values out in another format. Together they cover moving off .env files and moving between providers.
Import a .env file
Configure an encryption provider first (the quick start sets up age), then preview and import:
fnox import -i .env --provider age --dry-run
fnox import -i .env --provider age
fnox check --allEach KEY=value line becomes an encrypted secret in fnox.toml. Review the result, commit it, and delete .env once nothing needs it.
The import target must be an encryption provider, such as age, aws-kms, yubikey, or fido2. Importing into a remote store such as 1Password isn't supported; create those items with the vendor's tools and reference them instead.
Other formats and sources
fnox import -i secrets.json json --provider age
fnox import -i secrets.yaml yaml --provider age
fnox import -i secrets.toml toml --provider age
cat .env | fnox import --provider age --force # stdinJSON, YAML, and TOML input is a flat map of names to values. When reading from stdin, pass --force (or --dry-run): fnox can't ask for confirmation on the stream it is reading secrets from.
Select and rename
fnox import -i .env --provider age --filter '^DATABASE_' # only matching names (regex)
fnox import -i .env --provider age --prefix 'LEGACY_' # DATABASE_URL → LEGACY_DATABASE_URLAdd --profile staging to import into a profile, or --global to import into your global config.
Export
fnox export # KEY=value
fnox export --format shell # export KEY=value, for `source`
fnox export --format json # also yaml, toml
fnox export -o .env # write to a file
fnox export API_KEY DB_URL # only these secrets
fnox export --profile production --format jsonExported values are plaintext. Wrapping a command in fnox exec is safer whenever the tool allows it. If you must write a file, restrict its permissions (umask 077 first) and keep it out of git.
Notes:
- Export follows shell rules: secrets with
env = "exec"orenv = falseare left out unless you add--all. - Name keys (
fnox export API_KEY) to export only those. Only the named secrets are resolved, so nothing else is fetched or prompts for auth. A named key is included whatever itsenvsetting, and an unknown key is an error. as_filesecrets export as paths to temporary files, not their contents.- JSON, YAML, and TOML output wraps values in a
secretsobject next tometadata. Usejq '.secrets'to get a flat map thatfnox importaccepts. - Programs that start processes should use
fnox env --jsoninstead. It handles file secrets and variables to unset, and reports errors as JSON.
Recipes
Move from a vault to encrypted values
To keep the vault as the source of truth and only speed up reads, use fnox sync instead. To copy values out of the vault permanently:
fnox export --profile production --format json --all |
jq '.secrets' |
fnox import json --provider age --profile production --forceDocker Compose
fnox exec -- docker compose up passes secrets for Compose's ${VAR} interpolation without writing a file. Use fnox export -o .env only if a service needs an env_file.
Kubernetes
This uses process substitution, so run it in Bash or Zsh:
kubectl create secret generic app-secrets --from-env-file=<(fnox export)An onboarding template
fnox export always writes real values, whatever the file is called. Write example files by hand:
{ "DATABASE_URL": "postgresql://localhost/example", "API_KEY": "replace-me" }A teammate fills in a private copy and runs fnox import -i secrets.json json --provider age.
Catch leaked secrets before you commit
fnox scan looks for secret-shaped text, which produces false positives. fnox scan --values instead resolves the active profile's secrets (through the same path as fnox exec, so --profile, the daemon, and if_missing all apply) and reports any file that contains one of their actual values:
fnox scan --valuesFound 1 resolved secret value(s) in 1 file(s):
src/app.env:3:6 [resolved-secret] DB_PASSWORD = ****The output names the secret and never prints any part of its value. The exit code is 1 when anything is found. If a secret cannot be resolved, the scan fails with the provider's error (or warns and lists the unchecked secret names when if_missing is warn or ignore) rather than passing silently. If no secret at all could be resolved, the scan fails even then.
To avoid noise, --values skips empty secrets and secrets shorter than 8 characters, binary files, files over 5 MiB, and gitignored files (add --include-ignored to scan those). It replaces the pattern heuristics rather than adding to them. Note that plain-provider values and defaults live in fnox.toml itself, so use --ignore fnox.toml if you keep those there on purpose.
Scan only what is staged
--staged reads file contents from the git index, not the working tree, so it checks exactly what would be committed:
fnox scan --values --stagedPre-commit hook
fnox scan --install-hookThis writes .git/hooks/pre-commit (or the core.hooksPath directory if you set one) running fnox scan --values --staged, and blocks the commit if a secret value is staged. If fnox is not on PATH, the hook prints a notice and lets the commit through. fnox never overwrites an existing pre-commit hook: it stops and tells you, unless you pass --append to add the fnox check to the end of an existing shell-script hook. Installing twice does nothing.
If you manage hooks with hk or pre-commit, add the command there instead of installing a raw hook:
// hk.pkl
["fnox-scan"] {
check = "fnox scan --values --staged"
}# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: fnox-scan
name: fnox scan
entry: fnox scan --values --staged
language: system
pass_filenames: false
always_run: trueSee also
- Running commands:
exec,get, and file secrets. fnox importreference andfnox exportreference: every flag.fnox scanreference: every flag.- Local sync cache: cache a vault without migrating off it.