Skip to content

Work / Trunk / Wiki / Concepts

architecture-overview

Conceptdraftverified 2026-10-23

CONCEPT.ARCHITECTURE-OVERVIEW

How 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
Email 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

  1. 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 at v0.0.1.
  2. 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).
  3. 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. One AiAction row is written; the plan limit is checked first, in the same transaction.
  4. Review. The draft's rows become one commit on a new or an open branch.
  5. Branch. Every section shows the branch: Complete (main + the branch's rows) or Delta (only the rows).
  6. 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.
  7. 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.
  8. Merge. No conflicts (and on Team, one Verify run): the branch's rows go onto main as a new version; every other branch syncs.
  9. Into code. The CLI and MCP read a ref through /api/v1 and /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
Email 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 through withMember.
  • 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

Sources