fnox/How To/Set Up a Project with age and SSH Keys
Properties
- tags
- Diataxis/How To
- logseq-entity
- Logseq/Entity/Diataxis/How To
- see-also
- fnox/Golden Path
How to set up a project to use fnox with age and SSH keys
Overview
- Commit secrets to a project as age ciphertext in
fnox.toml, encrypted to each teammate's existing SSH public key, so anyone with a matching SSH private key can decrypt after agit pull. - Source: the age provider guide, read from a local clone of jdx/fnox at commit
03e9aee(2026-10-01). Commands are copied from that guide; the scenarios below have not been run.
- Commit secrets to a project as age ciphertext in
How it works
- Each value is encrypted with age to a list of recipients, the public keys in the
ageprovider'srecipientsarray. A recipient can be an SSH public key (ssh-ed25519orssh-rsa), a nativeage1...key, or an age plugin key. - Decrypting needs the private half of any one recipient. fnox looks for it in
FNOX_AGE_KEY, then the provider'sidentity, then itskey_file, thenFNOX_AGE_KEY_FILE, thenage.txtin the fnox config directory. For SSH keys, pointFNOX_AGE_KEY_FILEat the private key. - The ciphertext stores which recipients it was encrypted to. Editing the
recipientslist changes nothing already encrypted untilfnox reencryptrewrites it. That one fact drives the add and retire scenarios.
- Each value is encrypted with age to a list of recipients, the public keys in the
Prerequisites
- fnox installed and the
ageCLI. fnox can encrypt to SSH recipients without it; install it withbrew install ageif you also wantage-keygenfor a CI key. - Every person has an SSH key of type
ed25519orrsa(2048 bits minimum, 4096 recommended). - The SSH private key must not be passphrase-protected. Upstream says password-protected SSH keys are not supported; it advises a dedicated age identity or an age plugin instead of stripping the passphrase from your normal key. See fnox/How To/Use Apple Secure Enclave Touch ID for the plugin route on a Mac.
- fnox installed and the
Steps
Scenarios
A person adds a secret
A new team member joins
- 1. They send their SSH public key.
- 2. An existing member who can already decrypt adds it to
recipients. - 3. That member re-encrypts everything, previewing first:
- bash
fnox reencrypt -p age --dry-run fnox reencrypt -p age - 4. They commit and push. The new member pulls, sets
FNOX_AGE_KEY_FILE, and runsfnox get DATABASE_URL. - Without step 3 the new person's key is listed but every existing secret is still locked to the old set. Run it once per profile, for example
fnox reencrypt -p age -P staging -f.
A team member leaves
- 1. Delete their line from
recipients. - 2. Run
fnox reencrypt -p agewith an identity that can still decrypt, then commit and push. New commits are then encrypted without them. - 3. Rotate the underlying secrets. Removing a recipient cannot revoke access to ciphertext already in git history, and the leaver's key still decrypts those old commits. Upstream says to rotate the secret if access must end. The re-encryption changes the wrapping, not the secret's value.
- Rotation sequence for each secret: change it at its source,
fnox setthe new value, commit.
- 1. Delete their line from
Someone loses their SSH key
CI needs access
Troubleshooting
no identity matched any of the recipients: the private key in use is not the pair of any listed recipient. Compare your public key with therecipientsarray.failed to decrypt:FNOX_AGE_KEYandFNOX_AGE_KEY_FILEare both unset and there is noage.txtin the fnox config directory, or the file is unreadable.- An inline
FNOX_AGE_KEYoverrides the provider'sidentityandkey_file. Unset it if you meant to use an SSH key file. - SSH key rejected: confirm the type is
ed25519orrsaand the key has no passphrase.
Related
- fnox/Golden Path: the alternative where a vault is the source of truth and each person keeps a personal encrypted cache.
- fnox