Skip to content
Warrantv0.1

Passkey approvals

Above a threshold, settling needs a WebAuthn signature from a person.

Set passkey_threshold and any settlement at or above it stops mid-request and asks for a WebAuthn assertion before it completes.

{
  "passkey_required": true,
  "challenge": "…",
  "scope": "resolve"
}

How it interrupts

The first request to resolve an escalation above the threshold does not settle. It returns the object above. Resubmit the same call with an assertion:

{
  "resolution": "release",
  "assertion": {
    "challenge": "…",
    "credential_id": "…",
    "signature": "…"
  }
}

Only then does money move.

On a phone this is the fingerprint sensor, which is the point of approving from one — the escalation arrives as a notification, and the thing that authorises it is bound to the device in the reader's hand.

Three properties that make it worth having

The challenge is consumed on use. It is deleted in the same statement that checks it, so a captured assertion cannot be replayed. A second attempt with the same challenge gets 403 challenge_invalid.

Challenges expire. An expired one gets the same challenge_invalid, so a stale approval sitting in a tab is not a standing authorisation.

The signature counter must advance. A passkey whose counter goes backwards or fails to move is refused with 403 credential_replay. A counter that does not advance is the signature of a cloned authenticator.

An API key can never satisfy this

Settlement endpoints refuse an API key outright with 403 user_session_required, before any threshold is consulted.

That ordering matters. If a key could settle, the threshold would only apply to people, and the way round it would be to use a key — which is not a threshold, it is a speed bump.

When it is not configured

If no assertion verifier is wired on the deployment, an above-threshold settlement is refused with 403 passkey_unavailable. It is not waved through.

Failing closed is the only defensible direction here: the alternative is a deployment where the threshold silently does nothing, and nobody finds out until the money is gone.

Enrolment

Settings → Security in the console. A person can hold several passkeys — one per device — and each is listed with when it was added and last used.

Register one before you need it. With no passkey enrolled, an above-threshold approval is blocked rather than waved through, which is correct and also unhelpful at the moment you are trying to pay somebody.

Amounts and the threshold

passkey_threshold is an integer of the asset's smallest unit, compared as an integer.

"8400000"    8.40 USDC
8400000      rejected — a JSON number
"8.40"       rejected — not the smallest unit

A threshold you can drift past through floating-point rounding is not a threshold, which is why nothing in this comparison is ever a float.