One canonical graph. Everything else reads from it.
Ingestion, normalisation, clustering, attribution, risk and casework are separate services over one data model — which is what stops an alert, an investigation and a report from describing the same event three different ways.

How is the ChainwatchCrypto platform built?
The platform is built around one canonical transaction graph that every surface reads from, so a monitoring alert and a court-facing report describe the same event identically. Six layers sit above it, from blockchain data providers to presentation. No layer is written against a single vendor, and the frontend is never the source of truth.
Six layers, and the guarantee each one carries.
| Layer | What it is | What it guarantees |
|---|---|---|
| Provider layer | Blockchain data providers behind a single interface | A provider outage degrades freshness, never correctness. Providers can be swapped without touching investigation logic. |
| Normalisation | One schema for addresses, transactions, transfers and assets | Analysis code does not branch per chain, so a chain-specific bug cannot silently change a cross-chain result. |
| Clustering | Per-chain heuristics | Every grouping records the heuristic that produced it and that heuristic's confidence. |
| Attribution | Entity links | Enforced at the data model: no source, evidence and confidence means no assertion. |
| Risk | Deterministic scoring | Model versions retained, so any historical score can be recomputed with the model that produced it. |
| Casework | Cases, evidence, reports | References the graph rather than copying it, so a case cannot drift from the underlying data. |
Why nothing is written against one vendor.
// Every network implementation satisfies this interface. Investigation // code depends on the interface, never on a specific vendor SDK. export interface BlockchainProvider { network: NetworkId; getTransaction(hash: string): Promise<Transaction | null>; getAddress(addr: string): Promise<AddressSummary>; getTransfers(addr: string, page: Cursor): Promise<Page<Transfer>>; health(): Promise<ProviderHealth>; } // Degradation is explicit: a failed provider marks data stale rather // than returning a partial answer that looks complete.
If a provider fails, the affected network is marked stale in the interface and in any report generated during that window. A partial answer presented as a complete one is the most dangerous failure mode in this domain.
Adding a network means writing one provider implementation. It does not mean touching risk scoring, casework or reporting.
The frontend is never the source of truth.
This rule is absolute, and it is worth stating on a public page because it is the one most often broken.
Server-authoritative, always
What the browser can do
Render state it was given, and request an operation. Nothing else. A button in an admin console does not move value; it creates a request that then passes authorisation, risk checks, audit and an approval workflow before an execution service acts on it.
The portfolio surfaces in this build are labelled simulated and hold no real balances of any kind.
What this is built with, and why it is stated.
| Area | Choice | Reason |
|---|---|---|
| Delivery | Static HTML, CSS and vanilla JavaScript for the public site | No build step is required to deploy or to audit what is served. |
| Application API | PHP 8 over MySQL or PostgreSQL, or a Node and TypeScript service | Both reference implementations ship with the same schema and contract. |
| Database | Relational, with foreign keys and indexes defined in db/schema.sql | Financial and case state is relational because it needs constraints, not convenience. |
| Sessions | Server-side sessions, HttpOnly and SameSite cookies, CSRF tokens | No authentication state is held in client-readable storage. |
| Passwords | Argon2id where available, bcrypt as fallback | Never reversible, never logged, never transmitted beyond the auth endpoint. |
| MFA | TOTP with recovery codes | Recovery codes are single-use and stored hashed. |
| Storage | Encrypted object storage for evidence | Checksums recorded at write; access is logged. |
