Predicates
The three predicate methods on OctetSdk.loc. All three are async (Swift) / suspend (Kotlin) and do not throw. Runtime problems surface as INDETERMINATE verdicts, not exceptions.
isWithin
The primary predicate. Reads as: "my currently provable location 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
Reads as: "my currently provable location 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 not !isWithin. An INDETERMINATE is not outside. The separate call lets the caller assert the negative claim and get a proof of the negative. A verifier can check that proof just as easily as a YES. Negating an INDETERMINATE would erase the difference between "I'm not inside" and "I can't tell".
contains
Reads from the device's perspective: "my location contains this point within tol meters."
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").
Rolling your own
isWithin is the load-bearing primitive. contains is a thin one-liner over it. The same trick rolls custom predicates:
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.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 is redundant but harmless.
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.