# Verifier Quick Start

Build `octet-verify`, the open-source command-line tool that independently checks
an Octet proof, and use it to verify a proof.

:::note

This page is the how-to. [Verifying Proofs](/docs/mobile/concepts/verifying-proofs/) explains
what the verifier does and doesn't establish, and why it can run offline.

:::

---

## 1. Build it

The verifier is public Rust. Clone and build:

```bash
git clone https://github.com/octetproof/octet-verify
cd octet-verify
cargo build --release
# binary at ./target/release/octet-verify
```

If you have no proof of your own, the clone ships real ones under
`test-vectors/golden/1.0.0/`. They are historical and CI-signed, so widen the
freshness window and expect `attestation-root` to report `NOT-CHECKED`:

```bash
./target/release/octet-verify test-vectors/golden/1.0.0/android-onearth.bin \
  --hardware-pubkey test-vectors/golden/1.0.0/android-onearth.pubkey \
  --max-age-seconds 9999999999
```

The default build is offline and dependency-light. It pulls no networking
or JSON libraries, so the cryptographic path you're trusting stays small and
auditable. Two optional features sit behind build flags:

- `--features net` adds [backend fetch mode](#3-fetch-from-the-ingestion-api).
- `--features appattest` adds offline [hardware-attestation](/docs/mobile/concepts/device-attestation/)
  validation: iOS App Attest to Apple's root, and the Android key-attestation
  chain to Google's root. Without it, the `attestation-root` check stays
  `NOT-CHECKED`.
- `--features caller-metadata` adds `--caller-manifest` and `--content`, to check
  [content commitments](/docs/mobile/concepts/content-commitments/).

For proofs from SDK 3.1, build `octet-verify` at 1.8.0 or later. 1.5.0 checks a
3.1 proof's region binding, but checking caller content needs 1.8.0. To enforce a
verdict tier with `--require-verdict verified` (or `plausible`), build 1.6.0 or
later. A build older than 1.2.0 reports the session binding of a session-bound
proof (one made with a `sessionNonce`, see [Proof Binding](/docs/mobile/concepts/proof-binding/))
as `NOT-CHECKED` and still validates the rest of the proof.

---

## 2. Verify a proof you received

If you have the proof bytes as a file (the SDK can export one, or a backend can
hand you the raw bytes), that's all you need. There is no network, and no keys
to trust beyond what is inside the proof:

```bash
octet-verify proof.bin
# or: cat proof.bin | octet-verify
```

A passing run looks like this:

```
== octet-verify ==
verdict: VALID

  [       PASS] freshness              42 s old (limit 300 s)
  [       PASS] nullifier              replay token present (32 bytes)
  [       PASS] stage-chain            8 stages, hash linkage intact
  [       PASS] stage-signatures       all 8 stage signatures verify
  [       PASS] chain-assembly         final stage binds all 7 prior signatures
  [       PASS] field-binding          commitment, nullifier, zkProof bound to signed stage hashes
  [       PASS] semantic-binding        verdict / region / level / integrity / commitment bound
  [       PASS] region-claim           claims country:US
  [NOT-CHECKED] attestation-root       hardware key trusted as carried (rebuild --features appattest to validate)
  ...

11 pass · 0 fail · 0 warn · 3 not-checked
```

Use the exit code in automation:

| Code | Meaning |
|---|---|
| `0` | **authentic**: valid and signatures cryptographically verified |
| `1` | **invalid**: a check failed |
| `2` | usage / IO / decode / backend error |
| `3` | **inconclusive**: structurally valid but signatures not verified (e.g. no hardware key), or committed content that couldn't be resolved |

A CI gate can be `octet-verify proof.bin && deploy`. Exit `0` means
authentic, and an unverified proof exits `3`, never `0`.

Add `--json` for one machine-readable object per proof (`valid` reports
*authenticity*, plus `signatures_verified` and the `verdict` string):

```bash
octet-verify proof.bin --json | jq 'select(.valid == false)'
```

Useful flags: `--hardware-pubkey <file>` (supply the device key for iOS proofs
that carry no certificate), `--expect-region <id>` (assert a claimed region), and
`--max-age-seconds <n>` (widen the freshness window). See the repo's
[`INTEGRATION.md`](https://github.com/octetproof/octet-verify/blob/main/INTEGRATION.md)
for the full reference.

---

## 3. Fetch from the ingestion API

If your proofs are uploaded to the Octet proof ingestion API, the verifier can
fetch and check them in one step. This needs the network-enabled build:

```bash
cargo build --release --features net
```

The backend is untrusted. Fetched bytes run through the same checks
as a local file. Fetching requires a token that can read your license's proofs.

### Read credential

`--token` is an **`octet_read_` read credential**: a read-scoped, server-side token that is not tied to a device. Hold it on your backend or CI like an API key. It is not a license key, and it does not come from an app bootstrap.

Octet issues the credential. Request one, then export it:

```bash
export OCTET_TOKEN="octet_read_..."
```

The CLI exchanges it for a short-lived, read-scoped session internally.

Confirm fetch mode is enabled for your account before you rely on it in production.

### Fetch and verify

```bash
octet-verify fetch <proof-id> --backend https://api.octetproof.com --token "$OCTET_TOKEN"
```

Two more modes:

- `octet-verify watch ...`: poll for new proofs and verify each as it arrives (for example, to feed a live demo or dashboard).
- `octet-verify range --since <ts> --until <ts> ...`: verify every proof in a window. Exits non-zero if any fails.

> Proofs are stored for **less than 24 hours**, then purged. Fetch and verify
> promptly. An aged-out id returns `404`.

---

## 4. End-to-end flow

From device to independent verdict:

```mermaid
flowchart LR
    A[Your app + OctetSDK<br/>generate a proof] -->|upload| B[Ingestion API<br/>stores under 24h, bytes only]
    B -->|fetch by id| C[octet-verify<br/>checks signatures]
    C --> D[VALID / INVALID]
```

1. **Produce.** Your app calls the SDK. On a `YES`/`NO` it gets a signed proof
   and uploads it. (Setup is in the [iOS](/docs/mobile/getting-started/ios-quickstart/) and
   [Android](/docs/mobile/getting-started/android-quickstart/) quick starts.)
2. **Store.** The ingestion API keeps the raw bytes for less than 24 hours, keyed by
   id. It never decodes or signs anything.
3. **Verify.** `octet-verify fetch <proof-id> ...` pulls those bytes and validates
   them against the keys *inside* the proof. The verdict depends only on the
   bytes, not on anything the backend says.

A proof produced on a phone is confirmed by a separate, public
binary, using only the bytes and the keys inside the proof. If the proof was
altered after it was signed, the verifier rejects it. The default build does not
show that the signing key is genuine device hardware. For that, build with
`--features appattest` and pass `--require-attestation` (see
[Hardware attestation](/docs/mobile/concepts/verifying-proofs/#hardware-attestation-the-appattest-build)).

---

## Troubleshooting

| Symptom | Fix |
|---|---|
| `octet-verify: command not found` | Build it (step 1) and use `./target/release/octet-verify`, or put it on your `PATH`. |
| `verdict: INCONCLUSIVE` | The signatures weren't checked, usually an iOS proof with no embedded certificate. Pass the device key with `--hardware-pubkey <file>`. |
| `verdict: INVALID` | A check failed. The output names which one. Common during development: a stale proof from before a wire change, or a key that isn't the one that signed the proof. |
| `404` / no proof on fetch | The proof hasn't been uploaded yet, or it has aged past the retention window (less than 24 hours). Generate a fresh one and fetch within the day. |
| `... requires a build with the net feature` | Rebuild with `cargo build --release --features net` to use `fetch` / `watch` / `range`. |

## Reference

- [`README.md`](https://github.com/octetproof/octet-verify): overview and what the verifier does and does not check.
- [`INTEGRATION.md`](https://github.com/octetproof/octet-verify/blob/main/INTEGRATION.md): full CLI reference, flags, and consumer guide.
- [`VERIFICATION-SPEC.md`](https://github.com/octetproof/octet-verify/blob/main/VERIFICATION-SPEC.md): the byte-exact signing contract every check implements.
