One app, several experiences
The mock
/app demo and the pitch decks are intentionally separate from /live. Only /live and /ops touch real customer PII, and only they are behind the auth gate.Stack
- Next.js 16 (App Router, React 19, Turbopack). Dynamic route params are async (
params: Promise<{ id }>). SeeAGENTS.md, this Next.js has breaking changes vs. older versions. - Tailwind CSS v4 (
@themetokens insrc/app/globals.css), TypeScript strict,@/*→src/*. - Neon Postgres (
@neondatabase/serverless), the source of truth for/live. - Upstash Redis (REST), SMS conversation store + automation flags; falls back to an in-memory
Mapwhen unconfigured. - Anthropic Claude: the SMS bot brain and the in-OS assistant.
- Twilio: inbound/outbound SMS, routed through an A2P 10DLC Messaging Service.
- Hosted on Vercel (auto-deploy from
master).
The server-only boundary
Everything undersrc/lib/server/** is marked import "server-only" and must never be imported from a client component. Secrets are read only through src/lib/server/env.ts. Pure, client-safe analytics live in src/lib/live-data/compute.ts; the DB loaders live in src/lib/live-data/index.ts (React cache()’d, server-only).
Request flows
1
Inbound SMS
Twilio POSTs to
/api/sms/webhook → signature is verified → the message is persisted up front → the bot brain (bot.ts) runs its tool loop against bot-tools.ts → a TwiML reply is returned synchronously. The bot answers customers via TwiML, so the only outbound sendSms path is the ops manual reply.2
Ops manual reply / takeover
/live/inbox → /api/ops/reply → sendSms() (routed through the Messaging Service when TWILIO_MESSAGING_SERVICE_SID is set). Sending auto-takes-over the thread.3
OS screens
/live/* server components call the live-data loaders → Neon. Mutations go through gated, atomic API routes under /api/live/* and append an audit_events row.4
Automation chain
reorder →
sendInvoice (Clover adapter) → on payment (onInvoicePaid) → notify clinic/owner (Twilio) → optional Uber dispatch. Selection of stub vs. real provider happens only in adapters/index.ts.Auth gate
A single shared password (OS_PASSWORD) gates /live and /ops. /login sets a signed httpOnly session cookie (OPS_SECRET signs it); src/proxy.ts (Next 16 “proxy” = middleware) plus server-side checks enforce it. The ops API also accepts Authorization: Bearer <OPS_SECRET>. The pitch and mock /app stay public.
Next: the data model
See every Postgres table and why analytics are derived, not stored.
