KoraLinen Chain of Custody
Prototype · v0.1 · not live data
⬤ EMERGENCY MODE ACTIVE — Sahyadri Nashik Declared 14:02 · Mass casualty · Attest-later enabled · 23 movements flagged
Flow 01 · Highest frequency · Must complete under 30 seconds

Ward exchange

Housekeeping brings soiled linen to Room C and takes clean linen back. Both parties attest on one device. This is the screen the whole system lives or dies on — if it is slower than shouting at the supervisor, staff go back to the register.

9:41Room C · Sahyadri Nashik◍ offline

Room C

R. Kadam
Depot supervisor
Clean ready
Bedsheet 90×108184
Draw sheet96
Pillow cover212
Patient gown18 ⚑
Soiled awaiting dispatch
All types241
2 pending countersignatures
Emergency movements from 31 Jul still unattested. Month cannot close until cleared.
Who is collecting?
Soiled in — from Ward 3B
● soiled● infected tap chip to switch
Infected linen is counted separately — it is a segregation requirement and a different laundry rate.
Clean out — to Ward 3B
Suggested 1-for-1 against soiled received. Adjust freely — variance is recorded, not blocked.
Soiled received
Clean issued
Ward staff attests · M. Bhosale
Hand the device over. Enter your 4-digit code.
Attested · both parties
MovementMV-48213
RecordedQueued offline ◍
SyncsWhen signal returns
The record is append-only. If this was wrong, it is cancelled by a reversing entry — never edited, never deleted.
Try it: walk the full flow on the phone. Steppers are live, the variance warning is real, and any 4 digits complete the attestation.
Pick wardCount soiled inCount clean outAttestStamped

Why it is built this way

One device, two parties. The supervisor records, the ward staffer attests with their own code on the same handset. No second device, no network round-trip, no waiting.

Variance is recorded, never blocked. If 12 soiled come in and 9 clean go out, that is a real fact and the system takes it. Blocking the flow is how you teach staff to lie to it.

Offline-first. Basements and laundry docks have no signal. Movements queue locally and sync later. If the app fails once at the trolley, staff never open it again.

The code is not identity. It is a 4-digit attestation captured with actor, timestamp, device ID and coarse location, hashed into an immutable row. Not cryptographic — just enough to make "I never received those" untenable.

What this movement writes

append-only
movement
  id            MV-48213
  type          WARD_EXCHANGE
  from_node     WARD_3B          to_node  ROOM_C
  status        ATTESTED         emergency_ref  null
lines
  BEDSHEET_90   declared 12  confirmed 12  cond SOILED
  PILLOW_CVR    declared  8  confirmed  8  cond SOILED
  GOWN          declared  3  confirmed  3  cond INFECTED
  asset_ids[]   null   ← populated in Phase 4
attestations
  party A  R.Kadam    method PIN  14:22:09  dev A81C
  party B  M.Bhosale  method PIN  14:22:31  dev A81C
Flow 02 · Operational cockpit — not analytics

Room C supervisor

Sahyadri Multispecialty, Nashik · depot operated by Company A. This person needs to know what they can issue right now, what is owed back, and what is waiting on a signature.

Try it: receive consignment C-2291 below — it comes back short, and you will see the discrepancy object that replaces the argument.
Clean ready
492
4.1 days cover
Soiled awaiting
241
dispatch due today
At laundry
318
3 consignments
In transit
100
arriving 17:30
Condemned pending
14
awaiting approval

Consignments at laundry

Shubham Industrial
RefSentOutBackAge
C-229131 Jul100 3d
C-228829 Jul118118closedsettled
C-228426 Jul1401228d18 open
C-228024 Jul969010ddisputed

Needs you

4 items
Patient gowns below floor
18 clean · par 60 · 0.4 days cover
2 unattested emergency movements
31 Jul · blocks month close
C-2284 overdue 8 days
18 pieces unreturned · ₹4,320 exposure
14 pieces marked for condemnation
Needs approver — you cannot self-approve

Ward net position

28-day 1-for-1 variance
A ward persistently net-negative on exchange is the leak detector — it means more is going out than coming back.
ICU · 2nd
−6
Ward 3B
−31
OT complex
+3
Maternity
−8
net loss net gainmidline = balanced

Today's exchanges

11 · all attested
TimeWardInOutAttested
14:22Ward 3B2323RK · MB
13:05ICU1820RK · SF
11:40OT complex4242RK · AN
10:12Maternity1614RK · PD
08:55Ward 3B2727RK · MB
Flow 03 · Deliberately thin — a link, not an app install

Laundry provider

Shubham Industrial Laundry. Vendors will not install software or train staff. They get a magic link that does exactly three things: see what is expected, confirm a consignment, dispute a count.

Keeping this surface tiny is a design decision, not a shortcut. Every extra field here is a reason for the vendor to stop using it — and if the vendor stops, you lose attestation on the leg where most linen actually disappears.

Your consignments · Sahyadri Nashik

