github jdx/fnox v1.48.0
v1.48.0: Touch ID-protected macOS Keychain secrets and streaming proxy responses

41 minutes ago

The macOS keychain provider can now require Touch ID for each read, and fnox proxy run streams rule-domain responses so SSE and LLM clients get events as they arrive. Secrets can record the URL where a new value is issued. This release also fixes several 1Password problems: secrets lost their trailing newline, and environment:// references failed on CLI 2.42.0.

Added

  • Touch ID protection for macOS Keychain secrets (#1140, #1146) by @jdalton and @jdx. Set touch_id = true on a keychain provider:
    [providers.keychain]
    type = "keychain"
    service = "fnox"
    touch_id = true
    • Protected items are stored under a separate <service>.touch-id Keychain service. Before each read, fnox shows the invoking command, project path and secret name, then asks macOS for Touch ID. Canceling or failing the check blocks access, and there is no password fallback.
    • fnox asks for Touch ID on every protected read. With touch_id = true, fnox provider test creates a temporary protected item and reads it, so it checks real protected access.
    • The protected Keychain operation runs through a signed Fnox.app helper. Official releases publish it as a separate fnox-touch-id-<target>.zip asset. Install it in /Applications or next to the fnox binary. Without the helper, Touch ID operations fail with an Apple missing-entitlement error.
    • Source builds on macOS need Node.js 22.18.0 or later to build the approval UI. They also need a Developer ID provisioning profile, and the CLI and helper must be signed with the same team and identifier. Builds made with an SDK older than macOS 26 use an opaque approval panel instead of the glass style. See the keychain docs for helper packaging.
  • Secret URLs and fnox set --open (#1145) by @jdx. A secret can now include a url field for the page where you create a new value, such as a service's token settings page. The URL must use http or https. fnox set KEY --open prints that URL, opens it in your browser, and then shows the usual hidden prompt for the value:
    fnox set GITHUB_TOKEN --url <URL>   # record the URL only
    fnox set GITHUB_TOKEN --open        # open the page, then enter the new value
    --open needs an interactive terminal and can't be combined with VALUE, --from-file or --generate. fnox list shows a URL column only when at least one secret has a URL, and fnox tui shows the URL in the secret details.
  • Find 1Password items by tag (#1138) by @jdalton. fnox provider discover lists item metadata (ID, title, tags and vault) without reading any secret fields, so you can look up references before adding them to your config. It uses the provider's configured account and vault. If no vault is configured, it searches every vault the account can access. Tags can be comma-separated or passed more than once, and archived items are skipped.
    fnox provider discover op --tags project/example --json
  • Opt-in sudo Touch ID setup script (#1139) by @jdalton. scripts/enable-sudo-touch-id.sh can check, preview or enable Touch ID for sudo on macOS, using the --check, --dry-run and --apply modes. --json gives structured output. The script never elevates itself and only creates /etc/pam.d/sudo_local when that file doesn't exist. It refuses to change existing policies it doesn't recognize.

Changed

  • Streaming proxy responses (#1113) by @jdx. fnox proxy run no longer waits for a rule domain's full upstream response before forwarding it. It sends the headers right away and then sends the body in chunks as it arrives. Secret values are still redacted, including values split across two chunks. Before this change, streaming APIs such as SSE-based LLM endpoints delivered no events until the upstream finished, and agent clients could time out.
    • Responses no longer have a 10 MiB limit. Request bodies are still limited to 10 MiB.
    • If the upstream fails partway through a response, the client receives an incomplete chunked body instead of a 502.

Fixed

  • fnox proxy run no longer exits when a client disconnects mid-response (#1112) by @jdx. Previously, the proxy was killed by SIGPIPE (exit 141). The child command then pointed at a closed port and the proxy's CA file was left in the temp directory. proxy run and daemon serve now drop only the affected connection and keep running. Pipelines such as fnox get FOO | head behave as before.
  • 1Password values keep their trailing newline (#1131) by @jdx. Values read from 1Password now match the stored value exactly. This fixes SSH private keys in [files] that OpenSSH rejected with invalid format. Batched reads also no longer change multi-line values or drop empty ones. As a result, a stored value that really ends in whitespace now keeps it in environment variables too. Sync cache entries written by earlier versions keep the trimmed value until you refresh them.
  • 1Password environment:// references work with CLI 2.42.0+ (#1144) by @jdx. fnox now runs op environment secret list. It falls back to op environment read only when the CLI doesn't know the new command, which is the case for older betas.
  • fnox get and fnox sync resolve provider tokens stored as secrets (#1125) by @jdx. A provider token kept as its own fnox secret, such as BWS_ACCESS_TOKEN, is now resolved for get and sync as it already was for exec and export. sync still writes only the secrets you selected.
  • Shell hook reports the actual config error (#1130) by @jdx. When fnox.toml has a schema error, hook-env now warns with the parse error. Previously it reported a missing .config/fnox.local.toml, or showed no warning at all when that file existed.
  • Vault leases send ttl only when duration is set (#1129) by @jdx. This fixes unknown field: ttl errors from strict engines such as the GitHub secrets plugin on OpenBao. If you don't set duration, engines that honor ttl now use their role's default TTL instead of the 15 minutes fnox used to request. Set duration to keep the old behavior.
  • Daemon shell hooks pick up FNOX_AGE_KEY changes (#1137) by @Aschenbath. Adding, replacing or removing the inline age key in the calling shell now takes effect on the next hook request, without restarting the daemon.
  • fnox sync --local-file warns before removing stale local cache (#1135) by @jdx. When entries are marked for removal, --dry-run and the confirmation prompt now show a hint. Personal secrets synced by v1.36.0 or older lack local_source = true and look the same as cache copies, so you need to add that flag to keep them. The sync guide now documents local_source and how to recover those entries.

New Contributors

Full Changelog: v1.47.0...v1.48.0

💚 Sponsor fnox

fnox is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.

If fnox handles secrets or config for you or your team, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep fnox secure, free, and independent.

Don't miss a new fnox release

NewReleases is sending notifications on new releases.