Roadside assistance, rebuilt for the roads where coverage fails.
One platform connecting stranded drivers, verified mechanics and road-safety
authorities — with an emergency path that keeps working when the network does not.
Real screenshots of the running system, set into device frames — not mockups.
650
Assertions
56
Tables
138
Indexes
65
API routes
8
Languages
Every figure on this page was produced by running the thing it describes against a
database migrated from empty and seeded. Nothing here is estimated.
What it is
Four surfaces, one process, and a path that survives no signal
Citizen app
Request assistance, hold to raise an SOS, track a job, pay, and review — with a
diagnosis step that names a likely cause before dispatch.
Mechanic console
Dispatch inbox with ranked offers, the job lifecycle from accept to complete,
and settlement recorded against the booking.
RAKSHA authority
Live operational map, road-health scoring, and a confirmation queue for
detections that a person — never a model — acts on.
Feature phone
A complete booking over SMS with no app at all, in eight languages,
switched by texting LANG TA.
Showcase, not the live application. This page is static: screenshots,
recordings and measured evidence. The platform itself needs a long-lived process — it
holds open event streams and runs an offer-expiry sweeper — plus PostgreSQL with PostGIS.
One command runs the whole thing locally, and it is the same image
published from this repository.
How it works
From breakdown to resolution
Select a stage to see it in the running application.
Request or escalate
The home screen carries both paths: an ordinary assistance request, and an SOS that must be held for 1.5 seconds so a pocket press never calls a responder.
RAKSHA
Road-safety intelligence, with a human in the loop
Edge devices post road-damage detections; the platform scores road health and raises
an incident when severity and confidence both clear a threshold — and then waits.
Capture
Edge device, road imagery
→
YOLO11
Trained detector, four classes
→
Detections
Structured output, hashed for audit
→
Rules
Deterministic thresholds
→
Human
Confirms before any response
DETECTION
A model that was trained
YOLO11 on RDD2022 across four countries. The training script, dataset manifest and
per-epoch metrics are in the repository; the weights are gitignored.
DECISION
Rules, not inference
Severity and confidence thresholds decide whether a detection raises an incident.
The roadside diagnosis is a rule table returning rules-1.0.0.
RESPONSE
A person decides
A model-raised incident sits in AWAITING_CONFIRMATION. There is no
transition out of it that does not pass through a human (ADR-0005).
No language model anywhere.
There is no LLM or GPT dependency in any manifest and no chat endpoint called from any
source file — not in the API, the Android client, the web surfaces or the CV pipeline.
npm run no-llm fails the build if one ever appears, and it runs in CI.
AI evidence
The measured result, and what it is not
Metric
Value
mAP50
0.471
mAP50-95
0.226
Classes
4
Training data
4-country, 3.2k
Epochs
13
ONNX export
37 MB
Inference, CPU
~135 ms/img
Model yolo11s-multi-rich. Per-class mAP50: pothole 0.242, road damage 0.431.
Held-out validation only. These are validation figures from the
training run recorded in ai/README.md. They are not production accuracy,
not real-world accuracy, and not a guarantee — the model has never run against live
Indian road imagery at scale, and saying otherwise would be inventing a result.
The pipeline is 39 tests, standard library only, so the logic is checked on every push
without a multi-gigabyte install. The detection path stores a hash of each event and
never the frame itself (ADR-0006), so an audit trail exists without retaining imagery.
Engineering decisions
Four decisions worth defending
Each is enforced by something that fails the build if it regresses.
01
A model is never allowed to dispatch
A crash signal raises an incident that waits in AWAITING_CONFIRMATION
until a person moves it. A false positive that dispatches is worse than a false
negative that asks. ADR-0011 then bounds the mirror failure — nobody arriving — with
a review deadline per severity that escalates attention, never responders.
The SOS ladder falls back data → SMS → 112 → offline queue. The invariant
is that precisely one of them owns the report: a confirmed SMS must not also queue an
API replay; an unconfirmed one must. Getting this wrong sends two responders to one
accident, or none.
Two mechanics accepting one job: SELECT … FOR UPDATE with offer expiry
re-checked under the lock. A duplicate SOS: a unique index on a client-minted
reference. Double settlement: a partial unique index over settled payments. The audit
log is hash-chained, with Postgres RULES making UPDATE and DELETE no-ops.
Five gates run on every push: documented claims must match the measured file; every
file.ts:123 citation must still resolve; module boundaries hold; no
language model enters the system; and no personal data leaves India. The last one
found a live leak the day it was written.
The API and every web surface ship in one process. The surfaces are plain HTML, CSS and
JavaScript with no build step, so a second deployment unit would add a network hop and a
CORS boundary in exchange for nothing.
Clients
Citizen PWA, mechanic console, RAKSHA dashboard, live map — plus a native Android
client in Kotlin and Compose, and SMS for feature phones.
API
Fastify, 65 routes under a versioned /v1 contract, Zod validation at every
boundary, JWT with role-based access, and server-sent events for real time.
Data
PostgreSQL 16 with PostGIS. 56 tables, 138 indexes, 5 GiST indexes for geospatial
dispatch, 5 migrations, and an append-only audit log.
Request
Client or SMS gateway
→
Auth
OTP → JWT → role
→
Validation
Zod schema at the boundary
→
Domain
Guarded state machines
→
Postgres
Transaction, locks, constraints
→
Audit
Hash-chained, append-only
Infrastructure, drawn rather than photographed — rendered procedurally as geometry.
Nothing photographic is claimed and no stock image with unknown licensing enters the project.
Security
74 attacks, every one refused
The security suite is not a checklist. It is a set of attacks that must all fail — a green
run means each was attempted and refused, which is a stronger statement than
“the code looks right”.
Access
Cross-tenant reads and writes, role escalation and id manipulation — every read is
scoped by the caller, and a break-glass read is recorded with its reason.
Tokens and input
Forged and alg:none tokens, unsigned webhooks, SQL injection, oversized
input, and error bodies checked for leakage.
Integrity
The audit chain is verified on read and a forged link is reported as a broken chain.
Postgres RULES make UPDATE and DELETE on the log no-ops.
CI additionally kills PostgreSQL under a running API and asserts the platform is honest
about it — /health returns 503 naming the database while /v1/ping
still returns 200, so a client can tell “you have no network” from
“the platform is unwell” — then rehearses a backup and restore and re-verifies
the audit hash chain on the restored copy.
Evidence
Every suite, and the one that never ran
Suite
What it covers
Assertions
Status
Unit
No I/O; the only suite that runs standalone
122
PASS
End-to-end
Full citizen and mechanic journeys against a live API
189
PASS
Concurrency
Races settled by Postgres rather than by timing
75
PASS
Security
Attacks that must every one be refused
74
PASS
Gateway security
Telecom path, audit hash chain, OTP limits
27
PASS
Browser
Real Chrome driven over CDP
163
PASS
Total
Six suites, zero failures
650
PASS
Android
Kotlin unit tests; 21 cover the SOS ladder alone
66
PASS
CV pipeline
Standard library only, no model download needed
39
PASS
Razorpay sandbox
Needs a payment account; has never been executed
22
NOT RUN
Tested is not the same as claimed. The 22 Razorpay sandbox checks need a
KYC-verified account. They have never run, they are not part of the 650, and they are never
described as passing. Android lint reports zero errors and the release APK builds under R8.
The running system
Captured from the application, not designed for this page
Produced by scripts/capture-screens.mjs against the live API — regenerable in
about ninety seconds, so they cannot quietly drift from what the product does.
Select any image to enlarge.
Recordings
See the system in motion
Citizen journey
Sign in, diagnose, dispatch, track, pay — recorded against the running system.
RAKSHA replay
65 real detections replayed through the ingest path, twice, to show the replay is idempotent.
Run it yourself
The whole platform, one command
No account, no cloud, no credentials. It pulls the published image, brings up PostGIS,
migrates, seeds, and serves every surface. Running it twice is a no-op rather than an error.
git clone https://github.com/saatwik-1157/roadassist-bharat
cd roadassist-bharat
docker compose -f docker-compose.demo.yml up
Then open http://localhost:4000 — the citizen app, mechanic console, authority
dashboard and live map are all served by that one process.
Citizen
+917000000000
Authority
+919999900001
Mechanic
+919600000000
These are seeded demo accounts in a throwaway local database. In demo mode the one-time code
is returned by the API and fills itself in, so there is no password to publish — and nothing
here is a credential for any deployed system.
Why there is no hosted demo link. The image is built and published, and
CI boots it against a real PostGIS and runs the end-to-end and security suites against the
running container before publishing. A hosted instance additionally needs a managed PostGIS
database wired to it; that is configuration, not code, and until it is in place this page
will not link to a URL that does not answer.
Scope
Scope, by design
The project distinguishes implemented functionality from future scope, and says which is
which. These are named rather than implied:
Latency is measured single-user on one machine. The eight languages ship, but seven are
machine-translated and have not been reviewed by native speakers — the strings exist, the
quality is unverified, and those are different claims.