Work / Trunk / Wiki / Concepts
architecture-overview
Conceptdraftverified 2026-10-23
CONCEPT.ARCHITECTURE-OVERVIEWHow Babul works
Summary
The whole system on one page: the parts, where each runs, what it is built with, and how a change travels from a captured site to a version of main and into code. The pictures are in the design set, under designs/web/architecture/ (the architecture chip in the bar): who uses Babul, how work gets done, what it relies on, and a change from edit to version. Each diagram is drawn from its .json in designs/architecture/.
Why it matters
It is the plan the rebuild follows (2026-10-23-stack-revisited). Each screen, package and vendor choice has to fit the parts, the data and the cost written here.
Implications
1. The parts
| Part | Runs on | Does |
|---|---|---|
Web app (apps/web) |
Next.js 16: Vercel Hobby on staging; one small server in production | Every screen, the tRPC API for the screens, /api/v1 (read API for the CLI), /mcp (remote MCP), sign-in, Polar webhooks, the daily cron |
Worker (apps/worker) |
One Docker image: on the owner's machine for staging; beside the web app on the production server | Capture (walk the site in Chromium), the live hand-walk (CDP screencast over WebSocket), Verify, Analysis, image to HTML, sending email |
| Database | Neon Postgres, Prisma 7.10 | Everything a company owns, one row per fact; row-level security per company |
| Files | Cloudflare R2 (private), free to 10 GB, no fee for reads | Page HTML and CSS, screenshots, icons, exports |
| AI | Anthropic API, claude-sonnet-5-5 |
Edit the selection, image to HTML, the analysis, commit messages |
| Resend, React Email templates | Verify email, reset, invites, branch and merge notices | |
| Payments | Polar (merchant of record) | Team seats, monthly or yearly; extra AI actions |
CLI (packages/cli) |
The user's machine, npx @babul/cli |
Pull tokens, icons and component code; diff versions; pin a version |
MCP (packages/mcp) |
babul.dev/mcp (remote), stdio through mcp-remote |
Lets Claude Code, Cursor and others read a design and write code from it |
Two runtimes only: the web app for everything a request touches, one worker for everything that needs a real browser or runs for minutes.
2. The data, in one line each
- Company → Products → Members with an access level; a product is open to everyone or to chosen people.
- Capture run → Pages (one per address and role, a snapshot per size) → Tokens, Assets (icons, images), Components with variants and states, Journey edges.
- Ref:
main, a branch, or a version. Commit → changes: one row per(element, property) → value. - Draft: one per person per ref, the same change rows, private until Review.
- Version: a merge of a branch into main,
major.minor.patch, with its note. - Verify run and Analysis run → results and findings. AI action: one row per call, for metering.
- Subscription mirrors Polar; API token and OAuth grants for the CLI and MCP.
3. How a change travels
- Capture. The web app inserts a
CaptureRun. The worker takes it (FOR UPDATE SKIP LOCKED), walks the site with Playwright through its egress proxy (no private addresses), once as Open and once per role, and saves pages, screenshots and assets. Its own code turns computed styles into tokens, repeated structures into components, links into journey edges; svgo cleans icons. Axe checks accessibility in the same session. Main starts atv0.0.1. - Edit. A page is the real captured HTML in an iframe. Babul's own overlay draws selection and handles; each edit is one property change written to the draft (debounced), with an own undo stack. Words are typed in place (
contenteditable="plaintext-only"). Draft values are laid over the page as stylesheets (packages/render). - AI edit. The selection, the product's Styles and component list (cached) and the request go to Claude, which answers with one strict tool,
apply_changes: [{elementId, property, value}]. The changes land in the draft like any other. OneAiActionrow is written; the plan limit is checked first, in the same transaction. - Review. The draft's rows become one commit on a new or an open branch.
- Branch. Every section shows the branch: Complete (main + the branch's rows) or Delta (only the rows).
- Sync. When main moves, each open branch and draft is compared key by key with the last synced main and the new main (own pure function, about 30 lines). Same key changed on both sides with different values is a conflict: it blocks the merge, never the sync.
- Verify. The worker renders the branch and the target URL at each size, compares them with pixelmatch and compares the stored element properties, and gives 0–100 per page, marking what came from the branch.
- Merge. No conflicts (and on Team, one Verify run): the branch's rows go onto main as a new version; every other branch syncs.
- Into code. The CLI and MCP read a ref through
/api/v1and/mcp. Components are written by Babul's own converters (six frameworks × four stylings), tokens by its own six writers.
4. Choices, and why
| Area | Choice | Why | Runner-up |
|---|---|---|---|
| Capture | Playwright 1.64 + own walker | Address rules, page identity, roles and the egress proxy are already Babul's own; a crawler library would keep a second state | Crawlee |
| Worker host | Staging: the owner's machine. Production: the same small server as the web app | A long-lived browser and a sticky WebSocket for the hand-walk; ₹0 until there is a customer | A Fly Machine, when one server is not enough |
| Design extraction | Own code + svgo 4, css-tree 3 | No maintained library does token and component clustering; it is the product | — |
| Visual compare | pixelmatch 8 + own property compare | Pixels alone cannot say which property drifted | odiff |
| Accessibility | @axe-core/playwright 4.14 | Per-element results in the same session; drop Lighthouse | Lighthouse for performance only |
| Image to HTML | Claude vision → render → score → one repair | Reuses Verify; no Python service | screenshot-to-code's ideas |
| Page canvas | Real HTML in an iframe + own overlay | A canvas engine cannot lay out real CSS, so it cannot show a captured site faithfully | react-moveable |
| Journey | @xyflow/react 12, longest-path layout | A shallow graph with a known start needs no layout engine | dagre |
| Versioning | Property change rows + own three-way merge | Drafts are private and merges explicit; a CRDT would hide the conflicts Babul must show | Snapshots + microdiff |
| Presence | Postgres lastSeenAt heartbeat over tRPC |
"Who is here" needs no realtime vendor | Durable Object (partyserver) for cursors |
| Auth | Better Auth 1.7 (email, Google, 2FA, organisation, OAuth provider, device flow) | Already in; also serves MCP OAuth and CLI device login | WorkOS for SSO |
| Payments | Polar | Merchant of record, pays Indian individuals, has meters | Dodo Payments |
| Resend + React Email | Free to 100 a day, simple | Amazon SES | |
| Jobs | The CaptureRun-style table queue + attempts, runAfter; a daily cron (Vercel Cron on staging, the server's cron in production) |
Already built; no new vendor | pg-boss |
| Database | Neon + Prisma 7.10 (pinned), set_config per transaction for RLS |
Works on the pooled endpoint; branches per preview | Drizzle |
| Files | Cloudflare R2 | Free to 10 GB, no fee for reads; only packages/storage changes |
Vercel Blob |
| AI | @anthropic-ai/sdk direct, prompt caching, strict tools |
One provider, newest features first | AI SDK 7 + AI Gateway, if fallbacks are needed |
| CLI | commander 15, bundled by esbuild to one file | Most used, no dependencies | citty |
| MCP | @modelcontextprotocol/server 2.3 (spec 2026-07-28) + mcp-handler 2.3 |
Stateless remote MCP inside the web app, OAuth 2.1 via Better Auth; no install for users | stdio only |
| Codegen and tokens | Own converters and writers, golden snapshots + type checks | Input is Babul's own spec; no library writes all six formats | Mitosis, Terrazzo |
| Errors and abuse | Sentry Free, Postgres counters, Caddy on the server | ₹0, no Redis | Cloudflare Turnstile |
5. Packages after the rebuild
apps/web · apps/worker · packages/db · capture · render · versioning · codegen · storage · api-client · cli · mcp · ui · env · configs. Removed: kit (the owner's call), Lighthouse, and whatever the rebuild finds unused (knip). history is folded into versioning if they overlap.
6. Safety, once
- Every company row carries
clientId; Postgres RLS hides other companies' rows; the web app reads only throughwithMember. - Role passwords are sealed (AES-256-GCM) and opened only in the worker's memory.
- The worker's browser goes out only through its proxy, which refuses private addresses.
- AI never sees passwords, cookies or members; Anthropic does not train on API data.
- Limits: WAF on sign-in and sign-up, Postgres counters for AI and the API, BotID on sign-up.
7. Cost
- Staging: ₹0 a month. Vercel Hobby, Neon Free, R2 free, Resend Free, Polar's sandbox, Sentry Free; the worker on the owner's machine; AI answered by a stand-in.
- Production waits for the first customer. Then one small server for the web app and the worker: Oracle Cloud Always Free (Mumbai or Hyderabad, ₹0), else OVHcloud Mumbai 2 vCPU / 4 GB (about ₹586 a month with GST). Everything else stays on its free plan until outgrown → ₹0–600 a month, plus Polar's fee (about 5% + ₹44 a charge) and Claude by use.
- Move up only when customers make the server too small. 2026-10-23-stack-revisited has the table.
Related
- branch-verify-merge · capture-reads-the-product · tenant-isolation · onboarding-steps · design-set
- plans · access-levels · example-northwind
- 2026-10-12-stack · 2026-10-21-ref-in-the-path · 2026-10-23-no-branch-page · 2026-10-23-stack-revisited
- architecture — the six diagrams and the overview screen, in the design set
Sources
- 2026-10-23-stack-revisited (the research behind each choice, with versions and links)