L5 Revocation
L5 is the revocation ledger. Every agent and every counterparty can be revoked. The tree proves a party is not revoked at a specific timestamp. Without that proof, the bundle cannot ship.
Two tiers, two cadences. Immediate revocations flush every second so a sanctions hit reaches the network within one Tier 1 batch. Behavioral drift flushes every five minutes since it tolerates slower propagation.
What it does
For every party referenced by a Compliance Bundle (agent, counterparty), L5 returns:
- the active Merkle root,
- a non-membership proof showing the party is not revoked,
- the tree size at the time of proof generation.
A verifier checks the non-membership proof against the recorded root. If the proof verifies, the party was not revoked at bundle time.
Two-tier model
| Tier | Trigger | Flush | Use case |
|---|---|---|---|
| Tier 1 | OFAC hit, fraud confirmation, manual block | every 1 s | Sanctions, immediate freeze |
| Tier 2 | Behavioral drift, KYA demotion, support flag | every 5 min | Suspected misuse, soft action |
The bundle includes the active tier. A verifier checks the proof against the corresponding root. Tier 1 and Tier 2 use the same SMT structure but separate roots.
Tree shape
- Sparse Merkle Tree
- Depth 256 (supports 2^256 leaves, effectively unbounded)
- Keccak256 hash function (v1, matches L2 for cross-layer Merkle parity)
- Non-membership proofs O(log n) in size, around 1 KB at current tree size
The migration to Lean IMT and Poseidon-2 is queued behind the L4 ZK boundary in v2.
On-chain commitment
OrisL5RevocationRegistry Base SepoliaEach tier maintains its own root. Roots commit on chain on flush. The contract enforces monotonic versioning. Replay against a stale root fails because the verifier requires root.version >= bundle.revocation_witness.root.version.
What flows into the bundle
L5 contributes the revocation_witness block:
revocation_witness: { tier: u8 // 1 or 2 root: bytes32 // tree root at proof time not_present_proof: bytes // SMT non-membership proof tree_size: u64}The witness is generated in 2 ms or less. The proof is verified against the recorded root in 5 ms or less.
Revocation lifecycle
1. Trigger - OFAC list update → Tier 1 - Veris drift threshold → Tier 2 - Operator manual block → either tier │ ▼2. Revocation entry added to oris_revocation_entries │ ▼3. Builder collects entries for the current flush cadence │ ▼4. New SMT root computed + signed by Oris MPC ring │ ▼5. Root committed to OrisL5RevocationRegistry on Base │ ▼6. Next bundle assembly reads the new rootTotal time from trigger to active block: under one second for Tier 1, under five minutes for Tier 2.
SDK example
from oris import OrisClient
client = OrisClient(...)
# Revoke an agent (operator action)client.revocations.create( subject_did="did:ethr:84532:0x...", tier=1, reason="ofac_sdn_hit",)
# Fetch the current root + non-membership proofwitness = client.revocations.witness( subject_did=agent.did, tier=1,)
print(witness.root)print(witness.not_present_proof_len)print(witness.tree_size)import { OrisClient } from 'oris-sdk';
const client = new OrisClient({ ... });
await client.revocations.create({ subjectDid: 'did:ethr:84532:0x...', tier: 1, reason: 'ofac_sdn_hit',});
const witness = await client.revocations.witness({ subjectDid: agent.did, tier: 1,});
console.log(witness.root);console.log(witness.notPresentProofLen);console.log(witness.treeSize);Deploy state
Per-second cadence proven on Base Sepolia. The layer passed its full security audit.
Where to go next
- L6 Verifier for how the verifier consumes the non-membership proof.
- L4 Compliance Bundle for how the witness sits in the bundle payload.
- Incident response operations for the operator playbook on revocation.