Loop v1 — The Visibility Layer. Courier-agnostic tracking and proof-of-delivery backend for the Kaduna pilot.
# Loop v1 Backend — The Visibility Layer
A courier-agnostic tracking and proof-of-delivery backend for Nigerian sellers,
built for the **Kaduna pilot**. Loop does not own couriers, does not match
riders to sellers, and does not touch payments — it makes deliveries a
seller already arranges *visible*: to the seller, to the rider doing the
run, and to the customer waiting for their package.
Three consumers talk to this API:
1. **Seller dashboard** — OTP-authenticated, manages riders and deliveries.
2. **Rider job page** — opaque-token-authenticated, no rider account/app.
3. **Public customer tracking page** — unauthenticated, scoped to one
delivery via an unguessable link.
---
## 1. Tech stack
| Concern | Choice | Why |
|---|---|---|
| Runtime | Node.js + TypeScript (strict) | Type safety without a heavier runtime |
| Framework | Express | Small, well-understood, easy to keep "boring" |
| Database | PostgreSQL + Prisma | Relational integrity (unique constraints do real work here — see idempotency), migrations, typed queries |
| Validation | Zod | Runtime validation that also gives us static types for free |
| Seller auth | OTP + revocable JWT session | No passwords in v1; sessions are server-revocable (logout, multi-device) unlike a pure stateless JWT |
| Rider/tracking auth | Opaque random tokens, SHA-256 hash at rest | Bearer tokens embedded in links; never recoverable from the DB if it leaks |
| Logging | Pino | Structured, fast, redaction built in |
| Docs | OpenAPI 3 + Swagger UI (dev only) | `docs/openapi.yaml`, served at `/docs` |
| Tests | Vitest + Supertest | Fast, ESM/TS-native, integrates cleanly with a real Postgres test DB |
Deliberately **not** used: microservices, Kubernetes, Kafka, GraphQL,
Redis, blockchain. A single Postgres-backed modular monolith is the right
amount of machinery for one pilot city.
---
## 2. Project structure
```
src/
config/ env validation (Zod), Prisma client, logger
controllers/ thin HTTP layer — parse req, ca …