How I designed, built and now run a white-label platform for physiotherapy clinics:
a bilingual website, a real-time staff dashboard and an API, live in production
with We Treat.
Clinics wanted online booking, patient records with treatment history, and a
site in Arabic and English. I wanted one product I could sell to many clinics and
run alone, safely, for years.
Medical dataPatient files and treatment notes: who saw what must be provable.
Small clinicsRunning costs have to stay close to zero per clinic.
Shared front-desk PCsStrong sign-in, without passwords to forget or share.
Non-technical ownersBackups, security and updates must happen by themselves.
One engineerI build it and run every copy, so operations must be scripted.
Two languagesArabic right-to-left is a first-class layout, not a translation.
02 /
The architecture.
Three apps per clinic on Cloudflare’s edge. Hover or tab through the drawing: every
part explains itself.
One-page bilingual site branded from the dashboard: services, team, online booking (or WhatsApp when offline).
03 /
Decision records.
D-01
One isolated copy per clinic
Decision
Each clinic gets its own Worker, D1 database and R2 bucket, deployed from one codebase with wrangler deploy --env <clinic>.
Why
No query can ever reach another clinic’s patients, and backup, restore, migration or lock happen per clinic.
Trade-off
Migrations run once per clinic, so deploys and migrations are scripted.
D-02
No passwords: zero-trust sign-in plus approved devices
Decision
The dashboard sits behind Cloudflare Access (email code or Google). The API verifies Access’s signed token and looks the email up among active staff; a new browser stays pending until the owner approves it.
Why
Nothing to leak or reuse; a stolen login alone is useless on an unapproved device, and revoking a device signs it out at once.
Trade-off
Depends on Access, so the Access policy is kept in sync with the staff list automatically. Device tokens are stored only as SHA-256 hashes.
D-03
Roles and an audit log for medical data
Decision
Owner, admin, doctor and reception roles; reception never sees clinical notes. Sign-ins, every change, and every time a patient file is opened are logged.
Why
Clinics can answer “who looked at this patient?” and staff only see what their job needs.
Trade-off
More checks per request; kept in middleware so services stay simple.
D-04
Live dashboards on a Durable Object
Decision
One Durable Object per clinic holds the open dashboards’ WebSockets and fans out every change (a new booking, an approval).
Why
The front desk sees new bookings instantly; with WebSocket hibernation, idle connections cost nothing.
Trade-off
A second runtime piece to reason about, isolated per clinic like the rest.
D-05
A public booking form that can’t be abused
Decision
Rate-limited per IP, optional Turnstile check, at most two pending requests per phone number, and every request waits for staff approval.
Why
The only endpoint open to the internet is also the one bots would target.
Trade-off
A booking isn’t final until staff approve it; that matches how clinics already work.
D-06
Backups an owner never has to think about
Decision
Every night the whole clinic (every table and file) is zipped, encrypted with AES-256 and emailed to the owner’s addresses; any single email can restore everything with a tested script.
Why
Recovery doesn’t depend on me or on one provider; a failed backup sends a “FAILED” email instead of silence.
Trade-off
The archive password lives with the owner, outside the system: without it, no backup opens.
D-07
Moving a live clinic off its old server
Decision
The first clinic ran on a Node/MongoDB backend. A one-way copy script moved every record and file to D1 and R2, keeping the original ids.
Why
Every link and dashboard URL kept working, and row counts were compared after the copy.
Trade-off
Old password hashes were dropped on purpose: staff now sign in with Access.
D-08
A signed control API for the operator
Decision
A separate HMAC-signed API lets my operator panel read status, trigger a backup, approve the owner’s devices, or lock an unpaid clinic to read-only.
Why
Billing and support without touching the clinic’s data; the website stays up even when locked.
Trade-off
Locking is manual by design: no automation can switch off a clinic.
04 /
Proof it holds.
The risky parts are covered by automated tests that run against a real local
database, not mocks.
PASS Booking race conditions (no double booking under concurrent requests)
PASS CORS, cookies and sign-out behaviour
PASS Access rules and role permissions
PASS Hardening: headers, validation, rate limits
PASS Control API signatures
PASS Backup restore
05 /
The result.
In productionWe Treat runs its bookings, patients and website on it every day.
A new clinic is configurationAn environment block, a database, a bucket and one deploy command; no code changes.
Operated, not just shippedUptime checks every 15 minutes, nightly backups, billing and remote control from my operator panel.
Need a system like this built, or someone to own one?