Project showcase · source and image public

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.

Three phone screens from the running app: home with the SOS control, live job tracking, and mechanic dispatch with ranked offers

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.

Citizen home screen with the SOS control and emergency readiness

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.

RAKSHA authority dashboard showing a live map, road-health scoring and detection triage
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

MetricValue
mAP500.471
mAP50-950.226
Classes4
Training data4-country, 3.2k
Epochs13
ONNX export37 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.

Read ADR-0005 →
02

Exactly one channel owns an emergency

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.

Read ADR-0009 →
03

Races are settled by Postgres, not by timing

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.

Read ADR-0010 →
04

The build refuses to let the documents lie

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.

Read ADR-0002 →

Architecture

A modular monolith, on purpose

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
Isometric rendering of the deployment topology
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

SuiteWhat it coversAssertionsStatus
UnitNo I/O; the only suite that runs standalone122PASS
End-to-endFull citizen and mechanic journeys against a live API189PASS
ConcurrencyRaces settled by Postgres rather than by timing75PASS
SecurityAttacks that must every one be refused74PASS
Gateway securityTelecom path, audit hash chain, OTP limits27PASS
BrowserReal Chrome driven over CDP163PASS
TotalSix suites, zero failures650PASS
AndroidKotlin unit tests; 21 cover the SOS ladder alone66PASS
CV pipelineStandard library only, no model download needed39PASS
Razorpay sandboxNeeds a payment account; has never been executed22NOT 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:

iOSAndroid AutoIVR USSDSatelliteMesh networking ERSS-112 handoff (stubbed)Kubernetes TerraformAutoscaling Multi-zoneLoad testing

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.