github jdx/fnox v1.26.0
v1.26.0: Provider-Backed Age Identities & Vault Credential Commands

6 hours ago

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 set and other encryption-only paths don't require access to the backing identity provider.
  • Resolution order is FNOX_AGE_KEY -> identity provider 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 clear Circular dependency detected in provider configuration: age-a -> age-b -> age-a error.
  • 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, and namespace available as variables, and fnox injects VAULT_ADDR and VAULT_NAMESPACE into 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_command is only consulted when no token is configured.
  • FNOX_VAULT_NAMESPACE is now recognized alongside VAULT_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 alongside ListSecrets with a wildcard resource — neither action accepts a resource ARN scope.

New Contributors

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.

Don't miss a new fnox release

NewReleases is sending notifications on new releases.