Proof Binding
By default the SDK may reuse a recent buffered proof for a predicate call. Three optional parameters change that: sessionNonce binds a proof to one login, decisionRef binds it to one authorization decision, and forceFresh just mints a new proof with no binding.
The freshness buffer
A predicate call can be answered from a buffered proof that is still inside its validity window, rather than generating a new one every time. That keeps repeated checks cheap. When you need a proof tied to a specific request, or a proof made at this exact moment, use one of the three controls below. Each is an optional parameter on isWithin, isOutside, and contains.
Session binding — sessionNonce
Bind a proof to a single login. Your login backend issues a one-time nonce; you pass it as sessionNonce (opaque bytes, 1 to 512). The SDK commits a hash of the nonce inside the signed proof as a sessionBinding stage, so a verifier can confirm the proof was made for that login. Only the hash rides the proof, never the raw nonce. Passing a nonce forces a fresh proof (it bypasses the buffer). An empty or over-512-byte nonce returns INDETERMINATE / INVALID_SESSION_NONCE with no proof.
let nonce = try await loginBackend.issueNonce()
let v = await sdk.loc.isWithin(region: .country(isoCode: "US"), sessionNonce: nonce)
if v.result == .yes { send(v.proof!, to: loginBackend) }
val nonce: ByteArray = loginBackend.issueNonce()
val v = sdk.loc.isWithin(OctetRegion.country("US"), sessionNonce = nonce)
if (v.result == OctetVerdict.Result.YES) send(v.proof!!, loginBackend)
Decision binding — decisionRef
Bind a proof to a specific authorization decision. Pass an opaque, host-minted decision identifier (1 to 256 characters) as decisionRef. The SDK fetches a decision-scoped upload nonce (POST /v1/proofs/challenge with a decision_ref body) so the proofs backend stamps a trusted challenge_decision_ref anchor, and it tags the upload envelope with two opaque top-level fields: decision_ref (the find key you gave) and region_ref (the proof's own claimed region in canonical form, for example country(US)). A relying party can then fetch and trust exactly the proof for one decision. An over-256-character ref returns INDETERMINATE / INVALID_DECISION_REF with no proof and no network call.
The difference from sessionNonce: a session nonce binds the proof to a login and commits a hash inside the signed proof; a decision ref binds the uploaded proof to a decision through the upload envelope, and is the key a backend fetches it by.
Force fresh — forceFresh
Set forceFresh: true to bypass the buffer and mint a proof for this call, with none of the binding semantics above: no committed nonce, no decision-scoped challenge, no envelope tags. Use it when a proof must reflect the device's location right now rather than a cached fix that is still valid, for example a "refresh" button. It does not loosen any spoof check: a fresh proof still runs the full pipeline, and a call that cannot generate one returns noFix, never a cached YES.
Combining forceFresh with a binding is redundant but harmless, because a binding already bypasses the buffer.
Choosing between them
| Goal | Use |
|---|---|
| Prove for one login, verifiable in the signed proof | sessionNonce |
| Bind an uploaded proof to one authorization decision a backend fetches | decisionRef |
| A proof of the device's location right now, no binding | forceFresh |
| Cheap repeated checks | none (let the buffer serve) |
Where to go next
- Predicates: the exact parameter signatures.
- Verifying Proofs: how a bound proof is checked.
- Serialization: the fields a proof commits and serializes.