Margi

Master Team Brief โ€” Road Safety Hackathon 2026 (RoadSoS Track)
Organizer: CoERS & RBG Labs, IIT Madras
Deadline: May 31, 2026, 11:59 PM IST
Version: 1.0
Audience: Full hackathon team

The network failed. The golden hour didn't.

RoadSoS Track ยท IIT Madras

Margi Master Team Brief

๐Ÿ“… May 2026 ๐Ÿ‘ฅ Full team document โฐ Deadline: May 31, 2026

The network failed. The golden hour didn't.

Margi โ€” Complete Replication Bible

This is the master engineering record for Margi. It captures the real shipped architecture, every subsystem, the exact tech stack and versions, environment + deployment, and a full problem-to-solution engineering log so the entire project can be rebuilt from zero by a human or an AI model. The original aspirational planning brief (PWA vision) is preserved verbatim in Appendix A for history.

Project: Margi โ€” offline-first Golden Hour road-safety prototype
Team: Team NovaDrive ยท IIT Madras National Road Safety Hackathon 2026 (RoadSoS track, CoERS & RBG Labs / MoRTH)
Repository: github.com/Stormynubee/Margi
Primary client: novadrive-mobile/ โ€” native Expo SDK 54 Android app (com.margi.app, margi://)
Optional cloud: novadrive/ โ€” Next.js 14 Sarthi AI BFF + web relay (Vercel)
Brief site (this page): static HTML built from this file โ†’ roadsafetyhackathon-six.vercel.app
Tagline: When signal drops, the path still holds. The network failed โ€” the golden hour didn't.


Table of Contents


1. What Margi Really Is

Margi is a client-heavy, offline-first emergency system for Indian highway corridors. All medical and routing decisions run on-device; the network is an optional enhancement (SMS to 108, Supabase auth, Gemini LLM chat).

LayerReality
Primary clientnovadrive-mobile/ โ€” Expo SDK 54, package com.margi.app, version 2.0.0
Offline coreSTART triage FSM โ†’ SQLite facility ranking (~50 demo POIs) โ†’ Golden Hour Packet โ†’ QR (ND1: envelope) / SMS composer
Optional onlineSarthi Gemini BFF (novadrive/), Supabase auth + profile + NGO + dispatch audit, OSRM trip routing, HTTP dispatch hooks
Web mirrorBystander relay at /relay, plus a full web emergency wizard in novadrive/
Brief siteStatic HTML at docs/site/ (this page) deployed as a separate Vercel project
Tests225 Jest unit tests (73 suites) over lib / FSM / encoders / orchestrator / KB
Judge APKGitHub Actions builds margi-debug.apk

What Margi is NOT (honesty boundaries โ€” keep these in every pitch):

Margi provides decision support only. It is not a medical diagnosis. In an emergency, always call 108/112 when possible.

2. The Two Safety Lanes

Margi ships two parallel emergency experiences that share the offline core.

Lane A โ€” Golden Hour (road accident SOS)

flowchart LR
  SOS[Hold SOS 3s / Quick SOS / header SOS] --> Pick[Incident Tracker]
  Pick --> Act[Activation ~6s splash]
  Act --> Orch[runEmergencyOrchestrator]
  Orch --> ICE[ICE SMS intent]
  Orch --> S108[108 SMS intent]
  Orch --> Maps[Google Maps -> nearest trauma POI lat/lng]
  Orch --> Trauma[Trauma Response HUD]
  Trauma --> Triage[START triage FSM]
  Triage --> Route[Facility ranking]
  Route --> GHP[GHP + QR relay]

Lane B โ€” Naari Shakti (women's safety portal)

Gender-gated (self-reported on device) saffron + navy portal: SMS nearest police station, ICE alert, helpline 181, hold-to-activate (2s) distress HUD with optional recording, 112 national fallback when far from the demo seed.

LaneWhat the user gets
Golden HourIncident type โ†’ activation โ†’ automated orchestration (ICE SMS, 108 SMS, nearest trauma POI Maps) โ†’ trauma HUD with live incident timer, rear torch, Call ICE FAB, hospital navigation to facility coords โ†’ START triage โ†’ GHP + bystander QR
Naari ShaktiGender-gated portal โ€” SMS nearest police station, ICE alert, helpline 181, 2s hold distress with recording, 112 fallback

3. Repository Topology

roadsafetyhackathon/                  # monorepo root (git: Stormynubee/Margi)
โ”œโ”€โ”€ novadrive-mobile/                 # PRIMARY โ€” Expo SDK 54 Android app (ship this)
โ”‚   โ”œโ”€โ”€ app/                          # expo-router screens (tabs, emergency, naari, sarthi...)
โ”‚   โ”œโ”€โ”€ src/                          # lib, components, hooks, context, theme
โ”‚   โ”œโ”€โ”€ scripts/                      # APK build, sarthi check, seed gen, gradle patches
โ”‚   โ”œโ”€โ”€ assets/                       # icon/splash docs + models/
โ”‚   โ”œโ”€โ”€ app.json / app.config.js      # Expo config
โ”‚   โ”œโ”€โ”€ babel.config.js               # babel-preset-expo + reanimated plugin
โ”‚   โ”œโ”€โ”€ jest.config.js                # ts-jest, node env, src roots, *.test.ts
โ”‚   โ”œโ”€โ”€ tsconfig.json                 # extends expo base, @/* -> ./src/*
โ”‚   โ””โ”€โ”€ package.json                  # version 2.0.0
โ”œโ”€โ”€ novadrive/                        # Next.js 14 Sarthi BFF + web relay (Vercel)
โ”‚   โ”œโ”€โ”€ src/app/                      # web pages + api/sarthi/{health,chat}
โ”‚   โ”œโ”€โ”€ src/lib/sarthi/aiConfig.ts    # Gemini provider + probe
โ”‚   โ”œโ”€โ”€ vercel.json                   # separate Vercel project
โ”‚   โ””โ”€โ”€ package.json
โ”œโ”€โ”€ docs/                             # judge docs, specs, brief site source
โ”‚   โ”œโ”€โ”€ CANON.md                      # single source of truth for scope
โ”‚   โ”œโ”€โ”€ ARCHITECTURE.md               # technical architecture
โ”‚   โ”œโ”€โ”€ PHASE3_SETUP.md               # Supabase + Sarthi BFF deploy guide
โ”‚   โ”œโ”€โ”€ MARGI_MASTER_BRIEF.md         # THIS FILE -> builds the brief site
โ”‚   โ””โ”€โ”€ site/                         # build-docs.js -> index.html (deployed)
โ”œโ”€โ”€ supabase/migrations/              # profiles, volunteer_providers, dispatch_events + RLS
โ”œโ”€โ”€ data/corridors/                   # NH-48 POI verification CSV/JSON inputs
โ”œโ”€โ”€ scripts/                          # Python Overpass ingest + pytest (build-time only)
โ”œโ”€โ”€ .github/workflows/                # ci.yml, android-apk.yml, poi-ingest.yml
โ”œโ”€โ”€ vercel.json                       # ROOT project -> static brief site (docs/site)
โ”œโ”€โ”€ README.md / JUDGE_START_HERE.md / CHANGELOG.md / CONTRIBUTING.md / SECURITY.md

Critical deployment split (the single biggest gotcha):

Vercel projectvercel.jsonServesRoot directory
root (roadsafetyhackathon)repo-root vercel.jsonstatic brief site from docs/siterepo root
novadrive (Sarthi BFF)novadrive/vercel.jsonNext.js app with /api/sarthi/* and /relaynovadrive

The brief-site hosts (roadsafetyhackathon-six.vercel.app, margi-tau.vercel.app) return 404 for /api/sarthi/health. Sarthi must point at the separate novadrive project (e.g. novadrive-eta.vercel.app).


4. Complete Tech Stack And Versions

Mobile (novadrive-mobile/package.json, version 2.0.0)

ConcernPackageVersion
Runtimeexpo~54.0.0
Reactreact19.1.0
React Nativereact-native0.81.5
Routerexpo-router~6.0.23
Animationsreact-native-reanimated~4.1.1
Workletsreact-native-worklets0.5.1
Local DBexpo-sqlite~16.0.10
Camera / torchexpo-camera~17.0.10
Locationexpo-location~19.0.8
Sensors (crash)expo-sensors~15.0.8
Audio (voice)expo-audio~1.1.1
Speech (TTS)expo-speech~14.0.8
Hapticsexpo-haptics~15.0.8
Crypto (hash)expo-crypto~15.0.9
Secure storeexpo-secure-store~15.0.8
Storage@react-native-async-storage/async-storage2.2.0
Network state@react-native-community/netinfo^11.4.1
Backend SDK@supabase/supabase-js^2.106.2
QRreact-native-qrcode-svg^6.3.21
SVGreact-native-svg15.12.1
Compressionlz-string^1.5.0
Icons@expo/vector-icons^15.0.2
Fonts@expo-google-fonts/hanken-grotesk, @expo-google-fonts/public-sansโ€”
Localizationsrc/lib/translations/15-Language engine (modular registry)
Testsjest ~29.7.0 + ts-jest ^29.4.11TypeScript ~5.9.2

Cloud BFF (novadrive/package.json, version 0.1.0)

ConcernPackageVersion
Frameworknext14.2.35
Reactreact / react-dom^18
AI SDKai^6.0.191
Gemini provider@ai-sdk/google^3.0.79
OpenAI provider (spare)@ai-sdk/openai^3.0.65
QR (web)qrcode.react^4.2.0
Compressionlz-string^1.5.0
Iconslucide-react^1.16.0
Stylingtailwindcss^3.4.1

Build-time data pipeline (scripts/)

Python 3 + Overpass API โ†’ emergency_seed.db; validated by pytest in CI (poi-ingest.yml). Not bundled into the app at runtime โ€” the mobile app uses an inline seed.


5. Mobile App Architecture

novadrive-mobile is a client-heavy app: the offline core never requires a server.

flowchart TB
  subgraph mobile [novadrive-mobile]
    Plan[Trip + OSRM when online]
    J[Journey HUD + HoldSOS]
    Orch[emergencyOrchestrator]
    S[START Triage FSM]
    DB[(SQLite trauma POI)]
    G[GHP + QR + SMS 108]
    N[Naari Shakti engine]
    Sarthi[Sarthi KB + optional BFF]
    Plan --> J
    J --> Orch
    J --> S
    Orch --> DB
    S --> DB
    DB --> G
    N -.->|parallel| G
    Sarthi -.-> J
  end
  subgraph cloud [optional online]
    SB[(Supabase)]
    BFF[Sarthi BFF on Vercel]
  end
  mobile -.-> SB
  mobile -.-> BFF

Conventions


6. Expo Router Screen Map

app/
  _layout.tsx              # Root: fonts, providers, Stack
  index.tsx / splash.tsx   # Boot + splash
  home.tsx                 # Standalone home (also see (tabs)/explore)
  auth.tsx                 # Supabase sign-in (Guest mode bypasses)
  settings.tsx             # notifyEmergencyContacts toggle, voice sensitivity
  accessibility.tsx medical.tsx emergency-contacts.tsx
  sarthi.tsx               # full-screen Sarthi chat
  scan.tsx                 # bystander QR scanner
  naari-shakti.tsx         # women's portal
  journey.tsx
  (tabs)/
    _layout.tsx            # MargiTabBar + SarthiOverlayBridge + QuickMenuProvider
    explore.tsx            # Home tab (Drive mode, Quick SOS, Naari, brief, Sarthi FAB)
    drive.tsx              # Journey HUD โ€” Hold SOS lives here
    history.tsx            # Community hazards (~5 km), leaderboard
    profile.tsx            # Medical ICE, voice toggle, a11y
  emergency/
    selection.tsx          # incident type picker + cancel-SOS countdown
    activation.tsx         # 6s activation splash + runEmergencyOrchestrator
    locate.tsx             # GPS capture + reverse geocode
    triage.tsx             # START triage FSM UI
    route.tsx              # facility ranking list + Maps navigate
    response.tsx           # trauma HUD: timer, torch, ICE, QR, first aid
    packet.tsx             # GHP QR + relay URL
    relay.tsx              # bystander relay steps + RahVeer claim
  journey/ depart.tsx complete.tsx feedback.tsx
  settings/ journey-history.tsx
  trip/ _layout.tsx plan.tsx discover.tsx
  brief/ [slug].tsx
  rahveer/ index.tsx claim.tsx
  ngo/ index.tsx register.tsx

Canonical emergency flow: selection โ†’ activation โ†’ locate โ†’ triage โ†’ route โ†’ response โ†’ packet โ†’ relay.


7. Core Library Reference

novadrive-mobile/src/lib/ โ€” the brain of the app.

ModuleResponsibility
startTriageFSM.tsSTART protocol deterministic FSM โ€” initialState, getQuestion, applyAnswer
parseEmergencyText.tsoffline keyword โ†’ FSM slot prefill
facilitiesDb.tsexpo-sqlite seed (emergency_nodes) + rankFacilities(triage, lat, lng) (Haversine + tier filter)
ghp.tsbuildPacket, hashPayload, formatSms, encodeQrPayload, encodeQrRelayUrl, decodeQrPayload, verifyQrDecodedIntegrity
emergencySms.tsICE + 108 SMS intents, coords resolver (sos_hold, crash_detected templates)
emergency/emergencyOrchestratorPlan.tsplanEmergencyOrchestrator, buildMargiIceSmsBody, notifyEmergencyContacts gate
emergency/emergencyOrchestrator.tsrunEmergencyOrchestrator โ€” runs ICE + 108 + Maps in sequence
emergency/holdSosReleaseGrace.tsdebounce so a quick release does not skip incident picker
emergency/hospitalNavTarget.tsresolve Maps destination to the facility lat/lng
emergency/dispatchOrchestrator.tsoptional Supabase dispatch_events insert
emergency/incidentCatalog.ts incidentElapsed.tsincident types + live timer math
emergency/traumaSession.ts traumaAssistantOffline.tstrauma HUD session + offline first-aid copy
crashEngine.ts + crash/crashOrchestrator.ts crash/nativeCrashAdapter.tsaccel + speed fusion (journey-active only); native adapter stubbed
sarthiEngine.tscreateThread, sendMessage โ€” offline-first; cloud when online + BFF healthy + not a KB match
sarthiOffline.tsoffline reply wrapper โ†’ knowledge base
sarthi/sarthiKnowledgeBase.tsSARTHI_KB_ENTRIES pattern-matched offline answers (en/hi/ta)
sarthi/sarthiHealth.tscheckSarthiBffHealth() โ€” GET /api/sarthi/health, requires geminiReachable
sarthi/sarthiStrings.ts sarthiStatusCopy.tswelcome/fallback strings, status chip + connection banner copy
supabase/client.ts authSession.ts profileSync.tsSupabase client (SecureStore auth), session, profiles upsert
naariShakti/women's portal engine, stations, SMS, linking actions
voice/distress voice classifier (YAMNet path), default OFF
routing/OSRM + Nominatim trip routing
tts/narrator.tsspoken FSM prompts
storage.ts types.ts brand.ts relayChain.ts journeyDb.tspersistence, types, brand tokens, relay log, journey store

8. START Triage FSM

Deterministic finite state machine implementing the international START (Simple Triage And Rapid Treatment) protocol. The FSM, not the LLM, makes every medical decision.

States

AMBULATORY โ†’ BREATHING_CHECK โ†’ AIRWAY_REPOSITION โ†’ RESPIRATORY_RATE โ†’ PERFUSION_CHECK โ†’ MENTAL_STATUS โ†’ TAGGED

Transition rules (do not simplify incorrectly)

ConditionResult
Can walkGREEN โ†’ done
Not breathing โ†’ airway maneuver failsBLACK โ†’ done
Not breathing โ†’ airway maneuver succeedsโ†’ RESPIRATORY_RATE (NOT immediate RED)
Respiratory rate > 30/minRED
No radial pulse OR capillary refill โ‰ฅ 2sRED
Cannot follow simple commandsRED
All checks pass, non-ambulatoryYELLOW

Interface

export type TriageState =
  | 'AMBULATORY' | 'BREATHING_CHECK' | 'AIRWAY_REPOSITION'
  | 'RESPIRATORY_RATE' | 'PERFUSION_CHECK' | 'MENTAL_STATUS' | 'TAGGED';

export interface TriageContext {
  count: number;
  canWalk: boolean;
  breathing: boolean;
  respiratoryRateOver30: boolean;
  capillaryRefillOk: boolean;
  followsCommands: boolean;
  severeBleeding: boolean;
  triageResult: 'RED' | 'YELLOW' | 'GREEN' | 'BLACK';
  stateLog: string[];
}

UI: app/emergency/triage.tsx drives the FSM via AppContext.answerTriage, renders AnswerChips, and speaks prompts through tts/narrator.ts. Triage color feeds facility ranking.


9. Golden Hour Packet GHP

A structured, dispatch-ready brief (not raw chat logs). Built in src/lib/ghp.ts.

Schema

export interface GoldenHourPacket {
  id: string;                    // UUID-ish
  createdAt: string;             // ISO8601
  triage: 'RED' | 'YELLOW' | 'GREEN' | 'BLACK';
  location: { lat: number; lng: number; landmark?: string; nhCode?: string; nhKm?: number };
  victims: {
    count: number; canWalk: boolean; breathing: boolean;
    severeBleeding: boolean; capillaryRefillOk: boolean; followsCommands: boolean;
  };
  routing: { facilityName: string; facilityType: 'trauma' | 'hospital' | 'clinic';
             phone: string; etaMinutes: number; distanceKm: number };
  emergency: { dial: string; state: string; language: 'en' | 'hi' | 'ta' };
  relayChain: Array<{ at: string; deviceHint: string }>;
  integrity: string;             // SHA-256 hex digest (corruption check, not crypto signing)
}

Encoding and relay

StepMethod
1Build minimal payload
2Compress with lz-string
3Wrap in the ND1: envelope for relay URLs (encodeQrRelayUrl)
4Integrity via SHA-256 (hashPayload); verifyQrDecodedIntegrity rejects tampered/corrupt packets
5If the payload is too large for a reliable QR, the QR carries the minimal { id, triage, lat, lng, integrity } and the full GHP stays on screen + in SecureStore

SMS template (English)

ROAD EMERGENCY - Margi
Triage: RED | Victims: 1
Location: NH48 km 87 (13.082700, 80.270700)
Injuries: Not walking, breathing, severe bleeding suspected
TAKE TO: Apollo Trauma Center (12 min / 8.4 km)
Call: 108 (Tamil Nadu)
Generated offline via bystander relay.

The GHP screen (app/emergency/packet.tsx) renders the QR; a bystander scans it (in-app scan.tsx or the web /relay?p=ND1...), the packet is verified, the relay chain is appended, and an SMS to 108 is composed when signal returns.


10. Facilities SQLite And Trauma Tier Ranking

src/lib/facilitiesDb.ts opens an expo-sqlite database (emergency_seed.db, table emergency_nodes), seeds it on first launch from an inline SEED[] (~50 Chennai-corridor POIs) plus a POI_COORDS map, then ranks.

Schema

CREATE TABLE emergency_nodes (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  lat REAL NOT NULL,
  lng REAL NOT NULL,
  type TEXT NOT NULL,            -- hospital | clinic | police | ambulance
  trauma_tier INTEGER NOT NULL,  -- 1 trauma center, 2 hospital ER, 3 clinic
  emergency_phone TEXT,
  nh_corridor TEXT,
  state TEXT,
  verified INTEGER DEFAULT 0,
  verified_at TEXT
);
CREATE INDEX idx_coords ON emergency_nodes(lat, lng);
CREATE INDEX idx_tier ON emergency_nodes(trauma_tier);

rankFacilities(triage, lat, lng)

    • Filter by triage-appropriate trauma_tier (RED โ†’ 1,2 / YELLOW โ†’ 2,3 / GREEN โ†’ 3).
    • Compute Haversine distance to each candidate.
    • Sort ascending, take top 6, mark the first as recommended.
    • Offline ETA fallback: etaMinutes = (distanceKm / 40) * 60 (40 km/h ambulance average). Online uses OSRM duration.
Honesty: the NH48 verified pack is 50 OSM nodes (40 phone-verified) inside the Chennai corridor bbox. Outside it, the app runs baseline mode โ€” 108 + GPS + triage, no verified hospital routing until a regional pack ships. Naari uses Chennai demo police stations with 112 national fallback beyond ~150 km. Seed regeneration helper: scripts/generate-facilities-seed.mjs.

11. Emergency Orchestrator

The orchestrator turns one SOS into a coordinated burst of actions. Plan is pure/testable; the runner performs side effects.

  • Plan: src/lib/emergency/emergencyOrchestratorPlan.ts โ€” planEmergencyOrchestrator() + buildMargiIceSmsBody().
  • Runner: src/lib/emergency/emergencyOrchestrator.ts โ€” runEmergencyOrchestrator().
flowchart LR
  start[Activation done] --> coords[Resolve GPS coords]
  coords --> rank[rankFacilities -> nearest trauma POI]
  rank --> plan[planEmergencyOrchestrator]
  plan --> ice{notifyEmergencyContacts?}
  ice -->|yes & ICE set| iceSms[Open ICE SMS intent]
  ice -->|no| skip[skip ICE]
  iceSms --> s108[Open 108 SMS intent]
  skip --> s108
  s108 --> maps[Open Google Maps to facility lat/lng]
  maps --> trauma[Navigate to Trauma Response HUD]
  • notifyEmergencyContacts is a boolean in UserProfile.settings (default true in storage.ts, toggled in app/settings.tsx); the plan gates ICE SMS on it.
  • Maps navigation targets the facility coordinates (hospitalNavTarget.ts), not the user's pin โ€” a deliberate fix (see ยง26).
  • Triggered from app/emergency/activation.tsx after the countdown.

12. Hold SOS Quick SOS And Incident Tracker

All SOS entry points share one intentional path โ€” no premature SMS before an incident type is chosen.

EntryBehavior
Hold SOS (3s) on Drive HUDHoldSOSButton.tsx (haptics + progress ring) opens the Incident Tracker; holdSosReleaseGrace.ts prevents accidental skips on quick release
Quick SOS (Home)src/lib/home/quickSos.ts โ†’ Incident Tracker
Header SOSsame path

app/emergency/selection.tsx is the Incident Tracker (road accident / natural calamity) and includes a cancel-SOS countdown. After selection โ†’ activation.tsx (โ‰ˆ6s splash) โ†’ orchestrator runs.


13. Trauma Response HUD

app/emergency/response.tsx with src/components/emergency/TraumaResponseActionBar.tsx.

  • Live incident timer since SOS (incidentElapsed.ts).
  • Rear-flash torch via useTorch.ts (camera permission + torchOn) rendered through TorchCameraLayer.tsx (a hidden 1ร—1 CameraView with enableTorch). The torch logic and the camera layer are deliberately split (see ยง26).
  • Call ICE + Call Center FABs (contactActions.ts).
  • First-aid board (FirstAidBoard.tsx) + offline trauma copy (traumaAssistantOffline.ts).
  • Sarthi assistant available inline.

14. Naari Shakti Womens Safety Portal

Gender-gated (self-reported in Medical) women's safety lane with a saturated saffron + navy identity.

  • Routes: app/naari-shakti.tsx; components under src/components/naari/; logic in src/lib/naariShakti/.
  • Actions: SMS nearest police station, ICE alert, helpline 181, 2s hold-to-activate distress HUD with optional recording.
  • 112 national fallback when the user is far from the demo police seed.
  • Entry: Medical โ†’ set Female โ†’ Home NAARI SHAKTI card โ†’ enable portal โ†’ Safety Mode โ†’ hold Emergency Help 2s.

15. Crash And Distress Voice Engines

CrashEngine (crashEngine.ts)

Active only when journey.status === ACTIVE. Heuristic fusion:

    • Peak accelerometer above threshold within a window.
    • Speed before impact > 25 km/h.
    • Speed after < 5 km/h.
Output: a calm 15-second confirmation dialog (CrashCandidateModal.tsx). No automatic triage or 108 at timer zero โ€” the user confirms. Native OS crash APIs are stubbed (crash/nativeCrashAdapter.ts) unless a custom dev build supplies them.

Distress voice (voice/)

Optional YAMNet-based distress classifier, off by default (sensitivity setting in Profile). Even when triggered, the countdown does not auto-open the 108 SMS โ€” fail-safe against false positives. Evaluation: docs/VOICE_CLASSIFIER_EVAL.md.


16. Sarthi Assistant Offline And Cloud

Sarthi is the in-app AI helper. It is offline-first: a deterministic knowledge base answers without any network, and the cloud Gemini BFF is an enhancement.

Decision flow (src/lib/sarthiEngine.ts)

flowchart TD
  msg[User message] --> kb{KB match?}
  kb -->|yes, offline-first| offline[Knowledge base reply]
  kb -->|no| online{Online AND BFF healthy?}
  online -->|yes| cloud[POST /api/sarthi/chat -> Gemini]
  online -->|no| offlineFb[Offline reply + cloud-unavailable prefix]
  cloud -->|error| offlineFb
This offline-first design is exactly why a misconfigured BFF used to show the same canned intro for every message โ€” see the Sarthi entries in ยง26.

17. Sarthi BFF Internals

novadrive/ is a Next.js 14 app. Only two API routes matter.

src/lib/sarthi/aiConfig.ts

GET /api/sarthi/health (src/app/api/sarthi/health/route.ts)

Returns { ok, service, model, geminiConfigured, geminiReachable, provider, probeError? }, status 200 only when configured and the probe actually reaches Gemini. CORS enabled.

POST /api/sarthi/chat (src/app/api/sarthi/chat/route.ts)

A local helper novadrive-mobile/scripts/check-sarthi-bff.cjs hits both endpoints to validate a deployment from the dev machine.

18. Supabase Backend

Project Projectmargi (yllcmksndrhlektbjvcu). Migrations under supabase/migrations/.

MigrationContents
20260528_phase3_core.sqlTables profiles, volunteer_providers, dispatch_events; RLS policies; handle_new_user() trigger on auth.users
20260528_phase3_security_fix.sqlRevokes public RPC execution on handle_new_user()

Mobile integration:


19. Web Mirror And Bystander Relay

novadrive/src/app/ mirrors the emergency flow for laptop demos and, crucially, hosts the bystander relay:


20. Brief Site Build Pipeline

This very page is generated by docs/site/build-docs.js:

    • Reads docs/MARGI_MASTER_BRIEF.md (this file).
    • Splits out fenced mermaid blocks, converts the rest of the markdown to HTML with a small custom parser (headers, tables, lists, code fences, blockquotes, inline code, bold/italic, links, horizontal rules).
    • Generates the sidebar TOC from lines matching N. Label.
    • Writes docs/site/index.html and copies MARGI_MASTER_BRIEF.md (+ PDF if present) for download.
    • Mermaid renders client-side; a print stylesheet produces a clean PDF via "Save as PDF".
Run locally: node docs/site/build-docs.js. Deployed by the
root Vercel project (vercel.json: buildCommand: node docs/site/build-docs.js, outputDirectory: docs/site).

Anchor rule: a heading ## N. Title becomes id n-title (lowercased, punctuation stripped, spaces โ†’ hyphens). Keep headings ASCII and punctuation-free so the TOC links resolve.


21. Environment Variables

Never commit real values. Names only:

novadrive-mobile/.env

NamePurpose
EXPO_PUBLIC_SARTHI_API_URLSarthi BFF origin (the novadrive Vercel project, not the brief site)
EXPO_PUBLIC_SUPABASE_URLSupabase project URL
EXPO_PUBLIC_SUPABASE_ANON_KEYSupabase publishable key
EXPO_PUBLIC_TRAUMA_DISPATCH_URLoptional HTTP dispatch endpoint (trauma)
EXPO_PUBLIC_POLICE_DISPATCH_URLoptional HTTP dispatch endpoint (police)

novadrive/.env

NamePurpose
GOOGLE_GENERATIVE_AI_API_KEYGoogle AI Studio key for Gemini (server-side only)
SARTHI_GEMINI_MODELoptional model override (default gemini-2.5-flash)
AI_GATEWAY_API_KEY / VERCEL_OIDC_TOKENoptional alternative providers via AI Gateway

After changing mobile .env, restart Metro with npx expo start --clear (env is inlined at bundle time).


22. Local Development Setup

# Mobile (primary)
cd novadrive-mobile
npm install --legacy-peer-deps
npx expo install react-native-worklets babel-preset-expo
cp .env.example .env            # add Sarthi BFF + Supabase keys (optional)
npm test                        # Jest unit tests
npm run typecheck               # tsc --noEmit
npm run android                 # dev build + Metro (Android Studio JBR on Windows)
# or Expo Go on same Wi-Fi:
npm run start:lan

# Cloud BFF
cd ../novadrive
npm install
npm run dev                     # http://localhost:3000
npm run dev:lan                 # -H 0.0.0.0 -p 3000 for device testing

# Brief site
node docs/site/build-docs.js    # regenerate docs/site/index.html

Recommended SOS demo: Guest โ†’ Trip โ†’ Start Driving โ†’ calibration โ†’ hold SOS 3s โ†’ pick incident โ†’ activation โ†’ trauma response (verify ICE FAB, timer, torch, Maps opens toward the hospital, not the user pin).


23. Deployment Mobile APK

GitHub Actions .github/workflows/android-apk.yml:

  • Triggers on workflow_dispatch and published release.
  • Node 20 + Java 17 + Android SDK โ†’ cd novadrive-mobile && npm install โ†’ expo prebuild --platform android --clean โ†’ ./gradlew assembleDebug.
  • Uploads artifact margi-debug.apk; attaches it to the GitHub Release when triggered by a release.
Local APK: npm run android:apk (needs JDK 17+; scripts patch the Foojay Gradle resolver and resolve the JDK). Judges install the APK and tap
Continue as Guest.

24. Deployment Sarthi BFF

Deploy novadrive/ as a separate Vercel project.

From GitHub (recommended)

    • Import Stormynubee/Margi at vercel.com/new.
    • Root Directory โ†’ novadrive (not repo root).
    • Framework: Next.js (auto).
    • Build & Output Settings: Install npm install, Build npm run build, Output Directory empty (delete any docs/site value โ€” that belongs to the brief-site project).
    • Env var GOOGLE_GENERATIVE_AI_API_KEY (Production + Preview).
    • Deploy; note the URL.

From CLI

cd novadrive
npm install
npx vercel --prod
# add GOOGLE_GENERATIVE_AI_API_KEY in the dashboard, then redeploy

Verify

curl https://YOUR-BFF.vercel.app/api/sarthi/health
# expect: { "ok": true, "geminiConfigured": true, "geminiReachable": true, "model": "gemini-2.5-flash" }
node novadrive-mobile/scripts/check-sarthi-bff.cjs https://YOUR-BFF.vercel.app

Then set EXPO_PUBLIC_SARTHI_API_URL to that URL (no trailing slash) and restart Expo.


25. Testing Strategy


26. Engineering Problem And Solution Log

This is the institutional memory โ€” every non-trivial bug solved while building Margi, with the real root cause and fix. Read this before re-touching the relevant subsystem.

26.1 Metro bundling failed on useTorch.ts (JSX in a .ts file)

  • Symptom: Android Metro bundling error; torch hook would not compile.
  • Root cause: JSX (the hidden CameraView) lived in a .ts file; only .tsx may contain JSX.
  • Fix: split responsibilities โ€” src/hooks/useTorch.ts holds permission + torchOn logic only; the JSX moved to src/components/emergency/TorchCameraLayer.tsx. TraumaResponseActionBar.tsx renders the layer. Lesson: keep hooks JSX-free, or use .tsx.

26.2 SOS stuck on "ADVANCING NOW..." at timer 0

  • Symptom: Activation countdown reached 0 and froze on "ADVANCING NOWโ€ฆ".
  • Root cause: navigation was attempted before the router was ready, and the orchestrator ran before navigation, blocking the transition.
  • Fix: in app/emergency/activation.tsx, navigate first, then run the orchestrator, and guard navigation with useRootNavigationState(). Also corrected the triage type passed to the orchestrator.

26.3 Trauma screen crash โ€” FirstAidBoard missing

  • Symptom: opening the trauma response screen crashed.
  • Root cause: app/emergency/response.tsx referenced FirstAidBoard without importing it.
  • Fix: import FirstAidBoard from src/components/emergency/FirstAidBoard.tsx.

26.4 Sarthi returned the same canned reply for every message

  • Symptom: every message ("how are u", "I need sleep") returned the identical intro/fallback line, while the chip claimed the BFF was online.
  • Root causes (stacked):
1. Health lied โ€” the old health route returned ok: true when the API key merely
existed, without ever calling Gemini. 2. Chat failed silently โ€” the mobile send path caught BFF errors and fell back to getFallbackMessage() (the same boilerplate every time). 3. Wrong BFF URL โ€” the app was pointed at the static brief site (roadsafetyhackathon-six / margi-tau), which 404s on /api/sarthi/. - Health now does a real Gemini probe (probeSarthiGemini) and returns geminiReachable; the mobile health check requires geminiReachable !== false. - On cloud failure, the offline answer is prefixed with a visible "Cloud Sarthi unavailable" banner instead of silently repeating boilerplate; KB intents stay offline-first by design. - EXPO_PUBLIC_SARTHI_API_URL points at the dedicated novadrive project (e.g. novadrive-eta.vercel.app).

26.5 Fake "GEMINI BFF ONLINE" chip

26.6 UTF-8 BOM in the Vercel env key (character at index 0 has value 65279)

26.7 Gemini quota exceeded on gemini-2.0-flash

26.8 Health probe false-negative ("Empty model response" / "Unexpected probe reply: i")

26.9 Maps opened to the user's pin instead of the hospital

26.10 Premature SMS before incident selection / false alerts

26.11 Documentation drift (PWA vision vs shipped native app)

26.12 Windows parallel Gradle and C++ compiler OOM crash

26.13 Prominent 15-language selector reactive translation sync


27. Replicate From Scratch Playbook

To rebuild Margi from zero:

    • Monorepo: create novadrive-mobile/ (Expo), novadrive/ (Next.js), docs/, supabase/migrations/, scripts/, .github/workflows/. Add the two vercel.json files (root โ†’ brief site; novadrive/ โ†’ Next.js).
    • Mobile scaffold: npx create-expo-app (SDK 54), add expo-router, the dependency set in ยง4, the @/ alias, and react-native-reanimated/plugin in babel. Configure app.json (com.margi.app, margi://, camera/location/SMS permissions, plugins).
    • Offline core first (TDD): build startTriageFSM.ts (ยง8) โ†’ facilitiesDb.ts + seed (ยง10) โ†’ ghp.ts encoder + integrity (ยง9), each with .test.ts. This is the demo-critical path; it must work in airplane mode.
    • SOS path: HoldSOSButton โ†’ Incident Tracker (emergency/selection) โ†’ activation (navigate-then-orchestrate, router guard) โ†’ emergencyOrchestratorPlan/emergencyOrchestrator (ICE SMS โ†’ 108 SMS โ†’ Maps to facility) โ†’ trauma HUD (response.tsx + TraumaResponseActionBar + useTorch/TorchCameraLayer) โ†’ packet (QR) โ†’ relay.
    • Sarthi: offline KB (sarthiKnowledgeBase.ts) + engine (sarthiEngine.ts) offline-first; health gate (sarthiHealth.ts) requiring geminiReachable. Then the BFF (ยง17): aiConfig.ts (BOM-safe key, explicit provider, gemini-2.5-flash), /api/sarthi/health (real probe), /api/sarthi/chat.
    • Naari Shakti (ยง14), crash + voice off-by-default (ยง15).
    • Supabase (ยง18): apply migrations, wire client with SecureStore, keep Guest mode.
    • CI/CD (ยง23โ€“ยง25): ci.yml, android-apk.yml, poi-ingest.yml.
    • Brief site (ยง20): write this file; node docs/site/build-docs.js; deploy root project.
    • Wire env (ยง21) and verify health endpoints before claiming "online".
Golden rules learned the hard way: keep JSX out of .ts; navigate before side effects and guard the router; never report a service "online" without probing it; sanitize CLI-uploaded secrets (BOM); pick a model the key actually has quota for; route Maps to the destination, not the origin; never auto-SMS without explicit user confirmation.

28. Honesty Boundaries And Rejected Scope

TopicDemo truth
POI databaseNH48 verified pack (50 OSM nodes, 40 phone-verified) in the Chennai bbox; baseline 108 + GPS + triage elsewhere
Naari policeChennai demo stations; 112 national fallback beyond the seed
"production" tagv2.0.0-production = integration milestone, not clinical production
Sarthi31+ offline KB entries; cloud LLM optional
Genderself-reported on device, unverified in P0
Crash/voicesensor heuristics + experimental voice; no OS crash API in P0; no auto-SMS

Deliberately rejected (do not reintroduce without a team vote)

Rejected ideaWhy killed
Ultrasonic V2Vbrowsers block mic without gesture; road noise; unreliable demo
BLE / DTN meshbrowsers can't advertise BLE; iOS has no Web Bluetooth
Wi-Fi Direct beaconsnot available in web/RN-managed APIs
Always-on crash audio MLfalse positives, privacy, battery, research-grade scope
Curvature/anxiety physics enginewrong problem statement; OSM geometry too sparse
Background volunteer GPSunreliable background tracking; needs gov integration
Auto-dial 108platform policy; user must tap Send/Call

Strategic lesson: judges score reliable offline triage with valid POI data โ€” not machines chirping at each other. Margi's differentiator is information surviving dead zones (GHP + human QR relay).


29. Glossary

TermMeaning
STARTSimple Triage And Rapid Treatment โ€” accident-scene triage protocol
FSMFinite State Machine โ€” deterministic step logic
GHPGolden Hour Packet โ€” structured 108-ready emergency brief
ND1:Margi relay payload envelope (lz-string compressed) carried in QR / /relay?p=
trauma_tierhospital capability: 1 trauma center, 2 hospital ER, 3 clinic
Haversinegreat-circle distance between two GPS points
ICEIn Case of Emergency contact
108 / 112 / 181India ambulance / unified emergency / women's helpline
Naari ShaktiMargi's women's safety lane
BFFBackend-For-Frontend โ€” the novadrive Next.js server fronting Gemini
SarthiMargi's in-app AI assistant (offline KB + optional Gemini)
OSRMOpen Source Routing Machine โ€” road ETA
RLSRow Level Security (Supabase/Postgres)
Golden hourfirst ~60 minutes after trauma โ€” survival-critical
RoadSoSthe hackathon problem statement โ€” emergency services access
CoERSCentre of Excellence for Road Safety, IIT Madras

ResourceLink
GitHub repohttps://github.com/Stormynubee/Margi
Live brief site (this page)https://roadsafetyhackathon-six.vercel.app
Android APK workflowhttps://github.com/Stormynubee/Margi/actions/workflows/android-apk.yml
Google AI Studio (Gemini key)https://aistudio.google.com/apikey
CoERS eventhttps://coers.iitm.ac.in/event/national-road-safety-hackathon-2026/
Unstop (Indian track)https://unstop.com/hackathons/road-safety-hackathon-2026-iit-madras-1680515
OpenStreetMap / Overpasshttps://www.openstreetmap.org ยท https://overpass-api.de
OSRM demohttp://router.project-osrm.org
START referencehttps://en.wikipedia.org/wiki/Simple_triage_and_rapid_treatment

In-repo: docs/CANON.md (scope truth), docs/ARCHITECTURE.md, docs/PHASE3_SETUP.md, JUDGE_START_HERE.md, docs/SUBMISSION.md, novadrive-mobile/README.md, novadrive-mobile/docs/DEVICE_SMOKE_MATRIX.md.


31. Multilingual Support and Searchable Tactile Grid Selector

To ensure extreme accessibility across diverse Indian highway demographics and global locales, Margi features a robust, fully localized 15-Language Engine. This system empowers users and first responders to switch the interface language reactively on the fly.

Shipped Languages

LocaleNative NameEnglish NameRegion/Country
enEnglishEnglishGlobal
hiเคนเคฟเคจเฅเคฆเฅ€HindiIndia (National)
taเฎคเฎฎเฎฟเฎดเฏTamilIndia (Tamil Nadu)
esEspaรฑolSpanishGlobal
frFranรงaisFrenchGlobal
deDeutschGermanGlobal
zhไธญๆ–‡MandarinGlobal
jaๆ—ฅๆœฌ่ชžJapaneseGlobal
arุงู„ุนุฑุจูŠุฉArabicGlobal
ptPortuguรชsPortugueseGlobal
ruะ ัƒััะบะธะนRussianGlobal
bnเฆฌเฆพเฆ‚เฆฒเฆพBengaliIndia / Bangladesh
paเจชเฉฐเจœเจพเจฌเฉ€PunjabiIndia / Pakistan
mrเคฎเคฐเคพเค เฅ€MarathiIndia (Maharashtra)
teเฐคเฑ†เฐฒเฑเฐ—เฑTeluguIndia (Andhra/Telangana)

Key Capabilities

    • Searchable Tactile Grid Selector: Built as a full-width, expandable UI component in app/auth.tsx. It displays each language in a distinct, high-impact tile featuring native labels, English translations, and country flags. An embedded real-time search input filters the grid instantly.
    • Haptic Touch Integration: Utilizes native haptics (expo-haptics) to trigger medium and success haptic bumps on interaction, providing physical confirmation of language changes to the user.
    • Modular Translation Registry: The dictionaries are separated cleanly into src/lib/translations/ (en.ts, hi.ts, ta.ts, global_dicts.ts) and unified in index.ts.
    • Reactive App-Wide Translation: The selected locale is managed in the app-wide settings state (useApp()). Core auth screens and subsequent pages re-render reactively using the getAuthString() helper, keeping the active language synchronized across the entire user experience without requiring an app reload.

Appendix A โ€” Original Planning Brief (Historical PWA Vision)

The content below is the original aspirational brief. Margi shipped as a native Expo Android app, not the Next.js PWA + sql.js described here. Sections 1โ€“30 above are canonical. This appendix is retained for learning and provenance โ€” it documents the architecture we explored, the reasoning, and the rejected sensor-mesh path.

A.1 Core innovation (three pillars)

flowchart LR
  P1["1. START TRIAGE CHATBOT
(AI + FSM)"] --> P2["2. TRAUMA-TIER ROUTING
(not nearest pin)"] --> P3["3. GOLDEN HOUR PACKET (GHP)
+ QR HUMAN RELAY"]

The three pillars (offline START triage, capability-based trauma-tier routing, and the GHP carried through dead zones by a human QR relay) survived from this vision into the shipped app โ€” only the delivery platform changed from PWA to native Expo.

A.2 Original PWA system architecture

flowchart TB
    subgraph Client["Margi PWA (Next.js 14)"]
        Chat[Triage Chat UI]
        FSM[START Triage FSM]
        Router[Offline Spatial Router]
        GHP[GHP Builder + Compressor]
        QR[QR Generator / Scanner]
        IDB[(IndexedDB Relay Store)]
        SW[Service Worker / Serwist]
        SQL[(sql.js + emergency_seed.db)]
    end
    Chat --> FSM --> Router --> SQL
    FSM --> GHP --> QR --> IDB
    SW --> SQL

In the shipped app: Next.js PWA โ†’ Expo Router native; sql.js โ†’ expo-sqlite; IndexedDB relay โ†’ SecureStore + /relay; Serwist service worker โ†’ native offline (assets bundled in the APK); Web Speech API โ†’ expo-speech + optional YAMNet voice.

A.3 Original build-time data pipeline

OpenStreetMap -> Overpass API (nwr: hospitals, clinics, police, ambulance)
  -> ingestCorridors.py (parse names/phones/types, assign trauma_tier, tag corridor/state, manual verify CSV)
  -> emergency_seed.db -> bundled + submitted

This pipeline still exists under scripts/ (validated by poi-ingest.yml) and produces the verified NH48 pack, but the runtime app uses an inline seed in facilitiesDb.ts rather than loading the built DB file.

A.4 Original full runtime sequence

sequenceDiagram
    participant V as Victim Phone (Offline)
    participant FSM as START FSM
    participant DB as SQLite
    participant GHP as GHP Builder
    participant B as Bystander Phone
    participant SMS as 108 SMS
    V->>FSM: Start Emergency + GPS
    FSM->>FSM: Triage questions
    FSM->>DB: Query trauma-tier POIs
    DB-->>FSM: Ranked facilities
    FSM->>GHP: Build packet + hash
    GHP-->>V: Display QR + brief
    B->>V: Scan QR
    Note over B: Drives until signal returns
    B->>SMS: Auto-compose 108 message

End of Margi โ€” Complete Replication Bible ยท Team NovaDrive ยท CoERS Road Safety Hackathon 2026.
Maintainer note: docs/CANON.md wins on any scope dispute. Update the Problem and Solution Log (Section 26) whenever a non-trivial bug is fixed, and rebuild the brief site with node docs/site/build-docs.js.