Build B-03

OPPU

A conductor checks a student concession card on a moving bus, on a ghat road, in about two seconds, with no signal. A glance cannot tell a sibling from a student, and a screenshot of a QR code is everybody’s pass.

Status
TESTING
Year
2026
Domain
Cryptography / Offline systems / Public transport
Stack
TypeScript / WebCrypto ECDSA P-256 / IndexedDB / Vite / Vitest

Artifact

  transport authority root
        │                 │
     signs             signs
        │                 │
        ▼                 ▼
  institution key   conductor device
        │
     signs
        ▼
   credential  ──binds──▶  device key
                                │
                          signs status + clock
                                │
                                ▼
                        proof · 297 bytes · 30 s

  scanner holds the root and nothing else.
  no lookup · no network · no list to distribute

Four keys, three signatures, no lookup. The scanner trusts one root and can verify everything else from the bytes in front of it.

Signal chain

  1. 01 · InputA 297-byte proofMinted by the student’s wallet, reissued every 30 seconds, carried in a QR code. No network on either side.
  2. 02 · ProcessingTen checks, in orderTrust root, institution signature, credential signature, validity window, status token, emergency list, proof freshness, device signature, service class, entitled route.
  3. 03 · ProcessingTime the device can trustA signed beacon from the depot plus a monotonic counter. The operating-system clock is treated as a suggestion, so a conductor’s phone running nine minutes fast does not reject a whole depot.
  4. 04 · OutputA signed receiptPass serial, stop, distance, fare, subsidy. Queued offline, synced at the depot, and the subsidy claim becomes a sum of signatures instead of an estimate.

System architecture

Not documented yet.

Implementation

Every problem with the laminated card follows from one moment: two seconds, a moving bus, no signal. An app that phones a server fails exactly where students are, which is on the churam with no bars. And a QR code on a screen solves nothing, because a screenshot of a QR code is everybody’s pass.

What makes a screenshot fail

The wallet signs the current half-minute with a key generated non-extractable inside the phone. A screenshot carries a signature for a half-minute that has passed. The credential copied into a friend’s app carries the wrong device key. Both are rejected by arithmetic rather than by a conductor’s judgement, which matters, because the conductor is busy.

The scanner trusts exactly one key: the transport authority root. Institution keys and conductor devices are both countersigned by it, so adding a college is a signature rather than a database migration, and a forged key fails on a bus with no network in sight.

Revocation without a list

The pass carries a 68-byte token from its own institution saying not revoked as of this day, refreshed whenever the wallet has signal. A scanner refuses anything older than a week. Revoking a pass means declining to sign it again. Nothing is distributed to anyone, and the bus carries the same two kilobytes whether Kerala has fifty thousand students or two million.

A signed serial list survives, shrunk to the one job it is actually good at: same-day emergency revocation.

The part that carries the money

The concession is a subsidy the state owes the corporation, and today the claim is an estimate the two of them argue about, because nobody holds a per-boarding record anyone can audit.

Every acceptance writes a receipt signed by the conductor’s enrolled device. The claim becomes a sum of signed receipts. The audit becomes a signature check over the batch. And the fraud that offline verification genuinely cannot catch (the same pass boarding in two districts thirty-five minutes apart) shows up as arithmetic at reconciliation instead of not showing up at all.

Offline acceptance and central accountability are normally a trade-off. Signed receipts are how you get both.

The conductor never learns a name

A pseudonym, an institution and an entitlement decide the fare. Nothing else should be readable by a stranger on a bus, so nothing else is in the credential.

Every rejection prints a sentence a conductor can read out. Not computer says no. It says which check failed, and why.

Measurements

Proof size
297bytes
Proof lifetime
30seconds
Status token
68bytesrefused by a scanner once older than 7 days
Verification checks
10
Attack bench scenarios
11keys generated fresh at the moment you press the button
Engine tests
31

Source: Figures from the engine and its test suite.

Failures and iterations

No iterations logged yet.

Result

No result stated yet.

Current status

TESTING

Built for the ANAVANDI Hackathon 2026 selection round, SC-01. Deployed and demonstrable; not piloted on a real route.

Next step

Not decided yet.

Credits

Hari
Engine, verifier, ledger
Anamika Jaideep
Team SleepDeprived
Claude Opus 5
Co-author on commits

Links

guest@hari: ~esc to close