Regulator portal
Goal
Set up regulator-facing access. JWT-scoped read endpoints, HMAC-signed webhooks for SAR-flagged events, threshold decryption for the sealed envelope.
Prerequisites
- Regulator portal credentials issued by Oris.
- The regulator’s AGE public key (one of N quorum keys).
- A webhook endpoint capable of HMAC-SHA256 verification.
Step 1: Mint a scoped JWT
The regulator portal mints JWTs that are scoped to specific tenants and specific actions. The scope is enforced by the audit API.
POST https://api.useoris.xyz/v1/regulator/jwt{ "tenant_id": "tn_...", "scope": ["audit.read", "envelope.unseal", "webhook.subscribe"], "expires_in": 3600}The response carries a signed JWT. Send it as Authorization: Bearer <jwt> on subsequent requests.
Step 2: Subscribe to SAR webhooks
POST https://api.useoris.xyz/v1/audit/subscriptions{ "endpoint": "https://regulator.example.gov/oris-webhook", "events": ["sar.flagged", "revocation.tier1"], "secret": "shared_hmac_secret"}Every dispatch carries:
X-Oris-Signature: hmac_sha256(secret, timestamp + body)X-Oris-Timestamp: <unix-epoch>Five retries with exponential backoff (1m, 5m, 15m, 60m, 240m).
Step 3: Unseal an envelope
Sealed envelopes use AGE threshold encryption. The regulator quorum (typically 3 of 5) signs an unseal request.
# Each regulator member signs the unseal request offlinequorum_signature = quorum_sign(envelope_id, my_age_secret)
# Submit signatures from at least the thresholdplaintext = client.audit.unseal( envelope_id="env_...", quorum_signatures=[sig1, sig2, sig3],)
print(plaintext.bundle)print(plaintext.kyc_artifacts)print(plaintext.escalation_approvals)const plaintext = await client.audit.unseal({ envelopeId: 'env_...', quorumSignatures: [sig1, sig2, sig3],});
console.log(plaintext.bundle, plaintext.kycArtifacts, plaintext.escalationApprovals);The plaintext returns only with a valid quorum. Oris cannot read the envelope.
Verification
- JWT scope check: an out-of-scope call returns
403. - Webhook signature: every dispatch verifies HMAC-SHA256 against
(timestamp + body). - Unseal: requires at least the quorum threshold of valid signatures.
Troubleshooting
- 403 on audit fetch — the JWT scope does not include
audit.read. Re-mint with the correct scope. - Webhook signature mismatch — verify the shared secret matches and that timestamp is included in the signed payload.
- Unseal fails despite threshold — at least one signature is from a key not in the quorum. Check
key_idon each signature.
Where to go next
- L7 Audit for the disclosure architecture.
- Sealed envelope for the encryption model.
- Audit trail for retention and SAR rules.