Private sanctions screening and KYC review for small and midsize teams. AML-Filter runs in the browser, explains every score, and keeps customer data on the device.
Try AML-Filter — no account or installation required.
- Screens names against OFAC SDN, EU, UN, and UK OFSI lists.
- Returns an Assay-computed numeric score with the signed component evidence behind it, not a vague match label.
- Onboards customers and keeps a local KYC review queue and audit history.
- Imports
.csv,.xls, and.xlsxcustomer files and exports.xlsxsnapshots. - Checks for a new signed watchlist at boot and every 30 minutes while the tab is open. A changed list is loaded and customers are re-screened; an unchanged list is a no-op.
- Stores private customer state in SQLite-WASM on OPFS. There is no application server or hosted customer database.
AML-Filter is a reference implementation, not legal advice or a replacement for a qualified compliance program. See Limitations.
You need Node 22.13, pnpm, and internet access for the first build. The first run downloads and verifies the 23 MB MiniLM embedding model so the browser does not fetch model weights from a runtime CDN.
git clone https://github.com/hseshadr/aml-filter
cd aml-filter/frontend
corepack enable
pnpm install
pnpm --filter aml-filter-app devOpen the URL printed by Vite. No backend, API key, database, or account is required.
- Open Screen and search for a name. Inspect the numeric score and per-signal evidence on each result.
- Open Customers and add one customer, or import a CSV/XLS/XLSX file.
- Open Review to resolve possible matches and record the decision.
- Open Settings to enable additional lists and see their versions and ages.
- Return to Customers to check for list updates or export an XLSX snapshot.
Customer imports, screening, review decisions, and exports all happen in the browser.
AML-Filter is four small, separately tested pieces:
| Lego | Responsibility |
|---|---|
@amlfilter/publisher |
Converts public source lists into signed, content-addressed static bundles. |
@amlfilter/browser |
Verifies bundles, embeds the query, retrieves candidates, and composes the Assay scorer. |
@amlfilter/workstation |
Owns customer records, review state, rescans, and the SQLite audit ledger. |
| React app | Composes the three capabilities into Screen, Customers, Review, and Settings pages. |
Public lists -> publisher -> signed static bundle
|
v
Customer name -> browser engine -> numeric score + evidence
|
v
workstation review ledger
There is no backend in that path. Package boundaries are typed contracts; infrastructure details stay behind adapters. See Architecture for the full DAG and failure model.
- Customer data stays local. Customer records and review history live in SQLite-WASM on the browser's Origin Private File System.
- Verify before parse. The engine verifies the signed
latestpointer, monotonic sequence, manifest, and every content-addressed chunk before using any list data. - Fail closed. A bad signature, hash, rollback, or incomplete update does not become an active list.
- Safe spreadsheet boundaries. Imports are validated and bounded; exports escape spreadsheet formulas.
- Auditable decisions. Score receipts seal Assay method
amlfilter.additive.v2, ordered component contributions, and input fingerprints—not customer text. The local review ledger is append-only during a customer's lifecycle. - Deletion is explicit. Deleting a customer removes that customer's matches and review history in the same SQLite transaction.
The hosted page still needs a network connection to open and to check for list updates. There is no service worker, so this is not presented as a fully offline website.
The model and sanctions lists are large enough to exhaust a mobile tab if they are loaded carelessly. The app therefore:
- serializes boot behind one shared promise;
- keeps one runtime owner instead of compiling duplicate ONNX sessions;
- uses one-list-at-a-time vector residency on mobile, unknown-memory devices, and desktops reporting 8 GB or less;
- stores compressed, content-addressed list chunks behind one durable-store contract: OPFS when it opens, with an IndexedDB compatibility adapter for affected WebKit;
- disposes the old engine before a reload, then builds and swaps the replacement;
- prevents overlapping update checks and clears recurring timers on unmount.
The supported baseline is the current and previous desktop Chrome, Edge, Firefox, and Safari 17+. Mobile Safari and Chrome use the bounded-memory path. Embedded WebViews are outside the release contract. Screening requires Workers, durable browser storage (OPFS or IndexedDB), WebCrypto, Web Locks, and a secure context. The KYC workstation additionally requires OPFS for its SQLite database.
Read Memory architecture for the ownership and disposal invariants.
Three machine-owned signals replace hand-written status claims:
- The CI badge reports the latest
checks on
main. aml-filter.com/build.jsonreports the exact commit deployed to the live site.pnpm gatereproduces the release gate locally.
cd frontend
pnpm gate
curl -fsSL https://aml-filter.com/build.jsonThe gate runs strict type checks, lint, unit and coverage suites, production builds, the recall and evaluation gates, translation checks, signed-bundle contracts, and the real-browser KYC, receipt, bundle, and mobile lanes, including iPhone-shaped WebKit cold boot and reload. Physical iPhone Safari remains a device-level check.
The same gate is also a portable Dagger Function. With Dagger 0.21.8 installed, run it from any supported host or export the production build without reproducing CI setup:
dagger check
dagger call build export --path=frontend/app/distThe module is deliberately thin: it composes Dagger's native directory, container,
and cache objects around the existing repository commands. The legacy CI lane remains
in shadow until the new required check has proven stable on main.
cd frontend
pnpm --filter aml-filter-app build
pnpm --filter aml-filter-app previewThe build stages the verified model and ONNX-WASM assets from local dependencies. The production browser does not download executable code or model weights from a third-party CDN. Deployment and rollback instructions are in Deploy.
Publisher adapters support the U.S. Treasury OFAC SDN list, EU Consolidated list, UN Consolidated list, and UK/OFSI consolidated list. The committed fallback demo bundle uses fictional entities; it is safe for tests and local demonstrations. Production bundles are generated from the public sources described in Watchlist format.
Candidate retrieval uses in-browser MiniLM embeddings. @edgeproc/assay@0.5.0-dev.3
then applies the deterministic five-signal additive policy to vector similarity,
sequence similarity, alias, date-of-birth, and country evidence. Each result includes
the ordered contributions and a stable input hash; the signed score receipt seals that
evidence. Frozen golden fixtures lock score and tier behavior. The recall
gate measures retrieval against the real OFAC corpus with named spelling probes and
fails below its published floors. See Recall.
aml-filter/
├── frontend/ pnpm workspace
│ ├── app/ React + Vite browser app
│ └── packages/
│ ├── amlfilter-browser/ verification, retrieval, scoring
│ ├── amlfilter-publisher/ source adapters and signed bundles
│ └── amlfilter-workstation/ SQLite KYC workflow
├── eval/ independent Python evaluation harness
└── docs/ architecture and operating guides
- Quickstart — first screening and KYC workflow
- Architecture — capability contracts and data flow
- Memory architecture — mobile memory ownership
- Watchlist format — signatures and bundle schema
- Recall — evaluation corpus, metrics, and floors
- Operations — publishing and incident procedures
- Deploy — build, release, rollback, and live proof
AML-Filter is an engineering reference implementation. It is not legal advice, not a certified regulatory-compliance product, and not a substitute for a qualified compliance program or commercial screening vendor. Sanctions decisions have real consequences. A qualified reviewer must confirm possible matches against official sources and own any required filings. The software is provided “as is,” without warranty. See NOTICE.
