Skip to content
Oris Docs

Offline verification

Goal

Validate compliance bundles in environments without internet egress, or batch-validate thousands without hitting verify/bundle each time.

How it works

The L6 verifier signs every verdict with Ed25519. Cache the verifier pubkey once. Then validate the verdict signature locally. No further network calls needed.

Step 1: Cache the pubkey

from oris.protocol import OrisProtocol
import json
from pathlib import Path
p = OrisProtocol(network="base-sepolia")
pubkey = p.verifier.get_pubkey()
Path("verifier_pubkey.json").write_text(json.dumps({
"pubkey_hex": pubkey.pubkey_hex,
"fetched_at": pubkey.fetched_at,
"key_id": pubkey.key_id,
}))

The pubkey rotates only on key rotation events. Subscribe to the verifier.key_rotated webhook to refresh the cache when it happens.

Step 2: Validate a verdict locally

The verdict object contains everything needed: allow, reason_code, bundle_hash, verifier_signature. The signature covers (allow, reason_code, bundle_hash).

from oris.protocol import verify_verdict_offline
import json
cached = json.loads(Path("verifier_pubkey.json").read_text())
# verdict came from a stored payment record
ok = verify_verdict_offline(verdict, cached["pubkey_hex"])
assert ok

Batch verification

For audit replay or compliance disclosure:

verdicts = load_verdicts_from_archive() # your storage
bad = [v for v in verdicts if not verify_verdict_offline(v, cached["pubkey_hex"])]
assert not bad, f"{len(bad)} verdicts failed offline verification"

Troubleshooting

  • Signature check fails after a rotation — your cached pubkey is from before the rotation. Re-fetch and retry. The key_id field on the verdict matches the active key.
  • Bundle hash mismatch — the bundle was re-encoded with different canonical bytes. Use the canonical bytes from the original signing time.

Where to go next