Skip to content
Oris Docs

L7 Audit and disclosure

L7 LIVE on Base Sepolia + api.useoris.xyz

L7 is the regulator-facing surface. Every signed bundle, every policy evaluation, every key mutation, every state change lands here. The log is append-only, indexed for the regulator, and anchored hourly on Base. The retention horizon is seven years (BSA + AMLD).

What it does

For every signed action in the system, L7:

  1. Writes one row to oris_audit_log with tenant scoping (RLS enforced).
  2. Computes a hash chain link to the previous row.
  3. Encrypts the unredacted disclosure into a sealed envelope (AGE threshold).
  4. Auto-flags rows that match SAR rules.
  5. Hourly: computes a Merkle root over the new entries and anchors it on chain.
  6. Fires HMAC-SHA256 webhooks to subscribed regulator portals.

The row references stay in TimescaleDB. The unredacted contents stay in the sealed envelope. Oris cannot read the envelope without the regulator quorum.

Schema

oris_audit_log
id uuid PK
tenant_id uuid (RLS)
bundle_id_keccak bytes32
bundle_id_sha256 bytes32
agent_did string
action enum // payment.completed, payment.blocked,
// policy.updated, key.rotated, kya.promoted,
// kya.demoted, revocation.added, ...
verdict enum // allow / deny / escalated
reason_code string
sar_flagged bool
sealed_envelope_id uuid // pointer to encrypted disclosure
hash_chain_prev bytes32
hash_chain_curr bytes32 // sha256(prev || row_canonical)
created_at timestamptz
retention_until timestamptz // 7 years from created_at

Twelve fields, row-level security enforced by tenant_id. The hash chain is a SHA-256 link, scoped per tenant.

Hash chain integrity

Every entry includes hash_chain_prev and hash_chain_curr. Tampering with any row breaks the next row’s link. A verify endpoint walks the chain and returns:

{
"valid": true,
"chain_length": 4821,
"anchor_matches": true,
"last_anchor_block": 42019099
}

anchor_matches confirms the chain head matches the hourly Merkle anchor on chain.

On-chain anchor

OrisAuditLogRegistry Base Sepolia
0xAde6DC06178904194FaE72CC83C6d2ec65Ed34c8

Every hour Oris computes a Merkle root over the new entries and writes it to the registry. The contract is pausable and append-only. Even a privileged operator with full database access cannot rewrite history. The on-chain anchor catches them.

Sealed envelope

The envelope is AGE-encrypted with the regulator quorum’s public keys (typically 3-of-5 threshold). Inside:

  • the full unredacted bundle,
  • KYC artifacts that back the agent identity,
  • operator signing identity that approved the policy,
  • escalation approvals fired during the transaction.

Sealing happens at L4 bundle assembly time. The envelope rides into L7 audit log as a pointer. Oris stores the ciphertext but cannot read it. See sealed envelope for the disclosure flow.

SAR auto-flag

Rows that match any of these conditions get sar_flagged = true:

  • risk_tier = High or risk_tier = Blocked
  • sanctions_clean = false
  • amount_usd_e6 >= 10_000_000_000 (ten thousand USD threshold)
  • Tier 1 revocation event for the agent within the last 24 hours

Flagged rows roll up to the regulator review queue accessible through the regulator portal.

Webhook subscriptions

Regulator portals can subscribe to flagged events via webhook:

POST /v1/audit/subscriptions
{
"endpoint": "https://regulator.example.gov/oris-webhook",
"events": ["sar.flagged", "revocation.tier1"],
"secret": "shared_hmac_secret"
}

Every dispatch is HMAC-SHA256 signed with the shared secret + the request timestamp. Retries: 5 attempts with exponential backoff (1m, 5m, 15m, 60m, 240m). Webhook payload includes the bundle ids and the audit row id, never the unredacted contents.

Retention

Seven years from created_at. After the retention window, a daily sweeper deletes the row and its sealed envelope. The on-chain anchor remains (it is just a Merkle root, no PII).

SDK example

from oris import OrisClient
client = OrisClient(...)
# Verify the hash chain integrity
status = client.audit.verify(developer_id="dev_...")
assert status.valid is True
assert status.anchor_matches is True
# Query audit trail
rows = client.audit.list(
agent_id=agent.id,
action="payment.completed",
start_date="2026-05-01",
limit=50,
)
for row in rows:
print(row.id, row.action, row.verdict, row.sar_flagged)

Deploy state

Contract live on Base Sepolia. The layer passed its security audit with zero critical and zero important findings open.

Where to go next