The new website and admin for Low Cost Clinic, Dr. Jyoti Puthran’s general-physician practice in Banashankari. Patients read about care and fees, book, ask and give feedback in four languages, with no login and no online payment. The clinic team runs every booking, message and page from one admin. Patient details are encrypted, and every look at them is logged.
Clinic staff will want Parts 1 to 4. Anyone curious about the clinic will find what patients get, and how their details are kept, in Parts 1, 3 and 4. Developers can go straight to Parts 5 and 6. Move with the arrow keys, a swipe or the rail at the bottom; every slide has its own address (such as #18) to share.
Low Cost Clinic is a general-physician and internal-medicine practice in Banashankari, Bengaluru, run by Dr. Jyoti Puthran. It serves local patients with diabetes, blood pressure, thyroid, cholesterol, fever and infections, and people looking for de-addiction or mental-health care.
Free plans only. The domain is the one recurring cost.
Fees, hours, contacts, doctor details, page text, translations and photos live in the database and change in the admin.
Patients ask for a time online and the clinic confirms it. No login, no OTP, no online payment.
Messages are written in the admin and sent from the clinic’s own WhatsApp. Emails go out by themselves.
The clinic’s line: “Quality care that doesn’t cost a fortune.”
A public website and a staff admin on one database, built to the medical ethics rules (IMC 2002) and India’s data protection law (DPDP Act 2023 and Rules 2025).
Where it stands (14 Oct 2026): all 19 phases are built and checked; the preview is live at preview.lowcostclinics.in. Production waits on a short launch checklist (slide 62).
What anyone sees on the public website. It is written for older and less tech-savvy patients as much as anyone: every page leads to Call, WhatsApp or Book in one tap, and nothing asks for a login.
Every page is built from the database, so a fee or a phone number changed in the admin changes on every page, in every language, at once.
A patient who has read one service page knows where everything is on the next.
Mental health and de-addiction pages use the same layout. The WhatsApp message they prepare says only “Consultation with Dr. Jyoti Puthran”.
Doctors are expected to announce their fees (IMC 2002), so every fee is on the Fees page and in each service page’s fees box before anyone books. Fees are paid at the clinic; there is no online payment.


One question per step with a progress bar, built to work on a basic Android phone.
Right after booking, and from every message, the patient reaches a page of their own. There is no account and no password.
LCC-4K7Q), status, visit type, date, time, fee, address and map, a masked mobile number, what to bring and the booking’s history.For patients who cannot travel: name, mobile, locality (from the clinic’s served areas), PIN code, preferred date, a time window (morning, afternoon or evening), visit type, an optional short note and the consent box.
The fee shows before sending: the area’s fee, or “confirmed on the call”. An unlisted area reads “We may not be able to visit this area. Please call us.” The patient gets a private page, as for a booking.
The full street address is never collected on the website. Staff take it on the confirmation call.
Name, mobile, topic, message, how to reply, whether a message may be left, and consent, with the line “Please do not share medical details here.”
Every public form has invisible spam protection (Cloudflare Turnstile), a hidden trap field and a limit of 5 sends an hour per visitor.
Once a visit is marked completed the patient gets a feedback link: on WhatsApp from the clinic, or by email the next morning. A 1–5 star rating; four Yes / Somewhat / No questions (felt heard; diagnosis and treatment explained; waiting time acceptable; fees clear beforehand); an optional comment; an option to stay anonymous and a “You may contact me” tick. One response per visit; the link works for 14 days.
Feedback is never published. Testimonials count as self-promotion under IMC 2002, so the site has none.
A slim notice above every page (Holiday, Doctor on leave, Changed hours or General notice) in the visitor’s language, between its start and end times. The visitor can close it, and it never covers the Call or WhatsApp buttons.
A list with search, category, sort and pages, showing reading time and the “Medically reviewed on” date. Each article has headings, lists, highlighted boxes, images, references, an author box, a related service and a Book button.
Free PDFs (diet guides, log sheets, exercise sheets) by category, the visitor’s language first. No login, email or form before downloading. Downloads are counted per day and language, with nothing about the visitor stored.
Unique titles and descriptions, one address per page, a sitemap, and structured data that names the clinic, the doctor and each qualification. English is the indexed version; private pages, downloads and filtered lists are kept out.
The admin is where reception confirms bookings and sends messages, the editor keeps the pages right, translators work in their languages, and the owner approves what needs approving. It works on a computer, a tablet or a phone, in light or dark.
Staff sign in through Cloudflare Access with a one-time code sent to an allowed email address, or with Google. The site stores no passwords.
One table in the code lists every screen and the roles that may open it. The menu, the tabs, the route checks and even the user guide read that same table, so nobody is shown a screen they cannot open.
| Screens | Owner | Reception | Editor | Translator |
|---|---|---|---|---|
| Dashboard, user guide | ✓ | ✓ | ✓ | ✓ |
| Appointments (Day, Week) | ✓ | ✓ | — | — |
| Home-visit requests, enquiries | ✓ | ✓ | — | — |
| Feedback, service areas | ✓ | — | — | — |
| Message generator, log, daily messages | ✓ | ✓ | — | — |
| Pages, interface text, articles, resources, templates | ✓ | — | ✓ | ✓ |
| Media library, menus, clinic details, fees | ✓ | — | ✓ | — |
| Hours & closures, banners | ✓ | ✓ | ✓ | — |
| Booking rules | ✓ | ✓ | — | — |
| Publish and its history | ✓ | — | ✓ | — |
| Feature switches, users, audit log | ✓ | — | — | — |
Reception sees the message log without message text. Editors’ fee changes wait for the owner. Translators can’t publish or mark a page reviewed.
Day and Week views, with a badge for requests waiting for confirmation. Each row carries only the buttons that make sense for its status.
The same status colours appear everywhere in the admin: Requested Confirmed Completed Declined Cancelled No-show Expired. A move that isn’t allowed from the current status is refused, and arrival, completion and no-show belong to the visit day.
The street address is taken on the confirmation call and is not stored online.

