4 Commits
Author SHA1 Message Date
zeekay bae63692f0 the corporate name is Lux Industries Inc
Lux Partners Limited is the Isle of Man company and does not appear on a
public surface. Lux Industries Inc, a Delaware C corporation, is the one
these copyright lines, citations and terms name.

A bare "Lux Partners" is left alone unless the line says who owns
something — elsewhere it is the name of a site, not a party.
2026-08-28 16:43:59 -07:00
zeekayandHanzo Dev 3f540b7d8d screen: retry a publisher that blips, so one lost packet is not a lost day
The EU list answered "tls: received record with version 302 when expecting version
303" on one request, and the same host served the same 24.8 MB file correctly
seconds later — verified afterwards across three TLS configurations, all HTTP 200.
There was no retry, and the refresh runs daily, so that single lost packet cost a
full day of screening against a list 6,017 designations short.

Transport is retried three times with a growing wait. Nothing else is: a parse
failure is a disagreement about the schema and will fail identically on the next
attempt, so retrying it delays the refresh without changing the outcome. A
cancelled context stops immediately, because that is the deployment shutting down
rather than the publisher failing.

A publisher that is genuinely down is still reported, and the error names the
attempts made — an operator who cannot tell a blip from an outage cannot decide
whether to act. One publisher failing still does not abandon the others.

The download is a parameter rather than a call into the network, which is what
makes any of this testable: five tests cover the retried blip, the persistent
outage, the un-retried parse failure, the surviving sibling and the cancellation.
Eight mutants, all killed — including removing the retry, which is the defect.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 15:42:48 -07:00
zeekayandHanzo Dev 3c041ed331 merge the detection library and the screening rewrite into one engine
Two branches had independently rebuilt the same two packages, which is one
implementation too many. This resolves them into one.

Their side is the base for detection and screening, because it is better:

  - Rules name evidence, and SetRules refuses a set whose evidence this
    deployment cannot supply — so the eleven stub helpers that silently disabled
    thirteen rules cannot exist. A rule that cannot fire is worse than an absent
    one, because it still appears in the catalog a regulator reads.
  - One screening store, filled by the refresh and read by the endpoints. Two of
    them is why sanctions search answered "no match" for every name while
    designations were being loaded somewhere else.
  - Parsers for all four publishers against their real files, recovering what the
    single-parser path could not read at all: OFAC's digital-currency addresses,
    weak aliases held apart from screening candidates, and OFSI's row-per-name
    layout collapsed to subjects.
  - Name matching over runes, not bytes. Two distinct Han names scored 0.867
    byte-wise, above the 0.85 threshold, and 0.700 correctly.
  - Deviation from the median and MAD rather than the mean, because a monitoring
    baseline contains the outliers it is used to hunt.
  - A rule vocabulary with providers behind every term, in place of expressions
    reaching into a struct.

Kept from this side, because their branch did not have it:

  - The tenant comes from an authenticated identity, not from a request header.
  - A rule that could not run carries no weight. Their action() already capped a
    failing rule at review, which is right, but the hit still entered the score
    at full weight — so a broken rule saturated every transaction's score and the
    queue stopped ranking. Faults are now excluded from the aggregate while still
    raising their own alert and still forcing review.
  - Per-source readiness: which list, how many designations, how long ago, and
    the reason if the last attempt failed. Their store answers whether screening
    can be relied on at all; this answers what is behind it, which is what an
    operator can act on.
  - Behavioural scoring, retention, tokenisation, replay and reference data.

Conversion now refuses an unknown currency instead of passing the amount
through. Passing it through understates every currency worth more than a dollar,
so a payment three times over a reporting limit tested as sitting under it.

One ordering table for actions (types.ActionRank), one EvalAll signature, one
screening store, one readiness surface. 15 packages, tests and race clean.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 11:49:08 -07:00
zeekayandHanzo Dev 644d8be410 wire the engine to one screening store, and serve the catalog
The refresh filled a Base-backed sanctions store and the search endpoint queried
a separate empty in-memory one. Neither was reachable from the other, so
/v1/aml/sanctions/search returned no matches for every name, permanently, and
nothing in the system reported it. There is now one store, shared by the
endpoints and by the engine's screening term.

Screening refuses rather than reports clean. An unloaded or stale list set, a
list that parsed to zero entries, or a class the deployment cannot answer all
produce an error, which the engine turns into a review. Political exposure is
refused outright: there is no authoritative public list of politically exposed
persons and none of the four sanctions publishers is one, so answering from
sanctions data would report every customer as not politically exposed.

Each publisher is fetched and parsed by its own parser, and a failure for one
leaves the other three loaded rather than clearing them.

Transactions carry a direction and the customer's name and jurisdiction. The
previous ingestion path synthesised a customer from the identifier alone, so
every screening and jurisdiction rule evaluated against an empty name and an
empty country and could not fire whatever the engine did.

Case events check that the case belongs to the requesting organisation. A case
identifier is returned to whoever submitted the transaction, so possession of it
was authority to write on another tenant's investigation.

Health reports function rather than process: whether the lists are ready, which
loaded when, and how many terms this deployment can answer. Currency rates and
the higher-risk jurisdiction listing are loaded, never compiled in.

/v1/aml/catalog publishes the rules with their citations, the obligations claimed,
and the obligations outstanding.

Screening 31,338 real designations across the four published lists produced two
false positives, both a one-word list entry matching an unrelated three-part name
on a shared prefix. Raising the threshold could not separate them from genuine
transliterations, which score lower; the asymmetry could. A single-token name now
has to match a multi-token name near-exactly. Zero false positives after.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 11:17:45 -07:00