Network and nodes
The DigiFIR network is a permissioned Hyperledger Fabric 2.5 LTS network. Each member institution runs its own nodes and certificate authority, and all of them share one channel. Only identities issued by a member's certificate authority can take part, and every connection uses mutual TLS.
Topology
Each organisation also runs one Fabric CA server and one chaincode server per chaincode version. In the development network a tools container holds every command-line tool, so the host needs nothing but Docker.
Organisations
| Organisation | MSP ID | Nodes (development) | Pilot additions | Certificate authority |
|---|---|---|---|---|
| National ordering service (NBF / NIC) | OrdererMSP | orderer1, orderer2, orderer3 | The three orderers in separate failure domains | ca.orderer.digifir.local |
| State A police | PoliceStateAMSP | peer0 (anchor, endorsing), peer1 (replica) | Node keys in an HSM | ca.police-a.digifir.local |
| State B police | PoliceStateBMSP | peer0 (anchor, endorsing), peer1 (replica) | Node keys in an HSM | ca.police-b.digifir.local |
| Judiciary (High Court / eCommittee) | JudiciaryMSP | peer0 (anchor) | peer1 for availability | ca.judiciary.digifir.local |
| Forensic science laboratories | ForensicsMSP | peer0 (anchor) | peer1 for availability | ca.forensics.digifir.local |
In the reference data, State A is Rajasthan (state code RJ, collection tag A) and State B is Delhi (DL, tag B). There are no peers at police stations: officers work from phones, and the apps queue actions offline.
Channel
| Setting | Value |
|---|---|
| Channel name | firnational, created with the channel participation API (no system channel) |
| Capabilities | Channel V2_0, orderer V2_0, application V2_5, which PurgePrivateData needs |
| Consensus | etcdraft with 3 consenters; tick interval 500 ms, election tick 10, heartbeat tick 1 |
| Block cutting | A block every 2 seconds or every 10 transactions, whichever comes first; at most 4 MB, preferred 512 KB |
| State database | CouchDB 3.4.3, one per peer |
| Private data | Purge interval 1 on every peer, so a purged value leaves the private data history at the next block; transient store kept for 100 blocks |
| Chaincode | Chaincode as a service; peers have no Docker socket |
One channel for the whole country was chosen over one channel per state, because separate channels would hide a duplicate complaint filed in two states. Private data collections give each state its own confidential data on the shared channel.
Policies
The channel's governance rule is that channel-wide decisions need the judiciary and a majority of the other members. With the four founding members, that means the judiciary and two of the other three.
| Policy | Rule |
|---|---|
Application Admins | AND('JudiciaryMSP.admin', OutOf(2, the admins of the other three)) |
LifecycleEndorsement (chaincode definitions) | AND('JudiciaryMSP.peer', OutOf(2, the peers of the other three)) |
Endorsement (default) | The same rule with .peer principals |
Channel and orderer Admins | MAJORITY |
Readers, Writers (channel) | ANY |
| Each organisation | Readers: admin, peer or client. Writers: admin or client. Admins: admin. Endorsement: peer |
BlockValidation | OR('OrdererMSP.orderer'), so only an orderer node can sign a block |
Naming roles (.admin, .peer) in these policies keeps client identities, such as the backend's gateway keys, out of chaincode definitions. A network security review found that a looser policy let three gateway keys commit a forged chaincode definition. The fix was this rule, and a checker with 61 rules verifies the live channel configuration against it (network.sh checkChannel).
Each chaincode then sets its own endorsement policy:
| Chaincode | Endorsement policy |
|---|---|
identity-registry | Any two of the four organisations' peers |
fir-registry | The judiciary and one state police organisation |
evidence-custody | The forensic laboratories and one state police organisation |
judicial-actions | The judiciary and one state police organisation |
Records inside the chaincodes carry stricter key-level policies. A case, for example, can only be changed with the agreement of its own state's police and the judiciary. Chaincode overview explains them.
Certificate authorities and crypto
- Every organisation runs Fabric CA 1.5.22 with two CAs on one server:
ca-<org>issues enrolment certificates, andtlsca-<org>issues TLS certificates. A TLS key can therefore never sign a transaction. - No
cryptogenis used. All node, admin, user and service certificates come from Fabric CA. - NodeOUs classify every identity as
admin,client,peerororderer. - Identities are ECDSA P-256 only.
- Attributes for attribute-based access control (
digifir.role,state,district,station) are written into enrolment certificates. Identity and access explains how the chaincodes use them. - Node and user certificates are valid for one year and are renewed 30 days before expiry. CA certificates are valid for 15 years.
- In the pilot, each organisation's CA and node signing keys live in an HSM through PKCS#11. The published Fabric images lack PKCS#11 support, so the project rebuilds the peer and orderer images from the pinned Fabric source. TLS keys stay in files, because Fabric reads TLS keys only from files.
Software versions
| Component | Version | Notes |
|---|---|---|
| Fabric peer, orderer, tools | 2.5.16 | Pinned by image digest. Fabric 3.1.5 also passed the smoke test. |
| Fabric CA | 1.5.22 | Pinned by digest |
| CouchDB | 3.4.3 | Pinned by digest |
| Chaincode | Go 1.27.3, fabric-contract-api-go v2 | Built offline from vendored modules |
| Gateway client (backend) | @hyperledger/fabric-gateway 1.13.0 |
The configuration is prepared for a later move to Fabric 3.x with SmartBFT consensus: channel participation API, per-organisation orderer endpoints, P-256 identities only, and no Fabric 3.1-only chaincode APIs. That move makes sense once each institution runs its own orderer. Until then, Raft tolerates crashed orderers but not a dishonest operator of the ordering service.
Ports (development network)
All ports are published on 127.0.0.1 only.
| Node | gRPC | Operations (/healthz, /metrics) |
|---|---|---|
| orderer1, orderer2, orderer3 | 7050, 8050, 9050 (admin 7053, 8053, 9053) | 9443, 9444, 9445 |
| peer0 and peer1 of State A | 7051, 8051 | 9446, 9447 |
| peer0 and peer1 of State B | 9051, 10051 | 9448, 9449 |
| peer0 of the judiciary | 11051 | 9450 |
| peer0 of the forensic laboratories | 12051 | 9451 |
| CAs of orderer, State A, State B, judiciary, forensics | 7054, 8054, 9054, 10054, 11054 | 17054 to 21054 |
A running development network has 27 containers using about 1 GB of memory. The full network starts with one command:
./scripts/network.sh all # up, createChannel, deployCC and smoke test, about 3 minutes
./scripts/network.sh down # remove everything againCAUTION
The development network uses well-known default passwords and keeps private keys as plain files. Never expose it beyond the developer's machine. It refuses to publish ports on another interface while the default passwords are in use.
The pilot runs the same network on Kubernetes, one cluster per institution. See Deployment architecture.
Sources: digifir-network README.md, config/configtx.yaml, compose/compose-net.yaml, docs/SECURITY_REVIEW.md, docs/HSM.md; digifir-backend docs/architecture/DECISIONS.md ADR-03 and ADR-04; digifir-chaincode README.md.