The full-stack framework where the primary user is an AI agent.
Rails' opinions. Bun's speed. One command that means shippable.
Status: 1.1.0,
As of 2026-08. 27@ultimat3/*packages plus the unscopedcreate-ultimate— 28 in all — on npm in lockstep: one version, one commit, one tag. 1.1.0 is the first release published by the workflow, over OIDC trusted publishing with noNPM_TOKENand provenance attached; 1.0.0 was the manual bootstrap. Semver applies — a breaking change to a documented API needs a major. That is what the version number means: a stable API under semver, not a promise about your infrastructure.
Measured, and only this much:
| What was measured | The result |
|---|---|
| Realtime restart recovery | 50,000 real WebSocket clients against one sync node, SIGKILLed with no drain — no reconnect frame sent, so every client recovers on its own backoff. All 50,000 reconnected; 49,981 received a channel patch inside the window. Time-to-consistent p50 54.0s, p90 105.5s, max 145.7s |
| The DB-load half | 156,851 connect attempts shed by the shipped AcceptBudget (500/s, burst 2000) before reaching any query or snapshot path. Recovery is bounded by admission control, not by the matcher |
| What it is not | one node, in-process transport — the run never crossed NATS. Per-node recovery, not a multi-node result, and not a throughput or latency-under-load figure |
Reproduce it: bun run scripts/bench/restart-bench.ts --clients 50000 — the committed report and the run's own transcript are in scripts/bench/results/.
Not claimed at 1.1.0:
| Open | Where it stands |
|---|---|
| Two-platform deploy proof | 1.1.0 gave a scaffolded app a real deployable artifact — x new writes apps/web/server.ts, prerender.ts, a Dockerfile and docker-compose.prod.yml, and ROLE=migrate runs release-phase migrations. The proof is still open: the demo app on Compose and K8s from one image, with an invisible rolling restart, is milestone 11 and has not been demonstrated |
| Known gaps shipped in 1.1.0 | x build --target binary compiles and then crashes at import (FRAMEWORK_VERSION reads a package.json a single-file executable has none of) · docker-compose.prod.yml pairs a published host port with replicas: 3, which cannot work · the shared cache tier's Lua invalidation DELs keys it never declares in KEYS, so it fails on Dragonfly and Redis Cluster · resolveEnvironment now exists in both core and seo with different return types. All four are in CHANGELOG.md |
| Deferred to v2 | realtime tier 3 local-first (persist: true), the plugin API, multi-region replication, the Redis/NATS job drivers — each behind the interface that ships today. The job drivers throw X_NOT_IMPLEMENTED with a runnable fix: rather than pretending to work |
Never claimed: no adoption numbers, no production deployments, no testimonials. None exist yet, and this file will say so until they do.
Every framework built in the last fifteen years optimised for a human typing code. Ultimate assumes the code is written by an agent and reviewed by a tired senior engineer working through their own AI agent and AI reviewer.
The goal is the one Rails had: shrink the set of problems the author has to hold in their head, so they spend their attention on the app's features and not on the app's infrastructure. An agent that has to decide on a migration tool, a queue driver, a cache key scheme and an authz model has spent its budget before writing a feature.
That single change of audience rewrites every default:
| Because the author is an agent… | Ultimate does this |
|---|---|
| ambiguity costs tokens and correctness | one way to do each thing — no second-best path to choose between |
| repeated definitions drift | define once, project everywhere — one action becomes six artifacts |
| documented conventions get ignored | enforced, not documented — a violated convention is a build error |
| errors are the feedback loop | errors are instructions — stable code + cause + the exact fix command |
| "is it done?" needs a machine answer | x verify — green means shippable, and it's the whole contract |
| output must be machine-readable | --json on everything, end to end |
The framework wraps libraries so you don't have to. Your app wraps the framework so your agent doesn't have to. Two layers, one goal: the least app code that can express the app — more generated code is more bugs, so the unit of progress is lines not written.
| Layer | Wraps | So that |
|---|---|---|
| Bun natives | Postgres, Redis, S3, WS, the bundler, the test runner | a whole class of dependency never enters the lockfile — see the stack table below |
| Ultimate | those natives, behind eight primitives | an agent writes entity / action / job — never a connection pool, a queue, or a cache-key scheme |
| Your app | those primitives, behind your own domain vocabulary | a feature is a declaration, not an integration |
The rule that stops this becoming an abstraction tower: a wrapper must delete a decision, not rename one. Reinvention is reserved for the places where wrapping would leak the thing being avoided — which is why there is no ORM, and why the router is ours.
The framework makes the big decisions so the agent spends its budget on your product. Breadth is not the enemy of control; undeclared coupling is. Every capability arrives as an interface with one shipped implementation — assemble like Lego, and drop to the seam when Lego runs out.
bunx create-ultimate myapp && cd myapp && x devNo Docker. No env scavenger hunt. Embedded Postgres, in-process NATS, S3 → a local directory. What you get is a running app with auth, a seeded database, a working example route, and a dev dashboard at /_x.
Every @ultimat3/* dependency it writes is pinned to one exact version. They move together — never mix versions across the scope.
This is the load-bearing idea. You write one declaration:
export const publishPost = action({
input: t.object({ postId: t.uuid, notify: t.boolean.default(true) }),
output: PostView,
policy: can('post:publish', ({ input, actor }) => ownsPost(actor, input.postId)),
cache: { invalidates: [tag.post, tag.feed] },
mcp: { expose: true, description: 'Publish a draft post' },
async handle({ input, ctx }) {
const post = await ctx.posts.publish(input.postId);
if (input.notify) await ctx.jobs.enqueue(notifySubscribers, { postId: post.id });
return post;
},
});Ultimate generates all of this from it:
| # | Artifact | Detail |
|---|---|---|
| 1 | POST /api/posts/publish |
the HTTP route, with validation and authz wired |
| 2 | OpenAPI entry | deterministic output, diffed by x verify against the committed spec |
| 3 | typed RPC client | api.publishPost(...) — a typo is a compile error in the component |
| 4 | an MCP tool | identical authz. One policy, two surfaces. |
| 5 | a job-callable handle | enqueue the same logic as durable work, no rewrite |
| 6 | a contract test + policy test stub | passing, not a TODO |
Authz is defined once and enforced across HTTP, live queries, jobs, and MCP. Two authz systems is how every Meteor-like framework died.
Everything in the framework is one of these. If a feature doesn't fit, it doesn't ship.
| Primitive | Is |
|---|---|
entity |
a table + its domain type + invariants |
policy |
an authz rule, evaluated in every surface |
action |
a mutation or command (server-authoritative) |
mutator |
an action with an optimistic local twin (offline/realtime) |
query |
a read; optionally live (subscribable) |
job |
durable background work, optionally multi-step |
route |
a URL + render mode + metadata + offline strategy |
task |
a scheduled trigger (cron) that enqueues jobs |
→ The eight primitives, in full
Not "supported". Not "documented". Enforced, and impossible to get wrong.
| Concern | The default | The enforcement |
|---|---|---|
| i18n | flat catalogs, Intl for everything numeric |
a missing key in a shipped locale fails x verify; misses render loudly as ⟦key⟧ |
| Dark theme | semantic tokens, OS-following with an explicit override that wins | a raw hex in a component is a lint failure; one token source of truth |
| Timezones | store UTC, format with an explicit IANA zone | no formatter has an ambient default; a cron without a tz won't compile |
| Money | integer minor units + currency, always attached | cross-currency arithmetic is refused; the exponent comes from the ISO table, never /100 |
| SEO | typed metadata, JSON-LD, sitemap from the route table | a site/ route with no description is a build error |
| Offline | sw.js generated from the route table |
the offline fallback route is required by the type |
| Admin | Django-grade CRUD derived from the entity registry | defineAdmin() — 20 lines to a working dashboard |
| MCP | every action is a tool | and your app's dashboards expose their own MCP surface |
| Metrics | counters, gauges and histograms on the OpenTelemetry data model; /metrics in Prometheus text |
no dependency, and the Helm chart's HPA metrics are the ones the framework already emits |
| Secrets | Secret redacts by value — toString, toJSON, the logger, at any depth, under any key |
frozen, so a spread cannot unwrap it; .env.example is generated from the typed env declaration |
Render mode is a route-level property, not a global one. A landing page is static or ISR at a 0kb JS baseline; a dashboard streams. The site/ surface cannot import from app/ — a build error, not a lint warning — so the marketing path can never grow the app's bundle through a shared component.
| Surface | Default mode | JS baseline |
|---|---|---|
site/ |
static / isr |
0kb, enforced |
app/ |
stream |
a per-route budget that fails the build when blown |
api/ |
none | n/a |
→ Surfaces · Rendering and SEO
Three tiers, the same mutator shape at every rung. Tier 2 → tier 3 is a config flag, not a rewrite. Tiers 1–2 ship today; tier 3 lands in v2, behind the interfaces that are already here.
| Tier | What | Covers |
|---|---|---|
| 1 · Channels | ctx.publish(topic, msg) over Bun's native WS pub/sub |
presence, cursors, notifications |
| 2 · Live queries | declare server-side with a policy, receive a Solid signal | 90% of "realtime app" |
| 3 · Local-first (v2) | optimistic mutators, OPFS SQLite, offline queue, rebase | offline writes that reconcile |
→ Realtime design and its honest limits
The same app code on one PaaS dyno and on a replicated cluster. Climbing is a driver swap, an env var, and someone else's infrastructure — the eight primitives, their shapes, their authz, the manifest, the OpenAPI and the typed client never move.
| Rung | You run | App code change |
|---|---|---|
| 0 | one process on a PaaS, their managed Postgres | none |
| 1 | one service per ROLE, managed Postgres + a shared cache tier |
none, plus config |
| 2 | one box, Compose, all six roles, NATS beside them | none, plus config |
| 3 | Kubernetes, per-role HPAs, logical replication for the change feed | none, plus config |
| 4 | distributed SQL (YugabyteDB), JetStream R3, metrics and traces wired end to end | none for the datastore swap — with named incompatibilities |
This is the design, not a demonstration. As of 2026-08 exactly one point on it is measured — the 50k restart above, at one node. Rung 4 has never been run. 17-scale-ladder.md states rung by rung what is real today and what is intent, and names the places the invariant currently breaks. Fintech, agent platforms, multi-tenant dashboards: that is what the architecture is for, and nobody's production traffic has tested the claim yet.
→ The scale ladder · Running it for real · Mobile and desktop targets
| Layer | Decision | Why |
|---|---|---|
| Runtime | Bun ≥ 1.3, only | native SQL / Redis / S3 / WS / test / bundler / image — kills ~15 deps |
| HTTP | thin layer over Bun.serve |
we own the lifecycle, so context/tracing/authz can't be skipped |
| DB | Postgres, no ORM | entity() is the one table declaration; postgresDriver() emits hand-written parameterised SQL, so an agent reads the statement and self-corrects |
| Validation | Standard Schema, builtin provider default | dependency-free and shipped; ArkType/Zod/Valibot swap in behind configureSchemaProvider() with a ~40-line adapter you write |
| Auth | Better Auth, wrapped | MIT, self-hosted, with our policy layer on top |
| Frontend | SolidJS 2 + our own router | fine-grained reactivity; we vendor the router rather than track an alpha |
| Styling | SCSS modules + design tokens | no Tailwind (diff noise), no CSS-in-JS (runtime cost) |
| Jobs | Postgres queue default; Redis/NATS drivers in v2 | zero-infra start, a real scale path behind one interface |
| Observability | OpenTelemetry, always on | one trace across HTTP → job → live query |
Excluded on purpose: GraphQL · multi-runtime · multi-ORM · a second CSS solution · React Server Components · a plugin API in 1.x · vendor edge/KV primitives.
| From | What we took |
|---|---|
| Rails | convention over configuration, generators, one blessed path, batteries included |
| Meteor | realtime as a default, not an add-on |
| Next.js | per-route rendering modes, ISR, streaming shells |
| Laravel | queues, mail, storage, scheduler in-box; great error pages |
| Phoenix / LiveView | server-authoritative realtime, channels, presence |
| Zero / Replicache | optimistic mutators that run identically client and server |
| Inngest | durable step workflows |
| Elixir / OTP | supervision, graceful drain, role-based processes |
| Django | admin-grade introspection, migrations that don't lie |
Same three strings in the terminal, the browser overlay, and --json:
X_DB_DRIFT: schema differs from migrations
cause: table "posts" has column "publish_at" not present in any migration
fix: x db gen "add publish_at"
Every framework error carries a stable code, a cause, and a command that fixes it. → The error contract
A monorepo, so you can add mobile, desktop, or an extension later without restructuring — and so shared code stays shared.
myapp/
apps/
web/ site/ · app/ · api/ · shared/ ← the three surfaces
admin/ the generated admin dashboard
mobile/ native Swift/Kotlin, later
desktop/ Tauri, later
packages/
domain/ pure types + constants, no I/O
db/ entity declarations + SQL migrations, no business logic
core/ your business services — shared by web, admin, worker
i18n/ your catalogs
ui/ your components, on top of @ultimat3/ui
mcp/ your app's own MCP tools
app.config.ts the one config file
site/ cannot import from app/ — a build error, not a lint warning. That one rule is what stops a marketing page from pulling in the charting library through a shared <Button> that grew a dependency.
You write features. The layout, the boundaries, the components, and the plumbing are already there. → The generated app, explained
x new / dev / build / verify / deploy
x g resource|action|job|route|policy|entity|query|task # complete, passing tests — no TODO stubs
x db gen|migrate|reset|studio|branch
x mcp serve
x doctor # env, versions, drift, portsEvery command takes --json. → CLI reference
| Landed | What it is |
|---|---|
x serves in production |
serve.ts boots a role with no dev watcher and no /_x; ROLE=migrate applies migrations and exits — the release phase a PaaS asks for. x new writes apps/web/server.ts, prerender.ts, a Dockerfile and docker-compose.prod.yml |
| Metrics | counter / gauge / histogram on the OTel data model, a MetricExporter seam, and /metrics in Prometheus text with no dependency |
Secret |
redaction by value, at any depth, under any key, frozen against a spread |
resolveEnvironment() |
development | test | staging | production from ULTIMATE_ENV, plus renderEnvExample() so .env.example cannot drift from the typed declaration |
| Page-level UI | AppShell (with a working skip link), PageHeader, Section, Toolbar, defineTheme() as the one brand-override seam, and a generated CATALOG.md |
| Test harness | factory traits, associations and create(), plus sharedExamples / behavesLike |
docs/ops/ |
the operations manual — rungs, secrets, observability, datastore sizing, disaster recovery, runbooks |
| Design, not code | 16-app-targets.md (mobile + desktop) and 17-scale-ladder.md are specs, with nothing shipped behind them yet |
Full detail, including the four known gaps: CHANGELOG.md.
| Where | What |
|---|---|
| docs/idea/ | what and why — the design spec, primitive by primitive |
| docs/architecture/ | how it's built — internals, for changing the framework itself |
| docs/ops/ | how to run it — PaaS → Compose → Kubernetes, secrets, observability, datastore sizing, runbooks. Recommendations; the framework depends on none of it |
| the wiki | the reference manual and the one public documentation surface — every field, flag, and error code. Source in wiki/ |
| llms.txt | the machine-readable map — start here if you're an agent |
| packages/ui/CATALOG.md | 46 components with every prop and the token vocabulary, generated from source and drift-tested |
| examples/dummy/ | the reference app: every primitive, once, idiomatically |
Start here: the thesis → the primitives → adding a feature.
bun install
bun run verify # the 17-step gate: typecheck → lint → boundaries → tests → drift → budgets → manifest → roadmap. Green = shippable.Read CONTRIBUTING.md and docs/architecture/00-conventions.md first. The tier boundaries in that second file are enforced by bun run boundaries — a sideways import fails the build, by design.
Twelve milestones, each ending in a working demo app and a green x verify. Milestones 0–10 are shipped. Milestone 11 — deploy and docs — is open on one thing: the demo app proven on Compose and K8s from a single image, rolling restart invisible. Its artifacts all ship, and 1.1.0 closed the gap that made the proof impossible to attempt — a scaffolded app now produces a deployable image — but the proof itself needs real infrastructure and has not been run. The status markers in that table are enforced by x verify's roadmap step, so they cannot quietly rot.
The realtime kill criterion that 1.0.0 waived is now met: milestone 6 gated tier-2 realtime on a measured 50k-socket forced-restart benchmark, and that number is measured and committed — at one node, which is the scope stated above.
→ The full roadmap · The risks, stated plainly
MIT © developerz.ai