ChainwatchCrypto—blockchain intelligence and investigative operationsAll systems operational
Technology

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.

Provider-independentVersioned modelsServer-authoritative
Six blockchain processing layers feeding one canonical data spine and its analytical outputs.

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.

Layers

Six layers, and the guarantee each one carries.

LayerWhat it isWhat it guarantees
Provider layerBlockchain data providers behind a single interfaceA provider outage degrades freshness, never correctness. Providers can be swapped without touching investigation logic.
NormalisationOne schema for addresses, transactions, transfers and assetsAnalysis code does not branch per chain, so a chain-specific bug cannot silently change a cross-chain result.
ClusteringPer-chain heuristicsEvery grouping records the heuristic that produced it and that heuristic's confidence.
AttributionEntity linksEnforced at the data model: no source, evidence and confidence means no assertion.
RiskDeterministic scoringModel versions retained, so any historical score can be recomputed with the model that produced it.
CaseworkCases, evidence, reportsReferences the graph rather than copying it, so a case cannot drift from the underlying data.
Provider independence

Why nothing is written against one vendor.

lib/blockchain/provider.ts
// 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.
Graceful degradation

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.

No vendor lock in the analysis

Adding a network means writing one provider implementation. It does not mean touching risk scoring, casework or reporting.

Financial state

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

Balances, deposits and withdrawals originate server-side only
Transaction status is read from the chain, never from a client
Every financial operation carries an idempotency key
Duplicate execution is prevented at the service, not in the UI
High-value operations require dual approval before execution

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.

Stack

What this is built with, and why it is stated.

AreaChoiceReason
DeliveryStatic HTML, CSS and vanilla JavaScript for the public siteNo build step is required to deploy or to audit what is served.
Application APIPHP 8 over MySQL or PostgreSQL, or a Node and TypeScript serviceBoth reference implementations ship with the same schema and contract.
DatabaseRelational, with foreign keys and indexes defined in db/schema.sqlFinancial and case state is relational because it needs constraints, not convenience.
SessionsServer-side sessions, HttpOnly and SameSite cookies, CSRF tokensNo authentication state is held in client-readable storage.
PasswordsArgon2id where available, bcrypt as fallbackNever reversible, never logged, never transmitted beyond the auth endpoint.
MFATOTP with recovery codesRecovery codes are single-use and stored hashed.
StorageEncrypted object storage for evidenceChecksums recorded at write; access is logged.

Read the API contract.

Endpoint shapes, authentication, scopes, rate limits and webhook signing are all documented.