Domain-neutral hybrid boilerplate for TanStack Start, embedded Elysia, Prisma, Bun/River producers, a Go/River worker, and optional Socket.IO realtime.
apps/web is the only public application server. TanStack Start mounts the
Elysia app directly through apps/web/src/app/api/$.ts; there is no API
proxy or separate API process in the default Compose topology. /health is the
only non-/api Elysia mount at apps/web/src/app/health/index.ts. Web uses
Tailwind CSS and shadcn/ui primitives.
Web routing is directory-first under apps/web/src/app; see
.agent/web/routing.md for native TanStack tokens,
generated-tree ownership, and adapter isolation rules.
Web brand defaults are centralized in apps/web/src/lib/config.ts; see the
web guidance before changing application metadata or
public brand copy.
Browser -- same-origin /api/* --> TanStack Start + embedded Elysia --> Prisma --> PostgreSQL
Browser ----------------------> Socket.IO (optional apps/realtime)
apps/scheduler -- River TypeScript insert --------------------------> PostgreSQL
apps/worker ---- Go/River consume --> POST Elysia worker gateway --> Socket.IO
apps/migrate -- Prisma deploy, then River migrations -------------> PostgreSQL
Workspace ownership is explicit: apps/web owns routes and browser clients;
apps/api owns Elysia validation/envelopes and the worker notification gateway;
packages/application owns TypeScript business rules; packages/database owns
Prisma; packages/queue owns River TypeScript contracts/producers;
apps/scheduler only schedules/enqueues; apps/worker owns Go/River handlers;
apps/realtime owns Socket.IO ticket/room authorization and its private
publisher endpoint; apps/migrate owns one-shot migration orchestration.
Prerequisites: Bun 1.3.14, Go 1.25.7, Task, Docker, and PostgreSQL (or Compose).
task install
cp .env.example .env
task doctor
task migrate
task dev -- webRun multiple local runtimes with task dev -- web worker scheduler realtime.
The scheduler and worker are idle long-running runtimes until a consumer
installs domain work.
Configuration is YAML-first and loaded per runtime from config/. Secrets are
server-only: DATABASE_URL, worker keys, realtime internal URLs, and publisher
keys never enter browser code. The only browser-visible origin is
VITE_REALTIME_URL, consumed through Vite import.meta.env.
Realtime credentials are paired across the boundaries:
- API:
WORKER_NOTIFICATION_API_KEY,REALTIME_INTERNAL_URL,REALTIME_PUBLISH_API_KEY; - worker:
WORKER_NOTIFICATION_URL,WORKER_NOTIFICATION_API_KEY; - realtime:
REALTIME_PUBLISH_API_KEY,REALTIME_CORS_ORIGIN, and ticket secret; - web: public
VITE_REALTIME_URLonly.
The baseline has no product/example job, schedule, API route, or web walkthrough
installed by default. To add the executable example.realtime-notification.v1
walkthrough, copy and wire the files in
recipes/realtime-notification/README.md. The recipe keeps payloads as
invalidation signals, sends successful worker events to Elysia, and leaves
Socket.IO publishing to Elysia.
River kinds and JSON payloads are cross-language contracts documented in
contracts/jobs/README.md; breaking changes use a new .vN kind.
task doctor Verify tools, env, and migration test
task migrate Prisma migrations, then River migrations
task dev Web, Go worker, and scheduler
task dev -- web worker scheduler realtime Selected runtimes
task quality TypeScript tests/lint/types + Go vet
task build TypeScript build + Go binaries
task compose:up:detached Default db+migrate+web stack
task compose:jobs Optional worker and scheduler profile
task compose:realtime Optional Socket.IO profile
Compose exposes only web 4100 and realtime 4102; the one-shot migrate
service must complete before web or optional jobs start.
No auth system, product/domain model, Redis, multi-node Socket.IO adapter, or
default product schedule is included. Add product rules in the owning boundary,
write a failing focused test first, and run task quality and task build
before handoff.