no login · signed link
RefPicked upPiecesOwed backStatus
C-229131 Jul 08:10100100in wash
C-228426 Jul 07:5514018short 18
C-228024 Jul 08:30966disputed
C-227722 Jul 08:051120settled

C-2284 · short by 18

raised 29 Jul
We dispatched140
You returned122
Difference18
Value at contract rate₹4,320
Both counts are attested. Neither side is guessing.
Accepting closes the loop. Disputing does not delete anything — it moves the discrepancy to review with both signed counts attached.
Flow 04 · The commercial case

Company A · headquarters

Vestara Linen Services · 5 sites live. The point of this screen is not to show how much linen was lost. It is to show where it was lost — because loss you can localise stops being a write-off and becomes a debit note.

Shrinkage · 30d
2.4%
↓ from 6.1% pre-Kora
Pieces in circulation
14,820
across 5 sites
Open discrepancies
11
₹86,400 contested
Localised loss
91%
was 0% on registers
Linen at risk
₹1.4L
unreturned > 7 days

Loss decomposed by leg

30 days · pieces
This is the chart that ends the blame game. Before Kora, every one of these pieces sat in a single column called "missing".
Ward ↔ Room C
78
Room C ↔ Laundry
194
Inside laundry
43
Condemned (valid)
67
Unlocalised
33
recoverable from vendor recoverable from site absorbed by Company A

Vendor scorecard

laundry partners
LaundryReturn %Rewash %Turn
Shubham Industrial96.84.12.9d
Nirmal Wash Co.99.61.22.1d
Ganga Laundry98.92.83.4d
Pune Hygiene Svcs99.41.92.4d
Shubham's 96.8% costs roughly ₹38,000/month against Nirmal's benchmark. That is a contract conversation with a number attached.

Sites

5 live · 2 in onboarding
SiteTypeDeployedShrinkage 30dDays coverOpen itemsReconciliation
Sahyadri NashikHospital3,2403.8%4.14 openJul pending
Sahyadri PuneHospital4,1101.9%5.6clearJul signed
Grand MeridienHotel2,8801.4%6.2clearJul signed
Lilavati Wing BHospital2,4604.2%2.25 openJul pending
Aster AurangabadHospital2,1302.1%3.82 openJul signed

Open discrepancies by age

oldest first
RefSiteLegPcsValueAgeState
D-0412LilavatiDepot↔Laundry32₹7,68021ddisputed
D-0418NashikDepot↔Laundry18₹4,3208dreview
D-0421NashikWard↔Depot11₹2,6405dreview
D-0424AsterWard↔Depot7₹1,6802draised

Monthly reconciliation statement

the deliverable
The signed artifact that turns a dispute into a debit note. Generated per site, per month, from attested movements only.
Sahyadri Nashik · July 2026
Opening deployed3,240
Issued to wards4,812
Returned from wards4,734
Sent to laundry4,690
Returned from laundry4,496
Condemned (approved)67
Unaccounted123 · ₹29,520
Flow 05 · Phase 3 · Available-to-promise, not stock-on-hand

Network & emergency allocation

Cells are coloured by days of cover, not raw counts — 200 sheets means nothing until you know the site burns 40 a day. A temporary allocation is modelled as a loan, so the lending site's par never looks broken.

Try it: run the coverage simulator below, then declare an emergency to see how the system relaxes attestation without ever letting it become attest-never.

Days of cover · all sites

clean-ready + expected returns − committed demand
BedsheetDraw sheetPillow cvrBlanketGownTowel
Sahyadri Nashik 4.1184 pc 3.696 6.8212 5.274 0.418 1.888
Sahyadri Pune 7.9402 6.4188 4.8240 5.596 8.2210 4.1176
Grand Meridien 6.2288 n/a 7.1312 4.4120 n/a 6.6420
Lilavati Wing B 2.2142 0.934 1.998 3.862 2.170 3.2110
Aster Aurangabad 3.8168 4.288 3.9156 2.444 4.692 3.5128
◈ Strategic reserve 600held 200held 600held 150held 400held 300held
under 1 day 1–3 days 3–6 days surplus Reserve row shows pieces, not cover — it has no burn rate of its own.

Can I cover it?

Ranked sources with the risk each one creates.
Sahyadri Nashik needs
200 patient gowns in 6 hours

Open loans

receivables, not transfers
From → ToPcsAgeBack
Pune → Lilavati12018d0
Meridien → Nashik806d80
Reserve → Aster20064d40
Reserve → Aster is 64 days old. An unreturned loan past 60 days is functionally a transfer. Recall it, or formally rebase both sites' allocations.

Surplus tiers

not all headroom is lendable
Committed
Covers par + safety stock. Untouchable, even in emergency.
11,400
Flexible
Above par, below max. Auto-approved within a region.
2,180
Strategic reserve
Held centrally. Released only on an authorised emergency.
2,250
Without a reserve tier, every emergency raids a working site and creates the next stockout somewhere else.

Emergency mode

an explicit, logged state
Declared by an authorised role with a reason code, severity and expected duration. Declaring it:
  • relaxes countersignature to attest-later
  • unlocks strategic reserve and cross-site lending
  • switches ward targets from par to surge
  • flags every movement in the window so reconciliation expects noise