Topic and message stay hidden until an enquiry is opened, and each opening is logged. Call, reply with a template, mark contacted, close or mark as spam. When a patient withdraws consent, the enquiry is deleted that night.

Ratings and answers, the comment after opening, Reviewed or Needs follow-up, and a call-back only when the patient allowed it. Comments are deleted after 12 months.
Choose a use case, a language (the recipient’s by default), a recipient and a channel. The approved template is filled with the booking’s details; anything missing is listed. Edit, then Send on WhatsApp, Copy, Send email or Print.
Bookings, teleconsultation, home visits, follow-up, feedback, enquiries, clinic notices, fees, information and staff. Each has a text per language, variables, drafts and approval. Approved versions are kept, so the log can always show what was sent. Clinical topics need the owner’s approval.
No AI writes these messages. Staff fill approved wording, and nothing leaves without a person pressing Send.

Every WhatsApp opened or copied and every email, with stats, filters, delivery status from the email provider, “Send again” for a failed email, and a metadata-only CSV. Message text is the owner’s only.

Tomorrow’s WhatsApp reminders in one tap each; email reminders go by themselves at 6 pm. The staff schedule (8 pm) and summary (9 pm) carry references and counts, never names. A Tomorrow list prints for the desk.


Edits are saved as drafts. Publish sends every change that is ready to the website, which is rebuilt in 1–3 minutes while staff keep working.

Online booking answers “unavailable” until the booking rules are filled in. Nothing is guessed.
The owner can stop any feature, or show a language, at once: online booking, home visits, the enquiry form, banners, articles, resources, feedback, patient emails, WhatsApp buttons, staff digests, reminders, the generator, Hindi, Kannada, Marathi, analytics, teleconsultation and the sensitive visit types.

