What it is
A clinic system for dermatology, aesthetics and laser practices in Egypt. Appointments on an eight-state machine with database-level double-booking prevention. Patients. Prepaid session packages that deduct atomically when an appointment completes. Inventory tracked by lot and expiry, because Botox is measured in units and filler in millilitres and both go out of date. Consent records. Deletion requests under the Egyptian Personal Data Protection Law 151/2020.
FastAPI, PostgreSQL, Celery, React. 52 commits between 2026-09-11 and
2026-09-16 — 15 routers, 16 test files, 14 Alembic migrations. Live at
masar-api.kariem.dev, which returned 200 while this page was being written.
The whole product, in one sentence its own book insists on
«كل أصل إبداعي في العلامة لازم يقدر يتقسم لنصّين: الفوضى على اليمين، النظام على الشمال.» Every creative asset must be able to split in two: chaos on the right, order on the left.
In Arabic that is reading order — you start in the mess and move out of it. It is also the same before-and-after logic a dermatology clinic already sells with, turned back on the clinic’s own operations. Below is the real slider from the landing page — drag it yourself, same drag logic and CSS as the live product — followed by four more real screens: the inventory table, session packages, branch comparison, pricing. Not recreated, not mocked up.
اسحب للمقارنة




The moment worth the whole project
Multi-tenancy here is one database with rows scoped by clinic_id, and Postgres
Row-Level Security as the enforcement backstop rather than trusting every
query to remember its WHERE clause.
The policies were written. Then they were tested:
“First test run: RLS did nothing. Clinic B’s session could read and insert rows belonging to Clinic A.”
The cause is a detail that does not appear in any RLS tutorial. The application
connected as the role that owned the tables, and Docker’s postgres:16-alpine
image creates whatever you put in POSTGRES_USER as a superuser. Postgres
superusers bypass row-level security unconditionally — FORCE ROW LEVEL SECURITY does not change it, because the check never runs.
Every policy was correct. Every policy was ignored. The dashboard would have looked perfect.
The fix is a migration creating a dedicated bashra_app role with
NOSUPERUSER NOCREATEDB NOCREATEROLE and only the grants it needs. The lesson is
written into the repository:
“Never trust ‘RLS policy created’ as proof of isolation. Verify with a live cross-tenant read + write test using the actual role the application will connect as.”
And then the test itself was tested: RLS was manually disabled on one table to
confirm test_rls_coverage.py would actually catch it. A check that cannot fail
is decoration.
The same instinct, elsewhere
Login. Three independent limits — 5/min per IP-and-phone, a 20/min per-IP
backstop, and an account lockout after 10 failures in 15 minutes. The part that
matters is that the 401 is byte-identical whether the password was wrong or the
account is locked. Only the rate-limit case returns a distinct 429. An attacker
learns nothing from the difference, because there is no difference to learn.
Package expiry. Duration-based packages are enforced inside the same atomic
UPDATE that deducts the session, against the server clock:
AND (spt.duration_days IS NULL
OR pp.purchased_on + (spt.duration_days || ' days')::interval >= :today)Not a SELECT to check, then an UPDATE to deduct. One statement, because two statements leave a window between them.
Consent. Treatment consent and marketing consent are separate rows, not one
record with a type flag, and signed_at is always stamped by the server — a
legal record never trusts a client-supplied timestamp.
Where the domain lives in the schema
A general clinic system stores a product and a count. That is enough to run a pharmacy shelf and useless in an aesthetics clinic, because the two questions actually asked are “how much is left, in the unit this is sold in” and “which batch went into which patient”.
So an inventory item here carries a lot number, a unit that differs per product — Botox in units, filler in millilitres — and an expiry. The deduction happens inside the session that consumed it, in one statement, so the stock cannot drift from the treatment record. (The inventory screen is in the gallery above — lot numbers, expiry, stock levels, exactly as it renders.)
This is the unglamorous half of the recall question. If a filler batch is withdrawn, the answer is a query rather than an afternoon with a paper diary — and that only holds because the lot was a column from the first migration instead of a note typed into a description field.
The pricing is an architecture decision
The question a clinic owner asks before any feature question is what it costs, and the answer is the clearest statement of what the system believes. (Pricing screen above — three tiers, per clinic, not per seat.)
Priced per seat, the cheapest way to run a clinic is to share one login — and the audit trail the whole system exists to keep dies on day one. Priced per clinic, the receptionist gets her own account and every row keeps an author. The price list is where that decision becomes visible to someone who will never read the schema.
The same goes for the export clause. A system holding a practice’s entire appointment history has enormous natural lock-in; writing “leave with a copy of your data” into the pricing table is a decision not to use it.
What is not done
Two integrations are built and cannot be finished:
- WhatsApp delivery. The send path and webhook are implemented and verified.
No Meta Business App or verified WhatsApp number exists, so a send self-reports
{"status": "blocked"}rather than returning a success it did not achieve. - Egyptian e-invoicing (ETA). Blocked on a real tax ID and legal entity.
And the larger one: Bashra has no paying clinic on it. It is a complete system waiting on a real business, and the staging database’s one owner row is flagged in the project’s own notes as probable seed data rather than quietly treated as a customer.
Writing it up as shipped client work would be the exact failure the RLS story is about — a claim that looks correct until somebody checks. So: this is engineering depth, verified end to end, with no revenue attached to it yet.
The landing page says otherwise, and it is wrong
landing/index.html is the largest file in the repository, and it is a finished
Arabic sales page. It claims 4.9 stars from 120+ clinics, names six of them,
carries three signed testimonials with photographs of initials, and reports
-64% no-shows and +31% package revenue “after 90 days”.
None of that is true yet. It is pre-launch copy written for a product that intends to be used, and there is nothing unusual about that in a landing page.
It is stated here because a portfolio is a different instrument. The numbers on that page cannot be verified by anyone reading this, and quietly borrowing them would make this case study exactly the kind of claim the RLS section is about — correct-looking right up until somebody checks the role it is actually running as.
What the landing page is good evidence of: a complete Arabic-first product surface, RTL throughout, priced in EGP against the real local alternative, with a before/after comparison built to be dragged. Design work, not traction.
Why it sits at the end of a five-year line
The first thing I shipped, in 2021, put an Android Activity in direct conversation with Firebase and used two different Firebase databases at once, because I could not see that there was a decision to make.
This one writes an isolation policy, assumes the policy is lying, connects as the real production role, runs the attack, and only then believes it.
Same mistake class — something silently not doing what it claims. Caught at opposite ends of five years.
A footnote I did not expect to find
Bashra’s brand book ends its design-system chapter with a rule about colour:
«أي لون في الكود مش جاي من توكن = خطأ يوقف الدمج (lint rule)» A colour in the code that does not come from a token is an error that stops the merge.
The site you are reading enforces the same sentence about itself, from a
different direction — scripts/compass-verify.py fails the build if a hex
appears as a themed value in any stylesheet here.
Neither was written with the other in view. Both landed on the same instinct: a standard that cannot fail the build is not a standard, it is a preference with better formatting.
It is also why this page’s colours are Bashra’s and not mine. The palette above
comes from that chapter — #137A6B on #FBF9F6 in light, #083A34 carrying
rose #EFB3A6 in dark, because on their own dark surface the teal drops to
2.42