System architecture
DigiFIR is a three-tier system. Mobile and web clients sit at the front. A common backend relays signed actions and serves read views. A permissioned Hyperledger Fabric network, run jointly by the member institutions, decides what is valid and keeps the record. Outside government systems connect to the backend through adapters.
System context
Components
Clients
| Client | Technology | Users |
|---|---|---|
| Citizen app | Flutter, Android and iPhone, ten languages | Citizens |
| Citizen web portal | React 19 and TypeScript, built with Vite | Citizens without the app |
| Police app | Flutter, English and Hindi | Duty officers, SHOs, investigating officers, malkhana in-charges, registrars |
| Court app | Flutter, English and Hindi, phone only | Magistrates, the judiciary's registrar |
| Lab app | Flutter, English and Hindi | Laboratory reception, analysts, the laboratories' registrar |
| Officials' console | React web app, signed in by linking the official's phone | Auditors, court staff, High Court registry, magistrates, registrars |
| Public verification | A page of the portal | Anyone |
Each app keeps its own screens and legal flows. Underneath, every app uses the DigiFIR SDK (Dart for the Flutter apps, TypeScript for the portal and console). The SDK builds and signs actions, keeps an encrypted offline outbox, encrypts files and follows the event stream.
Backend
The backend is a modular monolith in TypeScript on Node 22: one codebase and container image, three processes.
| Process | What it does | Instances |
|---|---|---|
api | REST and server-sent events on Fastify 5. Validates every request against the OpenAPI contract, relays signed actions and signed reads, presigns file uploads, serves documents and public verification. | Any number; stateless |
projector | Reads full blocks from a peer, verifies the hash chain and orderer signatures, and writes read models, explorer tables, stream events and notification rows in one database transaction per block. | Exactly one active per database, plus a standby |
worker | Sends notifications, runs statutory clocks, renders documents, runs adapters to outside systems, scores duplicate hints, reconciles with CCTNS. | Any number; jobs queued on Redis |
Data stores:
| Store | Holds |
|---|---|
| PostgreSQL 16 | Read models rebuilt from the ledger; operational data such as sessions, rosters and notifications; the identity vault; the hash-chained audit log |
| Redis 7 | Rate limits, job queues, event fan-out, the set of revoked sessions |
| S3-compatible object storage | Encrypted evidence and attachment files named by their hash, encrypted documents, audit exports. SeaweedFS in development; Ceph RGW or NIC object storage in the pilot |
| HSM (PKCS#11) | The gateway identity keys and the vault's key-encryption key, one token per institution |
An institution can also run the same image in verifier mode (DIGIFIR_MODE=verifier) against its own peer. That runs only the projector and the explorer, with no write path, so the institution can check the ledger without trusting whoever runs the main backend.
Fabric network
Four member institutions share one channel, firnational: the police of State A, the police of State B, the judiciary and the forensic laboratories. A national ordering service with three Raft orderers orders transactions. Each institution runs its own certificate authority, its own peers with CouchDB, and its own chaincode servers. Network and nodes has the full topology.
Four chaincodes, written in Go, hold the rules:
| Chaincode | Holds |
|---|---|
identity-registry | Institutions, stations, courts, laboratories, officials' keys and roles, citizen DIDs, on-ledger configuration |
fir-registry | Every complaint and FIR from filing to disposal, the case diary, statements, arrests, duplicate hints, documents for verification |
evidence-custody | Exhibits, two-party custody hops, laboratory parcels and sealed reports |
judicial-actions | Court decisions and orders, applied to the case through fir-registry |
Government systems
Each outside system sits behind an adapter in the worker with a simulator that uses the real request and response shapes. Configuration switches an adapter between simulator and live. Adapters are asynchronous with retry and back-off, so an outside outage never blocks a ledger transaction. External systems lists them.
Trust boundary
Everything above the Fabric network is there for convenience and speed. The backend holds no person's key, and every chaincode function demands a person's signature. If the backend were compromised:
| It could | It could not |
|---|---|
| Delay or withhold an action. The person would notice: they hold the transaction ID, or see that they have none. | Create, change or backdate a ledger record. |
| Serve stale read views. Every page of a read view carries the block it is current to, and a receipt can be checked against another institution's peer. | Register an official. Registrars sign registrations on their own phones. |
| Read private values that pass through it while it is compromised. | Decrypt evidence files. Their keys are in private data collections and the backend never stores them. |
Two checks guard every write:
- Gate 1 asks who is acting. The chaincode verifies the signature on the action, loads the signer's role and jurisdiction from
identity-registry, and refuses replays and actions dated too far in the past or future. - Gate 2 asks who agrees. Fabric's endorsement policies require peers of specific institutions to sign the result. A new FIR, for example, needs the filing state's police and the judiciary.
Architecture decisions
The project records its binding choices as architecture decision records (ADRs). The main ones:
| ADR | Decision |
|---|---|
| ADR-01 | Four organisations (two state police, judiciary, forensics), with no separate regulator organisation; one national channel with per-state private data; the citizen files on the ledger and only the police register; duplicate hints never block; CCTNS stays the legal register in the pilot |
| ADR-03 | Fabric 2.5 LTS with Raft now; configuration prepared for SmartBFT on Fabric 3.x once each institution runs its own orderer |
| ADR-04 | Channel-wide decisions need the judiciary and a majority of the other members |
| ADR-05 | Four Go chaincodes from one module, deployed as chaincode as a service; no deletes, deterministic code, one event per transaction, forward-only state machines |
| ADR-06 | Gate 1 (signed action) and Gate 2 (endorsement) on every write |
| ADR-07 | Every person signs with an ECDSA P-256 key made on their own device; the backend relays through gateway identities that have no authority |
| ADR-08 | Aadhaar is never mandatory and never stored; DigiLocker or Meri Pehchaan in the pilot, with in-person verification at a station as the fallback |
| ADR-09 | Officials' phones are enrolled in person by a registrar; the SIM check is only a risk signal |
| ADR-10 | TypeScript on Node 22, Fastify 5, PostgreSQL 16, Redis 7, S3-compatible storage, fabric-gateway |
| ADR-11 | Writes go through the signed-action relay; shared data is read from read models; private data is read with signed read requests |
| ADR-12 | Private data per state; erasure purges private values and keeps the hashes |
| ADR-13 | Files encrypted on the device with AES-256-GCM, stored as ciphertext, hashes on the ledger |
| ADR-14 | A purpose-built ledger explorer and verifier, since Hyperledger Explorer is archived and FireFly signs with server-held keys |
| ADR-15 | Two tiers of duplicate hints: identical file hashes in the chaincode, attested text similarity off the chain |
| ADR-17 | Every outside system behind an adapter with a simulator and fault injection |
| ADR-18 | RFC 8785 canonical JSON, Unicode NFC, SHA-256, one set of test vectors across Go, TypeScript and Dart |
| ADR-20 | Docker Compose for development; Kubernetes with hlf-operator and Helm, hosted in India, for the pilot |
| ADR-21 | Officials use a read-mostly desktop console that they sign in to by approving on their phone |
Adding more states
The design adds a state without redesign. A new state police organisation brings its own certificate authority, two peers and two private data collections, and joins through a channel configuration update that rewrites the governance policies to include it. The procedure was rehearsed on the development network with a third state; Adding a State walks through it.
Other properties that help replication:
- One OpenAPI contract serves every app, so a state can build its own client against it.
- Reference data (districts, stations, courts, laboratories, legal sections) lives on the ledger and in the backend directory, not in app code.
- Configuration such as duplicate-hint thresholds and designations is versioned on the ledger and changed by registrars of three institutions.
- Outside systems are reached through adapters, so a state with different CCTNS or laboratory systems swaps the adapter, not the core.
Sources: digifir-backend docs/architecture/ARCHITECTURE.md, DECISIONS.md, BACKEND.md sections 1 and 15.6; digifir-network README.md.