github jdx/fnox v1.29.0
v1.29.0: Age plugins, config-relative paths, and Nix flakes

7 hours ago

A feature release focused on hardware-backed age encryption, more predictable path handling in config files, and faster GCP Secret Manager batches -- plus a Nix flake for the flake-curious.

Added

Age plugin recipients and identities (#569) -- @nightvisi0n

The age provider now supports age plugins, so hardware-backed and specialty recipients work end to end. That includes age-plugin-yubikey (YubiKey/PIV), age-plugin-se (Apple Secure Enclave), age-plugin-tpm, and friends.

Previously a plugin recipient like age1yubikey1... failed at config load with Failed to parse recipient ... incorrect HRP because the provider only understood native X25519 and SSH recipients. It now parses age::plugin::Recipient values, spawns the matching age-plugin-* binary for encryption, and attaches UiCallbacks to identity files so AGE-PLUGIN-* identities get PIN/touch prompts on decrypt.

Nix flake (#583) -- @o-az

fnox is now packaged as a Nix flake. Consume it from another flake:

inputs.fnox.url = "github:jdx/fnox";
# ...then reference inputs.fnox.packages.${system}.default

Or run it directly without installing:

nix run github:jdx/fnox

The flake also exposes a devShells.default with cargo, clippy, and rustfmt.

Fixed

Defaults are used when a provider is inactive (#572) -- @jdx

If a secret declared a default and the active profile did not have the referenced provider configured, resolution would fail instead of falling back. Single-secret and batch resolution now share the same fallback path, so fnox get and fnox exec return the default (including interpolated ${...} defaults that reference other secrets resolved in the same batch) when the provider is missing, batch fetch fails, or non-interactive auth aborts. A provider value still wins when the provider succeeds; cycles between fallback defaults are rejected explicitly.

Config paths resolve relative to the config file that declares them (#582) -- @jdx

Filesystem paths in provider configuration are now interpreted against the config file that defines them, not the current working directory. This matters especially for nested/imported configs, where a parent fnox.toml could point at keys/age.key and a child config in a subdirectory would silently miss it.

Affected fields:

  • age.key_file
  • keepass.database, keepass.keyfile
  • password-store.store_dir
  • foks.home
  • imports, and paths reported by fnox config-files

Rules:

  • Paths in config files resolve relative to the declaring file, and ~ expands to the user home.
  • Absolute paths are used unchanged.
  • CLI path arguments still resolve against the current working directory.
  • Environment-variable paths keep their existing behavior.

fnox daemon clear clears every running daemon (#581) -- @jdx

fnox daemon clear previously only talked to the daemon socket for the current profile, so caches on daemons started under other profiles were left stale. It now scans the daemon runtime directory, sends Clear to every live profile-scoped daemon, and ignores stale sockets during the sweep. When no live daemon responds, you still get the familiar "daemon not running" error.

Changed

Faster batch reads from GCP Secret Manager (#580) -- @nils-degroot

google-secret-manager previously used the default batch implementation, which created a fresh client per secret. The provider now implements get_secrets_batch directly: a single client is reused across the batch and up to 10 secrets are fetched concurrently, which noticeably reduces latency for configs with many GCP secrets. Missing payloads and non-UTF-8 values are handled more explicitly, and client-creation errors surface provider-specific auth guidance.

New Contributors

Full Changelog: v1.28.0...v1.29.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.