Attest-later must never become attest-never. Every unattested emergency movement lands in a queue someone must clear before the site can close its month. An emergency is exactly when linen goes missing, because nobody is counting.
Flow 06 · Adoption depends on this screen

Hospital administrator

Sahyadri Multispecialty, Nashik. If this reads as a surveillance tool pointed at housekeeping, the hospital sabotages the counts and you end up with worse data than the register. So it leads with what they are judged on — stockouts and cost per bed-day.

Stockout incidents · 30d
2
↓ from 17 pre-Kora
Pieces / occupied bed-day
4.8
peer benchmark 5.4
Linen cost / bed-day
₹41
− ₹7 vs June
Your exposure
₹18.7K
78 pcs unreturned from wards

Consumption by ward

pieces / occupied bed-day · 28d
ICU · 2nd
8.2
OT complex
7.1
Maternity
5.6
Ward 3B
4.4
Ward 2A
3.8
Day care
1.9
ICU and OT run high by clinical necessity — the number worth watching is a ward drifting upward without a case-mix reason.

Stockouts

daily · 28d
6 Jul3 Aug
Two incidents, both patient gowns, both traced to a late laundry return rather than ward hoarding. That distinction is the entire value of this screen to a hospital.
9 Jul · gowns4h 20m
21 Jul · gowns2h 05m

Linen held by your wards

what you are accountable for
WardParHolding now28d netUnreturnedStatus
ICU · 2nd floor6061−66at par
Ward 3B · General5544−3131review
OT complex9088+30at par
Maternity · 4th5052−88watch
Ward 2A4542−1212watch
Day care2524+10at par
Ward 3B is 31 pieces net-negative over 28 days with no clinical explanation. Kora does not accuse anyone — it just makes the pattern impossible to miss, which is usually enough.
The spine · append-only · enforced at the database, not in app code

The ledger

Stock is never edited. There is no "adjust quantity" button anywhere in Kora. Balance at any node is the sum of confirmed inbound minus confirmed outbound — double-entry, exactly like accounting. The moment someone can UPDATE a movement, the audit trail becomes decorative.

Read row MV-48197 and MV-48198 together. A wrong entry is not edited or deleted — it is struck by a reversing entry that points at the original, and both stay visible forever.
RefTimestampTypeFrom → ToItemDeclConfCondAttested
MV-4821303 Aug 14:22WARD_EXCHWARD_3B → ROOM_CBEDSHEET_901212soiledRK·MB ✦
MV-4821303 Aug 14:22WARD_EXCHROOM_C → WARD_3BBEDSHEET_901212cleanRK·MB ✦
MV-4820903 Aug 13:05WARD_EXCHROOM_C → ICUGOWN2020cleanRK·SF ✦
MV-4820403 Aug 09:40DISPATCHROOM_C → LAUNDRY_LALL_TYPES100100soiledRK·SL ✦
MV-4819802 Aug 16:11RECEIPTLAUNDRY_L → ROOM_CPILLOW_CVR4040cleanRK·SL
MV-4819702 Aug 16:48REVERSAL↳ reverses MV-48198PILLOW_CVR−40−40cleanRK·SL ✦
MV-4819602 Aug 16:50RECEIPTLAUNDRY_L → ROOM_CPILLOW_CVR4034cleanRK·SL ✦
MV-4819602 Aug 16:50RECEIPTLAUNDRY_L → REWASHPILLOW_CVR6rewashRK·SL ✦
MV-4819002 Aug 11:20CONDEMNROOM_C → CONDEMNEDBEDSHEET_901414end of lifeRK·AV ✦
MV-4818631 Jul 15:31WARD_EXCHROOM_C → ICUBEDSHEET_904040clean⚑ unattested
MV-4818531 Jul 15:12LOAN_OUTMERIDIEN → ROOM_CBEDSHEET_908080cleanGM·RK ✦
MV-4818028 Jul 18:00STOCKTAKEROOM_C adjustmentBEDSHEET_90−4countedRK·AV ✦

Rules the ledger enforces

No edits, no deletes. A wrong movement is cancelled by a reversing entry pointing at the original. Both rows survive.

Balances are derived, never written. Any node's stock is a query, not a column somebody can set.

Stocktake is the one sanctioned exception — and it produces an adjustment movement with a mandatory reason and an approver's attestation, not a silent edit.

Condemnation needs a second party. Otherwise it becomes the drain that hides theft.

Unattested rows are visible, not hidden. MV-48186 above is flagged from the emergency window and blocks month-close until cleared.

The Phase 4 seam

where QR/RFID slots in
Today Kora tracks item type × count. Per-item identity is an additive change, not a rewrite:
movement_line {
  item_type_id
  declared_qty
  confirmed_qty
  condition
  asset_ids[]   ← null today
}                 when tags exist, enforce
                  len(asset_ids) == confirmed_qty
Same table, same screens, same reports. Counting becomes scanning, and you unlock linen life, wash-cycle counts and per-item journey — without touching the ledger.