A feature release centered on more flexible authentication: the age provider can now bootstrap its identity from another provider (for example, the OS keychain), and the Vault provider and lease backend can obtain tokens from a user-defined shell command. Also includes a real put_secret_value for GCP Secret Manager and two fixes that make fnox export and hook-env better citizens in shell pipelines.
Added
Provider-backed age identities (#515) -- @k35o
The age provider now accepts an identity reference that resolves through any other configured provider. This makes the keychain-bootstrap pattern from v1.25.1 actually work end-to-end:
[providers]
keychain = { type = "keychain", service = "fnox" }
age = { type = "age", recipients = ["age1..."], identity = { provider = "keychain", value = "age-key" } }Implementation details worth knowing:
- Identities are resolved lazily during decryption, so
fnox setand other encryption-only paths don't require access to the backing identity provider. - Resolution order is
FNOX_AGE_KEY->identityprovider ref ->key_file-> deprecated CLI setting -> default~/.config/fnox/age.txt. - Cycle detection catches both direct self-references (
age->age) and mutual cycles between two age providers, producing a clearCircular dependency detected in provider configuration: age-a -> age-b -> age-aerror. - Nested age identities are supported: a bootstrap age provider can decrypt the identity used by a second age provider.
- Identity providers that require interactive auth are rejected in non-interactive mode with a hint to use
fnox exec.
credential_command for Vault provider and lease backend (#526) -- @jdx
Vault and OpenBao users can now resolve tokens through a configured shell command when no static token is provided, removing the need to share or pre-stage a VAULT_TOKEN:
[providers.vault_team_a]
type = "vault"
address = "$VAULT_ADDR"
namespace = "team-a"
path = "secret/team-a"
credential_command = "vault login -method=oidc -token-only"The same field is available on the Vault lease backend:
[leases.vault-db]
type = "vault"
address = "$VAULT_ADDR"
credential_command = "vault login -method=oidc -token-only"
secret_path = "database/creds/readonly"
method = "post"- Commands run through the platform shell (
sh -c/cmd /C), so pipes and redirects work. - The string is rendered as a Tera template with
address,path/secret_path, andnamespaceavailable as variables, and fnox injectsVAULT_ADDRandVAULT_NAMESPACEinto the environment. - Output is cached for ~5 minutes per process so resolving many secrets from the same provider doesn't repeat the login.
- 401/403 responses from Vault invalidate the cache so a stale token is refetched on the next call.
- Static config/env tokens still take precedence;
credential_commandis only consulted when no token is configured. FNOX_VAULT_NAMESPACEis now recognized alongsideVAULT_NAMESPACE.
put_secret_value for GCP Secret Manager (#530) -- @nils-degroot
The GCP Secret Manager provider's write path was previously a stub that returned put_secret not yet implemented. fnox set --provider gcp now actually writes: it adds a new secret version, and on 404 NOT_FOUND it creates the secret with automatic replication and retries the write. PERMISSION_DENIED responses surface as ProviderAuthFailed with the relevant IAM permission name (secretmanager.versions.add / secretmanager.secrets.create).
Fixed
FIDO2 keep-alive messages no longer corrupt fnox export (#506, resolves #465) -- @baprx
ctap-hid-fido2's "touch your key" prompts were being written to stdout, which broke eval "$(fnox export)" and similar shell-sourcing workflows. fnox now configures the FIDO2 client with with_keep_alive_msg_to_stderr(true) for both secret retrieval and setup, so stdout stays clean for the exported assignments.
hook-env respects $COLUMNS when stderr is not a TTY (#523) -- @davidolrik
Shell hooks capture stderr, so console::Term::size() could return a useless width when rendering the env-change summary. fnox now uses the TTY width when available, then $COLUMNS, and falls back to 80 columns — restoring the previous default and producing readable truncation in shell hooks.
Documentation
- AWS Secrets Manager IAM requirements (#513) -- @gaffneyc: The docs now make clear that
BatchGetSecretValue(introduced for batched lookups) must be granted alongsideListSecretswith a wildcard resource — neither action accepts a resource ARN scope.
New Contributors
- @nils-degroot made their first contribution in #530
- @k35o made their first contribution in #515
- @gaffneyc made their first contribution in #513
- @baprx made their first contribution in #506
Full Changelog: v1.25.1...v1.26.0
💚 Sponsor fnox
fnox is maintained by @jdx under en.dev — a small independent studio building developer tooling like mise, aube, hk, and more. Keeping fnox secure, maintained, and free is funded by sponsors.
If fnox is handling secrets or config for you or your team, please consider sponsoring at en.dev. Sponsorships are what let fnox stay independent and the project keep moving.