guest@6admin.io:~$

fnox and age: encrypted secrets in my repos, no server

Personal project secrets have always had the same problem: no Vault at home, .env files that should never be committed but always are and non-secret environment variables already well handled by direnv . In late 2025 I discovered fnox , the secrets manager from the author of mise , which fits that gap exactly: secrets live encrypted in the repo itself, serverless.

age: the brick

Under the hood sits age , FiloSottile’s encryption tool (the Go one). The stance: a file to encrypt, explicit keys, zero config options. age-keygen produces a pair: a private AGE-SECRET-KEY-... and a public age1... recipient. You encrypt a file toward a list of recipients; SSH keys also work as recipients. No web of trust, no keyserver, no keyring: two concepts and a readable format. It is what PGP should have been, without thirty years of archaeology.

fnox: the layer

fnox brings the project layer: one fnox.toml per repo, versioned with the code, declaring the secrets and their backend. Values are encrypted with age toward the declared recipients, so the repo carries the secrets, encrypted and a fresh clone with the right key sees everything; without the key, the repo stays cloneable and opaque. At runtime, fnox decrypts and exposes everything as environment variables. The detail that matters for my multi-machine usage: backends are providers, age locally but also 1Password and its remote cousins, mixable in the same file. The local secret rides age, the team secret points at the vault, the fnox.toml documents both.

In practice

The flow is four gestures: age-keygen once, the private key in the key manager and nowhere near a repo; recipients in the committed fnox.toml; fnox set to add a secret (the file re-encrypts); fnox exec to start the session with the variables in place. My cluster and dotfiles-manager secrets ride the same flow: fnox with age keys, in a dedicated env kept out of the tracked tree. The repo reviews like code: the diff of a secret shows that something changed, not the value.

The honest part

The tool is very young: the repo dates from October 2025, the API still moves and the documentation is catching up. Rotation is manual (re-encrypt), sharing means handing out the private key or adding a collaborator’s recipient and the age key is only as safe as its offline copy. fnox does not replace a KMS either: this is repo encryption, not centralized access management nor usage auditing. For personal projects and machines of mine, it is exactly the size of the problem.

The word at the end

  • The encrypted secret in the repo is the only version that does not diverge. An .env outside the repo is an .env that will lie.
  • age does one thing, encrypt files and that is why you trust it.
  • The versioned fnox.toml is the contract, the keys are the notebook. Like mise’s .tool-versions.
  • A secret diff shows the change without showing the value. Infra reviews gain a meaning.

[edit: 2026] Since then, fnox has stabilized and the described flow still holds: one file, recipients, fnox exec. The local tooling keeps converging: machine with Ansible, projects with mise , secrets with fnox and age.