Hatched hours are night. The free plan has five scheduled triggers per account and the site and its preview already use four, so there are just two: every 15 minutes and nightly. The 15-minute job checks the India time itself and runs each daily task once; a record of each run stops duplicates.
A clinic holds some of the most sensitive data there is. The rule here is to collect little, lock what is kept, log every look, and delete it on time. Everything in this part is checked by automated tests or by the security review.
Each form has a fixed list of fields and refuses anything else it is sent, so a field cannot be added by accident, or by someone tampering with the page.
Name, mobile, an optional email, preferred language, whether WhatsApp is OK, consent.
Name, mobile, locality, PIN code, preferred date and time window, visit type, an optional short note, consent.
Name, mobile, topic, message, how to reply, whether a message may be left, consent.
A rating, four short answers, an optional comment, stay anonymous or not, may the clinic call back.
Consent: the box is never ticked in advance. The version of the privacy notice, its language and the time are saved with each request.
Patient data is for the owner and reception only. Editors and translators never see a patient, and the most sensitive text (feedback comments and message text) is the owner’s alone.
| What | Owner | Reception | Editor | Translator |
|---|---|---|---|---|
| Bookings and home visits | ✓ | ✓ | — | — |
| Enquiries, after opening | ✓ | ✓ | — | — |
| Feedback ratings and comments | ✓ | — | — | — |
| Message log: who, when, which template | ✓ | ✓ | — | — |
| Message log: the text itself | ✓ | — | — | — |
| Audit log | ✓ | — | — | — |
| Website content | ✓ | — | ✓ | own languages |
| Dashboard counts | ✓ | ✓ | content only | content only |

Mental health and de-addiction care need more than encryption: a calendar reminder on a shared phone, or a message on a lock screen, can say too much.
Checked in the browser in Hindi and Marathi during the security review: the done screen, booking page and calendar file all read “Consultation with Dr. Jyoti Puthran”.
HTTPS only, with HSTS. Secrets live only in Cloudflare’s secret store; the encryption key is shared by the two Workers and nothing else.
Patient records, feedback comments and message text are deleted 12 months after the visit or enquiry; old sessions, rate limits and audit entries go too. When a patient withdraws consent, their enquiry is deleted that night.
The whole database is encrypted into private storage and kept 12 weeks. A weekly job copies the newest encrypted file outside Cloudflare. Restoring is written down and was tested.
A written runbook: tell affected people without delay and report to the Data Protection Board within 72 hours. Data requests are answered within 90 days, aiming for 7.
The site follows the IMC 2002 ethics regulations (the NMC 2023 conduct rules are on hold) and the DPDP Act 2023 with the DPDP Rules 2025, whose notice, security and breach duties start on 13 May 2027. It is built to them from day one.
| Medical ethics (IMC 2002) | What the site does |
|---|---|
| Registration number | On the doctor page and the footer, with the council |
| Announce fees | The full fee table, and a fees box on each service page |
| No self-promotion | Factual tone; “best”, “top”, awards and badges are banned, and tests scan every page for them |
| No testimonials | Feedback stays internal |
| No cure claims | “Manage” or “control”, never “cure” |
| Photograph, statistics | The doctor’s photo only with the doctor’s agreement, behind a switch; a diabetes statistic hidden until verified |
| Data protection (DPDP) | Status |
|---|---|
| Minimal forms, unticked consent, notice version stored | Met |
| Encryption; every staff view logged | Met |
| Deleted after 12 months | Met |
| Contact for data questions; breach runbook; 90-day requests | Met |
| Privacy notice in all four languages | English done others before their language goes on |
| Data-processing terms with Cloudflare and the email provider | Owner before launch |
From the requirements: “This is research, not legal advice; confirm with the Karnataka Medical Council or a healthcare lawyer.”
The last build phase read every code path against the privacy rules, ran the release check against a local copy, and tested all four languages in a browser. Five findings, all fixed the same day:
| # | What was wrong | What it would have done | The fix |
|---|---|---|---|
| F1 | The admin’s site address pointed at the admin itself | Every booking link in staff messages would have sent patients to the staff sign-in page | Corrected, and a test now compares the two Workers’ settings |
| F2 | Cloudflare could keep a log of every request | Booking codes, visitor addresses and the staff sign-in token would sit in logs | Request logs switched off in both Workers; covered by the config test |
| F3 | The admin logged raw error messages | Some errors quote what was typed, such as a name | Quoted parts are dropped; a test checks a name never reaches the log |
| F4 | Staff alerts were set to send from a Gmail address | Every staff alert and daily summary would have failed to send | Sent from the clinic’s own domain |
| F5 | The privacy notice said “write to [TBD]” | No published contact for data questions | The confirmed clinic email is in the notice |
F1 and F4 passed every test because tests set their own settings. The lesson went into the log: check the deploy configuration itself, not just the code.
Many of the clinic’s patients are older, read best in their own language and use a basic Android phone on a patchy connection. The site was built for them first: large text, big buttons, four languages, and pages that load fast.
Built to WCAG 2.2 AA, then checked page by page.


