Skip to main content

Compliance & Trust

What this covers / who it's for. The platform's compliance machinery framed as product guarantees: what a client, an operator, a regulator, or an auditor can rely on the system to do — case handling, account freezes, regulatory filings, the tamper-evident audit trail, and the encrypted document vault. Enforcement is in code, not policy documents.

Compliance cases — nothing waits forever

Compliance work items (sanctions hits, transaction alerts, held deposits, onboarding reviews…) become cases with a lifecycle of openin_reviewescalatedclosed (compliance module).

The guarantee: a case cannot silently rot. An SLA engine scans every minute; a case that breaches its deadline is escalated automatically, one rung at a time, and re-scans of an already-escalated case are no-ops (idempotent — no notification storms). Deadlines are severity- tuned per case type: sanctions/watchlist hits get 1 hour, RFIs and outgoing-transfer (custody AML) screening 4 hours, re-KYC and crypto travel-rule 24 hours, transaction alerts 48 hours. Closing a case requires a recorded disposition with a written rationale.

Cases open automatically from upstream signals — but only ever open a case. When an upstream event carries a compliance signal (a deposit held for missing sender information, an RFI, and — once the crypto-custody provider is live — a custody transaction's AML-screening result), the platform opens or advances the matching case: a custody-AML deny opens an outgoing-transfer screening case, an alert opens a transaction-alert case, an allow opens nothing. By deliberate design these automated lanes only open a case for a human to triage — they never freeze the account or block the money movement (see the never-auto-freeze principle below); stopping money stays a separate operator decision. Ship-state: the custody-AML lane is wired but dormant until the crypto-custody provider goes live — no real custody traffic reaches it yet (20-custody.md).

Account freeze & closure — precise levers, precise effects

InterventionWhat the user experiencesReversible?
FreezeLogin still works; every action — including reads on the money surfaces — is refused with ACCOUNT_FROZEN (403). Incoming items park as held items.Yes — unfreeze releases held items.
ClosureTerminal. Login itself is refused.No.

Honest scope statement: V1 freezes the whole account only. Narrower scopes (a single asset, a single address) are designed for but deliberately rejected today (FREEZE_SCOPE_UNSUPPORTED) rather than half-enforced. The freeze gate is wired into the command bus itself — every money-moving command checks it — with a route-level backstop, so there is no code path around it. Freeze, unfreeze, and close are operator actions behind step-up MFA.

N-eyes custody approval quorum — the flagship security direction (engine + payout consumer built; real custody release still stubbed)

Ship-state: the consensus ENGINE is built and NOW LIVE for fiat payouts, but NOT yet wired to real custody money. The approval-quorum core (increment A), the adaptive posture/lock engine (increment B), and the officer override surface (increment C) all exist, and the fiat payout consumer (§8-E) is built — real fiat payouts move through this N-of-M consensus today. Still deliberately not-yet-live: the custody release step is a stub (nothing routes real custody funds through it yet), and the automatic posture trigger is dormant (no production risk signal raises the posture on its own yet — see below). So the engine is a live protection on fiat payouts but only assembled and provable in code for custody. The real custody release is a later increment. Tracked in 24-custody-approval-quorum.md and decision D97.

Moving crypto out of custody is the single highest-blast-radius action on the platform, so the control is a tenant-configurable, amount-tiered N-eyes consensus: a money movement must collect a required number of distinct approvers (e.g. 1 eye under $1k, 2 under $10k, 3 under $100k, 5 at or above), where the band is fixed by the value at the moment of initiation, before it is released. Every vote requires fresh step-up MFA and a recorded comment. The engine is deliberately one engine, two consumers, now realised — the same quorum primitive governs both custody transfers and the fiat payout maker/checker (§8-E: the payout's single checker was replaced by this N-of-M vote), so the whole platform speaks one auditable consensus language rather than accreting divergent per-flow systems. Custody is the first consumer (built now, against a stub release); the fiat payout consumer is built — real fiat payouts move through this consensus today (a payments forwarder runs the provider submit on quorum). Only the custody release is still stubbed.

What the engine enforces today:

  • Dual control by construction (on higher-value movements). The person who initiates a movement is a distinct authority from the people who approve it, and initiating never counts as approving — self-approval is refused outright at every tier. On a movement that requires two or more eyes, even where a tenant opts to let the initiator count as one eye they can never be the sole eye — one person cannot both open and satisfy such a movement.
  • A value-tiered approval requirement. The number of distinct approving eyes scales with the movement's value: a low-value movement (below the tenant's smallest tier — the default is under $1,000) clears on a single approving eye, while higher tiers escalate to two, three, or more. The originally-planned universal "at least two eyes on any movement" floor was deliberately softened (2026-07-11) so a $50 movement is not forced through a multi-approver quorum; the strong dual-control promise holds for the higher-value tiers, and the requirement is fully tenant-tunable (and can be re-raised by the adaptive posture below). The one always-on floor is a sanity minimum of one approving eye — the gate is never a no-op. Quorum counts distinct identities, so the control's strength is bounded by how carefully the tenant provisions and grants its approvers (a first-class caveat, not an unqualified promise).
  • Nothing dies silently. A request that isn't decided within its window (default 24 hours) returns to the maker to be re-submitted rather than vanishing (a tenant may opt into hard-stop expiry instead); an expired request can never later be pushed to quorum.

Built, but dormant in production — the adaptive posture. What makes this a living security stance rather than a static rule is now built and tested: a raised posture can lift the required eyes, force self-approval off, and lock a policy against being weakened — adding friction while business continues (it tightens, but never auto-freezes; stopping money stays the separate, deliberate account freeze above). Today a tenant admin drives it directly through the policy editor (and, once a policy is locked, can only tighten it — never weaken or unlock it). What is deliberately dormant in production is the automatic trigger: no risk-signal source is wired to raise the posture on its own yet, so it never auto-tightens today (the same "no subscribable upstream signal yet" stance as the custody-AML lane above). When a posture is raised, a compliance case opens automatically for a human to review — that decoupled hand-off is built.

The backoffice officer override — built (the only way to unlock). A platform compliance/security officer has a backoffice surface (behind step-up MFA, a mandatory recorded reason, and a dedicated operator permission) to override any tenant's posture directly: set the level, set the required-eyes floor to any value including lowering it, and lock or UNLOCK a policy — the deliberate, audited human act that neither the tenant admin (who can only tighten their own policy) nor the automated system (tighten-only) can perform. Like the automated tighten, an officer override only touches the approval posture — it never freezes the account (stopping money stays the separate account freeze above).

Still deferred (later increments): the real custody release that replaces the stub (the fiat payout consumer is already built — §8-E); and landing the policy-in-force as a first-class line on the tamper-evident audit chain (today the required-eyes count is recorded on the request and as a domain event, not yet on the hash chain).

Regulatory filings — separation of duties in code

  • SAR (suspicious activity reports). A three-officer ladder: draftin_reviewsubmitted, where the drafter, reviewer, and submitter must be three distinct people. Reviewing your own draft is refused (SELF_REVIEW_FORBIDDEN); submitting a report you drafted or reviewed is refused (SELF_SUBMIT_FORBIDDEN) — and an unattributable submission is refused outright rather than waved through.
  • CTR (currency transaction reports). Candidates are surfaced on demand — the system aggregates a customer's completed incoming volume per day, converts to USD, and flags anyone above the configured $10,000/day threshold; operators then record the filing (candidaterecorded).
  • Travel-rule records. Opened automatically when a deposit is held for missing sender information (Money movement); an operator attaches the encrypted originator/beneficiary detail to move it from pending_originator_info to recorded. The encrypted detail is never surfaced back through any API or export.
  • Filing is a handoff in V1. Stables prepares the evidence and durably records the filing; the actual submission to regulators is performed by the finance rail (Conduit). The seam for direct filing exists; the current implementation records the handoff reference.

The audit trail — every attempt, tamper-evident

The strongest guarantee on the platform (audit module):

  • Every command attempt becomes an audit record — including the ones that failed, were denied permission, or were rejected by a business rule. A denied action leaves the same durable trace as a successful one; the denial record survives even though the action itself was rolled back. ("Show me everything this user tried to do" is answerable.)
  • Records are immutable and hash-chained. The database refuses updates and deletes on the audit table outright, and each record is linked to the previous one by a cryptographic hash — altering or removing any record breaks the chain visibly. Signed checkpoints anchor the chain, and a verification job re-checks it hourly (enabled by default).
  • Daily export to write-once storage (WORM — storage that physically refuses modification) exists and is verified after export, but ships disabled until the storage target is provisioned — a deploy-time gate, called out per the wiki's ship-state honesty rule.

The document vault — our copy first, controlled exits

Sensitive documents (KYB/KYC evidence and the like) live in an encrypted vault (documents module):

  • Store-before-forward. Our own encrypted copy exists and is committed before anything is forwarded to the provider. A provider outage can delay forwarding — never lose the document. Forwarding failures that exhaust retries land on an operator triage register (transient ones are re-driven automatically, hourly).
  • Per-document encryption. Each document has its own encryption key (envelope encryption) — which is what makes per-document crypto-shred possible.
  • Downloads only via ~90-second signed URLs, and every access is logged — which document, when, and whether it succeeded. The signed URL itself is the credential (permission was checked once, when it was issued), so behind a session-less front end the log may not always resolve to a specific person — but there is still no unauthenticated or permanent link to document bytes.
  • Malware-scan quarantine is built (uploaded bytes are quarantined until scanned clean; rejected bytes are never served) but config-gated and ships disabled until a scanner is deployed. With scanning off, documents are admitted as clean.
  • Right to be forgotten = crypto-shred: the document's key is destroyed, leaving an unreadable tombstone. Shredding is refused inside the regulatory retention window (SHRED_BLOCKED_BY_RETENTION) — by default the 7-year compliance floor — so an erasure request can never delete evidence the law requires keeping.

Where the trust lives, in one line each

GuaranteeEnforced by
No case rotsPer-minute SLA scan, idempotent escalation
No unilateral money movementMaker/checker + co-sign (Money movement)
No self-approved filingsThree-officer SAR ladder, refused in code
Frozen means frozenThe command-bus freeze gate + route backstop
Every attempt is on the recordHash-chained, append-only audit trail, hourly verification
Documents can't leak quietlySigned-URL-only downloads, per-access logging
Erasure vs retention resolved honestlyCrypto-shred blocked inside the retention floor

← Product wiki index