fir-registry
fir-registry keeps one CASE record for every complaint, from filing to the court's final order. Registration, Zero FIR routing, the case diary, statements, arrests, final reports, duplicate hints, statutory clocks and documents for public verification all live here. Court decisions reach the case through ApplyCourtOutcome, which only judicial-actions may call.
- Chaincode-level endorsement: the judiciary and one state police organisation.
- Key-level policy of a case and all its sub-records: the case's state police and the judiciary.
- Private data:
stateA_privateandstateB_private.
The case record
The case's ID is the ID of the action that created it. Among its public fields:
| Field | Meaning |
|---|---|
stateCode, status | Owning state and current status |
channel, lang | How the information arrived (APP, PORTAL, DESK, AGENT) and its language |
receivingStation, station, originStation | Where the information was given, the current jurisdictional station, and for a Zero FIR the station that registered it |
complainantDid | The citizen's DID, for citizen filings |
numbers | Complaint, FIR, Zero FIR, NCR and RIVAL numbers, and the CCTNS FIR number with its register's state |
filedAt, clientFiledAt | The commit time and the time the citizen signed |
incident, routing | Incident day and 5-character geohash; the stations covering that place and whether it is outside the receiving station's area |
narrativeCommit | The salted commitment of the narrative (its text is private) |
attachments | Up to 50 files: plain and ciphertext SHA-256, size, type and the hash of each file's key |
signature | The BNSS s.173(1)(ii) signature: state, methods, time and whether it was within three days |
sections, publish | BNS sections at registration; whether the FIR may be published (forced off for identity-protected offences) |
io | The investigating officer and the history of assignments |
referral, links, hints | Open referral to a magistrate, links recorded by court decisions, duplicate hints |
diary | Entry count and head hash of the hash-chained case diary |
report, court, disposal, quash | Final reports, the court and CNR after cognizance, disposal and quash details |
clocks | Statutory due days |
config | The configuration versions the last transition used |
Sub-records created with the case include hints, referrals, questions to the informant, information added later, diary entries, statements, arrests with their sealed segments, final reports, registered documents, escalations, links to external systems (xref) and erasure records.
Statuses
Statuses only move along the transitions below. Every other action is a new version of the case at the same status. Terminal statuses are LINKED, DISPOSED and QUASHED. An edge not listed is refused with ERR_STATE.
| # | From | To | Action and signer | Legal basis |
|---|---|---|---|---|
| T1 | (none) | FILED | complaint.file, citizen | BNSS s.173(1) |
| T2 | (none) | FILED | complaint.intake, duty officer or SHO (walk-in or agent) | s.173(1)(i) |
| T3 | FILED | UNDER_ENQUIRY | complaint.enquiry_decide APPROVE, supervisor of DSP rank or above | s.173(3)(i) |
| T4 | UNDER_ENQUIRY | ENQUIRY_CLOSED | complaint.enquiry_close, SHO | s.173(3)(i) |
| T5 | FILED, UNDER_ENQUIRY | NCR_RECORDED | complaint.ncr, duty officer or SHO | s.174(1) |
| T6 | FILED, UNDER_ENQUIRY, NCR_RECORDED, ENQUIRY_CLOSED | REGISTERED | fir.register, duty officer or SHO | s.173(1), s.2(1)(g), s.174(4) |
| T7 | FILED, UNDER_ENQUIRY | ZERO_FIR_TRANSFERRED | fir.register_zero, duty officer or SHO | s.173(1) |
| T8 | ZERO_FIR_TRANSFERRED | REGISTERED | fir.register at the receiving station | s.173(1) |
| T9 | FILED, UNDER_ENQUIRY, NCR_RECORDED | REFERRED | hint.refer, duty officer or SHO | DigiFIR's duplicate review |
| T10 | REFERRED | LINKED | court.referral_decide CONFIRM_LINK, magistrate | |
| T11 | REFERRED | Its status before the referral | court.referral_decide OVERRIDE, magistrate; the police then register it | s.173(1), s.175(3) |
| T12 | REGISTERED | UNDER_INVESTIGATION | fir.assign_io, SHO | s.175(1), s.176(1) |
| T13 | UNDER_INVESTIGATION | CHARGESHEET_FILED, CLOSURE_REPORT_FILED | fir.final_report, IO or SHO | s.193(3)(i) |
| T14 | CLOSURE_REPORT_FILED | DISPOSED | court.closure_decide ACCEPT, magistrate | s.193(3)(i) |
| T15 | CLOSURE_REPORT_FILED | UNDER_INVESTIGATION | court.closure_decide FURTHER_INVESTIGATION, magistrate | s.193(9) |
| T16 | CHARGESHEET_FILED | IN_COURT | court.cognizance, magistrate | s.210(1)(b) |
| T17 | IN_COURT | DISPOSED | court.dispose, magistrate | |
| T18 | ZERO_FIR_TRANSFERRED to IN_COURT | QUASHED | court.quash_record, High Court registry, or a magistrate recording the High Court's order | High Court's inherent power (s.528 as the apps cite it) |
Only court actions move a case backwards (T11, T15). Police cannot walk a record back, and no transition resets a statutory clock or erases a missed deadline.
Registration has guards of its own. An FIR needs the complaint signed under s.173(1)(ii), at least one cognizable section, and no open referral to a magistrate. Registering from ENQUIRY_CLOSED also needs the reason "fresh information".
Numbering
Numbers come from per-station counters that need the filing state's police and the judiciary to agree.
| Number | Format | Assigned by |
|---|---|---|
| Complaint | CMP-<station>-<yyyy>-<6+ digits>, for example CMP-RJ-JAI-PS07-2026-000123 | complaint.file, complaint.intake |
| FIR | <station>-<yyyy>-<6+ digits> (the ledger's number; the CCTNS number is recorded beside it) | fir.register |
| Zero FIR | ZERO-<station>-<yyyy>-<4+ digits> | fir.register_zero |
| NCR | NCR-<station>-<yyyy>-<4+ digits> | complaint.ncr |
| Referral | RIVAL-<station>-<yyyy>-<4+ digits> | hint.refer on a complaint |
The year is the IST year of the transaction. Two filings at the same station cannot both commit in one block, because both read the same counter; the backend queues them and re-endorses the second.
Signed actions
| Area | Actions |
|---|---|
| Complaint | complaint.file, complaint.intake, complaint.add_info, complaint.sign (device, eSign or desk), complaint.ask, complaint.enquiry_request, complaint.enquiry_decide, complaint.enquiry_close, complaint.ncr |
| Registration | fir.register, fir.register_zero, fir.handover (cross-state, with copies of the private data), fir.assign_io |
| Investigation | case.diary, case.statement, case.arrest, case.custody_event, fir.final_report |
| Duplicates | hint.attest (text-similarity service), hint.refer (officer) |
| Other | doc.register, clock.escalate, external.anchor (CCTNS, ICJS, eCourts, e-Sakshya, FSL LIMS, general diary), external.release, erasure.execute |
| From the court | ApplyCourtOutcome, invoked only by judicial-actions |
Arrest and custody
Each arrested person has one custody timeline: POLICE_CUSTODY, then PRODUCED, then police or judicial remand, then RELEASED. An arrest must carry the safeguards set in configuration, such as grounds informed and a relative informed. A remand order recorded by police never overrides a court-recorded order covering the same day. The timeline keeps up to 200 events in its current window. When a court order or release finds the window full, the window is sealed into a hash-chained segment, so a court's order is never refused.
Duplicate hints
Hints never block anything and are never shown to the citizen.
- Tier 1, inside the chaincode: when a new file's SHA-256 matches a file on another case or exhibit, a hint links the two. The empty file's hash is never matched.
- Tier 2, outside the chaincode: a hint service scores text similarity within one state and submits a signed
hint.attestnaming the registered model, its version and hash, and the scores, so the hint can be reproduced and challenged. - A duty officer or SHO may refer a pair to a magistrate with
hint.refer. A referred complaint gets a RIVAL number and moves toREFERRED.
Statutory clocks
The chaincode stores due days at transitions and accepts an escalation only when the overdue condition holds on ledger data.
| Clock | Starts | Due | Stops when | Basis |
|---|---|---|---|---|
SIGN_173_1_II | Filing | 3 days | The complaint is signed | s.173(1)(ii) |
ENQUIRY_173_3 | Enquiry approved | 14 days | The case leaves UNDER_ENQUIRY | s.173(3)(i) |
PRODUCE_58 | Arrest | 24 hours | The accused leaves police custody | s.58 |
INVESTIGATION_193_2 | Registration | 2 calendar months | A final report is filed | s.193(2), for the sections flagged in configuration |
PROGRESS_193_3_II | Registration | 90 days | A progress update shared with the informant, or a final report | s.193(3)(ii) |
DEFAULT_BAIL_187_3 | First remand day | The default-bail day (60 or 90 days) | Chargesheet filed, or the accused released | s.187(3) |
STATUS_REQUEST | A court's status request | 3 or 7 days | The police reply | Court practice |
LAB_TURNAROUND | Laboratory acceptance | The division's target | The report is sealed | The laboratory's own target, not a legal limit |
clock.escalate is signed by a DEADLINE_SERVICE key. Stop times are always ledger times, so a late production, signature, report or reply cannot erase the record that the deadline passed.
Queries
| Query | Purpose |
|---|---|
GetCase, GetCaseByNumber, GetHistory, ListSubRecords | A case, found by ID or any of its numbers, its versions and sub-records |
GetArrestTimeline | An accused's whole custody timeline, sealed segments included |
GetReferral, GetHint | Referrals and hints |
ReadPrivate, ReadPrivateBatch | Signed reads of private parts: narrative, complainant, victim, accused, attachment details and keys, information added, questions, notes, diary, statements, arrests, reports |
VerifyDocument | Public verification of a document by its SHA-256 |
FileHashRefs, PrivateKeyHash | Used by evidence-custody |
SearchCases | CouchDB queries for list screens only, never for decisions |
OnboardState is the system function that prepares the chaincode for a new state.
Source: digifir-chaincode docs/SPEC.md section 3 and section 10.2.