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 OrisProtocolimport jsonfrom 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,}))import { OrisProtocol } from 'oris-sdk';import { writeFileSync } from 'node:fs';
const p = new OrisProtocol({ network: 'base-sepolia' });const pubkey = await p.verifier.getPubkey();writeFileSync('verifier_pubkey.json', JSON.stringify(pubkey));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_offlineimport json
cached = json.loads(Path("verifier_pubkey.json").read_text())
# verdict came from a stored payment recordok = verify_verdict_offline(verdict, cached["pubkey_hex"])assert okimport { verifyVerdictOffline } from 'oris-sdk';import { readFileSync } from 'node:fs';
const cached = JSON.parse(readFileSync('verifier_pubkey.json', 'utf8'));const ok = verifyVerdictOffline(verdict, cached.pubkeyHex);if (!ok) throw new Error('verdict signature invalid');Batch verification
For audit replay or compliance disclosure:
verdicts = load_verdicts_from_archive() # your storagebad = [v for v in verdicts if not verify_verdict_offline(v, cached["pubkey_hex"])]assert not bad, f"{len(bad)} verdicts failed offline verification"const verdicts = await loadVerdictsFromArchive();const bad = verdicts.filter(v => !verifyVerdictOffline(v, cached.pubkeyHex));if (bad.length) throw new Error(`${bad.length} 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_idfield 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
- L6 Verifier for the signing scheme.
- Bundle verification guide for the live path.
- Audit compliance for the batch validation use case.