Anubis Compliance Engine — Regulatory Review

Two-layer transaction gating for tokenized securities · statuses: COMPLIANT / REQUIRES_EDD / NON_COMPLIANT / INCOMPLETE — never “block”
loading…

0 · Architecture

The FactSet is built from the request payload plus three deterministic producers (OFAC screener, country-risk tier from the FATF list, travel-rule VASP registry). Layer 1 then evaluates every active rule as a pure function of the FactSet, emitting one per-rule signal. Layer 2 folds those signals into a single status using a fixed priority order. The pipeline is deterministic, fails closed on missing data, and carries no opaque scoring — every status is traceable to a rule id, a citation, and the facts that produced it.

request payload ─┐ ├─► FactSet ─► Layer 1 ─► Layer 2 ─► status fact producers ──┘ (per-rule (composer: COMPLIANT · OFAC screener signal, priority REQUIRES_EDD · country risk tier pure fn) fold) NON_COMPLIANT · travel-rule registry INCOMPLETE

Layer 1 is engine/rule_evaluator.py. Layer 2 is gating/composer.py. Producers run in engine/fact_builder.py.

1 · The gating logic (Layer 2 — the approvable function)

Every transaction is evaluated by all active rules (Layer 1, pure per-rule signals), then a single composition function folds the signals into one status:

  1. Split rule results into incomplete, triggered (no exception), triggered with exception, not triggered, not applicable.
  2. If any triggered rule carries NON_COMPLIANT (sanctions hit, prohibited asset) → status NON_COMPLIANT. Incomplete rules are listed as informational flags below the primary flag.
  3. Else if any rule could not be evaluated for missing data → status INCOMPLETE, one flag per rule naming the missing facts.
  4. Else if any triggered rule carries REQUIRES_EDD → status REQUIRES_EDD.
  5. Else → status COMPLIANT; exception-path rules are included as informational flags so the exception basis is visible.

Layer 1 is ternary. Every predicate resolves to TRUE, FALSE, or UNKNOWN (fact missing). Each rule emits one of five signals that the composer above consumes:

  • NOT_APPLICABLE — the rule's applies_when scope is FALSE for this transaction.
  • NOT_TRIGGERED — applicable, but the base condition is FALSE.
  • TRIGGERED_NO_EXCEPTIONS — base TRUE and no exception applies; the rule's status contribution stands.
  • TRIGGERED_WITH_EXCEPTION — base TRUE and a named exception is TRUE; the exception status applies and the exception name is recorded.
  • INCOMPLETE_DATA — a required fact is UNKNOWN; missing fact names are surfaced on the flag.

Fail-closed rule: UNKNOWN in a base condition ⇒ INCOMPLETE_DATA ⇒ INCOMPLETE status (the transaction cannot be approved on missing facts). UNKNOWN in an exception ⇒ the exception is rejected (a caller cannot unlock an exception path by omitting the fact that would prove it).

Source of truth: gating/composer.py::compose. The code below is fetched from the running engine at /v1/gating/source — it cannot drift from what executes.

loading…

2 · Active rule catalogue loading review tally…

Export CSV

Loaded live from /v1/rules?scope=transaction. Each rule is a versioned YAML file with full citation; the engine evaluates exactly these files. Use the buttons below each rule to record ADGM verdicts in real time.

3 · Fact producers

Before Layer 1 runs, engine/fact_builder.py augments the request payload with derived facts from three deterministic producers. For security-critical namespaces (sanctions.*, investor.country_risk_tier) the producer wins — a caller-supplied value that conflicts with the producer is overridden and logged, so an integrating platform cannot blind the screen. Loaded live from /v1/facts/producers.

  • loading…

4 · Sample decisions (computed live)

Each card POSTs its payload to /v1/check on this server when the page loads — results below are live engine output, not screenshots.

5 · Parked rules (informational — not evaluated) loading…

Of the total rule library, rules are evaluated per transaction; the remaining are parked (asset-scope, institution-onboarding scope, or pending citation verification). They live in rules/_parked/ and are listed live from /v1/rules?scope=parked; hover a reason for the full rationale.

Rule IDJurisdictionTitleWhy parked