Predicates
The three predicate methods on OctetSdk.loc. All three are async (Swift) / suspend (Kotlin) and do not throw. A runtime problem produces an INDETERMINATE verdict.
isWithin
The primary predicate. Tests whether the device's provable location at atTime (default now) is inside region.
public func isWithin(
region: OctetRegion,
atTime: Date = Date(),
sessionNonce: Data? = nil,
decisionRef: String? = nil,
forceFresh: Bool = false
) async -> OctetVerdict
suspend fun isWithin(
region: OctetRegion,
atTime: Instant = Instant.now(),
sessionNonce: ByteArray? = null,
decisionRef: String? = null,
forceFresh: Boolean = false,
): OctetVerdict
Example
let v = await sdk.loc.isWithin(
region: .country(isoCode: "US"),
atTime: Date()
)
if v.result == .yes { ship(v.proof!) }
val v = sdk.loc.isWithin(
region = OctetRegion.country("US"),
atTime = Instant.now()
)
if (v.result == OctetVerdict.Result.YES) ship(v.proof!!)
isOutside
Tests whether the device's provable location at atTime (default now) is outside region.
public func isOutside(
region: OctetRegion,
atTime: Date = Date(),
sessionNonce: Data? = nil,
decisionRef: String? = nil,
forceFresh: Bool = false
) async -> OctetVerdict
suspend fun isOutside(
region: OctetRegion,
atTime: Instant = Instant.now(),
sessionNonce: ByteArray? = null,
decisionRef: String? = null,
forceFresh: Boolean = false,
): OctetVerdict
isOutside is a separate query, not the negation of isWithin. A YES from isOutside carries a proof that the device is outside region, and a verifier checks that proof the same way as any other. An INDETERMINATE from isWithin does not mean the device is outside. Negating it would merge two outcomes: the device is not inside region, and the SDK could not tell.
contains
Tests whether the point center is within tol meters of the device's provable location.
public func contains(
center: LatLon,
tol: Meters,
atTime: Date = Date(),
sessionNonce: Data? = nil,
decisionRef: String? = nil,
forceFresh: Bool = false
) async -> OctetVerdict
suspend fun contains(
center: LatLon,
tol: Meters,
atTime: Instant = Instant.now(),
sessionNonce: ByteArray? = null,
decisionRef: String? = null,
forceFresh: Boolean = false,
): OctetVerdict
Mathematically equivalent to isWithin(region: .disc(center: center, radiusMeters: tol), atTime:). Kept as a distinct verb because the device-centric reading is the natural one for proximity use cases ("am I near the delivery dropoff", "did I arrive at the geofence").
PLAUSIBLE containment
A disc answer, from contains(...) or a disc isWithin(...), carries an assurance tier on its proof. By default a disc query answers YES or NO only from a VERIFIED proof, and otherwise returns INDETERMINATE. New in 3.0: setting OctetConfig.advanced.acceptPlausibleContainment = true lets it answer from a PLAUSIBLE proof as well. Tight tolerances pass only with a tight location fix. Read proof.spoofingVerdict before treating a YES as authoritative.
let config = OctetConfig(
licenseKey: "",
advanced: AdvancedConfig(acceptPlausibleContainment: true))
val config = OctetConfig(
licenseKey = "",
advanced = AdvancedConfig(acceptPlausibleContainment = true))
Custom predicates
isWithin is the load-bearing primitive, and contains is a one-line wrapper over it. Custom predicates can be built the same way:
extension OctetLoc {
func isNear(_ center: LatLon, _ tol: Meters) async -> OctetVerdict {
await isWithin(region: .disc(center: center, radiusMeters: tol))
}
func isInsideAny(_ regions: [OctetRegion]) async -> OctetVerdict? {
for r in regions {
let v = await isWithin(region: r)
if v.result == .yes { return v }
}
return nil
}
}
suspend fun OctetLoc.isNear(center: LatLon, tol: Meters): OctetVerdict =
isWithin(OctetRegion.disc(center, tol))
suspend fun OctetLoc.isInsideAny(regions: List<OctetRegion>): OctetVerdict? {
for (r in regions) {
val v = isWithin(r)
if (v.result == OctetVerdict.Result.YES) return v
}
return null
}
Time semantics
All three predicates take an optional atTime. See Time Semantics for the live / historical / future regimes and the per-resolution validity windows. Default is "now". Future times beyond ±2 s come back INDETERMINATE / FUTURE_TIME.
Proof binding and freshness
All three predicates take three optional controls over which proof answers the call:
sessionNonce(opaque bytes, 1 to 512) binds the proof to one login. The SDK commits a hash of the nonce inside the signed proof, so a verifier can confirm the proof was made for that login. An empty or over-512-byte nonce returnsINDETERMINATE / INVALID_SESSION_NONCE.decisionRef(1 to 256 characters) binds the uploaded proof to one authorization decision, tagging the upload envelope withdecision_refandregion_ref. An over-256-character ref returnsINDETERMINATE / INVALID_DECISION_REF, with no network call. Passnil/null, not an empty string, when there is no decision.forceFreshbypasses the freshness buffer and mints a proof for this call, with none of the binding semantics above.
Each of sessionNonce and decisionRef forces a fresh proof. Setting forceFresh as well has no additional effect.
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) }
Proof Binding explains when to use each, and how a bound proof is verified.
See also
OctetRegion. The shapes you can pass.OctetVerdict. What you get back.- Proof Binding. Binding a proof to a login or a decision.
- Verdicts. The trichotomy and the
ReasonCodetaxonomy.