Skip to main content

Merlon Demo Tour

This tour walks through the local demo stack using two golden paths: Path A for a compliance reviewer (7–10 minutes) and Path B for a technical evaluator (5–7 minutes). Every customer, company, and transaction mentioned below is synthetic — generated by merlon-demogen, never a real person, business, sanctions/PEP list entry, or transaction. See deploy/seed/demo/STORY_IDS.md in the repository for the full label/UUID mapping this tour links against.

Run this only on your own machine. The demo stack has authentication disabled and is meant for local evaluation — never expose it on a publicly reachable host.

Start the local stack

docker compose -f docker-compose.demo.yml up --build

No environment variables are required. This starts db and api bound to 127.0.0.1:8080 with authentication disabled and around 1,015 synthetic customers and 98 alerts already loaded.

Once docker compose reports both services healthy, open http://127.0.0.1:8080. Anything you do while following this tour (case notes, status changes, an STR draft) persists until you tear the stack down.

Reset the dataset

docker compose -f docker-compose.demo.yml down -v
docker compose -f docker-compose.demo.yml up --build

down -v drops the demo Postgres volume, so the next up reloads the same deterministic synthetic dataset from scratch.

Path A — compliance reviewer (7–10 minutes)

  1. Dashboard (/) — start on the overview: customer/alert/case counts, the risk-tier distribution, and the severity-distribution chart.
  2. Alerts (/alerts) — find the one alert badged Critical. It belongs to demo-story-04 (Meridian Cross Trading Pte. Ltd.), or jump to it directly: /alerts/38d7a6ce-c160-5cf3-b748-ce2650893ff3.
  3. Alert detail — note the scenario_id (tm_rapid_movement, linked to the Rules view) and the description, then open one of the three related transaction badges — for example the inbound leg that starts the pass-through: /transactions/b3dbf56d-8e2a-5f1c-86d2-ddf35ce38bfd — a ¥3.2M inbound transfer from Hong Kong, followed within six hours by outbound transfers to Singapore and Malaysia that the alert's rapid-movement window flags together.
  4. Customer detail (/customers/61a626c6-ced4-536d-be74-41d6ca874e4d) — review the CDD score factor breakdown, then click Score to trigger a live re-score through the native engine and watch a new row land in the score history table with the same rule set and tier (Auditability First: the same input reproduces the same output).
  5. Case — open the case linked from the alert (/cases/3a55610e-d00f-5a34-8bfa-cc9753cbfa06), add a short note, and move it along a status transition. This is the write-experience step — the note and status change are yours, not pre-seeded.
  6. Reports (/reports) — pick the demo-story-04 alert from the list (its critical severity qualifies it) and generate an STR draft, then download the CSV or JSON export. The draft is generated for this workflow; the write request itself is what is recorded in the audit trail.
  7. Audit (/audit) — confirm that the score, case-note request, status change, and STR-draft request you just performed are present, attributed, and timestamped. This is the Auditability First principle closing the loop: every write action is recorded by the API's common audit middleware, not just the seeded history.

Path B — technical evaluator (5–7 minutes)

  1. Rules (/rules) — filter by type TM_SCENARIO, open rapid_movement (recorded on the Path A alert as scenario_id: tm_rapid_movement) and inspect its full definition as JSON, then use Export to download it as JSON or YAML. Rules are plain configuration, never hardcoded logic.

  2. Re-score a customer — from any customer detail page, click Score and confirm the new customer_score_history row records the rule set ID and version it was evaluated against.

  3. Backtest (/backtest) — enter rapid_movement as the candidate rule set, select a handful of customers, and run it. This replays the candidate scenario through the native engine against historical transactions without touching live alerts.

  4. Batch (/batch) — select a small customer subset and run a score or monitor batch; watch the dashboard counts change afterward.

  5. Finish on Audit (/audit) and System (/system) to see the backtest and batch runs recorded and the reported version/feature flags/component health, then fetch the live OpenAPI contract:

    curl http://127.0.0.1:8080/api/v1/openapi.json

More synthetic stories to explore

demo-story-04 is only one of six seeded stories (structuring, a mule account, a high-risk-country transfer, the rapid-movement pass-through above, dormant-account reactivation, and a compound case), plus screening hits against synthetic sanctions/PEP lists. Every fixed ID — customers, alerts, cases, and transaction ranges — is listed in deploy/seed/demo/STORY_IDS.md in the repository if you want to follow one of the others the same way.

Running without Docker

Prefer not to use Docker? Build once, generate the same synthetic dataset, then run the API in-memory with the same rule content and UI the demo compose stack uses:

make build # builds the Go API and ui/dist
make demogen # writes deploy/seed/demo/*.json

cd api
MERLON_SEED=true \
MERLON_AUTH_ENABLED=false \
MERLON_UI_DIR=../ui/dist \
MERLON_DEMO_DATA_DIR=../deploy/seed/demo \
MERLON_CDD_WEIGHTS_PATH=../content/_sample/cdd_weights/funds_transfer.yaml \
MERLON_TM_SCENARIOS_PATH=../content/_sample/tm_scenarios \
MERLON_COUNTRY_RISK_PATH=../content/_sample/country_risk_sample.yaml \
MERLON_SCREENING_LISTS_PATH=../deploy/seed/demo/screening_lists \
go run ./cmd/merlon-api

Open http://localhost:8080 — the same UI, the same 1,015 customers and 98 alerts, and the same native engine (rule content is the four MERLON_*_PATH variables above), backed by the in-memory store instead of PostgreSQL. Without all four rule-content variables, scoring and monitoring fall back to a disabled engine, so Path A step 4 and all of Path B would not work — set every one of them. There is no volume to drop: since this uses the in-memory store, stopping the process and running go run ./cmd/merlon-api again resets it to the freshly seeded dataset.