Docs/Concepts/Proof of Location

Proof of Location

In one sentence

A proof of location is a signed, time-stamped record that a device was inside a region at a given time, and anyone holding it can verify the signatures offline.

Why a claim needs a proof

A claim like "the device was in the US at 3 pm Tuesday" can come from the OS, the GPS chip, a browser API, or a mock-location app. All of them are easy to forge. The claim is only worth acting on if a third party (your backend, an auditor, a regulator) can check it without trusting whoever made it.

The Octet SDK produces those proofs. Every YES or NO it returns carries one. An INDETERMINATE carries none. You can:

  • Forward a proof to your backend and verify it offline with the open-source verifier.
  • Hand it to a compliance auditor to re-check months later.
  • Store it as a durable record of where the device was at that time.

How it works

The SDK combines several independent on-device signals (GNSS, cellular network identity, motion sensors) with platform attestation (Apple App Attest, Google Play Integrity) to produce a verdict and a cryptographic proof. The signing key lives in the device's hardware root of trust (the Secure Enclave on iOS, the Trusted Execution Environment on Android) and never leaves the chip. The proof is a signed protobuf that names:

  • The claimed region (for example country US, or a disc of radius 250 m).
  • The time of the fix and the interval over which the proof stays valid.
  • A confidence summary: a score from 0 to 1 and a set of flags.
  • An attestation chain from platform hardware (App Attest on iOS, Play Integrity on Android), which the SDK checks when it builds the proof.

You call isWithin(region) and get back a Verdict. On YES or NO the verdict carries a proof. If the SDK cannot get a trustworthy fix (rooted device, a mock-location app, or no usable GPS signal), it issues no proof and returns INDETERMINATE with a reason code.

flowchart LR
    A[Your app] -->|isWithin region| B[OctetSDK]
    B --> C{Can the SDK<br/>answer?}
    C -->|Yes| D[Verdict YES or NO<br/>+ signed proof]
    C -->|No| E[Verdict INDETERMINATE<br/>+ reason code]
    D -->|forward| F[Your backend]
    F -->|verify offline| G[octet-verify]

What the SDK does not do

  • Flag users as spoofers. It reports only whether a proof can be issued. Spoof signals fold into that decision.
  • Guarantee a YES. On an emulator, indoors with no cellular, on a rooted device, or with a mock-location app running, the result is INDETERMINATE.
  • Predict the future. A proof states where the device was at time T, not where it will be later. See Time Semantics.

Where to go next

  • Device Attestation. How App Attest and Play Integrity tie a proof to genuine hardware.
  • Verdicts. The YES / NO / INDETERMINATE values and how to read the reason code.
  • Regions. What kinds of areas you can ask about.
  • Time Semantics. How atTime and proof validity intervals interact.