?lang=hi).Every English text was copied into each language with a marker in front, switched on, and built. A browser then opened 20 pages in all four languages at 360 px and flagged any visible text without its marker.
Any text that stayed plain English was a string the switcher had missed. The marked copies were deleted afterwards.
The targets: the main content in under 2.5 seconds, a reply to a tap in under 200 ms, and a layout that doesn’t jump (shift under 0.1).
Lighthouse phone profile; lower is better
Seconds, Lighthouse phone profile; lower is better
| Measure | Before | After | Target |
|---|---|---|---|
| Layout shift, booking page | 0.475 | 0 | under 0.1 |
| Layout shift, translated pages | 0.6–0.9 | 0 | under 0.1 |
| Home page, main content | 3.4 s | 2.0–2.4 s | under 2.5 s |
For developers: how a static Astro site, two Cloudflare Workers, one SQLite database and two storage buckets do all of the above for Rs 0 a month, and how the rules that protect patients are enforced in code and tested.
npm workspaces, Node 24, TypeScript throughout: site/, admin/ and shared/ (crypto, booking, messages, backups, the content readers).
The brief allowed free plans only, for commercial use, on a service that never sleeps between visits. That ruled out most of the usual choices.
| Layer | Choice | Free limit (Oct 2026) |
|---|---|---|
| Site | Astro, static output | open source |
| Hosting | Workers + static assets | static free; 100k Worker requests a day |
| Publishing | Workers Builds, deploy hook | 3,000 build minutes a month |
| Database | D1 (SQLite) | 5M rows read, 100k written a day |
| Files | R2 | 10 GB, free egress |
| Admin sign-in | Cloudflare Access | 50 users |
| Spam | Turnstile + trap field + limits | free |
| Patient email | Resend | 3,000 a month, 100 a day |
| Considered | Why not |
|---|---|
| Next.js on Vercel Hobby + Supabase | Hobby is non-commercial; Supabase Free pauses after 7 quiet days |
| Netlify Free + Supabase | A monthly credit cap; the site pauses when credits run out |
| Payload CMS on Cloudflare | The best paid upgrade (about $5 a month) |
| Directus, Strapi | Need an always-on server with no reliable free host |
| Git-based CMS | Values would live in git, not a database |
The trade-offs: a custom admin to maintain; one vendor for hosting, data, login and email (weekly off-platform backups cover data loss, not downtime); past the daily database limits, booking and the admin stop until 5:30 am IST while static pages stay up, and the dashboard warns first.
The public site is built from the same database that holds the patients. So the build never reads that database directly.
Two tests guard it: the sandbox contains no patient table, and a second test puts patient rows in the source database and checks the built site still has none of them.
The test fires three overlapping bookings at the same instant against a real local database: exactly one wins.
Fifteen migrations build one SQLite database. Each table belongs to one of three groups, and only the first is ever exported to the build.
Encrypted columns accept only the sealed format; timestamps must fall on a quarter-hour; dates must be real dates.
Free a booking’s blocks on cancel; always keep one owner; keep approved template versions; switch off dependants; keep locked menu items; enforce the translation review rule.
Each translation has a draft row and a published row; publish swaps them in one batch, so the site never sees half an edit.
Every request to the admin passes the same middleware, in this order. A screen’s own code only runs after all six.
A content policy allowing only the admin itself, no framing, no referrer, never cached, never indexed.
A form post must come from the admin, or it is refused.
Cloudflare Access’s signed token is verified again: signature, audience, issuer, expiry.
The email must match an active staff user. Each sign-in is audited.
The route checks the role against the one screen table that also draws the menu.
Any change, view or export writes its audit row in the same batch.
Errors show a generic page, and quoted input is dropped from what is logged.
On a developer’s own machine only, a set email stands in for Access, and only when the request comes from localhost. A deployed Worker never sees localhost.
[TBD]Three Claude tools, each with its own job: Deep Research for the rules and the platform, Claude Design for every screen, and Claude Code for the build, one phase per session. A person made every decision along the way.
Each tool handed the next something written down. The three requirement documents sat in the middle: every session began by reading them, and they won every disagreement.
The rules before the code: medical ethics, the data law, free-plan limits, which stacks to avoid.
→ findings in the requirementsGeneral Requirements (what), Technical Decisions and Design (how), Database Schema (the tables). About 1,200 lines.
→ the source of truthEvery public page and admin screen as clickable prototypes, with tokens, layouts and copy.
→ 42 files indesign/One build phase per session, against the documents and the design: code, migrations, tests and docs.
→ 19 phases, 10–13 OctThe website, the admin, the staff guide, a runbook, a deployment guide and a security review.
→ preview live 13 OctWrote the brief, answered design-and-document conflicts, reviewed each phase and deployed.
Fees, hours, registration, wording and approvals. Until each answer came, the value stayed [TBD].
Within written rules: stay in the phase, test what protects patients, write down what went wrong.
Which medical ethics code applies (IMC 2002, since the NMC 2023 rules are on hold); what the DPDP Act 2023 and Rules 2025 require and from when (13 May 2027); why “cure” is off limits (Drugs and Magic Remedies Act 1954); and what counts as self-promotion: testimonials, “best”, awards, the doctor’s photograph, a success statistic.
Free-plan limits checked in October 2026, the chosen stack weighed against five alternatives (slide 48), and its trade-offs written down.
72 hours to report a breach to the Data Protection Board, 90 days to answer a data request, data-processing terms with every provider.
The research output itself isn’t kept in the repository; this is what the requirement documents record from it, with their own caveat: “This is research, not legal advice.”
[TBD] until the clinic confirmed it.Newsreader for headings and prices, Public Sans for text, Noto Sans for Devanagari and Kannada.
| # | The design had | Decided |
|---|---|---|
| D1 | A dark-mode toggle stored in the browser, where only the language was allowed | The owner chose the toggle; the browser keeps only the theme choice beside the language |
| D2 | Fonts loaded from Google | Fonts served by the site; Indian-language fonts only when chosen |
| D3 | Video follow-up bookable online | Behind the teleconsultation switch, off at launch |
| D4 | An optional email in booking | Kept: emails are sent only if one is given |
| D5 | A shorter enquiry form than the schema | The design’s fields only, until the clinic decides |
The documents win over the design. Each conflict had a default, so no phase waited for an answer.
The build was cut into 19 phases, each sized for one Claude Code session of about five hours. CLAUDE.md set the rules every session followed:
status.md, learnings.md and the phase’s design files.The owner decided on 10 Oct to build everything locally and deploy once, at the end. Phase numbers are in green; deployment and owner requests in amber.
Every session ended with an entry: what went wrong or surprised, the fix, and what the next session should do differently. Every session began by reading all of it. A few of its 180 lessons:
Exporting one constant from the Worker’s main file passed every test, then the local Worker refused to start.
Fix: a test checks that file exports only the handlerWith no referrer sent, browsers post forms with Origin: null. Only the browser check found it.
Each Indian-language letter is 9 characters once encoded, so a 500-letter Hindi comment broke the 4 KB form limit.
Fix: a 16 KB limit for forms with free textA database rule meant to accept only real dates accepted 2026-13-01: a comparison with NULL passes.
Fix: compare with IS, not =A unit test passed; the first real restore failed, because rebuilt tables sat in the wrong order.
Fix: restore in foreign-key order, one statement at a timeThe home page preloaded italic fonts it never shows, which cost a second on a phone.
Fix: upright styles only; 3.4 s became 2.0–2.4 sCloudflare’s request logs would have kept booking codes and visitor addresses.
Fix: request logs off, and a test reads the configThe banned-word check failed on “do my best” and “cannot be guaranteed” in supplied copy.
Fix: reword the copy, keep the ruleShell heredocs mangled or hung edit scripts in five separate sessions, and each time the log said so.
Fix: write the script to a file first. Eventually.[TBD] instead of a guess. No invented phone, fee or registration number; the release check fails until the clinic answers.Done All 19 phases built and checked; the preview is live at preview.lowcostclinics.in with its admin.
[TBD] is leftThe staff guide is inside the admin (Help → User guide). The features list, technical details, runbook, deployment guide and security review live with the code.