FIDO2
The fido2 provider encrypts values into fnox.toml with a key derived from a FIDO2 security key through the CTAP2 hmac-secret extension. Encrypting and decrypting both need the same physical key; fnox set writes new ciphertext.
| Config type | fido2 |
| Storage | Encrypted in config |
Writes (fnox set) | Yes |
| Requires | A FIDO2 key with hmac-secret support, plugged in |
FIDO2 support alone is not enough: confirm that your key and firmware support the hmac-secret extension. The provider is not included in Linux musl builds of fnox.
Quick start
Plug in the key and create the provider:
fnox provider add secure fido2Setup asks for the key's PIN (leave it empty if none is set), creates a credential with hmac-secret enabled, generates a random salt, and verifies the key with a second touch. It writes the result to fnox.toml:
[providers.secure]
type = "fido2"
credential_id = "a1b2c3..." # generated hex credential ID, abbreviated
salt = "d4e5f6..." # generated 32-byte hex salt, abbreviated
rp_id = "fnox.secure"Store a value, then use it. Each command asks you to touch the key:
fnox set API_KEY --provider secure
fnox exec -- ./start.shThe config is portable: on another machine, plug in the same key and it decrypts.
Authentication
fnox talks to the key directly; there is no CLI or account. Each fnox process asks for one touch per provider and keeps the hmac-secret response in memory until it exits, so fnox exec resolves every secret from the provider with a single touch.
When the key requires a PIN and pin is not set, fnox prompts for it on a terminal. fnox provider add does not store the PIN.
The provider needs interactive approval, so it cannot run in non-interactive mode or in CI. Use another provider for CI secrets.
Configuration
| Field | Required | Default | Description |
|---|---|---|---|
credential_id | Yes | None | Hex-encoded credential ID, generated by fnox provider add. |
salt | Yes | None | Hex-encoded 32-byte salt sent to the key, generated by fnox provider add. |
rp_id | Yes | None | Relying party ID of the credential. Setup uses fnox.<provider-name>. |
pin | No | None | Key PIN. When unset, fnox prompts on a terminal. |
All fields accept { secret = "NAME" }. Every provider also accepts auth_command and daemon_cache.
How values are stored
- fnox sends
saltto the key during an assertion forcredential_id. The key returns a 32-byte secret derived from its internal device secret. - HKDF-SHA256 derives a 256-bit AES key from that response, using the provider name as context.
- AES-256-GCM encrypts the value. fnox stores
base64(nonce || ciphertext || tag)as the secret'svalue.
The hmac-secret response is never written to disk.
Renaming providers invalidates ciphertext
The provider name is part of key derivation. Renaming a provider (for example, from secure to my_fido2) changes the derived key, making all previously encrypted secrets and cached lease credentials undecryptable. To migrate, keep the old provider available, create a new provider under the new name, and read values through the old provider before storing them with the new one. Verify the new values before removing the old configuration.
Protect lease credentials
Store long-lived master credentials with the key and mark them env = false, so only a credential lease uses them and only short-lived credentials reach your command:
[secrets]
AWS_ACCESS_KEY_ID = { provider = "secure", env = false }
AWS_SECRET_ACCESS_KEY = { provider = "secure", env = false }
[leases.aws]
type = "aws-sts"
role_arn = "arn:aws:iam::123456789012:role/dev-role"
region = "us-east-1"FIDO2 or YubiKey
| Feature | fido2 | yubikey |
|---|---|---|
| Key types | Any FIDO2 key | YubiKey only |
| Protocol | CTAP2 hmac-secret | HMAC-SHA1 challenge-response |
| PIN support | Yes | No |
| Key output | 32 bytes | 20 bytes |
Choose fido2 if your key supports hmac-secret. Choose yubikey if you use a YubiKey OTP slot configured for HMAC-SHA1 challenge-response.
Troubleshooting
FIDO2 authenticator does not support hmac-secret extension
The key created a credential but did not enable hmac-secret. Use a key and firmware that support the extension.
Failed to find FIDO2 device
fnox found no FIDO2 key. Plug the key in, close other applications that hold it open, and retry with fnox check --all. fnox provider test does not contact the key.
requires interactive authentication and cannot be used in non-interactive mode
The command ran with --non-interactive or FNOX_NON_INTERACTIVE set, or from shell integration, the MCP server, or the TUI. Run fnox exec in a terminal instead.
See also
- YubiKey: HMAC-SHA1 challenge-response alternative.
- Credential leases: use hardware-protected credentials to mint short-lived ones.
- Plugin support: use a FIDO2 key through an age plugin instead.