A depot to door parcel courier for India, built as one clean backend project I could actually finish. Sellers drop parcels at the depot in their city, we sort and batch them by destination, long lanes go by train and short ones by truck, and a delivery partner takes them the last mile with an OTP handover. The name comes from daak, the old postal relay, plus dash. Purani daak, nayi raftaar.
demo_video.mp4
- One depot per serviced city (12 cities seeded), every depot sits near a railway.
- We never pick up. The seller brings parcels to the depot. That keeps the model honest and the first mile free.
- All parcels on the same origin to destination lane ride together as one batch. That is the whole cost story of a 3PL: one truck, many parcels.
- Delivery is confirmed by an OTP. The partner presses "send OTP" at the doorstep, the receiver gets an automated call with the code, the partner types it in.
| service | port | job |
|---|---|---|
| gateway | 8080 | JWT check at the edge, rate limit, injects trusted X-User headers |
| auth | 8081 | login, refresh, onboarding, credential mails via Gmail SMTP |
| tracking | 8082 | depots, parcels, batches, partner flow, OTP call via 2factor.in |
| frontend | 5173 (dev) | React + Vite + Tailwind, multilingual (en, hi, mr, ta, bn, te) |
- admin sees the whole network: a map of India with every depot pinned, click a depot
for active orders, stock inside, and what left today by truck or train. The seeded
password is overwritten from
ADMIN_PASSWORDon boot. - seller is onboarded with business name, GST number, address, contact email and phone. The system issues a uniform login (seller123456@daakdash.in) and mails the credentials. Sellers book single parcels or paste a CSV backlog.
- city depot operator is scoped to one city. Scans in dropped parcels, dispatches lanes (mode picked automatically by distance), receives inbound batches.
- delivery partner is scoped to one city too. Goes online, gets up to 12 parcels, cannot go offline until they are delivered. Send OTP at the door, deliver or mark the receiver absent (retry after 3 days).
Recipients never log in. They track by number on a public page with the depot timeline.
BOOKED -> AT_ORIGIN_DEPOT -> SORTED -> IN_TRANSIT -> AT_DEST_DEPOT -> OUT_FOR_DELIVERY -> DELIVERED, with RECEIVER_ABSENT looping back to the destination depot. The transitions are guarded in one place and the parcel row carries a version column, so two operators racing the same parcel produce one winner and one clean conflict instead of a silent overwrite.
docker compose up -d --buildWithout mail or SMS keys everything still works: the credential mail and the OTP are written to the service logs instead, so you can test the full flow on a laptop.
The landing page (frontend/src/pages/Landing.tsx, sections in
frontend/src/components/landing/) opens on a pre-rendered low-poly world. It is one connected
landscape: the depot, a serpentine truck road over the hills, the railhead, the city where
partners deliver, and the HQ tower by the sea. All of it is in one 50 s film. Scrolling steps
through the scenes while the page is locked. Each step flies the camera to the next scene, and
that scene then loops seamlessly. Jumps of several scenes chain the flights without a cut, and
going back plays the same flights on a reversed cut of the film. Smooth scrolling and reveals
use Lenis and GSAP. With prefers-reduced-motion, the page skips the lock and the autoplay.
The world is generated by a Blender script, so you re-render it instead of hand-editing it:
B=~/Applications/blender-4.5.14-linux-x64/blender # Blender 4.5 LTS
$B -b -P blender/daakdash_world.py -- --mode stills --scale 50 --out /tmp/dd # one still per scene
$B -b -P blender/daakdash_world.py -- --mode anim --out /tmp/dd # 1200 PNG frames (~1 h on an RTX 2050)
$B -b -P blender/daakdash_world.py -- --mode tiles --out /tmp/dd/tiles # service tile renders
$B -b -P blender/daakdash_world.py -- --mode hotspots --out /tmp/dd # hotspot screen coords
blender/encode_film.sh /tmp/dd/frames # forward + reversed cuts and posterScene timing (LOOP/TRANS frames) lives in the script and in
frontend/src/components/landing/heroFilm.ts. Change both together. After you move a
camera, paste the new hotspot coordinates into heroFilm.ts.
mvn -f tracking/pom.xml testTestcontainers spins up a real Postgres for the repository tests. The state machine has its own unit test. Frontend is typechecked with tsc.