Docs/API Reference/OctetVerdict

OctetVerdict

The outcome of a predicate call. Returned by isWithin, isOutside, contains on OctetSdk.loc.

Shape

public struct OctetVerdict: Sendable {
    public let result: Result
    public let reason: ReasonCode
    public let message: String
    public let proof: LocationProof?
    public let validity: Interval?
    public let queriedAt: Date
    public let confidence: ConfidenceSummary
    public let achievableLevel: ProofLevel?   // non-nil only when reason == .insufficientPrecision

    public enum Result: Sendable, Hashable {
        case yes, no, indeterminate
    }

    public enum ReasonCode: Sendable, Hashable {
        case ok
        case noFix
        case futureTime
        case staleFix
        case noProofAtResolution
        case insufficientPrecision
        case attestationFailed
        case mockLocationDetected
        case sdkNotRunning
        case notYetReleased
    }
}
data class OctetVerdict(
    val result: Result,
    val reason: ReasonCode,
    val message: String,
    val proof: LocationProof?,
    val validity: Interval?,
    val queriedAt: Instant,
    val confidence: ConfidenceSummary,
    val achievableLevel: ProofLevel? = null,  // non-null only when reason == INSUFFICIENT_PRECISION
) {
    enum class Result { YES, NO, INDETERMINATE }

    enum class ReasonCode {
        OK, NO_FIX, FUTURE_TIME, STALE_FIX, NO_PROOF_AT_RESOLUTION,
        INSUFFICIENT_PRECISION, ATTESTATION_FAILED, MOCK_LOCATION_DETECTED,
        SDK_NOT_RUNNING, NOT_YET_RELEASED,
    }
}

Field invariants

  • proof is nil/null iff result == .indeterminate/INDETERMINATE. Enforced as a precondition / require at construction. A YES or NO always carries a proof. An INDETERMINATE never does.
  • validity is non-nil exactly when proof is non-nil. Both move together.
  • queriedAt is the atTime that was passed in. Echoed back so callers do not have to thread it themselves.
  • confidence is always populated, even for INDETERMINATE (with an empty summary in that case).
  • achievableLevel is non-nil only when reason == .insufficientPrecision. It names the best proof level the SDK could reach for this query right now. New in 1.1. The SDK never silently down-levels to it on your behalf.

result. The trichotomy.

Value Meaning
.yes / YES Predicate holds. Proof attached.
.no / NO Predicate provably does NOT hold. Proof attached.
.indeterminate / INDETERMINATE SDK cannot answer. See reason.

Never collapse .indeterminate to .no silently. See Verdicts for the rationale.

reason. The ReasonCode.

Code Result Meaning
ok / OK YES or NO Proof covered atTime. Predicate evaluated cleanly.
noFix / NO_FIX INDETERMINATE SDK started but no fix yet (also: running on simulator / emulator).
futureTime / FUTURE_TIME INDETERMINATE atTime is in the future beyond ±2 s clock-skew tolerance.
staleFix / STALE_FIX INDETERMINATE atTime falls outside the validity window of any cached proof.
noProofAtResolution / NO_PROOF_AT_RESOLUTION INDETERMINATE Cached proof is too coarse for the query (country proof, city query).
insufficientPrecision / INSUFFICIENT_PRECISION INDETERMINATE Conditions cannot support the requested precision. Read achievableLevel.
attestationFailed / ATTESTATION_FAILED INDETERMINATE Play Integrity / App Attest verdict not COMPLIANT.
mockLocationDetected / MOCK_LOCATION_DETECTED INDETERMINATE OS flagged mocked location.
sdkNotRunning / SDK_NOT_RUNNING INDETERMINATE Octet.start(...) was never called or session ended.
notYetReleased / NOT_YET_RELEASED INDETERMINATE Predicate path declared by the API but not wired in this SDK build.

message. For humans only.

message is a free-form string useful for log lines and debug UI. Do not parse message for control flow. Parse reason instead. The text is intentionally informal. The SDK reserves the right to refine it across versions.

proof. The cryptographic witness.

LocationProof is the signed protobuf that a verifier checks against Octet's public keys. Its full shape is part of the protocol, not the SDK's public surface. See Serialization for the v1 alpha caveat about how LocationProof is currently rendered through .toJson().

For most integrators the proof is opaque. Forward it to your verifier as-is.

validity. The proof's temporal window.

The interval over which the underlying proof answers the predicate as stated. Not "how long the verdict will stay true". See Time Semantics.

public struct Interval: Sendable, Hashable {
    public let from: Date
    public let to: Date
}
data class Interval(val from: Instant, val to: Instant)

Inclusive-inclusive, from <= to.

confidence. The score and flags.

ConfidenceSummary surfaces the SDK's overall confidence score and a list of flags (mock-location detected, VPN active, GNSS anomaly, and so on). A strict caller can treat a low-confidence YES as effectively INDETERMINATE:

let strictYes = verdict.result == .yes && verdict.confidence.overallScore > 0.8
val strictYes = verdict.result == OctetVerdict.Result.YES &&
                verdict.confidence.overallScore > 0.8

The SDK never applies this collapse for you. The policy lives on the caller side.

See also