15 modules that already know about each other. You stay in charge of every change.
MAXSTACK is a TypeScript app framework for building with a coding agent. Your database, admin UI, forms, validation, REST API, auth and MCP tools all derive from one typed spec — so an agent describes the product instead of hand-writing the plumbing.
Add auth, billing, teams and audit — 15 modules in all — one command each, prerequisites resolved for you, and every one of the 4,047 possible combinations proved to install identically. Nothing an agent proposes counts until you accept it, and nothing the platform regenerates touches a file you have taken over. Fast on day one. Still fast on day fifty — measured, on a project that got there.
v0.11.12 on npm · pre-1.0: the spec vocabulary and CLI can still change between minor versions. What is held stable is the part you would notice: the platform never overwrites a file you have taken ownership of, and every release is gated on that.
Measured by maxstack eval across 11 benchmarks — every one green, every one byte-deterministic. Leverage runs 4.1–8.8 across the set, so the median is the middle of a real spread, not its ceiling. Every figure on this page is generated from run 805cfebf73a1 (2026-08-07).
Fast on day one. Still fast on day fifty.
Anything can generate an app once. The speed that matters is whether change number fifty still takes an afternoon — which is a property of the platform, not of the model. So we built the project that gets there and measured it: 56 changes on one app, in the order they happened, each one replayed through the real operation and regeneration path. Cost per change over the first half: 1.23. Over the second half, with roughly twice the pages: 1.27. The second change stayed as cheap as the first, and so did the fifty-sixth.
The requirement this was built to satisfy “I want to give some high-level requirements and get an actual production-level app.” — the note that started the project, before any of it existed.
- 01A typed source of truth
Code, DB, admin UI, forms, docs, and tests all derive from validated, typed artifacts — Payload CMS, generalized to whole applications.
- 02AI at the framework level
Every project ships an MCP server and skills, so any agent already knows how to extend it — no bespoke integration.
- 03A living human-interaction model
Structured decisions, review-first, provenance on every artifact, safe change — not a chat that silently overwrites your code.
- 04Harnesses and evals
An instrumented factory measures how fast and correctly AI builds and changes apps, so improvement is evidence-driven, not vibes.
An agent on a blank repo
- Hand-writes the schema, admin, forms, API, and auth — every time
- Spends its context window on plumbing instead of your product
- Regenerates over the file you edited, silently
- Gets slower with every feature, as the surface it must hold grows
MAXSTACK
- Writes one typed line; the platform derives the rest — a median 6.7 artifacts per declaration
- Context goes to your product, because the plumbing is a runtime, not a file
- Every change is reviewed before it counts, and the platform never overwrites a file you took over — so change 56 is as safe as change 1
- Plain code on a standard stack — eject any file, own it forever
Pick what your product needs. They already know about each other.
A starter kit hands you a fixed set to delete from, wired on day one and yours to maintain from day two. These install through the same validated operation an agent uses, resolve each other's prerequisites topologically, and carry versioned upgrade codemods — so a module can move forward under an app that is already live. That last part is the whole difference, and breadth is what makes it visible: nobody feels an upgrade path on a product with six moving parts.
And the combinations are tested, not assumed. 15 modules is 4,047 prerequisite-closed combinations; every one of them is installed in several valid orders and asserted byte-identical, so two people who added the same modules in a different sequence are running the same app. The catalog is capped at 16 for exactly that reason — breadth is bounded by what the gate can prove, not by ambition. The combination gate · the upgrade gate.
Authentication
Sessions and password login on better-auth, plus the role field every other module gates against.
stands alone
Audit log
An append-only log of who did what to which record — the sink several other modules write into.
stands alone
Transactional email
Transactional email on React Email and Resend, templated from the same typed artifacts.
stands alone
File storage
Declared file fields: per-field MIME allowlists and size caps, expiring viewer-bound reads, image derivatives.
stands alone
Scheduled work
Background and recurring work as a declaration, with a handler stub you own and a job table you can read.
brings auth with it
Webhooks
Signed outbound deliveries with retries and a delivery log, subscribable per event.
brings auth + audit with it
Observability
Structured request logs, traces and health endpoints, wired without you touching a route.
stands alone
Data compliance
Data-subject export and erasure, retention windows, and the consent record behind them.
brings auth + audit with it
API keys
Programmatic access scoped per resource and action — enforced in the permission layer, not at the route.
brings auth with it
Feature flags
Declared feature flags, targeted server-side, retired through a first-class op, provably unable to affect generation.
brings auth with it
Organizations & members
Teams, invitations and multi-tenant membership on top of the auth substrate.
brings auth with it
Preferences
Typed per-user and per-organization settings, with the settings form derived from them.
brings auth + members with it
Notifications
In-app and emailed notifications that respect each recipient’s declared preferences.
brings auth + email + preferences with it
Billing & entitlements
Tiers, subscriptions and entitlement checks, with pricing as a typed op on the spec.
brings auth with it
Admin metrics
A metrics dashboard over auth and audit, entitlement-gated at runtime.
brings auth + audit with it
An agent proposes. You decide what becomes true.
Every entity, field, page and pricing tier carries its provenance — suggested, accepted, or manual — and generators ground only on accepted ones. So an agent can propose freely and nothing it proposed is in your app until you said so. That is a structural property, not a setting: there is no mode in which a proposal skips the queue.
A queue, not a diff you scroll past
Proposals arrive as reviewable rows with the reason they were made. Accept, reject, or clear a batch — and a batch refuses to include a risky row rather than sweeping it along.
Decisions are recorded, not remembered
An append-only decision ledger keeps the rationale next to the change. When the same question comes back in four months, the answer is in the repo.
You can see what you now own
The ownership pane names every file you have taken over, what it was derived from, and how far behind it has drifted — information, not a demand.
Nothing bypasses it
Every path that can write the spec — CLI, MCP, the workbench itself — is declared and covered by an invariant test, so a new way to change your app cannot appear unattributed.
Three proposals waiting, one open decision, and every row in the spec panel tagged with where it came from. This is the whole surface — there is no second place a change can land from.
A single turborepo on the committed 2026 stack.
The stack is fixed on purpose. Swapping a piece takes a written decision record and a migration plan — never a fresh repo, which is the point: the whole product is a bet that settled decisions stay settled.
withMeta), and the page/UX layer (a typed template registry). Provenance flags, an append-only decision ledger, and typed spec-ops are in the base types from day one.POST /mcp endpoint.maxstack eval runs spec → generate → change → validate headlessly and records the numbers that gate decisions — above all the change-expressibility ratio and regeneration-safety. Evidence, not assertion.Add one entity. Grow a whole admin surface.
One spec-op is the seed — that is the whole authoring surface, and it is what an agent writes. Sprout builds the typed table from it at runtime and derives the entire CRUD surface from there: no per-entity code, no scaffolding to maintain, and no schema file to keep in sync.
{ "op": "data.addEntity", "args": { "entity": { "id": "e-task", "name": "task", "fields": [ { "name": "title", "type": "string", "required": true }, { "name": "done", "type": "boolean", "required": true }, { "name": "priority", "type": "enum", "required": true, "options": ["low", "medium", "high"] } ] } } } // validated, then appended to the op log with its author recorded
Sprout builds the table from that op, at runtime ↓
// constructed in memory by the runtime; the database is reconciled // with CREATE TABLE / ADD COLUMN IF NOT EXISTS. Shown for shape only. priorityEnum = pgEnum('priority', ['low', 'medium', 'high']) task = pgTable('task', { id: uuid('id').primaryKey().defaultRandom(), title: withMeta(text('title'), { label: 'Title', required: true, }), done: withMeta(boolean('done'), { label: 'Done' }) .default(false), priority: withMeta(priorityEnum('priority'), { label: 'Priority' }), })
…and derives all of this from it ↓
List / detail / create / edit at /admin, no per-entity code.
Labels, placeholders, and constraints straight from the metadata.
Sortable, filterable columns derived from filterable flags.
Zod 4 schemas from min/max/required — client & server.
Resource endpoints with the same rules the forms enforce.
RBAC-gated per-resource CRUD tools — agents get them for free.
Add a field to the spec and it's writable on the very next request — no restart, no migration ceremony. Additive-only DDL means a change can't strand your data.
You don't wait for codegen to have a real app.
The /admin surface is grounded in your project's accepted spec. Accept a field in the workbench queue and it appears in the form on the next request — rows persist in on-disk Postgres. Generators land the route code later, on your schedule.
Search, per-column filters, a date range, CSV export, row actions — all derived from the spec. None of it written per-entity.
One command to an app with rows in it.
Node ≥ 22 is the only prerequisite — no account, no checkout, no build step. maxstack start takes a sentence and hands back a running app with sample data already in the tables. From there you run claude and describe the next thing; building is a conversation, not a stack of commands.
npx maxstack start "a task tracker for a small team"
One package — the CLI plus the prebuilt web runtime it pins (maxstack-runtime) — so start, dev, build and deploy all work from the install, with nothing to compile.
-
start
A sentence in, a running app out
Scaffolds the project, lands the spec-ops your description implies, seeds sample rows, and serves. You land on an
/adminwith data in it, a/workbenchto review from, and aPOST /mcpendpoint any agent can drive.--no-seedif you would rather start empty. -
claude
Then just talk to it
startalready wrote.mcp.jsonand.claude/skills, so any agent has the platform's tools from its first message. You describe the next change; it lands typed spec-ops overPOST /mcp. The CLI verbs are the same vocabulary if you would rather type. -
add
Pick the modules you need
15 of them, each installed through the same validated op path — with prerequisites resolved for you, and an upgrade codemod for when the module moves forward.
-
validate
You stay in charge of every change
Nothing an agent proposes becomes grounding truth until you accept it in the workbench, and
validateis the standalone gate: spec valid, ownership manifest intact, regeneration safe. Trust nothing that has not passed it.
Prefer the scaffold without the starting spec, the rows, or the server? maxstack init is exactly that, and maxstack demo seeds sample data into an existing project.
Always reach for the lowest rung that expresses the change.
Every change goes up an explicit ladder — cheaper rungs absorb more, and regeneration stays safe by construction. The weight is what the change cost you; the platform's job is to keep changes on the low rungs.
Spec-op most changes · zero hand-written code
Tell the agent "add a renewsOn date field to Subscription." It applies data.addField over MCP; the field appears in your review queue; you Accept; the admin form has it on the next request and every regeneration derives it everywhere.
Regen-as-diff spec touches generated files
regenerateAsDiff re-derives and returns a reviewable unified diff instead of overwriting. Protected files are surfaced, never proposed. The batch lands only if the validate gate passes.
Slot-fill custom UI inside a generated page
Generated pages render <Slot name="…"/> wired to a stable, user-owned *.slots.tsx file. The generator wrote the stub once and never touches it again — edit it freely. The generated file keeps regenerating; your slot survives byte-identical.
Eject you own the whole file, forever
Eject copies the generated file, strips the banner, and flips its ownership to ejected in the manifest. From then on regeneration never overwrites it — it's yours, plain code on a standard stack, including the responsibility to keep it current.
What that guarantee measures out to: across every benchmark and every one of the 56 changes on the long-lived project, 0 files the platform did not own were overwritten — and 6 files taken over by hand on that project (2 ejected, 4 filled slots) held their bytes exactly through every later change.
The invariants that make it safe: additive-only spec-ops + additive DDL · manual rows survive regeneration · generators ground only on accepted entities · a writer that checks the ownership manifest before every write, so it can never clobber — overwrite a file you had taken over. Every change ends with maxstack validate.
5 verbs you'll use. 24 in total when you want them.
You don't have to touch any of these — the agent drives the same vocabulary over MCP. They're here for when you'd rather type than talk. Every verb runs standalone from the npm install: dev serves the runtime that ships with the CLI, build vendors it into your project. The 5 marked everyday below are the ones worth knowing; the rest are situational, and the reference is generated from the command tree so it cannot drift from this count.
A sentence in, a running app with rows in it out — scaffold, land the implied ops, seed, serve. --no-seed, --no-dev.
Scaffold a project — spec.json, config, generated app/, durable runtime dir. --desc, --backend pglite.
Apply a typed, additive spec-op from the CLI — the same vocabulary agents use over MCP.
Terminal-native sugar for a data.addEntity op — --field title:text! --field done:bool, then --accept --gen.
Add one field to an entity — sugar for a data.addField op, e.g. add-field task dueOn:date!.
Add a default list page for an entity — sugar that compiles to a page.addPage op.
Regenerate the app tree from the accepted spec. Never-clobber: unchanged spec ⇒ all-unchanged output. --upgrade redraws against the current framework generators instead of the pinned ones.
Install a feature bundle (auth, billing, …) through the validated spec-op path, with prerequisites.
Take whole-file ownership of a generated route by id. Ownership flips to ejected in the manifest — never re-clobbered.
The standalone gate: spec valid · manifest intact · regeneration safe. Trust nothing that hasn't passed it.
Run the app — /admin, /workbench, and POST /mcp on localhost:3000. Regenerates on boot and watches the spec.
Seed sample data into the project's data dir so there's something to explore before you touch the spec.
Structurally clone the workspace into .maxstack/runtime/ — a self-contained, vendored runtime.
Ship it — docker run or Fly, the owned slots executing in the built runtime.
What needs you, in order — worst first, with the reason why. Bulk accept/reject and undo; --section exposure for what's publicly reachable.
Report what is actually running — CLI and runtime versions, staleness, store lock, dev server, MCP reachability.
The north star is change-safety — and it's measured.
Time-to-Nth-safe-change: the effort and success rate of landing a change to an already-live app with the regeneration-safety suite still 100% green. This is the one number the whole platform exists to move.
| Change kind | Weight | What you did |
|---|---|---|
| spec op · apply-op | 1 | Expressed it as a typed op; platform generated it |
| spec op · regen-diff | 2 | Reviewed an agent-produced diff |
| slot fill | 3 | Wrote code into a cross-file extension slot |
| eject | 5 | Took whole-file ownership, and the upkeep that comes with it |
| off-surface | 8 | Built it outside the spec entirely; the platform absorbed none of it |
Cheaper = more absorbed. The ordering op < diff < slot < eject < off-surface is the invariant — the moat, in cost terms.
weightPerSafeChange · the long-lived project
──────────────────────────────────
first half 1.23 → second half 1.27 (×1.03)
Two non-negotiable gates run every maxstack eval: determinism (same spec ⇒ byte-identical generation) and regeneration-safety (nothing you own gets overwritten, zero violations). A safety violation exits the nightly eval non-zero.
And one number we publish against ourselves. Expressibility is the share of a frozen backlog of 119 real product asks that the platform absorbs as a typed operation rather than as code you have to write and then own: 0.71 today, up from 0.59 the day that backlog was frozen. It will never reach 1.00, and we would not want it to — the same backlog contains work that should stay hand-written, which puts a ceiling of 0.75 on the figure. Against the headroom that ceiling actually leaves, the platform has captured 0.74; the target is 0.80. Separately, 22 asks in the live backlog have no operation or slot at all — a published gap, not a rounding. The frozen backlog can only rise by the platform getting better, never by the corpus getting easier.
Run 805cfebf73a1 (2026-08-07): 11 benchmarks green · cost per safe change 1.58–2.40 across the corpus, 1.29 over the long-lived project.
Sketch of the deployed front page — technews ships standalone, so this one isn't a capture. The /admin and /workbench shots above are.
Built start-to-finish through the platform's own surfaces.
technews is a Hacker-News-shaped app assembled entirely through agent spec-ops, human review ops, and generators — then run standalone via maxstack build and deployed. An owned submit slot and a ranked front page execute in the built runtime. Every surface it uses is the same one you get from the npm install:
The honest boundaries.
Every one of these is a real limit today, not a coming-soon. If one of them is your requirement, this is the wrong tool and you should know that from the page rather than from the install.
Not mobile
Web only — React Router on the server, rendered in a browser. There is no native target and none planned.
Not a hosting product
maxstack build vendors a self-contained runtime into your project and deploy wraps Docker or Fly. You bring the infrastructure and the Postgres.
Not a design tool
You get a coherent admin surface derived from the spec, not a canvas. Bespoke UI is a slot you write by hand, deliberately.
Not in a browser tab
It starts with npx on your machine, on your filesystem, in your git history. If a hosted editor is the requirement, the builders win that comparison.
Pre-1.0
The spec vocabulary and CLI can still change between minor versions. What is held stable is ownership: the platform does not overwrite files you have taken.
No speedup multiple
There is no control arm — no "same backlog, an agent, a blank repo" measurement — so no N× faster claim appears anywhere here. Earning one is open work.
The alternatives you are actually weighing: the builders (v0, Lovable, Bolt) start in a browser tab and have no review model, so nothing stops a regeneration from landing on code you changed. The starter kits (Shipfast, Vercel’s SaaS Starter, T3) hand you a fixed set to delete from, wired on day one and yours to maintain from day two — no upgrade path. We published the module-by-module version of that comparison.
Read the framework, not just the pitch.
Everything you need — setup, the change ladder, the workbench, the metrics protocol, the deploy runbook. The reference pages are generated from the source they document, so they cannot drift.
Quickstart
Install, scaffold, and make your first spec-driven change in five minutes.
User guide
Setup, first project, and making the 2nd → 50th change safely.
Command reference
Every verb and flag — generated from the command tree itself.
Spec-ops
The typed operations that can change a spec, with their arg schemas.
MCP reference
The 15 platform tools a spec-driving agent uses, and how to connect.
The ownership ladder
Spec-op → regen-diff → slot → eject, and the never-clobber invariants.
Deploy
maxstack build to a vendored runtime, then Docker or Fly.
Everything else
Workbench, metrics, security baseline, ADRs, and the project history.
Describe what you want.
Keep every decision about it.
Node ≥ 22 and npm — the whole prerequisite list. One command scaffolds the project, lands the spec-ops your sentence implies, seeds rows and serves. From there you add modules, review what the agent proposes, and own whatever you decide to own.
init already wrote .mcp.json and .claude/skills — no claude mcp add, no hand-editing. Just run claude in the project and every agent gets the platform's tools over stdio — present in every session, no port, no ordering.
Framework before AI · Config first · No black boxes · No vendor lock-in