Inspiration
Small and mid-size carriers in Southern Ontario run dispatch out of spreadsheets, phone calls, and a TMS that doesn't talk to the ELD. The costly decisions are the quiet ones: adding a second load to a truck that looks empty but isn't, sending a driver who will run out of hours two stops in, or finding out a POD was never signed three weeks later when the invoice bounces.
We wanted a dispatcher to see the full consequence of a decision — added distance, added on-duty time, corridor spread, weight and pallet-position fill, remaining hours — before committing it, rather than after.
What it does
RoadStar DispatchOS is a unified dispatch workspace covering the whole loop:
- Dispatch and consolidation — a load can run as its own trip or be consolidated onto a truck that still has room. Stops are re-sequenced and the cost is priced first: added distance, added on-duty time, delivery-point spread, resulting weight and pallet fill, and the driver's remaining hours.
- Explainable planning — a fleet-wide optimizer scores deadhead, on-time performance, HOS buffer, and future positioning, with hard constraints kept separate from soft scores. Every recommendation is approval-first and reversible.
- Live operations — SSE telemetry, geofences, automated detention, track-and-trace, and an Ontario 511 incident feed that can propose ETA changes an operator must approve.
- Exceptions inbox — equipment, pickup, HOS, detention, traffic, and unsigned-POD exceptions with stable IDs and acknowledgement state.
- 3D trailer loading — stop-aware load planning with real stack layers, floor-bearing and fragility limits, irregular cargo, locks, manual placement, and an unloading replay, backed by a Java xflp solver with a browser fallback.
- Driver workflow — a separate driver experience with its own paperwork tab and in-app proof-of-delivery photo capture.
- Three AI features — a dispatcher copilot that answers five fixed questions in plain language, POD capture where an unsigned document automatically becomes an exception, and rate-confirmation intake that pre-fills the new-load form from a PDF or a photo.
How we built it
React 19 and TypeScript on Vite for the web app. Supabase (Postgres, Auth, Storage, Realtime) for state, with forward-only migrations, RLS on every exposed table, tenant predicates, and security-definer RPCs for anything privileged. Snapshot saves go through a compare-and-swap RPC, so a stale writer gets an explicit revision conflict instead of silently overwriting a colleague.
Four services run side by side: the web app, an SSE telemetry simulator, a Java xflp loading solver, and an integration gateway fronting Ontario 511 and the AI provider. Everything is containerized with Docker Compose, with an optional self-hosted OSRM routing stack built from the Geofabrik Ontario OpenStreetMap extract.
The AI layer runs on SPUR Compute (OpenAI-compatible) behind the gateway, under one rule:
The model narrates or extracts; code computes and decides.
A model never creates, changes, or deletes a record. Hours of service, ETAs, distances, detention minutes, and dates are computed in code. The browser builds deterministic facts, the model only phrases them, and a validator checks every citation, identifier, and measured number in the answer against those facts. The API key never reaches the browser — the browser authenticates with its Supabase session, and the gateway calls the provider using the caller's own token, so RLS applies to every read and write it makes on a user's behalf.
Testing: unit suites for the gateway spawned against fake provider and Supabase servers, a Postgres harness that runs the migration in a container and asserts per-role access with pinned SQLSTATEs, Playwright journeys including accessibility sweeps, and a deterministic release gate.
Challenges we ran into
The vision model lied about token size. The provider's vision tier estimates tokens from the base64 text, not the image, so a 98 KB photo that really costs about 2,900 tokens was refused against a 32k context window. Small test photos passed, so the bug only surfaced with realistic ones. We added a browser encode budget that steps resolution and quality down until the payload fits, plus a hard gateway limit that refuses oversized requests without ever calling the model.
A blank signature line reads as "signed" if you ask a model directly. Asking for a boolean gave wrong answers. We changed the model's job to reporting visual evidence only — handwritten mark, typed name only, blank line, or no signature area — and derived the boolean in code.
Grounding a copilot without free text. We restricted it to five fixed questions and validate the output: citations must resolve, identifier-shaped tokens must exist in the directory, and every measured number must appear in the supplied facts.
Making security guards real. We mutation-tested each new guard by disabling it in turn and requiring the matching test to fail. One surviving mutant exposed a missing scenario — an assigned driver attaching a rate confirmation — which we then covered.
Production-only bugs. The pdf.js worker was served as application/octet-stream under nosniff, which broke PDF import in production while working fine under Vite. The quality gate now serves the built bundle through the production gateway and fails on any script with a non-executable MIME type.
Accomplishments that we're proud of
Live verification against the provider: all five copilot questions grounded, 15/15 fields extracted from a text rate confirmation, 14/15 from a photographed and scanned one (temperature correctly left null), and 4/4 on signed/unsigned POD detection — with the built bundle containing no key and no provider host.
Every AI feature has a non-AI fallback, so a provider outage degrades the product instead of blocking dispatch. And the database enforces authorization itself: driver accept, start, and decline are validated and audited inside Postgres, not in the client.
What we learned
Drawing the line between what a model does and what code does was the single highest-leverage decision in the project. Every reliability problem we hit came from asking a model for a judgment instead of an observation.
We also learned that a security guard you haven't tried to break is a guess. Mutation testing changed what we believed about our own coverage.
What's next for RoadStar DispatchOS
Provisioning the Ontario OSRM graph and enabling strict route verification in production; real ELD and TMS vendor adapters through the outbox and idempotency contracts already in the schema; a BOL importer on the extraction schema that is implemented but has no UI yet; multi-currency rate handling; and expanding beyond the 44 Southern Ontario city centres currently supported.
Stated plainly: HOS is planning guidance, not a certified ELD; telemetry in the demo is labelled as simulated; and TMS samples are read-only demo data. Those boundaries are documented in the repo, and closing them is the roadmap.
Built With
- css
- docker
- docker-compose
- github-actions
- java
- node.js
- openstreetmap
- osrm
- pdf.js
- playwright
- postgresql
- react
- server-sent-events
- supabase
- three.js
- typescript
- vite
- vitest
- xflp
Log in or sign up for Devpost to join the conversation.