TapGuard

Everyday track · fare validation

Offline-first transit fare validation with live clone detection, for the Nairobi Metropolitan Mass Rapid Transit System. A gate has to decide in under a second whether a tap is genuine — in a tunnel, with no network. That is exactly where cloned cards win.

Interactive

Operator console

Drive it yourself. Two station gates, a rider wallet, a USSD handset on *384#, a network switch and a reconciliation backend. A seven-step checklist advances as you go.

Open the console
90 seconds · narrated

Lost card, no smartphone

A rider blocks a lost card over USSD on a feature phone and the gates refuse it minutes later. Real DTMF keypad tones.

Play the USSD simulation
88 seconds · narrated

Field simulation

Watch the whole scenario play out as an animated, narrated walkthrough with synthesised station audio. Scrub, jump by chapter, or mute the voice and narrate it live.

Play the simulation

What the gate checks, in order

  1. Signed deny-list — is this card revoked? Works with no network.
  2. HMAC-SHA256 signature — is the media genuine? Verified on the gate itself.
  3. Counter monotonicity — is this a replayed tap token?
  4. Impossible travel — is this card also at another station, faster than physics allows?
token = HMAC-SHA256( key_card , cardID | counter | stationID | timestamp )

The gap, and how it closes

NetworkedGates share a last-seen table, so a clone at a second station is refused in real time.
OfflineEach gate is an island and legitimately accepts. This is the moment a clone pays off.
ReconciledBuffers merge, the collision is arithmetic, the card is revoked and the signed deny-list is pushed everywhere.

The rider channel, on a feature phone

*384#Balance, top-up, last trips and a plain-language reason for any refusal — no smartphone required.
Self-service blockA rider who loses a card blocks it themselves. The deny-list is re-signed and pushed to every gate.
Honest when offlineWith the link down the block is queued, not claimed as applied, and reaches gates at the next reconcile.

Verify the logic yourself

The rules are checked independently of the interface. This replays the full scenario and asserts every outcome.

node verify/detection-logic.mjs