kovra-native-macos
The macOS Touch ID Confirmer for kovra (spec §8, §14.1; layer L8, [host]).
This crate is the native half of the confirmation broker. It renders the
core-authored ConfirmRequest in a macOS LocalAuthentication (LAContext)
dialog and returns Approved | Denied | TimedOut. It is a third Confirmer
implementation beside CliApproveConfirmer and FileConfirmer.
- Free core, not enterprise:
corenever depends on this crate; injection points the other way (this crate depends onkovra-core). - No invariant is touched. The dialog only shows what the core put in the request (I16); approval happens at the sensor, outside the model's process (§8.2, no self-approve); timeout fails safe to denial (§8); no secret value is ever rendered or logged (I7/I12).
- Cross-platform: the
LAContextbinding is gated undercfg(target_os = "macos"). Off-macOS the crate compiles to a no-op stub (biometrics_available()→false,prompt()→Denied), so Linux CI builds the whole workspace and the CLI auto-falls-back to the file broker.
Confirmer selection (KOVRA_CONFIRMER)
Selection lives in the CLI's Ctx::confirmer() factory:
KOVRA_CONFIRMER |
Behavior |
|---|---|
| unset (default) | biometric on macOS with automatic fallback to file when biometrics is unavailable; always file on non-macOS |
biometric |
native Touch ID prompt if the host can prompt; otherwise fall back to file |
file |
always the cross-process kovra approve <id> file broker |
"Biometrics unavailable" = not macOS, or no biometric hardware, or the user is
not enrolled (LAContext canEvaluatePolicy: fails). Any unrecognized value uses
the default.
[host] hardware-validation checklist (M4) — blocking sign-off for Done
The real LAContext path is not exercised by automated tests (no hardware in
CI). A human must validate the following on an M4 with Touch ID enrolled. This is
the blocking gate for marking KOV-15 Done.
Setup: a throwaway vault in passphrase mode, a high/prod secret, and
KOVRA_CONFIRMER unset (default biometric) unless noted.
- Approve (happy path).
kovra show secret:prod/db/password→ Touch ID dialog appears → touch the sensor → the value is revealed. Audit log records an approval (no value). - Deny. Trigger the prompt again → tap Cancel / use the wrong finger → the operation is denied, no value revealed, audit records a denial.
- Timeout ⇒ deny. Trigger the prompt and do not respond until the
confirm_timeout(CLI default 120s) elapses → the operation fails closed (timed out, treated as denial), no value revealed. - I16 dialog content. For an injection (
kovra run … -- /usr/bin/deploy …against aprod/highref), confirm the dialog shows:- the exact resolved command/argv as the prominent first line (not paraphrased),
- the coordinate address, sensitivity, environment (with
prodhighlighted), and origin, - any requester description rendered only under the "provided by requester (untrusted)" fence — never as the headline.
- No self-approve (§8.2). Confirm there is no way for the invoking process
(the agent /
kovra run) to satisfy the prompt programmatically — only a human touch at the sensor approves. - Biometric-disabled fallback. Disable/disenroll Touch ID (or run on a Mac
without it) → with
KOVRA_CONFIRMERunset or=biometric, the CLI must fall back to the file broker (kovra approve --listshows the pending request; approving in another terminal releases the blockedshow/run). - No leak (I7/I12). Across all of the above, confirm the secret value never appears in: the dialog text, the audit log, stdout/stderr of the prompt, or any file written by the confirm path. Only addresses/commands appear.
Threading & shim notes
- The native prompt is invoked from the CLI main thread (the CLI is
synchronous).
evaluatePolicy:reply:is non-blocking and fires its reply on a private framework queue; we block the main thread on anmpscchannel the reply signals — no dedicated runloop pump is built. - No
extern "C"shim was needed:objc2-local-authentication0.3.2 exposes the full surface used (LAContext::new,canEvaluatePolicy:,evaluatePolicy:localizedReason:reply:).