Skip to content

Latest commit

 

History

History
68 lines (54 loc) · 6.65 KB

File metadata and controls

68 lines (54 loc) · 6.65 KB

Stack

One choice per layer. No alternatives, no adapters, no driver: config for things that are not deployment concerns.

Locked

Layer Decision Rationale
Runtime Bun >= 1.3 (target 2.0), only. No Node APIs unless via node: and unavoidable. natives replace whole dependency trees; one runtime means one perf profile to reason about
HTTP thin layer over Bun.serve routes; own the lifecycle for ALS context / tracing / authz policy must run on every surface — impossible if a third-party router owns the request
DB Postgres, no ORM — the framework owns its SQL Postgres does queue, pubsub, vectors, logical replication. entity() is the one table declaration and postgresDriver() emits hand-written parameterised SQL, so an agent reads the statement and self-corrects
Validation Standard Schema interface; our dependency-free builtin validators blessed, exposed as t one schema drives runtime parse + TS type + OpenAPI + MCP tool schema; Standard Schema is the seam, so ArkType/Zod/Valibot swap in behind configureSchemaProvider() with a ~40-line adapter you write — none ships
Auth Better Auth, wrapped, with our policy layer on top sessions/OAuth/passkeys are solved; authorization is ours because it must be identical in HTTP, WS, jobs, and MCP
Frontend SolidJS 2 + our own minimal router fine-grained reactivity → streaming shells cost ~0 hydration; the router must own render mode + offline strategy, so it can't be a dependency
Styling SCSS modules + design tokens, one brand seam (defineTheme()) build-time only, zero runtime, dark theme is a token flip. defineTheme() validates values rather than escaping them — a ; or a </style> is a refusal, not an injection. No Tailwind, no CSS-in-JS
Page structure AppShell / PageHeader / Section / Toolbar from @ultimat3/ui, on top of 46 catalogued components a page an agent assembles from named composites is a page whose skip link, landmarks and heading order are already right
Jobs Postgres queue default; redis / nats drivers behind one interface the outbox needs the same transaction as the write — only possible in-DB
Realtime 3 tiers: channels → live queries → local-first. Own protocol, Zero-shaped mutators one mutator shape at every rung; see 03-realtime.md
Transport NATS (or Redis streams) for fanout; sync nodes stateless no sticky sessions, so sync scales on connection count alone
Observability the OpenTelemetry data model, always on: spans, traceparent, counters, gauges, histograms, each behind an exporter seam not a flag — an agent debugging production needs traces that already exist. As of 2026-08 the shipped exporters are a no-op and an in-memory one: there is no OTLP exporter, and /metrics is a renderer that no role mounts (11-topology.md)
Money integer minor units + ISO currency code, Intl.NumberFormat at the edge Money = { minor: number; currency: string }. Never a float
Time store UTC, format with Intl.DateTimeFormat + explicit IANA tz a date formatted without a timeZone is a bug waiting for a user in Auckland
i18n flat key catalog, loud misses (⟦key⟧), Intl for numbers/dates/money a missing key must be visible in dev and a x verify failure, not silently English

What Bun natives replace

Each row is a dependency subtree that never enters the lockfile.

Bun primitive Replaces Deps killed (approx)
Bun.sql pg, pg-pool, pg-connection-string, postgres, connection-pool wrappers ~8
Bun.redis ioredis / redis + its command/parser packages ~5
Bun.s3 @aws-sdk/client-s3, @aws-sdk/s3-request-presigner + the AWS SDK core chain ~25
Bun.serve WebSockets ws, socket.io, engine.io, uWebSockets.js ~10
bun test vitest / jest, @types/jest, coverage + mock + snapshot plugins ~30
Bun.build esbuild/rollup/vite + framework plugin + postcss chain ~40
Bun.Transpiler / macros ts-node, tsx, swc, babel presets ~15
Bun.password bcrypt / argon2 native addons ~4
Bun.file / Bun.write fs-extra, graceful-fs, globby ~6
bun --hot nodemon, concurrently, HMR middleware ~5

One more subtree dies with no Bun native behind it — a pure-TypeScript framework pipeline, written because native addons are blocked (15-risks.md):

Framework primitive Replaces Deps killed (approx)
@ultimat3/core image (PNG/JPEG decode, resize, encode) sharp + libvips native binary + imagemin plugins ~12

Order of magnitude: a conventional equivalent stack is ~1,200 transitive packages; Ultimate's target is under 40 direct dependencies for the whole framework. Fewer packages is not vanity — it is fewer install failures, fewer CVE pages, and a smaller surface for an agent to misread.

Costs, stated plainly: no native-addon packages, and long-running-process maturity is less proven than Node's. See 15-risks.md.

Excluded

Excluded Instead
GraphQL typed action + query; OpenAPI is generated
Multi-runtime (Node/Deno/workerd) Bun only
Multi-ORM no ORM at all — one hand-written Postgres driver
Tailwind / CSS-in-JS / a second CSS system SCSS modules + tokens
React Server Components Solid stream render mode + <Suspense>
A plugin API before v1 fork the blessed path; extension points earn their way in
Vendor edge functions, KV, image loaders containers + our cache tiers + standard CDN headers
ESLint + Prettier Biome (one binary, one config)
A separate migration tool x db gen / x db migrate, drift is a x verify failure

Versions

As of 2026-08: Bun 1.3 is the floor, Bun 2.0 the target. SolidJS is pinned to 1.9.14 — the stable line, not Solid 2. The repo previously pinned 2.0.0-experimental.16, which shipped the reactivity core and no renderer at all; Solid 2 has since moved on to 2.0.0-beta.N and split the DOM half into a separate @solidjs/web package, so that pin was stranded on an abandoned prerelease naming line. Stable is the deliberate choice: the framework publishes to npm in lockstep, and every app inherits whatever reactive core is pinned here — a beta core is a risk 28 packages hand to their users. Solid 2 is revisited when it is stable, not before. There is no ORM and no schema library to pin: the SQL driver (postgresDriver()) and the validators behind t are both ours, so a change to either is framework work by definition, never app work.