Logo Lanfrica

Athurlux/pearl-trails

Domain:

digital infrastructure

Record type:

software
Creator:
Ath
Host:
Lodge and campsite booking for Uganda. Next.js 16 + TypeScript on Cloudflare Workers, Neon Postgres, Drizzle. Real availability with a Postgres exclusion constraint, idempotent booking, staff ops console, 144 tests. # Pearl Trails *Stays that stay with you.* Lodges, campsites and experiences across Uganda — the Pearl of Africa. **Pearl Trails MVP v1.0.** A traveller can discover a stay, request a booking, plan their trip and return to it. Staff can sign in, review what came in, respond to it, and change the catalogue — with every consequential action recorded. There is no payment layer, and the product never implies one. See Payments. ## Release 7 — Operations Console The internal tool that makes the product operable, at `/ops`. ``` Staff sign in ↓ Overview requests waiting · arrivals in 7 days · recent activity ↓ Bookings search, filter, page — all in Postgres, all in the URL ↓ Booking traveller, stay, experiences, estimate, internal notes, history ↓ Act confirm · cancel · expire — only the legal moves are offered ``` ### Authentication Staff sign in with an email and password. Sessions are a random 260-bit token in an `HttpOnly`, `SameSite=Lax`, `Secure` cookie; only a hash of it is stored, and expiry is enforced in the query rather than in application code. Passwords are PBKDF2-HMAC-SHA256 with a per-password salt. Accounts are created interactively — there is no seeded default account, because a committed default credential is a public one: ```bash npm run staff:create ``` Failed sign-ins are throttled per account rather than per IP: an IP key is defeated by rotating addresses and punishes shared connections, which in Uganda is the common case. `docs/decisions/006-staff-authentication.md` records why this is purpose-built rather than a library, and — importantly — states plainly that **Cloudflare Workers caps PBKDF2 at 100,000 iterations**, which is below current OWASP guidance. That is the weakest part of the release, and moving to Argon2id via a maintained library is the upgrade path. ### Authorisation Every `/ops` page and every operations action begins with `requireStaff()`, which returns the signed-in staff member or red …