Skip to content

Work / Trunk / Wiki / Decisions

2026-10-12-stack

Decisioncanonicalverified 2026-10-12

DECISION.2026-10-12.STACK

The stack: one Next.js app, one worker, one Postgres

Decision

Part Choice
Repo Turborepo + pnpm
Web and API one Next.js app (App Router), React, Tailwind. The API is tRPC inside it
Capture a Node worker driving Chromium with Playwright
Database Postgres with Prisma
Jobs the CaptureRun table is the queue. A worker takes the oldest waiting row with FOR UPDATE SKIP LOCKED. No Redis, no queue library
Sign-in Better Auth, self-hosted: email and Google. Companies, members, teams and invites are Trunk's own tables
Files Cloudflare R2, through the S3 API
AI Anthropic SDK
Email Amazon SES
Tests Vitest, and Playwright for end-to-end
Hosting one server running Docker Compose: web, worker, Postgres, a reverse proxy

Why

The owner asked for the cheapest stack that fits. Two things decide it.

  • Capture needs a real browser in a long-lived process. That rules out serverless-only hosting. Once one server exists, running everything on it is the cheapest setup: one fixed bill, no per-request or per-user fees.
  • Every client of the API is this app's own TypeScript. A second API server and a schema language would be extra code with no user.

Each paid service is replaced by something free to run: sign-in is a library, the queue is a table, files have no download fee. The AI is the only cost that grows with use, and it is metered (plans).

Alternatives

  • The Scrvio stack (NestJS, GraphQL). Known, and its sign-in, storage and billing code exists. It adds a second server and code generation. Its storage and billing logic is still reused, as plain modules.
  • Firebase, as in AskCal and Chitragupt. Components, variants and their usages are relational. Firestore fits them badly.
  • A hosted browser service for the live session. Paid per minute. The worker streams its own browser instead.

Impact

  • apps/web, apps/worker, packages/db, packages/capture, packages/ui.
  • Prisma and S3-style storage match Scrvio, Learning Platform and Dwarseva. Anthropic matches AskCal and Chitragupt.
  • The worker opens addresses typed by users. It must refuse private network addresses.
  • Company data is protected in the database itself. See tenant-isolation.
  • Moving to managed hosting later is a deploy change. Nothing in the code assumes one server.

Not settled: the hosting provider and the payment provider. Both are chosen when their step comes.

Status

Accepted 2026-10-12. The owner asked for the recommendation by cost and fit.

Sources

  • The owner's answers in the session of 2026-10-12.
  • The package.json files of Scrvio, Learning Platform, Dwarseva, AskCal and Chitragupt.
  • ~/git/products/PLAYBOOK.md, "What to charge for".