Verify a bundle
Goal
Take a bundle id (or the canonical bytes) and verify it independently. No trust in the SDK or the original sender.
Prerequisites
- The bundle id or the canonical bytes hex. Both are returned by
payments.send(). - The signer pubkey (the agent’s Ed25519 public key from the dashboard).
Step 1: Fetch the bundle
from oris.protocol import OrisProtocol
p = OrisProtocol(network="base-sepolia")bundle = p.bundles.fetch(bundle_id="bnd_...")
print(bundle.canonical_bytes_hex)print(bundle.tx_intent_hex)print(bundle.signer_pubkey_hex)import { OrisProtocol } from 'oris-sdk';
const p = new OrisProtocol({ network: 'base-sepolia' });const bundle = await p.bundles.fetch({ bundleId: 'bnd_...' });console.log(bundle.canonicalBytesHex, bundle.txIntentHex, bundle.signerPubkeyHex);Step 2: Verify against the live verifier
verdict = p.verifier.verify( bundle_bytes_hex=bundle.canonical_bytes_hex, tx_intent_hex=bundle.tx_intent_hex, signer_pubkey_hex=bundle.signer_pubkey_hex,)
if not verdict.allow: raise RuntimeError(f"deny: {verdict.reason_code}")const verdict = await p.verifier.verify({ bundleBytesHex: bundle.canonicalBytesHex, txIntentHex: bundle.txIntentHex, signerPubkeyHex: bundle.signerPubkeyHex,});
if (!verdict.allow) throw new Error(`deny: ${verdict.reasonCode}`);Step 3: Confirm the verdict signature
The verdict is itself Ed25519-signed by the verifier. Cache the verifier pubkey and confirm the signature:
pubkey = p.verifier.get_pubkey()ok = p.verifier.verify_response_signature(verdict, pubkey.pubkey_hex)assert okconst pubkey = await p.verifier.getPubkey();if (!p.verifier.verifyResponseSignature(verdict, pubkey.pubkeyHex)) { throw new Error('verdict signature invalid');}Verification
The combination of (1) live verifier verdict + (2) verifier pubkey signature check guarantees the bundle was approved by the deployed verifier at evaluation time. Cache the pubkey aggressively; rotation events are rare and announced via webhook.
Troubleshooting
verdict.reason_code = SIGNATURE_INVALID— the bundle bytes were modified after signing. Re-fetch from the source.verdict.reason_code = NONCE_REPLAYED— the same nonce was submitted twice in the 30-second window. Generate a fresh nonce.verify_response_signaturereturns False — the cached pubkey is stale. Re-fetch and retry.
Where to go next
- Offline verification for the cache-only pattern.
- L6 Verifier for the verifier reference implementation.
- Verify API for the HTTP endpoint reference.