toiljs 0.0.102 → 0.0.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,14 +53,14 @@ export const TOIL_DOCS: Record<string, string> = {
53
53
  "getting-started/migrating.md": "# Migrating an existing React app\n\nHow to bring a React app you already have into toiljs. The frontend usually moves over with small changes. The backend is the real work, because a toiljs server is not Node.js, and this page is honest about what that means.\n\n## Why and when\n\nMove to toiljs when you want your frontend and backend in one typed repo, deployed to the edge, with a built-in global database, and you are willing to rewrite your server logic against toiljs's rules. If you only want a React bundler, toiljs is more than you need. If you want the full-stack, typed, edge model, this is the payoff.\n\nThe safest approach is **not** to convert your old project in place. Instead, scaffold a fresh toiljs project and move code into it piece by piece. A fresh project comes with the required presets, config, and routing already wired, so you spend your time on your code, not on plumbing.\n\n```sh\ntoiljs create my-app\ncd my-app\n```\n\n## The big picture: what moves where\n\n```mermaid\nflowchart LR\n subgraph OLD[\"Your current app\"]\n FE[\"React components,<br/>pages, styles, assets\"]\n BE[\"Node/Express backend,<br/>DB access, npm libs\"]\n end\n FE -->|\"mostly copy\"| C[\"toiljs client/\"]\n BE -->|\"rewrite\"| S[\"toiljs server/\"]\n C -.->|\"generated typed client\"| S\n S --> DB[(\"ToilDB\")]\n```\n\n- Your **React frontend** copies into `client/` with modest changes (routing and asset paths).\n- Your **backend** is **rewritten** into `server/` as toilscript, because it compiles to WebAssembly, not Node.\n\n## The frontend: what changes\n\n`client/` is a normal Vite + React app, so most of your frontend works as is. The common adjustments:\n\n- **Routing becomes file-based.** toiljs has no `<Routes>`/`<Route>` config. A file at `client/routes/about.tsx` is the `/about` page; `client/routes/blog/[slug].tsx` is `/blog/:slug`. Move each page component to the matching file and delete your router setup. Use `Toil.Link` for navigation instead of your router's `Link`. See [Routing](../frontend/routing.md).\n- **Static assets move to `client/public/`.** Files there are served as is (images at `/images/...`, plus `favicon` and `robots.txt`).\n- **Global styles move to `client/styles/`**, and are imported from `client/toil.tsx`. See [Styling](../frontend/styling.md).\n- **Your React npm packages are fine.** The client is regular JavaScript, so component libraries, state managers, and browser APIs all work.\n- **Data fetching changes** if you want the typed client: replace hand-written `fetch('/api/...')` calls with the generated `Server.REST.*` methods. You can keep raw `fetch` too, but you lose the type safety. See [Fetching data](../frontend/data-fetching.md).\n\nMetadata and SEO that you used a helmet library for is built in: set a `metadata` export per route, or use `Toil.Head`. See [Metadata and SEO](../frontend/metadata.md).\n\n## The backend: the honest part\n\nThis is where migration takes real effort. Read this section carefully before you plan the work.\n\nYour toiljs server is compiled by **toilscript** into WebAssembly. toilscript accepts a **strict subset of TypeScript**. It looks like TypeScript, but treat it as a small, separate language that happens to share the syntax. The practical consequences:\n\n### What is NOT available on the server\n\n- **No arbitrary npm packages.** You cannot `import` a library from `node_modules` into `server/`. There is no `express`, no `pg`, no `stripe` SDK, no `lodash`. If your route logic leans on npm libraries, that logic has to be reworked.\n- **No Node.js APIs.** No `fs`, `http`, `process`, `Buffer`, `path`, or other Node built-ins. The server runs in a sandbox, not in Node.\n- **No DOM or browser APIs**, because it is not a browser either.\n- **No connecting to your existing database.** There is no connection string and no driver. Your data moves to **ToilDB**, the built-in database.\n\n### What you use instead\n\ntoiljs replaces the common needs with built-in globals and decorators, so you rarely miss the missing libraries:\n\n| You used to reach for | On the toiljs server you use |\n| --- | --- |\n| `express` routes / a router | `@rest` controllers with `@get` / `@post` ([HTTP routes](../backend/rest.md)) |\n| A REST client between services | `@service` / `@remote` typed RPC ([RPC](../backend/rpc.md)) |\n| Postgres / Mongo / Redis | [ToilDB](../database/README.md) families (documents, counters, events, views) |\n| `jsonwebtoken`, session middleware | Built-in [auth](../auth/README.md) and cookies |\n| `crypto` from Node | the `crypto` global (synchronous Web Crypto) ([Crypto](../services/crypto.md)) |\n| `nodemailer` / an email SDK | the built-in email service ([Email](../services/email.md)) |\n| `process.env` | `Environment.get()` / `Environment.getSecure()` ([Environment](../services/environment.md)) |\n| `Date.now()`, timers | the `Time` global ([Time](../services/time.md)) |\n\n### Type system differences\n\ntoilscript uses precise, explicit numeric types instead of JavaScript's single `number`. You will write `i32`, `u64`, `f64`, and even `u256`, and be explicit about integer sizes. Values that must cross the wire are `@data` classes with concrete fields, not free-form objects. There is no `any`-style duck typing to lean on. The full rules, and how server types map to `bigint` and friends on the client, are in [Types](../concepts/types.md).\n\n### Two behaviors to design around\n\n- **Memory resets every request.** Each request gets a fresh `.wasm` instance and its memory is wiped afterward. A module-level variable does not persist. Anything durable goes in ToilDB. (This is the same rule you met in [Your first app](./first-app.md).)\n- **Reads and writes are split.** A `@get` is a read-only query; a `@post` is a write action. Operations that scan unbounded data are not allowed in a request handler; they move to a [`@derive`](../background/derive.md) or a background [daemon](../background/daemons.md).\n\n## Configuration\n\nTwo config files replace the various configs you may have had:\n\n- **`toil.config.ts`** is your client and build config: SEO defaults, image optimization, page transitions, and dev-server options. It uses `defineConfig`. See [Configuration](../concepts/config.md).\n- **`toilconfig.json`** is the low-level server (wasm) build config. The scaffold sets sensible defaults, and you usually leave it alone.\n\nEnvironment variables move from a `.env` you read with `process.env` to `.env` / `.env.secrets` files you read with `Environment.get()` / `Environment.getSecure()`. Secrets and plain vars are kept in separate buckets. See [Environment and secrets](../services/environment.md).\n\n## Step by step\n\n1. **Scaffold a fresh project** with `toiljs create` and get it running with `npm run dev`. Start from something that works.\n2. **Move the frontend.** Copy components into `client/components/`, turn each page into a file under `client/routes/`, move styles into `client/styles/`, and static assets into `client/public/`. Swap your router's `Link` for `Toil.Link`. Install your client-side npm dependencies.\n3. **Get the client rendering** against the routes, even if the data is still stubbed or points at your old backend. Fix routing and asset paths first.\n4. **Rebuild the backend, one route at a time.** For each old endpoint, add a `@data` model in `server/models/`, a `@rest` controller in `server/routes/`, and move its data into a ToilDB family. Import each new route in `server/main.ts`. This is the bulk of the effort; go endpoint by endpoint.\n5. **Move persistence to ToilDB.** Map each table or collection to a ToilDB family (a document store, a counter, an event log, or a view). See [Database overview](../database/README.md) for choosing a family.\n6. **Switch the client to the typed client.** Replace `fetch('/api/...')` calls with `Server.REST.*` (or `Server.<service>.*` for RPC). Now a backend change that breaks the contract shows up as a client type error.\n7. **Run the doctor.** `toiljs doctor` checks your wiring (routes, RPC generation, config, dependencies) and points at anything still off. Add `--fix` to let it repair what it can.\n\n## When toiljs is not the right move\n\nBe honest with yourself about the backend rewrite. Migration is a poor fit if:\n\n- Your server depends heavily on npm libraries or Node-only APIs that have no toiljs equivalent, and rewriting them is not worth it.\n- You must keep talking to an existing external database or service that toiljs cannot reach. (Daemons can make outbound HTTP calls in some cases, but general server-side networking is limited by design.)\n- You need long-lived in-process server state that does not fit the per-request, database-backed model.\n\nIn those cases, you can still adopt toiljs for the frontend and keep your existing backend, calling it with plain `fetch`. You just give up the single-repo, end-to-end-typed benefits for that part.\n\n## Gotchas and notes\n\n- **Do not try to `import` server code into the client or vice versa.** They compile with different rules. The only bridge is the generated `shared/server.ts`.\n- **One `@data` type per file** under `models/` matches the tooling's expectations.\n- **`shared/server.ts` is generated**, so it will not exist until your first build, and you never edit it.\n- **Plan the backend as a rewrite, not a port.** The frontend moves; the backend is re-expressed in toiljs's model. Budget for that.\n\n## Related\n\n- [Getting started overview](./README.md)\n- [Backend overview](../backend/README.md)\n- [Types](../concepts/types.md)\n- [Database overview](../database/README.md)\n- [The CLI reference](../cli/README.md)\n- [Configuration](../concepts/config.md)\n",
54
54
  "getting-started/project-structure.md": "# Project structure\n\nA tour of every folder and file in a toiljs project, what each one is for, and the single most important question: **where does this code run, the browser or the edge?**\n\n## Why this matters\n\ntoiljs blends frontend and backend into one repo, so the same `.ts` file extension can mean two very different things. A file in `client/` becomes JavaScript that runs in your user's browser. A file in `server/` becomes WebAssembly that runs on the edge, with different rules and different available APIs. Knowing which folder you are in tells you what you are allowed to do. Keep this mental split and everything else falls into place.\n\n```mermaid\nflowchart TD\n subgraph Browser[\"Runs in the browser\"]\n C[\"client/*\"]\n end\n subgraph Edge[\"Runs on the edge (WebAssembly)\"]\n S[\"server/* -> release.wasm\"]\n end\n subgraph Generated[\"Generated glue (types + config)\"]\n SH[\"shared/server.ts\"]\n CFG[\"toil.config.ts / toilconfig.json\"]\n end\n C -. \"typed calls\" .-> SH\n SH -. \"HTTP / realtime\" .-> S\n S --> DB[(\"ToilDB\")]\n```\n\n## The top level\n\nThese files sit in your project root.\n\n| File | What it is | Runs where |\n| --- | --- | --- |\n| `package.json` | Scripts (`dev`, `build`, `lint`, `typecheck`, `format`) and dependencies (`toiljs`, `react`, `toilscript`, ...) | tooling only |\n| `toil.config.ts` | **Client and build config.** Uses `defineConfig` to set SEO, images, page transitions, and dev options. | tooling only |\n| `toilconfig.json` | **Server (wasm) build config** for toilscript: the entry file, the output `.wasm` path, and low-level compile options. You rarely edit this. | tooling only |\n| `tsconfig.json` | TypeScript config for the client (`client/`, `shared/`, `emails/`). Extends `toiljs/tsconfig`. | tooling only |\n| `eslint.config.js` | Linting preset (`toiljs/eslint`). | tooling only |\n| `.prettierrc` | Formatting preset (`toiljs/prettier`). | tooling only |\n| `.prettierignore` | Files Prettier should skip (generated files). | tooling only |\n| `.gitignore` | Ignores `build/`, `.toil/`, generated files, and your `.env` files. | tooling only |\n| `.vscode/settings.json` | Tells VS Code to use the project's TypeScript so the toilscript editor plugin loads. | editor only |\n| `toil-env.d.ts` | **Generated** editor types for client globals like `Toil.Link` and `Toil.Image`. Do not edit. | editor only |\n| `toil-routes.d.ts` | **Generated** list of your real route names, so `Toil.Link href=\"...\"` type-checks. Filled in on the first build. | editor only |\n| `README.md` | Your project's readme. | docs |\n| `CLAUDE.md`, `AGENTS.md`, etc. | Optional AI-assistant hint files that point tools at the toiljs docs. | docs |\n\nTwo folders you may also see at the root:\n\n- **`.toil/`** is a working directory toiljs manages (a build cache and a copy of the docs). It is gitignored. You never edit it.\n- **`.env` and `.env.secrets`** are files **you** create when you need local environment variables or secrets during `toiljs dev`. They are gitignored so you never commit them, and the edge loads their real values out of band in production. Your server reads them with `Environment.get(\"KEY\")` and `Environment.getSecure(\"KEY\")`. See [Environment and secrets](../services/environment.md).\n\n## `client/` (runs in the browser)\n\nThis is a normal React app. You can use React libraries and browser APIs here freely.\n\n```text\nclient/\n toil.tsx the entry point; mounts your app\n layout.tsx the root layout wrapping every page\n 404.tsx the not-found page\n global-error.tsx the top-level error page\n routes/ file-based pages\n components/ shared React components\n styles/ global stylesheets\n public/ static files served as-is\n```\n\n- **`toil.tsx`** is the entry file. It imports your global styles and calls `Toil.mount(...)` to start the app. You rarely change it beyond the style imports.\n- **`layout.tsx`** is your root layout: the header, footer, and page shell that wrap every route. Its `children` prop is the current page.\n- **`404.tsx`** renders when no route matches. **`global-error.tsx`** renders when a route throws.\n- **`routes/`** is where pages live, and the file name **is** the URL. `routes/index.tsx` is `/`, `routes/about.tsx` is `/about`, `routes/blog/[slug].tsx` is `/blog/:slug`. This is called **file-based routing**. See [Routing](../frontend/routing.md).\n- **`components/`** holds React components you reuse across pages. Nothing here is a route.\n- **`styles/`** holds your global CSS (or Sass, Less, or Stylus, if you chose one). See [Styling](../frontend/styling.md).\n- **`public/`** holds static files served exactly as they are: `favicon`, `robots.txt`, and an `images/` folder (reachable at `/images/...`). The `public/index.html` is the base HTML shell your app mounts into.\n\nA single global, `Toil`, is available in client code without an import (for `Toil.Link`, `Toil.Image`, and `Toil.Head`). It is typed by the generated `toil-env.d.ts`.\n\n## `server/` (runs on the edge, as WebAssembly)\n\nThis is your backend. It is compiled by toilscript into one `.wasm` file. Remember the two rules: **memory resets every request**, and **this is not Node.js** (a strict TypeScript subset, no arbitrary npm packages). See [Backend overview](../backend/README.md) and [Types](../concepts/types.md).\n\n```text\nserver/\n main.ts the entry: wires the handler + imports your modules\n tsconfig.json server-only TS config (loads the toilscript editor plugin)\n toil-server-env.d.ts generated editor types for server globals\n core/ your request handler and shared logic\n models/ @data classes\n routes/ @rest controllers (HTTP)\n services/ @service / @remote (typed RPC)\n migrations/ ToilDB schema migrations\n scheduled/ reserved for scheduled tasks\n```\n\n- **`main.ts`** is the entry the build compiles. It does three required things: it sets `Server.handler` (a factory that returns one fresh handler per request), it re-exports the wasm entry points (`export * from 'toiljs/server/runtime/exports'`), and it defines the `abort` hook. It also `import`s your other server modules so a direct toilscript run builds the same code.\n- **`core/`** holds your top-level `ToilHandler` (often `AppHandler.ts`): the first code that sees each request. It can dispatch to your `@rest` controllers and then fall through to any hand-written logic.\n- **`models/`** holds your `@data` classes, one type per file. A `@data` class is a typed message that can cross the wire between client and server (and into ToilDB). See [Data types](../backend/data.md).\n- **`routes/`** holds your `@rest` controllers: classes decorated with `@rest`, `@get`, and `@post` that expose HTTP endpoints. See [HTTP routes](../backend/rest.md).\n- **`services/`** holds `@service` classes and free `@remote` functions: typed remote calls the client makes as plain function calls (no URLs). See [Typed RPC](../backend/rpc.md).\n- **`migrations/`** holds ToilDB schema migrations. When you change the shape of a stored `@data` type, you add a `<Type>.migration.ts` here that carries old records forward. The compiler enforces this convention. See [Documents](../database/documents.md).\n- **`scheduled/`** is reserved for scheduled tasks. New decorated files anywhere under `server/` are picked up automatically by the build.\n- **`tsconfig.json`** and **`toil-server-env.d.ts`** are editor-support files. They teach your editor about the server globals (`crypto`, `Cookie`, `Environment`, and friends) so it stops flagging them. They do not affect the build.\n\n### How the build discovers your server code\n\nYou do not register routes in a config file. The compiler scans every `.ts` file under `server/` and picks up anything that declares a decorated surface (`@rest`, `@service`, `@remote`, `@data`, `@user`, `@database`, and so on). Importing those files from `main.ts` is still good practice: it keeps a direct `toilscript` run building the exact same server.\n\n## `shared/` (generated glue)\n\n```text\nshared/\n server.ts GENERATED typed client (do not edit)\n```\n\n**`shared/server.ts` is written for you** by the server build. It contains:\n\n- A typed `Server` object the browser uses to call your backend: `Server.REST.*` for HTTP routes and `Server.<service>.*` for RPC.\n- The client-side codecs for every `@data` class, so responses come back as real typed objects.\n- A `getUser()` helper for reading the signed-in user on the client.\n\nBecause it is generated, it does not exist in a fresh project and it is gitignored. It appears the first time you run `toiljs dev` or `toiljs build`. Never hand-edit it; change your server code and it regenerates.\n\n## `build/` (compiled output)\n\n```text\nbuild/\n server/release.wasm your compiled backend (+ release.wat, a readable text form)\n client/ the bundled React app (from Vite)\n```\n\nThis is what actually ships. It is gitignored and recreated by `toiljs build`. You do not edit anything here.\n\n## Putting it together: one request\n\n```mermaid\nsequenceDiagram\n participant B as Browser (client/)\n participant SH as shared/server.ts\n participant W as server.wasm (server/)\n participant DB as ToilDB\n B->>SH: Server.REST.likes.like()\n SH->>W: HTTP POST /likes\n W->>DB: counter.add(key, 1)\n W-->>SH: typed LikeCount response\n SH-->>B: { count }\n```\n\nThe browser calls a typed method, the generated client turns it into an HTTP request, your `.wasm` handles it and touches ToilDB, and a typed result comes back. You wrote both ends; the middle is generated.\n\n## Gotchas and notes\n\n- **A `.ts` file's folder decides its rules.** The same code that works in `client/` may not compile in `server/`, because the server is a strict subset without Node APIs.\n- **Do not edit generated files.** `shared/server.ts`, `toil-env.d.ts`, `toil-routes.d.ts`, and `toil-server-env.d.ts` are all regenerated by the build and will overwrite your changes.\n- **One `@data` type per file** under `models/` keeps things tidy and matches the convention the tooling expects.\n- **`build/` and `.toil/` are disposable.** Delete them and the next build recreates them.\n\n## Related\n\n- [Your first app](./first-app.md)\n- [Frontend overview](../frontend/README.md) and [Routing](../frontend/routing.md)\n- [Backend overview](../backend/README.md)\n- [Database overview](../database/README.md)\n- [Configuration](../concepts/config.md)\n- [Decorators reference](../concepts/decorators.md)\n",
55
55
  "getting-started/README.md": "# Getting started\n\ntoiljs is a full-stack web framework: you write a React frontend and a TypeScript backend in one project, and toiljs ships both together. This section takes you from \"nothing installed\" to \"a small feature running end to end.\"\n\n## What toiljs is\n\nThink of a normal web app as two programs that have to agree with each other:\n\n- A **frontend**: the React code that runs in your user's browser.\n- A **backend**: the code that runs on a server, answers requests, and talks to a database.\n\nNormally these live in separate projects, speak to each other over hand-written HTTP calls, and drift apart until something breaks at runtime. toiljs puts both in one repository and wires them together with types, so a change on one side shows up as a compile error on the other side instead of a bug in production.\n\nThe twist is what your backend becomes. You write it in TypeScript, but toiljs does not run it in Node.js. Instead, a compiler called **toilscript** turns your backend into **WebAssembly** (often shortened to \"Wasm\"): a small, fast, sandboxed program that runs at the **edge**. \"Edge\" just means servers spread all over the world, close to your users, so requests do not have to travel to one far-away data center. A worldwide database called **ToilDB** is built in, so you can store and read data without setting up or connecting to a database yourself.\n\nNew terms, defined once:\n\n- **WebAssembly / Wasm**: a compact binary format that runs code in a locked-down sandbox at near-native speed. Your backend compiles to a single `.wasm` file.\n- **toilscript**: the compiler that turns your TypeScript backend into that `.wasm` file. It accepts a strict subset of TypeScript (more on that below).\n- **Dacely edge**: the global network of servers that runs your `.wasm` backend.\n- **ToilDB**: the built-in database that lives on the edge next to your code.\n\n## The client / server / shared mental model\n\nEvery toiljs project is organized into three folders. The most important thing to learn first is **where each piece of code actually runs**.\n\n```mermaid\nflowchart LR\n A[\"Your project<br/>(one repo)\"] -->|toiljs build| B[\"client bundle<br/>React + JS\"]\n A -->|toilscript compiles| C[\"server.wasm<br/>your backend\"]\n B --> U[\"User's browser\"]\n C --> E[\"Dacely edge<br/>(worldwide servers)\"]\n E --> D[(\"ToilDB<br/>global database\")]\n U <-->|\"HTTP / realtime\"| E\n```\n\n- **`client/`** is your React app: pages, components, and styles. It is bundled by [Vite](https://vitejs.dev) and runs **in the browser**.\n- **`server/`** is your backend: HTTP routes, database access, auth. It is compiled by toilscript to `build/server/release.wasm` and runs **on the edge** (and locally when you run the dev server).\n- **`shared/`** holds a file that toiljs **generates for you** (`shared/server.ts`). It is a fully typed client: the browser calls your backend through a `Server` object, and TypeScript checks every call. You do not write this file by hand.\n\nHere is the loop in one picture:\n\n```mermaid\nflowchart TD\n W[\"client/routes/page.tsx<br/>(browser)\"] -->|\"Server.REST.likes.like()\"| G[\"shared/server.ts<br/>(generated typed client)\"]\n G -->|\"HTTP request\"| S[\"server/routes/Likes.ts<br/>(your @rest route, in wasm)\"]\n S -->|\"read / write\"| DB[(\"ToilDB\")]\n S -->|\"typed response\"| W\n```\n\nYou write both ends in TypeScript, and the generated `shared/server.ts` in the middle keeps them in sync.\n\n## Two rules to keep in mind\n\nThese two facts explain most of how toiljs backends behave. They are covered in depth later, but it helps to meet them now.\n\n1. **The server runs one fresh instance per request.** Every request gets a brand-new copy of your `.wasm`, and its memory is wiped when the request ends. So a normal variable you set in one request is gone by the next request. Anything that must survive (accounts, counters, posts) has to go into **ToilDB** or another store. Nothing in a plain module-level variable persists.\n\n2. **The server is not Node.js.** toilscript compiles a strict subset of TypeScript, so you cannot `import` an arbitrary npm package into `server/` or use Node APIs like `fs`. Instead, toiljs gives you built-in globals for the common needs: `crypto`, cookies, email, the database, and more. See [Types](../concepts/types.md) and [Decorators](../concepts/decorators.md) for the details.\n\nThe client side, by contrast, is normal React. You can use React libraries and browser APIs there as usual.\n\n## The getting-started path\n\nWork through these pages in order:\n\n1. **[Installation](./installation.md)**: check your Node.js version and install the toiljs command-line tool.\n2. **[Create a project](./create-project.md)**: scaffold a new app and see what you get.\n3. **[Project structure](./project-structure.md)**: a tour of every folder and file, and where each one runs.\n4. **[Your first app](./first-app.md)**: build a tiny feature end to end, a page that calls a backend route and reads and writes one piece of ToilDB data.\n5. **[Migrating an existing app](./migrating.md)**: bring a React app you already have into toiljs.\n6. **[Deploy](./deploy.md)**: build for production and self-host it, and how the managed edge fits in.\n\n## Related\n\n- [Documentation home](../README.md)\n- [The CLI reference](../cli/README.md)\n- [Frontend overview](../frontend/README.md)\n- [Backend overview](../backend/README.md)\n- [Database overview](../database/README.md)\n- [Compute tiers (where code runs)](../concepts/tiers.md)\n",
56
- "introduction/design-principles.md": "# Why toil is built this way (the RSG bar)\n\ntoil is opinionated on purpose. Almost every design decision traces back to one internal rubric it\nholds itself against, and this page shows the rubric and the choice each axis forces.\n\n## The rubric in a paragraph\n\n**RSG** (Resilience and Scale Grade) is toil's own internal rubric for how resilient, distributed,\nfast, lean, and secure an app really is, scored as a single letter from AAA down to D. It grades\nnine axes, and the one rule that matters is this: **your grade is your weakest axis**, never the\naverage and never the best. To earn AAA, all nine must be AAA at the same time. A globally edged\nfrontend on a single-region database is capped by the database. A worldwide system serving a\none-second app is capped by latency. The lowest column sets the grade, every time. RSG is not an\nexternal certification and no auditor issues it; it is a design mirror the team holds up to find\nthe weakest link and fix it first. The full rubric lives at the repository root in\n[`RSG.md`](../../RSG.md).\n\nThe reason for the weakest-link rule is the most common lie in this space: calling something\n\"global scale\" because the read path is global, while the write path is one box in one region.\nAveraging would let that one strong axis hide the weak one. The minimum refuses to.\n\n## The nine axes, and the one choice each one forces\n\nEach axis names a way a system can be weak. Each row is the single design decision toil makes so\nthat axis cannot be the thing that caps it.\n\n| RSG axis | What it grades | The toil design choice that hits it |\n| --- | --- | --- |\n| **Topology + distribution** | How close your code runs to users, and in how many places | Edge compute: your frontend and backend both run on nodes next to users, worldwide, not in one origin region. |\n| **Availability** | What survives a failure | Cross-region failover with no single point of failure, so losing a node or a region does not take the app down. |\n| **Data path** | Where data is read and *written* (the hard one) | ToilDB's per-key-home model distributes the **writes**, not just the reads. See [How toil is distributed](./distributed.md). |\n| **Delivered p99 latency** | The end-to-end time the user actually feels (measured, under 100ms = AAA) | An allocation-free hot path, measured rather than assumed, so the response is fast for real, not just on paper. |\n| **Program performance + efficiency** | Hot-path code quality, and cost per request (no brute-forcing latency with a big server bill) | No blocking work on the request path; the fast path does no wasted work, so speed comes from the code, not from overprovisioning. |\n| **Dependencies** | How much of your critical path you own (zero third-party on it = AAA) | An owned, batteries-included stack: nothing third-party sits on the critical request path for you to be unable to inspect or fix. |\n| **Security** | How hard the system is to break, and how bad a breach would be | Post-quantum password login (the password never reaches the server in usable form), sandboxed WebAssembly backends, and Subresource Integrity on every asset. See [Security](../concepts/security.md). |\n| **Client performance + reach** | How well the shipped app runs on old and low-end devices as data grows | A lean React client: a small bundle and linear-or-better hot paths, so it stays smooth on weak hardware, not just new flagships. |\n| **Modern stack + compatibility** | Current protocols, *with* graceful fallback for older clients | HTTP/3, QUIC, and WebTransport where they fit, negotiating down cleanly so nobody one version behind gets a blank screen. |\n\n## The punchline\n\ntoil is opinionated because being AAA on **every** axis at once forces these choices. You cannot\nreach the top grade with a fast frontend on a centralized database, or a global system running\nslow code, or a modern stack that only works on the newest browser. The weakest-link rule closes\nevery one of those escape hatches. Any framework that lets a single axis slip is, by its own\nhonest scoring, not AAA. Holding all nine at once is the whole reason toil looks the way it does.\n\n## Related\n\n- [How toil is distributed](./distributed.md): the data-path axis in depth, and why distributing\n writes is the hard one.\n- [What makes toil hyper-scalable](./hyperscale.md): the topology, latency, and program axes in\n practice.\n- [Security](../concepts/security.md): the security axis and its hard caps.\n- [`RSG.md`](../../RSG.md) at the repository root: the full rubric, the internal mirror this page\n summarizes.\n",
57
- "introduction/distributed.md": "# How toil is distributed\n\nDistributing a website's reads is easy. Distributing its writes is the hard part almost nobody solves, and it is why \"global\" apps are usually only half global. Here is the problem, and how ToilDB (the database built into toil) actually distributes the writes.\n\n## Reads are easy, writes are hard\n\nA read never changes anything, so you copy your data to servers worldwide and let each user read the nearest copy. Every copy agrees: a reader in Tokyo and one in Paris both get a fast, local answer.\n\nA write is a change, and two writes to the *same thing* can collide. A counter says `10`. Tokyo and Paris both read `10`, both add one, both write `11`. The real answer was `12`, so one add vanished with no error. That is a **write conflict**.\n\nYou could make every copy agree before accepting a write, but the network will eventually split (a **partition**), and the **CAP tradeoff** says that during a split you keep only two of consistency, availability, and partition tolerance. You either refuse the write (correct but unavailable) or accept it on one side and reconcile later (available but briefly inconsistent). Distributing writes is a real tradeoff to design around.\n\n\n## So almost everyone centralizes the write database\n\nFaced with that, nearly every stack keeps **one** primary write database in **one** region and spreads only read replicas worldwide. All writes funnel to that one box, one at a time, so conflicts cannot happen.\n\nIt is a reasonable choice, and it hides two costs the [RSG rubric](./design-principles.md) flags on its data-path axis:\n\n- **Far writes are slow.** Post from Tokyo to a primary in Virginia and your write crosses the planet and back before anything saves. The page was local; the action was not.\n- **The primary is a single point of failure.** One region holds every write, so if it has a bad day, nothing anywhere can be changed.\n\nThe read path is global; the write path is one machine in one city. Under RSG's weakest-link rule, that single data path caps the whole system.\n\n## ToilDB's answer: every key has a home\n\nToilDB gives **every key its own home**. A key is the label you store data under (a user id, a username, a room name; see the [database overview](../database/README.md)). Each key is assigned one home: the single source of truth that orders its writes.\n\nTwo things follow, and together they are the whole trick:\n\n- **Writes to one key are safe.** Every write to a key travels to that key's home, which **serializes** them (applies them one at a time, in order). Both counter adds are ordered at the counter's home, so the result is `12`. No global lock over the whole database.\n- **Writes spread worldwide.** Different keys get different homes, so total write load spreads out. Tokyo users' data can home near Tokyo, Paris users' near Paris. No single box every write funnels through, so no single bottleneck.\n\n```mermaid\nflowchart TB\n WA[\"write user:aiko\"] --> KA[\"home near Tokyo<br/>key: user:aiko\"]\n WB[\"write user:bruno\"] --> KB[\"home near Paris<br/>key: user:bruno\"]\n WC[\"write room:general\"] --> KC[\"home near Ohio<br/>key: room:general\"]\n KA -.->|\"replicate for<br/>local reads\"| KB\n KA -.-> KC\n```\n\nReads stay local: each key still replicates out, so a reader anywhere gets a nearby copy. Those copies are **eventually consistent**, meaning that for a brief moment (usually milliseconds) after a write lands at the home, a far read can lag before it catches up. For almost all app data this is invisible; the [database overview](../database/README.md) has the full picture.\n\nWhich location owns a key is decided by a shared formula (rendezvous hashing) every node computes the same way, so any node routes a write to the right home with no central coordinator. A key's home can move to follow demand, without rehashing the database.\n\n## The seven families pick the right consistency tool\n\nOne \"home orders the writes\" rule fixes the counter, but different jobs want different guarantees. So ToilDB ships **seven families**, each a collection type tuned for one shape of data, each exposing only the operations that are safe and fast for it:\n\n| Family | What it gives |\n| --- | --- |\n| [Documents](../database/documents.md) | A record you look up by id |\n| [Counters](../database/counters.md) | Conflict-free tallies: adds from anywhere merge, no lost updates |\n| [Unique](../database/unique.md) | A one-of-a-kind claim (a username); the home picks exactly one winner |\n| [Capacity](../database/capacity.md) | Limited stock (tickets); reserve/confirm/cancel holds prevent overselling |\n| [Events](../database/events.md) | An append-only log in one agreed order |\n| [Membership](../database/membership.md) | Sets of who belongs to what |\n| [View](../database/views.md) | A read-optimized result a background job builds |\n\nDistributing writes is not one problem with one answer; each family is the right tool for one shape.\n\n## The hard machinery toil provides so you do not have to\n\nThe per-key-home model only works if a lot of unglamorous machinery runs reliably across a flaky network, and ToilDB owns it: per-key **placement** and safe **rehoming** (a rising epoch plus a fencing token so the old owner stops the instant the new one takes over), ordered **cross-region replication** with per-stream cursors that detect and backfill gaps, **idempotent apply** so redelivered writes cannot double-count, **capacity escrow** and **tenant quotas**, and **failover** with snapshot re-seeding for a cell that has fallen too far behind. Getting all of these right at once is exactly why truly distributed websites are rare.\n\n**Still being finished:** live multi-cell **WAN routing** (wiring many real regions into one running mesh) and the full database-level **leader fencing** on the write path (the host-side leader gate is the current version). The design is settled; the last-mile host wiring is what remains.\n\n## Related\n\n- [The database (ToilDB)](../database/README.md): families, keys and values, and eventual consistency in depth.\n- [Compute tiers](../concepts/tiers.md): where your code runs, the compute side of the same story.\n- [What makes toil hyper-scalable](./hyperscale.md): the mechanisms that let one small program serve the planet.\n- [Why toil is built this way (the RSG bar)](./design-principles.md): the rubric behind the data-path axis.\n",
58
- "introduction/how-it-works.md": "# How toil works\n\nWhat your project compiles into, and what happens when a user makes a request. Every term is defined as it appears.\n\n## What \"build\" produces\n\n`toiljs build` turns your one TypeScript project into three outputs, because your code has two homes: the browser and the edge.\n\n- **Client bundle.** Your React app plus any pages toil renders ahead of time, packaged as ordinary web files (HTML, JS, CSS, images). Runs in the browser.\n- **`server.wasm`.** Your backend. You write it as normal TypeScript classes in `server/`, and the **toilscript** compiler turns it into WebAssembly. It runs on the edge, not in the browser and not in Node.\n- **Generated typed client (`shared/`).** A small browser-side client toil generates from the shape of your backend. Your React code calls a normal-looking async function, and the types line up end to end: rename a field on the server and the frontend stops compiling until you fix it.\n\n**WebAssembly** (WASM), in two sentences: a compact binary format that runs at close to native speed with no interpreter warm-up, and runs **sandboxed**, in a locked box that cannot open files, reach the operating system, or make network calls on its own. It touches the outside world only through the small, fixed set of functions the host hands it (read the request, build a response, query the database), which is what makes it safe to pack many apps onto one shared box. ([Why that matters for scale](./hyperscale.md).)\n\n## The request lifecycle\n\nIt all happens on the **edge node nearest the user**, with no trip to a central origin.\n\nTwo terms first:\n\n- **The edge** is a fleet of servers spread across many cities. A request is served by whichever node is physically closest, which means lower latency (the delay before something happens).\n- An **origin server** is the single far machine a traditional site calls back to for anything real. toil has none: your backend and its database are replicated out to the edge, so there is nothing far away to call.\n\n```mermaid\nsequenceDiagram\n participant U as User's browser\n participant E as Nearest edge node\n participant W as server.wasm (your handler)\n participant DB as ToilDB (local copy)\n\n U->>E: Request (page or API call)\n alt Path is a page/asset it can serve\n E-->>U: Prerendered / SSR page or static file\n else Path is a dynamic API call\n E->>W: Decode bytes, call your handler\n W->>DB: Read / write (a local, nearby copy)\n DB-->>W: Result\n W-->>E: Response\n E-->>U: Response\n end\n```\n\n1. **The request lands on the closest edge node.** The network routes the user there automatically.\n2. **Page or code?** If the path is a prerendered page, a server-rendered page, or a static asset, the edge serves it and never wakes your backend. This is the fast path for most page loads.\n3. **Otherwise it runs your backend.** The edge decodes the raw bytes into a `Request` object and calls the single entry point of your `server.wasm`, which routes to your handler.\n4. **Your handler reads and writes locally.** When it needs stored data it talks to [ToilDB](../database/README.md), which has a copy right there at the edge. No ocean crossing.\n5. **Your handler returns a `Response`,** toil encodes it back to bytes, and the edge sends it to the browser.\n\nThe mental model for your backend: a function of the request. Bytes in, bytes out, one request at a time.\n\n### Stateless by default\n\nA fresh copy of your handler serves each request, and the next request might be served by a node on the other side of the planet. So anything you set on a field does not survive. This is a feature: interchangeable copies with nothing to coordinate are what let the backend scale worldwide. When you need something to persist, write it to ToilDB. See the [backend overview](../backend/README.md#stateless-by-default).\n\n## The pieces\n\nFive parts make up a running toil app; you have now met all of them.\n\n| Piece | What it is | Where it runs |\n| --- | --- | --- |\n| **React client** | Your frontend UI, the client bundle from the build. | The user's browser |\n| **toilscript backend** | Your TypeScript backend compiled to `server.wasm`. | The edge |\n| **The Dacely edge** | The Rust runtime that terminates the connection, serves pages, and runs your WASM. | Servers in many cities |\n| **ToilDB** | The globally distributed database, replicated next to your code. | The edge |\n| **The four tiers** | Where and for how long a piece of backend code lives. | L1 nearest, up to L4 worldwide |\n\nMost of your backend is the stateless, per-request handler above (tier **L1**). Long-lived connections (L2/L3) and single-worldwide scheduled jobs (L4 daemons) run on other tiers. Full detail, and how the build assigns them, is on the [tiers page](../concepts/tiers.md).\n\n## Related\n\n- [Backend overview](../backend/README.md): the request/response model and the sandbox in depth.\n- [The database (ToilDB)](../database/README.md): where persistent, shared state lives.\n- [Compute tiers](../concepts/tiers.md): L1 request, L2/L3 stream, L4 daemon.\n- [What makes toil hyper-scalable](./hyperscale.md): why this design serves the planet cheaply.\n",
59
- "introduction/hyperscale.md": "# What makes toil hyper-scalable\n\n**Hyper-scale** is serving very large, worldwide traffic at low latency without rebuilding your app as it grows. The test: when traffic goes from a thousand users to a hundred million across every continent, do you rewrite the system, or just run more of it?\n\nMost stacks scale the easy half (serving pages and cached reads from many places) but leave the hard half (writes, where data actually changes) in one region, so they slow down at once when enough far-away users start writing. toil scales both halves out together. It promises a design where scaling is cheap, adding more identical edge nodes with no central part everything funnels through, not a specific requests-per-second number.\n\n## The mechanisms\n\n**1. Compute next to the user.** toil has no origin server. Your `server.wasm` and its database are replicated to the [edge](../concepts/tiers.md) and run on the node nearest each user, so there is no slow hop to a faraway box. This is the biggest latency win, and the rest of the design exists to support it.\n\n```mermaid\nflowchart LR\n subgraph Origin[\"Everything funnels to one origin\"]\n direction TB\n A1[\"User (Tokyo)\"] -->|slow| O[(\"Origin + DB<br/>(Virginia)\")]\n A2[\"User (Paris)\"] -->|slow| O\n A3[\"User (Sydney)\"] -->|slow| O\n end\n subgraph Toil[\"Edge + distributed DB scales out\"]\n direction TB\n B1[\"User (Tokyo)\"] --> E1[\"Edge + data (Tokyo)\"]\n B2[\"User (Paris)\"] --> E2[\"Edge + data (Paris)\"]\n B3[\"User (Sydney)\"] --> E3[\"Edge + data (Sydney)\"]\n E1 <-.->|\"replicate\"| E2\n E2 <-.->|\"replicate\"| E3\n end\n```\n\nThe left side has one hot center every user drags a request to and back from, a bottleneck no amount of caching removes. The right side has no center: add a city, add an edge node.\n\n**2. WASM isolation and density.** Each site compiles to its own tiny, [sandboxed](./how-it-works.md#what-build-produces) WASM module that starts fast and cannot touch another tenant's files, memory, or network. Hard per-request limits (a memory cap in the tens of MiB, a **hard compute cap** that cuts off a looping handler, rate limits, and hostile-wasm containment) let one box safely hold many tenants at once, which is what makes compute in many cities affordable instead of a luxury.\n\n**3. Allocation-free hot path.** The code that runs on every request wastes nothing: no per-request allocations, and no garbage-collection pauses (so no random latency spikes under load). This earns latency with lean code rather than by overprovisioning hardware to hide slow code, which is a bar toil holds itself to explicitly ([the RSG rubric](./design-principles.md)).\n\n**4. Stateless tier over a distributed database.** A fresh copy of your handler serves each request and keeps nothing, so every node is interchangeable and you scale out purely by adding more. The data they share lives in [ToilDB](../database/README.md), which distributes **writes** too, not just reads, so there is no single box every write funnels through. The write mechanism and its honest trade-off (eventual consistency) are in [how toil is distributed](./distributed.md).\n\n**5. Modern transport.** The edge speaks HTTP/3 over QUIC (with graceful fallback to HTTP/2 and HTTP/1.1, plus WebTransport for realtime), and its networking is tuned to keep the connection-level cost of each request low as traffic grows. You configure none of it.\n\nThe five reinforce each other: take any one away and a bottleneck reappears. No density and edge compute is too expensive to spread; no distributed writes and the database caps you; a wasteful hot path and you are back to buying latency with servers.\n\n## An honest note on numbers\n\nThis describes a design, not a benchmark: real throughput and latency depend on your hardware, where your users are, how your data is shaped, and how your handler is written, and toil removes central bottlenecks and keeps per-request cost low without making a slow handler fast or repealing the speed of light between continents.\n\n## Related\n\n- [How toil is distributed](./distributed.md): distributing the writes, the hard problem this rests on.\n- [Why toil is built this way (the RSG bar)](./design-principles.md): the efficiency check behind the hot path.\n- [Compute tiers](../concepts/tiers.md): L1 through L4, and the stateless request model.\n- [How toil works](./how-it-works.md): the build outputs and the request lifecycle.\n- [The database (ToilDB)](../database/README.md): families, homes, and eventual consistency.\n",
60
- "introduction/modern-stack.md": "# The modern stack: what toil gives you that others do not\n\nMost frameworks give you a way to write code and then send you shopping: a database, an auth provider, email, a rate limiter, analytics, a realtime service, a job runner, all wired together and kept in sync by you. toil owns those parts instead: built in, and on from the first line. This page is the catalog of what ships; for how the edge and distribution actually work, see [How toil works](./how-it-works.md) and [How toil is distributed](./distributed.md).\n\n## What is built in\n\n| Feature | What it is | Why it matters |\n| --- | --- | --- |\n| Edge compute over HTTP/3 | Frontend and backend both run on servers in many cities, over HTTP/3 with automatic fallback to HTTP/2 or HTTP/1.1. | Code runs next to the user, so there is no slow round trip to one origin. |\n| [ToilDB](../database/README.md) | A global database with no connection string and seven families (documents, unique, counter, events, capacity, membership, view). | Distributes writes, not just reads, so a thousand servers can write at once without a single-region bottleneck (trade: eventual consistency). |\n| [Post-quantum auth](../auth/README.md) | Password login via `server: { auth: true }`; the password becomes an ML-DSA-44 key in the browser and the server stores only the public key. | The password never crosses the wire in a replayable form, so a breached server yields no usable passwords, and there is no identity vendor to rent. |\n| Automatic SRI | A SHA-384 fingerprint on every local script, preload, and stylesheet, plus an import map covering the whole module graph. | Tampered assets simply do not run, even if a CDN or cache hop is compromised. |\n| [Email](../services/email.md) | `EmailService.send(...)` or a reusable `EmailTemplate` with `{{placeholder}}` bodies, through a provider you configure once. | Verification codes, resets, and receipts are one call: validated, per-tenant capped, and off the worker while the provider replies. |\n| [Rate limiting](../services/ratelimit.md) | `@ratelimit` on a route, counted at the edge in an exact cross-worker limiter keyed on the caller's network address. | Over-limit clients are rejected before your code runs, blunting brute-force and spam. |\n| [Analytics and time series](../services/analytics.md) | The `Analytics` global reads your site's own counters: `self()` for a snapshot, `series(metric, range)` for a graph. | A real usage dashboard with no extra code and no analytics vendor, correct across every edge location. |\n| [Realtime streaming](../realtime/README.md) | A `@stream` class with `@connect`/`@message`/`@close`/`@disconnect` hooks, opened from React with `useChannel`; WebTransport in production, WebSocket in dev. | A resident instance stays alive per connection and remembers state next to the user (chat, presence, progress). |\n| [Daemons and @derive](../background/README.md) | `@daemon` runs one global background worker on a schedule (interval or cron) with lease-based failover; `@derive` re-runs when its source data changes to refresh a View. | Nightly jobs and rollups run once globally, with no cron server, queue, or leader election to run yourself. |\n| [Owned globals](../services/README.md) | No-import cookies (read/set, sign or encrypt), crypto (hash, HMAC, AES, random), time (the edge clock), and environment/secrets. | Your request's small dependencies live in one system, and your compiled program carries no credential. |\n| [Toolchain](../cli/README.md) | `toiljs create` scaffolding, a shared ESLint config, Prettier plus a decorator-aware plugin, an editor plugin, one CLI, and `toiljs doctor --fix`. | Set up for you and wired by types, so a server change is a compile error at your desk, not a production bug. |\n| LLM-friendly docs | A machine-readable `llms.txt` plus a generated `.toil/docs/` folder and pointer files (`CLAUDE.md`, `AGENTS.md`, editor rules), refreshed on every build. | An assistant reads your current conventions instead of guessing from stale training. |\n\n## Built in versus assemble it yourself\n\n| Capability | toil (built in, zero setup) | Typical stack (you assemble it) |\n| --- | --- | --- |\n| Edge and transport | Frontend and backend worldwide over HTTP/3 | A separate edge product, often reads-only |\n| Database | ToilDB: global, distributed writes, seven families | A managed database, usually single-region for writes |\n| Auth | Post-quantum login; the server holds no password | A rented identity provider or hand-rolled hashing |\n| Email, rate limiting, analytics | Built-in primitives, one call each | A separate SDK or vendor per capability |\n| Realtime and background | `@stream`, `@daemon`, `@derive` | A realtime service plus a cron server, queue, and leader election |\n| Asset integrity and toolchain | Automatic SRI, ESLint/Prettier/editor plugins, one CLI | Manual or skipped; configured and maintained by you |\n| Critical-path ownership | The core is toil's own | A mix of vendors you cannot inspect or fix |\n\n## Why it is all a default\n\nNone of this is an upgrade you unlock later. toil grades itself against [RSG](./design-principles.md) (Resilience and Scale Grade), whose one rule is that your grade is your weakest axis, never the average, so these batteries exist to keep any single axis from quietly capping the whole. Where the honest trade fits your project (ToilDB is not general SQL, the server language is a strict TypeScript subset, the catalog is younger than long-established platforms), the built-in stack is the whole point; [Why toil](./why-toil.md) says where it does not.\n",
61
- "introduction/README.md": "# Understanding toil\n\nThis section is the \"why.\" It explains what toil is, the problem it solves, how it works underneath, and why it is built the way it is. If you read nothing else first, read this: it is what turns \"another framework\" into \"oh, that is the point.\"\n\n## The one big idea\n\nAlmost every website has a split personality. The pretty part (pages, buttons) is served from servers all over the world, close to you. The important part (the database, where your data lives and changes) sits in **one place**, one region, often one machine. Post a comment in Tokyo when the database is in Virginia and your click flies halfway around the planet and back before anything happens.\n\ntoil removes the split. Your **frontend** (React) and **backend** (TypeScript, compiled to a tiny WebAssembly program) both run at the **edge**, near your users, and **ToilDB** is distributed too, so writes do not travel to one far-away box. One language, one project, one deploy, running close to everyone.\n\n```mermaid\nflowchart TB\n subgraph Ordinary[\"The ordinary web\"]\n U1[\"User in Tokyo\"] -->|fast| E1[\"Edge (pretty part)\"]\n E1 -->|slow, one region| DB1[(\"Database in Virginia\")]\n end\n subgraph Toil[\"toil\"]\n U2[\"User in Tokyo\"] -->|fast| E2[\"Edge near Tokyo<br/>(frontend + backend + data)\"]\n end\n```\n\nThat is the entire pitch. The rest of these pages is how toil pulls it off, and why almost nobody else does.\n\n## Read these in order\n\n1. **[Why toil? Who is it for?](./why-toil.md)** The problem with today's stacks, who benefits most, and the honest cases against.\n2. **[The modern stack](./modern-stack.md)** The full catalog of modern tech baked in with zero setup.\n3. **[How toil works](./how-it-works.md)** The whole machine end to end: React client, WebAssembly backend, the edge, ToilDB, the four compute tiers.\n4. **[What makes toil hyper-scalable](./hyperscale.md)** What \"hyper-scale\" means, and the mechanisms that let one small program serve the planet.\n5. **[How toil is distributed](./distributed.md)** The hardest problem in web infrastructure, distributing the writes, and how ToilDB solves it.\n6. **[toil versus other frameworks](./vs-other-frameworks.md)** An honest comparison with Next.js, Rails, Django, serverless, edge runtimes, and backend-as-a-service.\n7. **[Why toil is built this way (the RSG bar)](./design-principles.md)** The rubric toil grades itself against.\n\n## The short version\n\n- **Who it is for:** people building real products who want global speed and reliability without a platform team or ten stitched-together vendors. See [Why toil](./why-toil.md).\n- **Why it is fast:** the code runs next to the user, with no slow trip to a central origin. See [Hyper-scale](./hyperscale.md).\n- **Why it is different:** it distributes the writes, not just the reads. See [Distributed](./distributed.md).\n- **Why it is safe:** the backend is a sandbox, passwords never reach the server in a usable form, secrets never ship in the code, and the browser verifies every asset it loads. See [Security](../concepts/security.md).\n\nWhen you are ready to build, jump to [Getting started](../getting-started/README.md).\n",
62
- "introduction/vs-other-frameworks.md": "# toil versus other stacks\n\nAn honest look at where each stack you already use hits a ceiling, and why toil bets on a different shape. Each of these tools is genuinely good at what it was built for, so the goal is not to crown a winner but to show *where* each one typically caps.\n\nWe grade with the [RSG rubric](./design-principles.md), whose rule is that a system's grade is its **weakest** axis, never the average. So the useful question is not \"what is it great at?\" but \"what quietly caps it?\" For most stacks the answer is the same axis: the **data path** (how and where writes happen), sometimes joined by **dependencies** (how much of the critical path you rent versus own).\n\nRSG is toil's own internal rubric, not an external standard, and every stack below can be configured many ways. The caps described are the *typical* production shape, not a claim that they are unavoidable.\n\n## Where each stack caps\n\n| Stack | Great at | Typical binding axis | Why it caps there |\n| --- | --- | --- | --- |\n| **Next.js / Vercel** | DX, React, global edge reads | data path | Global reads, single-region writes: a sudden write-heavy spike concentrates on one DB box that edge caches and read replicas cannot relieve, while serverless cold starts add latency and per-invocation billing climbs exactly when load peaks |\n| **Rails / Django** | Maturity, batteries included | topology / availability / data | Centralized single-region monolith: one place to be near, one place to fail, and one primary every write must reach |\n| **Serverless functions** (Lambda, Cloud Functions) | Elastic stateless compute | data path | Distributes compute, not state; the central DB stays the write bottleneck, and a cold-start burst adds latency and cost right when traffic surges |\n| **Edge runtimes** (Workers, Deno Deploy) | Code at the edge, near users | data path | Distributes compute beautifully, but the DB you attach is usually single-region (Durable Objects / D1 excepted, below) |\n| **BaaS** (Supabase, Firebase) | Fastest to start | dependencies + data path | You rent a managed service you cannot inspect or fix, and writes resolve against a primary |\n| **toil** | Owned stack, distributed writes | aims for no single binder | Every key has one home region that serializes its writes; auth, DB, email, and jobs are owned, so the usual caps are designed out (latency and client axes are still yours to earn) |\n\n## One line each\n\n- **Next.js / Vercel:** superb reads and DX, but a sudden spike (a viral launch, a flash sale, a timed drop where everyone writes in the same second) lands as a thundering herd on the one write region, so latency, timeouts, and per-invocation cost climb together while edge caching helps only the reads.\n- **Rails / Django:** mature and productive, capped by its single-region shape; you can add replicas and standbys to climb, but distributed *writes* are not in the default model.\n- **Serverless functions:** great elastic compute, but it is stateless compute in front of a central database, so a write burst still bottlenecks on that store and each cold invocation bills separately.\n- **Edge runtimes:** the closest in spirit to toil's compute model, yet edge compute in front of a central database is just a faster front door to the same bottleneck.\n- **Cloudflare Durable Objects / D1:** the closest mainstream analog to toil's idea, and credit is due. A Durable Object gives one object a single-writer home that serializes its writes, the same shape as ToilDB's per-key home. The difference is packaging: with the edge-runtime approach you assemble the pieces yourself (runtime, object or DB product, auth, email), whereas toil ships distributed writes, the seven database families, auth, email, streaming, and jobs as one integrated owned stack. Which you prefer is a genuine trade-off.\n- **BaaS (Supabase, Firebase):** fastest to start, but the convenience is a managed service on your critical path, and writes still resolve against a primary, so it is **dependency-bound and data-bound**.\n\n## Being honest about toil's own limits\n\nRSG grades toil by the same weakest-link rule, and some axes are not handed to you for free:\n\n- **Younger, smaller ecosystem.** Fewer integrations, tutorials, and hosting options than the mature stacks above. If your project is defined by a large existing integration catalog, that gap is real today.\n- **The server language is a TypeScript subset.** toilscript compiles a strict subset of TypeScript to WebAssembly: no arbitrary npm packages or Node APIs on the server, built-in globals instead. That is the price of the small, fast, safe sandbox.\n- **ToilDB is not SQL.** It is seven purpose-built families, not a relational engine, so heavy ad-hoc joins and existing SQL schemas are not its shape (see the [database overview](../database/README.md)).\n- **Some axes you still earn.** RSG measures delivered latency, program performance, and client performance from *your* code. toil removes the structural caps, but it cannot make slow application code fast or a bloated client light.\n\n## toil's bet\n\nEvery stack above is capped in almost the same place: the write path is one box in one region, or the critical path leans on a service you rent. toil's bet is to refuse both at once, own the whole stack and distribute the writes, so the axes that usually cap a \"global\" system are designed out and the only limits left are the ones your own code sets.\n\nWhether that bet fits *your* project is the honest checklist in [Why toil](./why-toil.md).\n\n## Related\n\n- [Why toil? Who is it for?](./why-toil.md): the problem toil solves and the honest cases where you should not use it.\n- [How toil is distributed](./distributed.md): the mechanism behind distributed writes (every key's single home region).\n- [Why toil is built this way (the RSG bar)](./design-principles.md): the weakest-link rubric this comparison uses.\n",
63
- "introduction/why-toil.md": "# Why toil? Who is it for?\n\ntoil is a full-stack framework: you write a React frontend and a TypeScript backend in one project, and toil runs both (plus a database) close to every user, worldwide.\n\nThe thesis in one line: toil is the modern full-stack tech a developer would actually want, AAA-grade from the very first line, and hyper-scalable at the same time. Even a simple pizza site gets top-tier infrastructure with zero setup. Distributed writes are one pillar of that. The modern stack that just works is the heart.\n\n## The problem with today's stacks\n\nTwo problems, really.\n\n**Read-global, write-central.** Your pages load fast from caches worldwide. But a *write* (a comment, a like, an order) usually travels to one database in one region. A user in Sydney writing to a database in Virginia pays for the round trip. That single region is also a single point of failure.\n\n**The ten-vendor tax.** A typical production stack is stitched from rented services: a frontend host, serverless functions, a managed database, auth, email, a queue, a cache, analytics, realtime. Each is its own account, bill, SDK, and failure mode. You did not set out to be a systems integrator, but the stack hands you the job.\n\nAnd every third-party service on your **critical path** (what must work for a request to succeed) is a black box you cannot inspect, patch, or fully secure. When it is slow, you are slow. When it is breached, part of you is breached.\n\nThe result: a solo builder and a funded startup hit the *same* wall. Good, safe, fast infrastructure means assembling and babysitting a lot of parts, so most people settle for less.\n\n## What toil does instead\n\nClose the gap, own the pieces, and make the good version the *default* version. Four pillars.\n\n### 1. AAA-grade from the first line\n\nTop-tier infrastructure on day one, on the smallest project, with zero setup: edge compute (your code runs near users worldwide), one-line post-quantum login, automatic tamper-proofing of your app's code, HTTP/3, and a global database already there.\n\nA pizza site and a planet-scale app start from the same baseline. \"AAA-grade\" is the actual bar toil grades itself against; see [design principles](./design-principles.md).\n\n### 2. Batteries-included, and owned\n\nAuth, database, email, rate limiting, analytics, realtime streaming, and background jobs are all built in and are toil's own. Nothing third-party sits on your critical path.\n\nBecause they are one system, the parts already fit. You are not gluing ten SDKs together and praying they agree. Full catalog: [The modern stack](./modern-stack.md).\n\n```mermaid\nflowchart TB\n subgraph assemble[\"Usual way: assemble ten vendors\"]\n direction TB\n A1[\"Your app\"] --> V1[\"host\"]\n A1 --> V2[\"functions\"]\n A1 --> V3[\"database\"]\n A1 --> V4[\"auth\"]\n A1 --> V5[\"email\"]\n A1 --> V6[\"rate limiter\"]\n A1 --> V7[\"analytics\"]\n A1 --> V8[\"realtime\"]\n A1 --> V9[\"jobs\"]\n end\n subgraph toil[\"toil: one owned stack\"]\n direction TB\n A2[\"Your app\"] --> T[\"edge + database + auth + email +<br/>rate limiting + analytics +<br/>streaming + jobs, built in\"]\n end\n```\n\nHonest boundary: \"owned\" means the *core* of a working app is toil's, not that outside services are banned. Call a payment provider or another API and you still can.\n\n### 3. A modern DX that just works\n\nTypeScript end to end, one repo, one deploy, wired by types: change a field on the server and the frontend stops compiling until you fix it (a compile error at your desk, not a production bug).\n\nThe toolchain is set up for you: ESLint, Prettier (with a plugin for toil's decorators), an editor plugin, one CLI, and a `doctor` that fixes common problems in place. The docs are even LLM-friendly, so an AI assistant reads your current conventions instead of guessing. More in [The modern stack](./modern-stack.md).\n\n### 4. Hyper-scalable and distributed (one pillar, not the whole story)\n\nYour backend compiles to a tiny sandboxed **WebAssembly** module (a compact, locked-down binary that runs at near-native speed), so one edge box safely runs many apps, which makes running near everyone affordable.\n\nAnd the database, **ToilDB**, distributes the *writes*, not just the reads: every key has one **home** region that orders its writes, while data replicates outward for fast local reads. Distributing writes is the hard part almost nobody does. The trade is eventual consistency: a far read can lag a few milliseconds. See [How toil works](./how-it-works.md) and [How toil is distributed](./distributed.md).\n\n## Who it is for\n\n- **Solo builders and small teams:** a full, global, secure stack without hiring a platform team. The pizza site is first-class.\n- **Latency-sensitive apps:** writes that resolve near the user, not across an ocean.\n- **Global apps:** logic and data near users on every continent.\n- **Realtime apps:** chat, presence, and live cursors on built-in streaming, not a bolted-on vendor.\n\nSame install for the smallest project and the largest. You grow into the scale; you do not rebuild to reach it.\n\n## When not to use toil\n\n- **You need SQL or heavy joins.** ToilDB is seven purpose-built families, not a general SQL engine. See the [database overview](../database/README.md).\n- **You lean on the Node ecosystem.** The server is a strict TypeScript subset compiled to WebAssembly: no arbitrary npm packages, no Node APIs, built-in globals instead.\n- **You are happy single-region and simple.** If one region already fits, toil's distribution is effort you do not need.\n- **You need a big integration catalog today.** toil is younger, and that catalog is smaller.\n\nNone of these are permanent, and the right tool is the one that fits the job in front of you.\n\n## Related\n\n- [The modern stack](./modern-stack.md): the full, verified catalog of what is built in.\n- [How toil works](./how-it-works.md): the whole machine end to end, from React client to ToilDB.\n- [toil versus other frameworks](./vs-other-frameworks.md): an honest, axis-by-axis comparison.\n- [Security](../concepts/security.md): the defaults that are already on.\n- [Getting started](../getting-started/README.md): install the tool and build a small feature.\n",
56
+ "introduction/design-principles.md": "# Why toil is built this way (the RSG bar)\n\ntoil is opinionated on purpose, and the purpose is a single one: reach top-tier (\"AAA\") infrastructure, and ship it as the **default**. AAA-grade tech is normally something you assemble from a dozen vendors and babysit with a team that understands distributed systems. toil makes the good version the version you get out of the box, in one framework, with zero configuration. A solo builder starts from the same baseline as a funded team.\n\nEvery decision on this page traces back to that one goal. This page is the rubric toil holds itself against, and the principles that rubric forces.\n\n## The bar: one grade, set by the weakest link\n\n**RSG** (Resilience and Scale Grade) is toil's internal rubric for how resilient, distributed, fast, lean, and secure an app actually is, scored as a single letter from **AAA** down to **D**. It grades nine axes, and one rule runs the whole thing: **your grade is your weakest axis**, never the average and never the best. To earn AAA, all nine must be AAA at the same time.\n\nA globally edged frontend on a single-region database is capped by the database. A worldwide system running slow code is capped by latency. The lowest column sets the grade, every time.\n\nThat rule exists to kill the most common lie in this space: calling a system \"global scale\" because the read path is global, while the write path is one box in one region. Averaging lets the strong axis hide the weak one. The minimum refuses to. RSG is not an external certification and no auditor issues it; it is a mirror the team holds up to find the weakest link and fix it first. The full rubric lives at the repository root in [`RSG.md`](../../RSG.md).\n\n## The principles the bar forces\n\nAiming for AAA-as-default is not a slogan. It forces a specific handful of decisions, and they are the reason toil looks the way it does.\n\n### 1. The good, safe, fast version is the default, not an upgrade\n\nOn most stacks the resilient path is a paid tier and the secure path is a checklist you get to later. toil inverts that: the strong version is what you get **before** you configure anything.\n\n- **Post-quantum login in about one line.** Enable auth and a password is stretched through an OPRF (ristretto255) and Argon2id into a deterministic ML-DSA-44 keypair. Only the public key ever leaves the device, and login runs ML-KEM-768 mutual auth so the client verifies the server too. The password never reaches the server in a usable form. ML-DSA and ML-KEM are the NIST-standardized algorithms meant to survive a quantum computer. You do not assemble any of that; you switch it on.\n- **Tamper-proofing is on, not opt-in.** Every shipped asset carries SHA-384 Subresource Integrity plus importmap integrity, so a modified script is rejected by the browser instead of run. Nothing to remember to turn on.\n\n### 2. Batteries are included, and owned\n\nAuth, [ToilDB](../database/README.md), email, rate limiting, analytics, realtime streaming, background jobs, materialized views, scheduled jobs, sessions and cookies, an environment and secrets store: all built and owned by toil, all first-party. That is not just convenience. It is the Dependencies axis, where zero third-party code on the critical path is what earns AAA. Nothing you cannot inspect or fix sits between a request and its response.\n\n### 3. One framework, not ten vendors\n\nYou write a React frontend and a TypeScript backend in **one** project. toil runs both, plus the database, near your users worldwide. The browser calls the backend through a generated, fully typed `Server` client, so there is no hand-written fetch and no drift between the two halves. One project, one deploy, one mental model, instead of a frontend host plus an API host plus a database vendor plus an auth vendor plus a queue, each glued to the next.\n\n### 4. Modern foundations, not legacy tooling\n\ntoil bets on current technology rather than propping up old tooling.\n\n- **WebAssembly backends.** Your TypeScript compiles to a small, sandboxed WASM module. Because the sandbox is real, one edge box safely runs **many** tenants at once, and that multi-tenancy is exactly what makes running near everyone affordable.\n- **Post-quantum crypto** as the login default (see principle 1), not classical crypto you swap out later.\n- **End-to-end types** from the generated `Server` client, so a backend change that breaks the frontend is a compile error, not a production 500.\n- **QUIC and WebTransport** carry realtime ([@stream](../concepts/tiers.md)).\n\n### 5. Honesty about the limits\n\ntoil grades itself on honesty, so the docs and the product both state limits plainly rather than overclaim.\n\n- **Distributed writes** are real in design and tested in the core: every key has one home region that orders its writes while data replicates outward for fast local reads (see [How toil is distributed](./distributed.md)). But live multi-region deployment is configuration-gated, not on by default, and the local dev database is a single in-process store. toil is **built** to distribute writes worldwide, and the mechanism is real; it is not the claim that every app is already running a live global write cluster today.\n- **Compute tiers:** the per-request edge (L1) is live and real. Regional, continental, and global-daemon tiers are opt-in and deployment-gated, not always-on for every app.\n- **Analytics** is real on the edge; the dev server returns sample data.\n- **Auth secrets** ship as clearly-insecure dev placeholders so `toiljs dev` just works. A real deployment must set its own.\n- toil is younger than the incumbents and its integration catalog is smaller. [vs other frameworks](./vs-other-frameworks.md) covers when not to reach for it.\n\n## The bar in nine axes\n\nEach axis names a way a system can be weak. Each row is the single design decision toil makes so that axis cannot be the thing that caps it.\n\n| RSG axis | What it grades | The toil design choice that hits it |\n| --- | --- | --- |\n| **Topology + distribution** | How close your code runs to users, and in how many places | Edge compute: frontend and backend both run on nodes next to users, worldwide, not in one origin region. |\n| **Availability** | What survives a failure | Cross-region failover with no single point of failure, so losing a node or a region does not take the app down. |\n| **Data path** | Where data is read and *written* (the hard one) | ToilDB's per-key-home model distributes the **writes**, not just the reads. See [How toil is distributed](./distributed.md). |\n| **Delivered p99 latency** | The end-to-end time the user actually feels | An allocation-free hot path, measured rather than assumed, so the response is fast for real, not just on paper. |\n| **Program performance + architecture** | Hot-path code quality and cost per request (no brute-forcing latency with a big server bill) | No blocking work on the request path; speed comes from the code, not from overprovisioning. |\n| **Dependencies** | How much of your critical path you own (zero third-party on it = AAA) | An owned, batteries-included stack: nothing third-party sits on the critical request path for you to be unable to inspect or fix. |\n| **Security** | How hard the system is to break, and how bad a breach would be | Post-quantum password login, sandboxed WebAssembly backends, and Subresource Integrity on every asset. See [Security](../concepts/security.md). |\n| **Client performance + reach** | How well the shipped app runs on old and low-end devices as data grows | A lean React client: a small bundle and linear-or-better hot paths, so it stays smooth on weak hardware, not just new flagships. |\n| **Modern stack + compatibility** | Current foundations, *with* graceful fallback for older clients | WebAssembly backends, post-quantum auth, and QUIC/WebTransport realtime, negotiating down cleanly so a client one version behind still works. |\n\n## The punchline\n\ntoil is opinionated because being AAA on **every** axis at once forces these choices, and it delivers them as the default so you do not have to earn each one by hand. You cannot reach the top grade with a fast frontend on a centralized database, or a global system running slow code, or a modern stack that only works on the newest browser. The weakest-link rule closes every one of those escape hatches. Any framework that lets a single axis slip is, by its own honest scoring, not AAA. Holding all nine at once, out of the box, is the whole reason toil looks the way it does.\n\n## Related\n\n- [How toil is distributed](./distributed.md): the data-path axis in depth, and why distributing writes is the hard one.\n- [What makes toil hyper-scalable](./hyperscale.md): the topology, latency, and program axes in practice.\n- [Security](../concepts/security.md): the security axis and its hard caps.\n- [vs other frameworks](./vs-other-frameworks.md): the honest \"when not to use toil\".\n- [`RSG.md`](../../RSG.md) at the repository root: the full rubric, the internal mirror this page summarizes.\n",
57
+ "introduction/distributed.md": "# How toil is distributed\n\nDistributing a website's reads is easy. Distributing its writes is the hard part almost nobody solves, and it is why \"global\" apps are usually only half global. Here is the problem, and how ToilDB (the database built into toil) is built to distribute the writes.\n\n## Reads are easy, writes are hard\n\nA read never changes anything, so you copy your data to servers worldwide and let each user read the nearest copy. Every copy agrees: a reader in Tokyo and one in Paris both get a fast, local answer.\n\nA write is a change, and two writes to the *same thing* can collide. A counter says `10`. Tokyo and Paris both read `10`, both add one, both write `11`. The real answer was `12`, so one add vanished with no error. That is a **write conflict**.\n\nYou could make every copy agree before accepting a write, but the network will eventually split (a **partition**), and the **CAP tradeoff** says that during a split you keep only two of consistency, availability, and partition tolerance. You either refuse the write (correct but unavailable) or accept it on one side and reconcile later (available but briefly inconsistent). Distributing writes is a real tradeoff to design around, not a bug to patch away.\n\n## So almost everyone centralizes the write database\n\nFaced with that, nearly every stack keeps **one** primary write database in **one** region and spreads only read replicas worldwide. All writes funnel to that one box, one at a time, so conflicts cannot happen.\n\nIt is a reasonable choice, and it hides two costs the [RSG rubric](./design-principles.md) flags on its data-path axis:\n\n- **Far writes are slow.** Post from Tokyo to a primary in Virginia and your write crosses the planet and back before anything saves. The page was local; the action was not.\n- **The primary is a single point of failure.** One region holds every write, so if it has a bad day, nothing anywhere can be changed.\n\nThe read path is global; the write path is one machine in one city. Under RSG's weakest-link rule, that single data path caps the whole system.\n\n## ToilDB's answer: every key has a home\n\nToilDB gives **every key its own home**. A key is the label you store data under (a user id, a username, a room name; see the [database overview](../database/README.md)). Each key is assigned one home: the single source of truth that orders its writes.\n\nTwo things follow, and together they are the whole trick:\n\n- **Writes to one key are safe.** Every write to a key travels to that key's home, which **serializes** them (applies them one at a time, in order). Both counter adds are ordered at the counter's home, so the result is `12`. No global lock over the whole database.\n- **Writes spread worldwide.** Different keys get different homes, so total write load spreads out. Tokyo users' data can home near Tokyo, Paris users' near Paris. No single box every write funnels through, so no single bottleneck.\n\n```mermaid\nflowchart TB\n WA[\"write user:aiko\"] --> KA[\"home near Tokyo<br/>key: user:aiko\"]\n WB[\"write user:bruno\"] --> KB[\"home near Paris<br/>key: user:bruno\"]\n WC[\"write room:general\"] --> KC[\"home near Ohio<br/>key: room:general\"]\n KA -.->|\"replicate for<br/>local reads\"| KB\n KA -.-> KC\n```\n\nReads stay local: each key still replicates out, so a reader anywhere gets a nearby copy. Those copies are **eventually consistent**, meaning that for a brief moment (usually milliseconds) after a write lands at the home, a far read can lag before it catches up. For almost all app data this is invisible; the [database overview](../database/README.md) has the full picture.\n\nWhich location owns a key is decided by a shared formula (rendezvous hashing) every node computes the same way, so any node routes a write to the right home with no central coordinator. A key's home can move to follow demand, without rehashing the database.\n\n## The seven families pick the right consistency tool\n\nOne \"home orders the writes\" rule fixes the counter, but different jobs want different guarantees. So ToilDB ships **seven families**, each a collection type tuned for one shape of data, each exposing only the operations that are safe and fast for it:\n\n| Family | What it gives |\n| --- | --- |\n| [Documents](../database/documents.md) | A record you look up by id |\n| [Counters](../database/counters.md) | Conflict-free tallies: adds from anywhere merge, no lost updates |\n| [Unique](../database/unique.md) | A one-of-a-kind claim (a username); the home picks exactly one winner |\n| [Capacity](../database/capacity.md) | Limited stock (tickets); reserve/confirm/cancel holds prevent overselling |\n| [Events](../database/events.md) | An append-only log in one agreed order |\n| [Membership](../database/membership.md) | Sets of who belongs to what |\n| [View](../database/views.md) | A read-optimized result a background job builds |\n\nDistributing writes is not one problem with one answer; each family is the right tool for one shape.\n\n## The hard machinery toil provides so you do not have to\n\nThe per-key-home model only works if a lot of unglamorous machinery runs reliably across a flaky network, and ToilDB owns it: per-key **placement** and safe **rehoming** (a rising epoch plus a fencing token so the old owner stops the instant the new one takes over), ordered **cross-region replication** with per-stream cursors that detect and backfill gaps, **idempotent apply** so redelivered writes cannot double-count, **capacity escrow** and **tenant quotas**, and **failover** with snapshot re-seeding for a cell that has fallen too far behind. Getting all of these right at once is exactly why truly distributed websites are rare.\n\n## What is real today, honestly\n\nThis is the hard part almost nobody does, so here is a straight account of where it stands.\n\nThe per-key-home model and all of its core logic (placement, rehoming, replication, idempotent apply, escrow, quotas, failover) are **built and tested**. The mechanism is real, not a diagram.\n\nWhat is not on by default is the **live multi-region deployment**: wiring many real regions into one running mesh (WAN routing) and the [ScyllaDB](https://www.scylladb.com/) storage that backs a production cluster are **configuration-gated**, so you switch them on for a real deployment rather than getting a live global write cluster automatically. The full database-level **leader fencing** on the write path is also still landing (a host-side leader gate is the current version). The design is settled; the last-mile host wiring is what remains.\n\nOn your laptop, `toiljs dev` runs a single **in-process** database: everything homes in one place, so your code behaves exactly as it will worldwide, but the distribution only spreads out once you deploy with the multi-cell backing configured. You write your app the same way either way; that is the point.\n\nThe honest one-liner: toil is **built** to distribute writes worldwide, the mechanism is real, and turning it on across live regions is a deployment step, not a rewrite of your app.\n\n## Related\n\n- [The database (ToilDB)](../database/README.md): families, keys and values, and eventual consistency in depth.\n- [Compute tiers](../concepts/tiers.md): where your code runs, the compute side of the same story.\n- [What makes toil hyper-scalable](./hyperscale.md): the mechanisms that let one small program serve the planet.\n- [Why toil is built this way (the RSG bar)](./design-principles.md): the rubric behind the data-path axis.\n",
58
+ "introduction/how-it-works.md": "# How toil works\n\nThe whole machine, end to end: what you write, what the build produces, and what happens when a user makes a request. Every term is defined as it appears. You configure none of the moving parts below; this page just explains them.\n\n## One project, two homes\n\nYou build a toil app as a single project, and your code lives in two places.\n\n- **`client/`** is a **React** frontend (with Vite): file-based routing, data loaders, the usual components. It runs in the user's browser.\n- **`server/`** is your backend, written in **TypeScript with decorators**. You mark a class `@rest` to expose HTTP endpoints, `@service` for typed RPC, `@stream` for realtime, `@daemon` for background jobs, `@database`/`@collection` for stored data, and `@auth`/`@user` for login. There are no servers to provision and no routes to wire by hand: the decorator is the wiring.\n\nThat is the whole surface. One repo, one language family, one `toiljs dev`.\n\n## What the build produces\n\n`toiljs build` turns that one project into three kinds of output, because the browser and the edge need different things.\n\n**1. The client bundle.** Your React app plus any pages toil renders ahead of time, packaged as ordinary web files (HTML, JS, CSS, images) that run in the browser. Every shipped asset is fingerprinted with SHA-384 Subresource Integrity, so a browser refuses a file that was tampered with in transit.\n\n**2. The server WASM.** Your `server/` backend, compiled by the **toilscript** compiler into a small, sandboxed **WebAssembly** module. It runs on the edge, not in the browser and not in Node. The build emits three artifacts, one per compute tier:\n\n| Artifact | Holds | Runs |\n| --- | --- | --- |\n| `release.wasm` | the hot request path (`@rest`, `@service`) | on every request |\n| `release-stream.wasm` | realtime handlers (`@stream`) | for the life of a connection |\n| `release-cold.wasm` | background and scheduled jobs (`@daemon`) | on a timer, worldwide-singleton |\n\n**3. The generated typed client (`shared/server.ts`).** toil reads the shape of your backend and generates a small browser-side client from it. Your React code calls a normal async function (`Server.REST.*`, `Server.Stream.*`, `Server.<service>`) and the types line up end to end: rename a field on the server and the frontend stops compiling until you fix it. No hand-written `fetch`, no drift.\n\n**WebAssembly, in two sentences:** a compact binary format that runs at close to native speed with no interpreter warm-up, inside a **sandbox**, a locked box that cannot open files, reach the operating system, or make network calls on its own. It touches the outside world only through the small, fixed set of functions the host hands it (read the request, build a response, query the database), which is exactly what makes it safe to pack many apps onto one shared machine. ([Why that matters for scale](./hyperscale.md).)\n\ntoilscript is a strict TypeScript dialect (explicit value types, no `any`, no runtime `RegExp`); the [decorators reference](../concepts/decorators.md) covers the full set.\n\n## The edge node\n\nYour WASM does not run on a laptop or in one central data center. It runs on the **edge**: a fleet of Rust runtime nodes spread across many cities, where each user is served by whichever node is physically closest. There is no **origin server**, no single far machine every request funnels back to, because your backend and its data are already at the edge.\n\nTwo things make this work at the density it needs.\n\n- **Multi-tenant on shared boxes.** Because each app is a tiny sandboxed module, one edge box safely runs many apps at once. That shared density is what makes putting compute near everyone affordable instead of a luxury.\n- **A custom userspace datapath.** The node handles network packets itself in userspace (multi-queue, bypassing the operating system's network stack) to keep the per-request cost low as traffic grows. **QUIC** and **WebTransport** ride on top to power realtime `@stream` connections.\n\n## The request lifecycle\n\nA request never leaves the neighborhood. It lands on the nearest edge node, and everything happens right there.\n\n```mermaid\nsequenceDiagram\n participant U as User's browser\n participant E as Nearest edge node (Rust)\n participant W as WASM host\n participant G as Your guest module\n participant DB as ToilDB (local copy)\n\n U->>E: Request over the network\n alt Page or static asset\n E-->>U: Prerendered / SSR page or file\n else Dynamic API call\n E->>W: Decode raw bytes into a Request\n W->>G: Call your handler\n G->>DB: Read / write nearby data\n DB-->>G: Result\n G-->>W: Response object\n W-->>E: Encode to bytes\n E-->>U: Response\n end\n```\n\n1. **The request lands on the closest edge node.** The network routes the user there automatically.\n2. **Page or code?** If the path is a prerendered page, a server-rendered page, or a static asset, the edge serves it directly and never wakes your backend. This is the fast path for most page loads.\n3. **Otherwise the host runs your guest.** The **WASM host** (trusted Rust) decodes the raw bytes into a `Request`, then calls into your **guest module** (your sandboxed WASM) at its single entry point, which routes to the handler you wrote.\n4. **Your handler reads and writes locally.** When it needs stored data it talks to [ToilDB](../database/README.md), which has a copy right there at the edge. No ocean crossing.\n5. **Your handler returns a `Response`,** the host encodes it back to bytes, and the edge sends it to the browser.\n\nThe mental model for your backend: a function of the request. Bytes in, bytes out, one request at a time.\n\n### Stateless by default\n\nA fresh copy of your handler serves each request, and the next request might be served by a node on the other side of the planet, so anything you set on an instance field does not survive. This is a feature: interchangeable copies with nothing to coordinate are what let the backend scale worldwide by simply adding more nodes. When you need something to persist, write it to ToilDB. (See the [backend overview](../backend/README.md#stateless-by-default).)\n\n## Where the data lives: ToilDB\n\nState lives in [ToilDB](../database/README.md), toil's own database, replicated next to your code at the edge. It offers seven purpose-built families (Documents, Unique, Counter, Events, Membership, Capacity, View), so you pick the shape that fits instead of forcing everything into one table.\n\nIts hard trick is **distributed writes**. Every key has one **home region** that orders its writes, while the data replicates outward so reads stay fast and local everywhere. Most stacks distribute reads but keep writes pinned to one region; toil is built to distribute both.\n\nBe honest about where this stands. The home-region model and its core logic are real and tested, but a *live* multi-region deployment (WAN routing, the ScyllaDB backing) is configuration-gated, not switched on for every app by default, and a far read can lag its home by a few milliseconds (eventual consistency). Your local `toiljs dev` database is a single in-process store. The write model and its trade-off are covered in [how toil is distributed](./distributed.md).\n\n## The compute tiers\n\nNot all backend code has the same lifespan. toil sorts it into four tiers, and the build assigns each piece automatically from its decorator.\n\n| Tier | What runs here | Status |\n| --- | --- | --- |\n| **L1** | the stateless per-request hot path (`@rest`, `@service`) | live, the default |\n| **L2 / L3** | longer-lived regional and continental work, `@stream` realtime | opt-in, deployment-gated |\n| **L4** | a single worldwide `@daemon` for background and scheduled jobs | opt-in, deployment-gated |\n\nMost apps live entirely on **L1**, and that tier is real and running today. L2 through L4 are built and opt-in, not always-on for every app. Full detail is on the [tiers page](../concepts/tiers.md).\n\n## The pieces\n\nFive parts make up a running toil app. You have now met all of them.\n\n| Piece | What it is | Where it runs |\n| --- | --- | --- |\n| **React client** | your frontend UI, the client bundle from the build | the user's browser |\n| **toilscript backend** | your `server/` code compiled to sandboxed WASM | the edge |\n| **The edge node** | the Rust runtime that terminates the connection, serves pages, and runs your WASM | servers in many cities |\n| **ToilDB** | the database with distributed writes, replicated next to your code | the edge |\n| **The four tiers** | where and for how long a piece of backend code lives | L1 nearest, up to L4 worldwide |\n\nNone of these need configuring to get the good version. You write React and decorated TypeScript; the build, the edge, and the database are the default, not a stack you assemble and babysit.\n\n## Related\n\n- [Backend overview](../backend/README.md): the request/response model and the sandbox in depth.\n- [The database (ToilDB)](../database/README.md): families, home regions, and eventual consistency.\n- [Compute tiers](../concepts/tiers.md): L1 request, L2/L3 stream, L4 daemon.\n- [Realtime streams](../realtime/streams.md) and [background daemons](../background/daemons.md): the other two artifacts.\n- [What makes toil hyper-scalable](./hyperscale.md): why this design serves the planet cheaply.\n",
59
+ "introduction/hyperscale.md": "# What makes toil hyper-scalable\n\n**Hyper-scale** is serving very large, worldwide traffic at low latency without rebuilding your app as it grows. The test: when traffic goes from a thousand users to a hundred million across every continent, do you rewrite the system, or just run more of it?\n\nPlenty of stacks can reach that scale if you throw money at it: dedicated infrastructure per app, a rented vendor for each moving part, and an ops team to keep the seams from tearing. toil was built for the same scale from the other direction: **cheaply and efficiently**. The whole design is aimed at making top-tier reach the default, not a budget line. Cost is the point of this page.\n\n## Why hyper-scale is normally expensive\n\nRunning near your users usually means paying for it in three places at once:\n\n- **Dedicated infrastructure per app.** Each app gets its own boxes, its own containers, its own always-warm capacity. Spread that across many cities and you are renting a lot of mostly-idle hardware.\n- **A far-away origin.** Most stacks serve pages from everywhere but send anything real back to one origin server in one region. Every write and every dynamic call pays for a round trip across the planet.\n- **A centralized write database.** The reads scale out; the writes funnel into one primary in one city. That box is both the bottleneck and the thing you overprovision to keep ahead of.\n\nEach of those is a cost multiplier, and they stack. toil removes all three.\n\n## How toil makes it cheap\n\nThree mechanisms do the work, and they reinforce each other.\n\n**1. Density: one box safely runs many apps.** Your backend compiles to its own tiny, [sandboxed](./how-it-works.md#what-build-produces) WebAssembly module that starts fast, runs at near-native speed, and cannot touch another tenant's files, memory, or network. Because the sandbox is that tight, **one shared edge box holds many apps at once** instead of one app per machine. That multi-tenant density is what makes running near everyone affordable: you are not renting dedicated hardware in fifty cities, you are one tenant on boxes that are already there and already busy.\n\n**2. Edge locality: no trip to a central origin.** toil has no origin server. Every request runs on the [edge](../concepts/tiers.md) node nearest the user, on the per-request **L1 hot path**: the network hands the bytes to the WASM host, your handler runs, a response goes back, all in one place. There is no slow hop to a faraway box to make the request \"real.\" Removing the origin removes both the latency and the standing cost of running one.\n\n**3. Local reads, homed writes.** The data your handlers share lives in [ToilDB](../database/README.md), replicated outward so every edge node reads from a copy right next to it. Writes are not centralized either: every key has one **home region** that orders its writes, while the data replicates out for fast local reads. So you get nearby reads everywhere without a single primary that every write funnels through, and without paying to overprovision one. The mechanism and its honest trade-off (eventual consistency) are in [how toil is distributed](./distributed.md).\n\n```mermaid\nflowchart LR\n subgraph Origin[\"The expensive shape: everything funnels to one origin\"]\n direction TB\n A1[\"User (Tokyo)\"] -->|slow| O[(\"Origin + DB<br/>(Virginia)\")]\n A2[\"User (Paris)\"] -->|slow| O\n A3[\"User (Sydney)\"] -->|slow| O\n end\n subgraph Toil[\"The cheap shape: shared edge + homed writes\"]\n direction TB\n B1[\"User (Tokyo)\"] --> E1[\"Edge box + local data (Tokyo)\"]\n B2[\"User (Paris)\"] --> E2[\"Edge box + local data (Paris)\"]\n B3[\"User (Sydney)\"] --> E3[\"Edge box + local data (Sydney)\"]\n E1 <-.->|\"replicate\"| E2\n E2 <-.->|\"replicate\"| E3\n end\n```\n\nThe left side has one hot center every user drags a request to and back from, plus its dedicated fleet, a cost no amount of caching removes. The right side has no center and no per-app fleet: add a city, add a shared box, and every tenant on it gets that city for near nothing.\n\n## Why this is cheap, in one line each\n\n- **Density** means the cost of an edge presence is split across many tenants, not carried by one app.\n- **Edge locality** means no origin fleet to run and no cross-planet round trip to pay for on every real request.\n- **No dedicated infra** means you scale by adding interchangeable shared nodes, not by standing up a new stack per app.\n\nTake any one away and a cost reappears: no density and running near everyone is a luxury again; no edge locality and you are back to paying for an origin; a centralized write path and the database caps you and gets overprovisioned to compensate.\n\n## An honest note\n\nThis is the design, not a benchmark. Real throughput and latency depend on your hardware, where your users are, how your data is shaped, and how your handler is written. toil removes the central bottlenecks and keeps per-request cost low; it does not make a slow handler fast or repeal the speed of light between continents.\n\nA few things are honestly staged, not \"already everywhere\":\n\n- The per-request **L1** edge path is live and real. The **L2** regional, **L3** continental, and **L4** global-daemon tiers are opt-in and deployment-gated, not always-on for every app. See the [tiers page](../concepts/tiers.md).\n- ToilDB's home-region write model and its core logic are real and tested, but **live multi-cell deployment** (WAN routing, the ScyllaDB backing) is configuration-gated, not on by default. The local dev database is a single in-process store. [How toil is distributed](./distributed.md) is honest about what is finished.\n\n## Related\n\n- [How toil is distributed](./distributed.md): distributing the writes, the hard problem this rests on.\n- [Compute tiers](../concepts/tiers.md): L1 through L4, and the stateless request model.\n- [How toil works](./how-it-works.md): the build outputs and the request lifecycle.\n- [The database (ToilDB)](../database/README.md): families, homes, and eventual consistency.\n- [Why toil is built this way (the RSG bar)](./design-principles.md): the efficiency check behind the hot path.\n",
60
+ "introduction/modern-stack.md": "# The modern stack: what toil gives you that others do not\n\nMost frameworks give you a way to write code and then send you shopping: a database, an auth provider, email, a rate limiter, analytics, a realtime service, a job runner, each its own account, bill, and SDK, all wired together and kept in sync by you. toil owns those parts instead. They ship in one framework, they are toil's own (nothing third-party sits on your critical path), and they are on from the first line with zero configuration. This page is the full catalog.\n\nThe point is not that toil bundles a lot. It is that the good version is the default version: a solo builder gets the same baseline as a funded team, without assembling or babysitting ten vendors. For how the edge and worldwide distribution actually work, see [How toil works](./how-it-works.md) and [How toil is distributed](./distributed.md).\n\n## The backend, built in\n\nYour TypeScript backend declares what it needs with a decorator or a one-line config flag. toil provides the machinery.\n\n| Feature | What it is | Why it matters |\n| --- | --- | --- |\n| [Post-quantum auth](../auth/README.md) | Password login via `server: { auth: true }`. The password is stretched (OPRF + Argon2id) into an ML-DSA-44 keypair in the browser; only the public key is sent; login runs ML-KEM-768 mutual auth so the client also verifies the server. | Real post-quantum cryptography (the NIST-standardized algorithms), in about one line. The password never reaches the server in a usable form, so a breached server yields nothing replayable, and there is no identity vendor to rent. |\n| [ToilDB](../database/README.md) | A global database with no connection string and seven purpose-built families. Its differentiator is distributed writes: every key has one home region that orders its writes, while data replicates outward for fast local reads. | Distributes writes, not just reads, so a thousand servers can write at once with no single-region bottleneck. This is the hard part almost nobody does (trade: eventual consistency, a far read can lag a few ms). |\n| [Email](../services/email.md) | `EmailService.send(...)`, or a reusable `EmailTemplate` with `{{placeholder}}` bodies, through a provider you configure once. | Verification codes, resets, and receipts are one validated, per-tenant-capped call, off the worker while the provider replies. |\n| [Rate limiting](../services/ratelimit.md) | `@ratelimit` on a route, counted at the edge in an exact cross-worker limiter keyed on the caller's network address. | Over-limit clients are rejected before your code runs, blunting brute-force and spam. |\n| [Analytics](../services/analytics.md) | The `Analytics` global reads your site's own counters: `self()` for a snapshot, `series(metric, range)` for a graph. | A real usage dashboard with no extra code and no analytics vendor. |\n| [Realtime streaming](../realtime/README.md) | A `@stream` class with `@connect` / `@message` / `@close` / `@disconnect` hooks, opened from React with `useChannel`. WebTransport (over QUIC) in production, WebSocket in dev. | A resident instance stays alive per connection and remembers state next to the user: chat, presence, live progress. |\n| [Background jobs (`@daemon`)](../background/daemons.md) | One global background worker on a schedule (interval or cron), with lease-based failover so exactly one instance runs worldwide. | Nightly jobs run once globally, with no cron server, queue, or leader election to run yourself. |\n| [Scheduled jobs (`@scheduled`)](../background/daemons.md) | A method that fires on an interval or cron expression, driven by the same lease so it runs once across the fleet. | Recurring work (cleanup, digests, syncs) without standing up a scheduler. |\n| [Materialized views (`@derive`)](../background/derive.md) | A function that re-runs when its source data changes and writes the result into a View family. | Rollups and denormalized read models stay fresh automatically, so reads are cheap without a hand-rolled pipeline. |\n| [Sessions and cookies](../services/cookies.md) | No-import cookie read/set, signed or encrypted, plus the session layer that backs `@auth`. | Login state and small per-user data without a session store to provision. |\n| [Environment and secrets](../services/environment.md) | A per-tenant environment store for config and secrets, read at the edge, never baked into the shipped WASM. | Your compiled program carries no credential; secrets live in one place, disjoint from public config. |\n\n### The seven ToilDB families\n\nOne database, seven shapes, each tuned to its access pattern rather than bent out of a single table model.\n\n| Family | For |\n| --- | --- |\n| [Documents](../database/documents.md) | General structured records. |\n| [Unique](../database/unique.md) | Uniqueness constraints (one email, one handle). |\n| [Counter](../database/counters.md) | High-throughput increments (views, likes, quotas). |\n| [Events](../database/events.md) | Append-only logs and feeds. |\n| [Membership](../database/membership.md) | Set membership and relationships (who is in what). |\n| [Capacity](../database/capacity.md) | Bounded resources and seat/slot allocation. |\n| [View](../database/views.md) | Read models materialized by `@derive`. |\n\n## The frontend, built in\n\nThe React client is Vite-fast and typed end to end. The pieces that usually take a build config, a fetch layer, and an SEO plugin are already wired.\n\n| Feature | What it is | Why it matters |\n| --- | --- | --- |\n| [File routing](../frontend/routing.md) | Routes are files under `client/routes/`. The tree is your URL map. | No router config to hand-maintain; add a file, get a route. |\n| [Loaders with revalidation](../frontend/data-fetching.md) | A route `loader` fetches its data before render and revalidates on demand. | Data arrives with the page, not after a client-side waterfall. |\n| [Two rendering paths](../frontend/rendering.md) | Build-time prerender for SEO by default, plus opt-in edge SSR with `export const ssr = true`, both hydrated with `hydrateRoot`. | Static-fast pages where you can, live server rendering where you need it, one codebase. |\n| [Typed `Server` client](../frontend/data-fetching.md) | A generated client (`Server.REST.*`, `Server.Stream.*`, `Server.<service>`) so the browser calls the backend with end-to-end types. | No hand-written fetch and no drift: rename a field on the server and the frontend stops compiling until you fix it. |\n| [Image with LQIP](../frontend/images.md) | A built-in `Image` component with blur / low-quality placeholders and reserved aspect ratio. | Fast, layout-stable images with no CLS and no separate image service. |\n| [Metadata and SEO](../frontend/metadata.md) | Per-route title, description, canonical, and Open Graph (including `og:image`), baked into the served HTML head. | Correct SEO and share cards in view-source, not assembled by JavaScript after load. |\n| [Page search](../frontend/search.md) | A static index of each route's title, description, keywords, and Open Graph, generated at build. | In-app search with no search vendor (see the caveat below). |\n| [SHA-384 SRI](../concepts/security.md) | Subresource Integrity plus importmap integrity on every shipped script, preload, and stylesheet, across the whole module graph. | A tampered asset simply does not run, even if a CDN or cache hop is compromised. |\n\n## Owned, not rented\n\nEverything above is toil's own code on your critical path, not a black box you cannot inspect, patch, or secure. That is the difference the table below draws.\n\n| Capability | toil (built in, zero setup) | Typical stack (you assemble it) |\n| --- | --- | --- |\n| Database | ToilDB: global, distributed writes, seven families | A managed database, usually single-region for writes |\n| Auth | Post-quantum login; the server holds no password | A rented identity provider or hand-rolled hashing |\n| Email, rate limiting, analytics | Built-in primitives, one call each | A separate SDK or vendor per capability |\n| Realtime and background | `@stream`, `@daemon`, `@scheduled`, `@derive` | A realtime service plus a cron server, queue, and leader election |\n| Sessions, secrets, cookies | Owned globals, no store to provision | A session store and a secrets manager to run |\n| Frontend integrity and SEO | Automatic SHA-384 SRI, Image/LQIP, metadata, page search | Plugins and services bolted on per concern |\n| Critical-path ownership | The core is toil's own | A mix of vendors you cannot inspect or fix |\n\nHonest boundary: \"owned\" means the core of a working app is toil's, not that outside services are banned. Call a payment provider or another API and you still can.\n\n## The honest caveats\n\ntoil grades itself on honesty, so read these before you count on anything.\n\n- **Distributed writes are built, live multi-cell is config-gated.** The home-region model and its core logic are real and tested, but live multi-region deployment (WAN routing, the ScyllaDB backing) is opt-in, not on by default. The local dev database is a single in-process store. toil is built to distribute writes worldwide; not every app is running a live global write cluster today.\n- **Analytics is a dev stub locally.** It is real on the edge; the local dev server returns sample data.\n- **Auth secrets ship as dev placeholders.** The session HMAC, OPRF seed, and ML-KEM key are clearly-insecure placeholders so `toiljs dev` just works. A real deployment must set its own (per-tenant auto-generation at domain registration is the plan).\n- **Page search indexes static metadata only.** Routes whose metadata comes from a dynamic `generateMetadata` are not in the index.\n\n## Why it is all a default\n\nNone of this is an upgrade you unlock later. toil grades itself against [RSG](./design-principles.md) (Resilience and Scale Grade), whose one rule is that your grade is your weakest axis, never the average, so these batteries exist to keep any single axis from quietly capping the whole. Where the honest trade fits your project (ToilDB is not general SQL, the server language is a strict TypeScript subset, the catalog is younger than long-established platforms), the built-in stack is the whole point. [Why toil](./why-toil.md) says where it does not.\n",
61
+ "introduction/README.md": "# Understanding toil\n\ntoil is one framework that gives you AAA-grade, hyper-scale infrastructure with zero configuration and no networking or distributed-systems knowledge required. You write a React frontend and a TypeScript backend in one project; toil runs both, plus a database, close to your users worldwide. Post-quantum login is one line. It just works out of the box.\n\nTop-tier infrastructure is normally something a funded team assembles from ten rented vendors and babysits with deep expertise. toil makes the good version the default version, in a single framework, so a solo builder starts from the same baseline as that team. This section is the \"why\": what toil is, the problem it solves, how it works underneath, and why it is built the way it is. If you read nothing else first, read this. It is what turns \"another framework\" into \"oh, that is the point.\"\n\n## The one big idea\n\nAlmost every website has a split personality. The pretty part (pages, buttons) is served from machines all over the world, close to you. The important part (the database, where your data lives and changes) sits in **one place**, one region, often one machine. Post a comment in Tokyo when that database is in Virginia and your click flies halfway around the planet and back before anything happens.\n\ntoil is built to remove the split. Your **frontend** (React) and **backend** (TypeScript, compiled to a tiny WebAssembly program) both run at the **edge**, near your users. And **ToilDB** is built to distribute the writes too, so a write does not have to travel to one far-away box: every key has a home region that orders its writes, while the data replicates outward for fast local reads. One language, one project, one deploy, running close to everyone.\n\n```mermaid\nflowchart TB\n subgraph Ordinary[\"The ordinary web\"]\n U1[\"User in Tokyo\"] -->|fast| E1[\"Edge (pretty part)\"]\n E1 -->|slow, one region| DB1[(\"Database in Virginia\")]\n end\n subgraph Toil[\"toil\"]\n U2[\"User in Tokyo\"] -->|fast| E2[\"Edge near Tokyo<br/>frontend + backend + data\"]\n end\n```\n\nThat is the entire pitch. Distributing reads is easy and everyone does it; distributing the **writes** is the hard part almost nobody does, and it is the reason most \"global\" apps are only half global. The rest of these pages is how toil pulls it off, and why it stays honest about what is live today versus what is built and deployment-gated.\n\n## Read these in order\n\n1. **[Why toil? Who is it for?](./why-toil.md)** The problem with today's stacks, who benefits most, and the honest cases against.\n2. **[The modern stack](./modern-stack.md)** The full catalog of modern tech baked in, owned by toil, with zero setup.\n3. **[How toil works](./how-it-works.md)** The whole machine end to end: React client, WebAssembly backend, the edge node, and ToilDB.\n4. **[What makes toil hyper-scalable](./hyperscale.md)** What \"hyper-scale\" means, and the mechanisms that let one tiny program serve the planet.\n5. **[How toil is distributed](./distributed.md)** The hardest problem in web infrastructure, distributing the writes, and how ToilDB is built to solve it.\n6. **[toil versus other frameworks](./vs-other-frameworks.md)** An honest comparison with Next.js, Rails, Django, serverless, edge runtimes, and backend-as-a-service.\n7. **[Why toil is built this way (the RSG bar)](./design-principles.md)** The internal rubric, graded AAA down to D on its weakest axis, that toil holds itself against.\n\n## The short version\n\n- **Who it is for:** people building real products who want global speed and reliability without a platform team or ten stitched-together vendors. See [Why toil](./why-toil.md).\n- **Why it is fast:** the code runs next to the user, with no slow trip to a central origin. See [Hyper-scale](./hyperscale.md).\n- **Why it is different:** it is built to distribute the writes, not just the reads. See [Distributed](./distributed.md).\n- **Why it is safe:** the backend is a sandbox, passwords never reach the server in a usable form (real post-quantum auth), secrets never ship in the code, and the browser verifies every asset it loads. See [Security](../concepts/security.md).\n\nOne framework, zero config, AAA-grade from the first line. When you are ready to build, jump to [Getting started](../getting-started/README.md).\n",
62
+ "introduction/vs-other-frameworks.md": "# toil versus other stacks\n\nAn honest, axis-by-axis look at where toil trades differently from the stacks you already use. Every tool below is genuinely good at what it was built for, so the goal is not to crown a winner but to be concrete about what you gain and what you give up. toil is also younger than all of them, and that shows up in a few rows.\n\nThe short story: toil trades a large, mature ecosystem for one owned framework that distributes writes and ships the fast, safe defaults for free. Whether that trade fits your project is the honest question this page tries to answer.\n\n## The comparison at a glance\n\n| Axis | A typical modern stack | toil |\n| --- | --- | --- |\n| Pieces on the critical path | A dozen rented vendors (host, functions, database, auth, email, queue, cache, analytics, realtime), each its own account, bill, and SDK | One framework you write in; the core services are toil's own, so nothing third-party sits on the critical path |\n| Where writes land | One primary region: reads go global, writes crawl back to that box | Every key has one home region that orders its writes, while data replicates outward for fast local reads (built to distribute; live multi-cell is configuration-gated) |\n| Server runtime | A Node process, container, or VM per app | A small sandboxed WebAssembly module; one edge box safely runs many apps (multi-tenant), which is what makes running near everyone affordable |\n| Client-to-server calls | Hand-written fetch/REST, types drift until something breaks in production | A generated, fully typed `Server` client with no raw fetch: change a field on the server and the frontend stops compiling until you fix it |\n| Auth | Bolt on a provider or roll your own | Post-quantum login (ML-DSA + ML-KEM) built in, enabled in about one line |\n| Getting to production | Assemble CDN, cache, regions, hardened auth, and CI yourself | Zero config: the good version is the default version |\n| Toolchain | A decade of bundler configs, transpiler shims, CommonJS-versus-ESM interop, and `node_modules` churn | Built on modern web tech (React + Vite client, one CLI), far less legacy to fight |\n| Database | Mature SQL: joins, ad-hoc queries, huge tooling | Seven purpose-built families (Documents, Unique, Counter, Events, Membership, Capacity, View): fast and distributed, but not relational |\n| Package ecosystem | The full npm and Node universe | A strict TypeScript subset with built-in globals; no arbitrary npm packages or Node APIs on the server |\n| Integration catalog | Large, mature, well documented | Smaller and younger |\n| Single-region app | Dead simple, nothing to distribute | Distribution you may not need |\n\nThe top rows lean toil's way, the bottom rows lean the incumbents' way, and that split is the whole story. toil is designed to win the structural axes (one owned stack, distributed writes, a multi-tenant sandbox, end-to-end types, built-in modern auth) and it concedes the ecosystem axes (SQL, the Node universe, catalog size) that maturity buys.\n\n## Where each stack fits\n\n**Next.js / Vercel.** Superb React DX and global edge reads. The ceiling is the write path: pages cache worldwide, but a write (a comment, an order, a flash-sale click) still resolves against one primary region, and serverless cold starts add latency and per-invocation cost exactly when a spike hits. toil keeps a React-first client and moves the write to a home region near the data.\n\n**Rails / Django.** Mature, productive, batteries included, an enormous ecosystem and a deep hiring pool. If you are happy in one region this is a great and boring choice. The default shape is a single-region monolith with one primary that every write must reach; you can add replicas and standbys to climb, but distributed writes are not in the model.\n\n**Serverless functions (Lambda, Cloud Functions).** Elastic, stateless compute that scales to zero. But it is stateless compute in front of a central database, so a write burst still bottlenecks on that store, and each cold invocation bills and lags on its own.\n\n**Edge runtimes (Workers, Deno Deploy).** The closest in spirit to toil's compute model: your code runs near users. The catch is that the database you attach is usually single-region, so edge compute becomes a faster front door to the same central write bottleneck.\n\nCloudflare Durable Objects and D1 are the closest mainstream analog to toil's idea, and credit is due: a Durable Object gives one object a single-writer home that orders its writes, the same shape as ToilDB's per-key home. The difference is packaging. With the edge-runtime approach you assemble the pieces yourself (runtime, object or database product, auth, email, realtime); toil ships distributed writes, the seven database families, auth, email, streaming, and background jobs as one owned stack. Which you prefer is a real trade-off.\n\n**Backend-as-a-service (Supabase, Firebase).** The fastest thing to start with, and often the right call for a prototype. The convenience is a managed service on your critical path that you cannot inspect or patch, and writes still resolve against a primary.\n\n## Where the incumbents still win\n\ntoil does not win every axis, and some gaps are real today. Be honest about them:\n\n- **Mature ecosystems.** More tutorials, more answered questions, more hosting options, and more people who already know the tool.\n- **SQL and joins.** If your data is relational and you live in ad-hoc queries and joins, a SQL database is the right tool. ToilDB is seven purpose-built families, not a relational engine (see the [database overview](../database/README.md)).\n- **The Node package universe.** The toil server is a strict TypeScript subset compiled to WebAssembly: no arbitrary npm packages or Node APIs, built-in globals instead. That is the price of the small, fast, multi-tenant sandbox.\n- **Bigger integration catalogs.** toil is younger, so the catalog of ready-made integrations is smaller. If your project is defined by a large existing integration catalog, that gap matters.\n- **Single-region simplicity.** If one region already fits your users, toil's distribution is effort you do not need.\n\nNone of these are permanent, and the right tool is the one that fits the job in front of you.\n\n## toil's bet\n\nMost stacks cap in nearly the same place: the write path is one box in one region, or the critical path leans on services you rent and cannot fix. toil's bet is to refuse both at once, own the whole stack and distribute the writes, so the structural caps are designed out and the limits left are the ones your own code sets.\n\nKeep the mechanism honest. The home-region model and its core logic are real and tested, but live multi-region deployment (the WAN routing and the ScyllaDB backing) is configuration-gated rather than on by default, and the local dev database is a single in-process store. Compute works the same way: your code runs at the edge on every request, while the regional, continental, and global-daemon tiers are opt-in. So the accurate claim is \"toil is built to distribute writes worldwide, and the mechanism is real,\" not \"every app is already running a live global write cluster today.\"\n\nWhether that trade fits your project is the checklist in [Why toil](./why-toil.md).\n\n## Related\n\n- [Why toil? Who is it for?](./why-toil.md): the problem toil solves and the honest cases where you should not use it.\n- [The modern stack](./modern-stack.md): the full, verified catalog of what is built in.\n- [How toil is distributed](./distributed.md): the mechanism behind distributed writes (every key's single home region).\n- [Why toil is built this way](./design-principles.md): the bar toil grades itself against.\n",
63
+ "introduction/why-toil.md": "# Why toil? Who is it for?\n\ntoil is a full-stack framework: you write a React frontend and a TypeScript backend in one project, and toil runs both (plus a database) close to every user, worldwide.\n\nThe thesis in one line: toil is the modern full-stack tech a developer would actually want, AAA-grade from the very first line, and hyper-scalable at the same time. Even a simple pizza site gets top-tier infrastructure with zero setup. Distributed writes are one pillar of that; the modern stack that just works is the heart.\n\n## The problem with today's stacks\n\nShipping a good app today means fighting your own infrastructure. Five ways it fights back.\n\n**Reads go global, writes go to one region.** Your pages load fast from caches worldwide, but a *write* (a comment, a like, an order) crawls back to a single database in a single region. A user in Sydney writing to Virginia pays for the whole round trip, and that one region is a single point of failure for everyone.\n\n**You became a systems integrator by accident.** A production stack is a dozen rented services stitched together: a frontend host, serverless functions, a managed database, auth, email, a queue, a cache, analytics, realtime. Each is its own account, bill, SDK, and outage. And every one on your *critical path* (what must work for a request to succeed) is a black box you cannot inspect, patch, or secure: when it is slow you are slow, when it is breached part of you is breached.\n\n**It runs on yesterday's tooling.** The typical stack sits on a decade of accumulated legacy: bundler configs, transpiler shims, CommonJS-versus-ESM interop, framework churn, and a `node_modules` folder heavier than the app itself. You fight the toolchain and chase version bumps instead of building. The modern web platform moved on; the stack did not.\n\n**Overhead at every layer.** Heavy runtimes, slow builds, serverless cold starts, oversized JavaScript shipped to the browser, and a network hop between every service. Each layer piles on latency and cost that the user feels and you debug.\n\n**Fast and safe are opt-in, never the default.** Good performance and real security are things you assemble by hand: a CDN here, a cache there, careful data fetching, region tuning, hardened auth, current cryptography. Miss a piece and you are slow or exposed. The good version is always the extra work, so most people ship the lesser one.\n\nThe result: a solo builder and a funded startup hit the *same* wall. Top-tier infrastructure means assembling and babysitting a lot of parts, so most settle for less.\n\n## What toil does instead\n\nEvery design decision in toil serves one goal: AAA-grade infrastructure as the *default*, not the thing you assemble by hand. All of it in one framework, not ten rented vendors. Zero configuration, and no distributed-systems or networking expertise required. It just works out of the box, quantum-proof login included, on a stack built for modern tech instead of a decade of legacy tooling.\n\nFour pillars.\n\n### 1. AAA-grade from the first line\n\nTop-tier infrastructure on day one, on the smallest project, with zero setup: edge compute (your code runs near users worldwide), a global database already there, automatic tamper-proofing of every shipped asset (SHA-384 Subresource Integrity), and quantum-proof login in about one line.\n\nThat login is real post-quantum cryptography. Your password is stretched into a signing key on your device and never reaches the server in a usable form, and the handshake uses ML-DSA and ML-KEM, the NIST-standardized algorithms meant to survive a quantum computer. Most stacks bolt current crypto on by hand, if at all; here it is the default.\n\nA pizza site and a planet-scale app start from the same baseline. \"AAA-grade\" is the actual bar toil grades itself against; see [design principles](./design-principles.md).\n\n### 2. Batteries-included, and owned\n\nAuth, database, email, rate limiting, analytics, realtime streaming, and background jobs are all built in and are toil's own. Nothing third-party sits on your critical path.\n\nBecause they are one system, the parts already fit. You are not gluing ten SDKs together and praying they agree. Full catalog: [The modern stack](./modern-stack.md).\n\n```mermaid\nflowchart TB\n subgraph assemble[\"Usual way: assemble ten vendors\"]\n direction TB\n A1[\"Your app\"] --> V1[\"host\"]\n A1 --> V2[\"functions\"]\n A1 --> V3[\"database\"]\n A1 --> V4[\"auth\"]\n A1 --> V5[\"email\"]\n A1 --> V6[\"rate limiter\"]\n A1 --> V7[\"analytics\"]\n A1 --> V8[\"realtime\"]\n A1 --> V9[\"jobs\"]\n end\n subgraph toil[\"toil: one owned stack\"]\n direction TB\n A2[\"Your app\"] --> T[\"edge + database + auth + email +<br/>rate limiting + analytics +<br/>streaming + jobs, built in\"]\n end\n```\n\nHonest boundary: \"owned\" means the *core* of a working app is toil's, not that outside services are banned. Call a payment provider or another API and you still can.\n\n### 3. A modern DX that just works\n\nBuilt on today's web platform, not a decade of accumulated legacy: TypeScript end to end, one repo, one deploy, wired by types. Change a field on the server and the frontend stops compiling until you fix it (a compile error at your desk, not a production bug).\n\nThe toolchain is set up for you: ESLint, Prettier (with a plugin for toil's decorators), an editor plugin, one CLI, and a `doctor` that fixes common problems in place. The docs are even LLM-friendly, so an AI assistant reads your current conventions instead of guessing. More in [The modern stack](./modern-stack.md).\n\n### 4. Hyper-scalable and distributed\n\nYour backend compiles to a tiny sandboxed **WebAssembly** module (a compact, locked-down binary that runs at near-native speed), modern tech rather than a heavyweight legacy runtime, so one edge box safely runs many apps and running near everyone stays affordable.\n\nAnd the database, **ToilDB**, distributes the *writes*, not just the reads: every key has one **home** region that orders its writes, while data replicates outward for fast local reads. Distributing writes is the hard part almost nobody does. toil is built to do it worldwide and the mechanism is real and tested; live multi-region deployment is configuration-gated rather than on by default, and once it is on, a far read can lag a few milliseconds (the eventual-consistency trade). See [How toil works](./how-it-works.md) and [How toil is distributed](./distributed.md).\n\n## Who it is for\n\n- **Solo builders and small teams:** a full, global, secure stack without hiring a platform team. The pizza site is first-class.\n- **Latency-sensitive apps:** writes that resolve near the user, not across an ocean.\n- **Global apps:** logic and data near users on every continent.\n- **Realtime apps:** chat, presence, and live cursors on built-in streaming, not a bolted-on vendor.\n\nSame install for the smallest project and the largest. You grow into the scale; you do not rebuild to reach it.\n\n## When not to use toil\n\n- **You need SQL or heavy joins.** ToilDB is seven purpose-built families, not a general SQL engine. See the [database overview](../database/README.md).\n- **You lean on the Node ecosystem.** The server is a strict TypeScript subset compiled to WebAssembly: no arbitrary npm packages, no Node APIs, built-in globals instead.\n- **You are happy single-region and simple.** If one region already fits, toil's distribution is effort you do not need.\n- **You need a big integration catalog today.** toil is younger, and that catalog is smaller.\n\nNone of these are permanent, and the right tool is the one that fits the job in front of you.\n\n## Related\n\n- [The modern stack](./modern-stack.md): the full, verified catalog of what is built in.\n- [How toil works](./how-it-works.md): the whole machine end to end, from React client to ToilDB.\n- [toil versus other frameworks](./vs-other-frameworks.md): an honest, axis-by-axis comparison.\n- [Security](../concepts/security.md): the defaults that are already on.\n- [Getting started](../getting-started/README.md): install the tool and build a small feature.\n",
64
64
  "realtime/channels.md": "# Channels (`useChannel`)\n\n`useChannel` is the client-side React hook for realtime. It opens a live connection from the browser to a server `@stream` box, tracks whether it is connected, collects the messages that arrive, and gives you a `send` function. It reconnects on its own if the connection drops.\n\n## What a channel is\n\nA **channel** is simply an open, two-way connection between one browser and the server, viewed from the client's side. On the server that connection is handled by a [`@stream`](./streams.md) box. On the client you hold the other end with `useChannel` (a React hook) or `connectChannel` (a plain function, no React).\n\nYou have already met the two-piece model in the [realtime overview](./README.md): a `@stream` on the server, a channel on the client. This page is the client half.\n\n## The `useChannel` hook\n\n`useChannel` is available on the global `Toil` object in your route files, so no import is needed:\n\n```tsx\nexport default function Ping() {\n const chan = Toil.useChannel({ path: '/echo' });\n\n return (\n <main>\n <p>Status: <strong>{chan.connected ? 'connected' : 'offline'}</strong></p>\n <button type=\"button\" onClick={() => chan.send('ping')}>Send ping</button>\n <ul>\n {chan.messages.map((m, i) => (\n <li key={i}><code>{typeof m === 'string' ? m : '(binary frame)'}</code></li>\n ))}\n </ul>\n </main>\n );\n}\n```\n\n### What it returns\n\n`useChannel(options?)` returns an object with three things:\n\n| Field | Type | What it is |\n| ----------- | -------------------------- | ---------------------------------------------------------------------- |\n| `connected` | `boolean` | `true` while the socket is open. Re-renders when it changes. |\n| `messages` | `(string \\| ArrayBuffer)[]`| every frame received so far, in order. A new frame re-renders. |\n| `send` | `(data) => void` | send one frame to the server (a no-op until the socket is open). |\n\nA frame is either a **string** (text) or an **`ArrayBuffer`** (binary). Send accepts a string or binary data. To send or read structured data, encode and decode it yourself:\n\n```ts\nchan.send(new TextEncoder().encode('hello')); // send bytes\nconst text = (m: string | ArrayBuffer) =>\n typeof m === 'string' ? m : new TextDecoder().decode(m); // read either kind as text\n```\n\n### Options\n\n```ts\nToil.useChannel({\n path: '/echo', // which @stream route to connect to. Default: '/_toil'\n url: 'wss://...', // full ws(s):// override (wins over path). Usually omit this.\n reconnect: true, // auto-reconnect after an unexpected close. Default: true\n reconnectDelay: 1000 // ms to wait before each reconnect attempt. Default: 1000\n});\n```\n\nThe most important option is **`path`**: it points the channel at a specific `@stream` route. `{ path: '/echo' }` connects to a stream mounted with `@stream('echo')`. If you omit `path`, it connects to the default `/_toil` channel.\n\nYou do not build the `ws://` or `wss://` URL yourself. The hook derives it from the current page (a page served over `https` uses `wss`, otherwise `ws`), and on the production edge the transport is upgraded to WebTransport for you. Your code stays the same.\n\n### Lifecycle and cleanup\n\n`useChannel` connects when the component mounts and closes when it unmounts, so a channel lives exactly as long as the page that uses it is on screen. React's rules apply: call it at the top level of your component, not inside a loop or condition.\n\n### `connectChannel`: the non-React version\n\nIf you need a channel outside a React component (in a plain module, a store, an event handler), use `connectChannel`. It takes a callback for each incoming frame and returns a handle:\n\n```ts\nimport { connectChannel } from 'toiljs/client';\n\nconst chan = connectChannel(\n (frame) => console.log('got', frame),\n { path: '/echo' }\n);\nchan.send('hello');\nchan.close(); // stop and stop reconnecting\n```\n\n`useChannel` is `connectChannel` wrapped for React (it manages the `connected` / `messages` state for you).\n\n## The typed client: `Server.Stream`\n\n`useChannel` deals in raw frames. For a typed experience, toiljs also generates a client per `@stream` class at `Server.Stream.<ClassName>`, described in full on the [Streams](./streams.md#reaching-a-stream-from-the-browser) page. Use whichever fits: `useChannel` for a quick reactive hook, `Server.Stream.<Name>.connect()` for typed messages and explicit `onMessage` / `onClose` callbacks. Both open the same kind of connection to the same box.\n\n## A full chat-style example\n\nHere is a chat UI wired end to end: a React page that sends what you type and shows every reply, plus the server box it talks to.\n\n### Client (`client/routes/chat.tsx`)\n\n```tsx\nimport { useState } from 'react';\n\nexport default function Chat() {\n const chan = Toil.useChannel({ path: '/room' });\n const [draft, setDraft] = useState('');\n\n const asText = (m: string | ArrayBuffer): string =>\n typeof m === 'string' ? m : new TextDecoder().decode(m);\n\n function submit(): void {\n if (draft.length === 0) return;\n chan.send(draft); // one send() becomes one @message on the server\n setDraft('');\n }\n\n return (\n <main>\n <h1>Chat</h1>\n <p>Status: <strong>{chan.connected ? 'connected' : 'offline'}</strong></p>\n <ul>\n {chan.messages.map((m, i) => <li key={i}>{asText(m)}</li>)}\n </ul>\n <input\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}\n placeholder=\"Say something\"\n />\n <button type=\"button\" onClick={submit}>Send</button>\n </main>\n );\n}\n```\n\n### Server (`server/streams/Room.ts`)\n\n```ts\n@stream('room')\nclass Room {\n private seen: i32 = 0;\n\n @connect\n onConnect(): void {\n this.seen = 0;\n }\n\n @message\n onMessage(packet: StreamPacket): StreamOutbound {\n this.seen = this.seen + 1;\n const text = new TextDecoder().decode(packet.bytes());\n const reply = '#' + this.seen.toString() + ': ' + text;\n return StreamOutbound.reply(Uint8Array.wrap(String.UTF8.encode(reply)));\n }\n}\n```\n\nDo not forget to import the stream in `server/main.stream.ts` (see [Streams](./streams.md#the-mainstreamts-file-a-separate-tier)):\n\n```ts\nimport './streams/Room';\n```\n\nRun `toiljs dev`, open the page, and every line you send comes back numbered, live.\n\n### An honest limitation: this echoes, it does not broadcast\n\nRead the example carefully. Each browser has its **own** `Room` box, and that box replies only to **its own** connection. So in this version, two people in \"the same room\" do **not** see each other's messages: each just sees their own, echoed back. That is enough for a private assistant, a progress feed, or a single-player game, but it is **not** a shared chat room where one message reaches everyone.\n\nTo send one message to **many** connected users (true broadcast, the heart of a group chat or a live feed), you need the server to **fan out** a message to every subscriber. That is what `@channel` is for.\n\n## `@channel`: server broadcast (not yet available)\n\n> **Status: planned, not live in the current runtime.** A `@stream` class that declares a `@channel` hook is **rejected** by the edge today (\"stream channels are not a v1 runtime ABI\"), and the compiler does not yet emit it. The information below describes the intended shape so you can plan for it, not an API you can call right now.\n\nThe idea is **publish/subscribe** (\"pub/sub\"), a standard pattern for broadcast:\n\n- A **channel** is a named topic, for example a chat room id.\n- A connection **subscribes** to a channel to start receiving everything sent to it.\n- Anyone can **publish** a message to the channel, and every subscriber receives it.\n- A connection **unsubscribes** (or disconnects) to stop.\n\nWith `@channel`, the server box would join a connection to a named topic and publish messages to it, and the edge would deliver each published message to every subscriber across all the connections in that topic, no matter which worker or node each one landed on. That is the missing \"one to many\" piece that turns the echo example above into a real shared room. The plan already reserves per-plan limits for it (a cap on subscribers per channel and on message size), so the surface is designed; it is the runtime delivery that is not shipped yet.\n\n### The picture: world-wide sync (the design)\n\nWhen `@channel` ships, one `publish` will fan out across the whole edge mesh, reaching every subscriber wherever they are, at nearly the same moment. That is the **world-wide sync** idea: a live session where everyone sees the same update together, whether they are in the same city or on opposite sides of the planet.\n\n```mermaid\nflowchart TB\n Pub[\"One publish()<br/>(a chat line, a goal, a game tick)\"] --> Mesh[\"Dacely edge mesh<br/>(routes to every subscriber's core)\"]\n Mesh --> A[\"Subscriber in Tokyo\"]\n Mesh --> B[\"Subscriber in Paris\"]\n Mesh --> C[\"Subscriber in New York\"]\n Mesh --> D[\"...every other subscriber, at once\"]\n```\n\nThe mechanism meant to make this fast is the same one that already spreads plain connections across the edge: because the edge always knows where each subscriber's connection lives, a broadcast is a direct fan-out to a known set of destinations, not a search for who is listening. For the wider picture, see [Built for massive fan-out and world-wide sync](./README.md#built-for-massive-fan-out-and-world-wide-sync) in the overview.\n\nKeep the status in mind: the diagram above is the **intended shape**. The delivery runtime is not shipped yet, so treat it as the plan, not an API you can call today.\n\n**What you can build today:** anything where each user talks to their own box (assistants, per-user live updates, single-player sync, progress streams). **What needs `@channel`:** shared rooms and one-to-many broadcast. Until it ships, a common workaround is to persist messages to [the database](../database/README.md) and have clients poll or re-read, which is simpler but not instant.\n\n## Gotchas\n\n- **Frames are `string` or `ArrayBuffer`.** Decode binary frames with `TextDecoder`; there is no automatic parsing in `useChannel` (the typed `Server.Stream` client is the structured option).\n- **`send` before \"connected\" is dropped.** It is a no-op until the socket is open. Guard on `chan.connected`, or expect the first eager sends to be lost.\n- **`messages` grows forever.** It keeps every frame received. For a long-lived page, slice it or keep your own bounded list.\n- **One box per connection.** `useChannel` does not broadcast between users; that is the `@channel` feature described above.\n\n## Related\n\n- [Realtime overview](./README.md): the two-piece model and when to use realtime.\n- [Streams](./streams.md): the server `@stream` class this hook talks to.\n- [The database (ToilDB)](../database/README.md): where to persist messages that must outlive a connection or reach users who are offline.\n",
65
65
  "realtime/README.md": "# Realtime\n\nRealtime is how your app pushes data to the browser the instant it happens, instead of the browser having to ask again and again. In toiljs you get realtime from two small pieces: a server class marked `@stream`, and a client React hook called `useChannel`.\n\n## What \"realtime\" means\n\nA normal web request is one round trip. The browser asks (\"give me the todos\"), the server answers, and the connection closes. If you want fresh data a second later, you have to ask again. That is fine for a page load or a form submit, but it is a poor fit for anything that changes on its own: a live chat, a game, a price ticker, a progress bar, a presence indicator (\"3 people online\").\n\nRealtime flips it around. The browser opens **one long-lived connection** and keeps it open. After that, either side can send a message at any time, as many times as it likes, with no new handshake. That open connection is often called a **socket**.\n\n## WebTransport in plain words\n\nTo hold that long-lived connection open, the browser and the Dacely edge speak a protocol called **WebTransport**.\n\nHere is all you need to know as a beginner:\n\n- WebTransport is a modern, built-in browser feature (like `fetch`, but for a persistent two-way connection).\n- It runs on top of **HTTP/3** and **QUIC**, which are the newest, fastest versions of the web's plumbing. They are built for low latency (messages arrive quickly) and for surviving network changes (your phone switching from Wi-Fi to cellular without dropping the connection).\n- You do not write any WebTransport code by hand. toiljs gives you a tiny client API, and it uses WebTransport underneath on the production edge.\n\nOne helpful detail for later: in local development (`toiljs dev`) the same client API runs over a **WebSocket** instead, because a WebSocket is simpler to serve from one local process. A WebSocket is the older, widely supported \"keep a socket open\" browser feature. The point is that **your code is identical** in dev and in production; only the transport underneath differs, and toiljs picks it for you.\n\n## When to use realtime (and when not to)\n\nReach for realtime when **the server needs to talk first**, or when messages fly back and forth quickly:\n\n| Use realtime when... | Use a plain HTTP request when... |\n| --------------------------------------------- | ------------------------------------------------ |\n| A chat or comment thread updates live. | You load a page or a list once. |\n| A multiplayer game syncs moves and positions. | You submit a form and get one answer. |\n| You show live presence or typing indicators. | You fetch data on a button click. |\n| You stream progress of a long job. | The data rarely changes, or the user pulls it. |\n\n**Games are a first-class use case, not an afterthought.** Realtime multiplayer (player moves, low-latency input, live presence, per-player state) is one of the hardest things to build on the plain web, and it is exactly what toil streaming is designed for. More on why, and how far it is built to scale, in [Built for massive fan-out and world-wide sync](#built-for-massive-fan-out-and-world-wide-sync) below.\n\nIf a single request-and-response does the job, prefer that: it is simpler, it caches well, and it needs no open connection. Plain requests in toiljs are [HTTP routes](../backend/rest.md) and [typed RPC](../backend/rpc.md). Reach for realtime only when the \"ask again and again\" model genuinely gets in your way.\n\n## The two pieces\n\nRealtime in toiljs is always a pair:\n\n1. **The server: a `@stream` class.** You write a small class and mark it `@stream`. The edge turns it into a **resident box**: a live instance that is created when a connection opens, handles every message on that connection, and is torn down when it closes. It has four lifecycle hooks (`@connect`, `@message`, `@close`, `@disconnect`). See [Streams](./streams.md).\n\n2. **The client: a hook.** From your React UI you open the connection and send or receive messages. The low-level way is the `useChannel` hook; the typed, generated way is `Server.Stream.<ClassName>.connect()`. See [Channels](./channels.md).\n\n```mermaid\nsequenceDiagram\n participant B as Browser (React)\n participant E as Dacely edge\n participant S as Your @stream box\n B->>E: open connection (WebTransport, or WebSocket in dev)\n E->>S: create the box, run @connect\n Note over S: the box is now resident for this connection\n B->>E: send \"hello\"\n E->>S: @message(\"hello\")\n S-->>B: reply \"hi back\"\n B->>E: send \"still here\"\n E->>S: @message(\"still here\")\n S-->>B: reply \"yep\"\n B->>E: close\n E->>S: run @close, then destroy the box\n```\n\nNotice that the box lives across **many** messages in that diagram. That is the whole idea: because it is the same instance every time, it can remember things between messages (a counter, who you are, what room you joined). A normal HTTP handler forgets everything after each request; a stream box does not, for as long as the connection stays open.\n\n## Built for massive fan-out and world-wide sync\n\nRealtime gets exciting when a single event reaches **many** people at nearly the same moment. Picture a live sports app: a goal is scored, and every fan watching sees it light up together. Picture a multiplayer game: one player moves, and everyone in the match sees it happen right away. That shape, one event fanning out to a huge live audience, is what toil streaming is built for. We call it **world-wide packet sync**: everyone in the same live session gets the same update at nearly the same time, wherever they are on the planet.\n\n```mermaid\nflowchart TB\n P[\"One event<br/>(a goal, a chat line, a game move)\"] --> E[\"Dacely edge<br/>(spread across cores and cities)\"]\n E --> S1[\"Player in Tokyo\"]\n E --> S2[\"Player in Paris\"]\n E --> S3[\"Player in Sao Paulo\"]\n E --> Sn[\"...many more, at once\"]\n```\n\n### Why it can go so wide\n\nThree design choices make massive scale possible, without any single machine or single lock becoming the ceiling.\n\n1. **Connections spread across the fleet in parallel.** Every new connection is handled on its own, with no shared lock in the middle for connections to fight over. There is no central bottleneck that every connection has to pass through, so adding machines adds capacity in a straight line.\n2. **The session follows the user.** A phone that switches from Wi-Fi to cellular keeps its exact session, in-memory game state and all. A network change does not drop the connection or reset the box.\n3. **The session sits near the user.** The edge is a fleet of servers in many cities. A `@stream` runs at a regional or continental node (see [Compute tiers](../concepts/tiers.md)), so the round trip to a player stays short no matter where on the map they are.\n\n### An honest word on numbers\n\nThe framework is **designed** for very large live audiences, aiming at the scale of millions of concurrent connections in a single live session as its ambition. That is the target the architecture is built toward, not a number measured in these docs. Real capacity depends entirely on your deployment: how many machines and cores you run, how big each message is, how often it is sent, and where your users are. We do not publish a benchmarked figure here. What we can say honestly is **why** it scales (the three design choices above), and that the design removes the usual single-machine and single-lock ceilings that cap other realtime stacks.\n\nOne more honest note. Fanning a single message out to **other** users' connections (true one-to-many broadcast, the arrows in the diagram above) is the job of `@channel`. The client side of that (`useChannel`, `Server.Stream`) is live today. The server-side broadcast that reaches every subscriber across the mesh is **planned, not shipped yet**. See [Channels](./channels.md) for exactly what works now and what is coming, so you can build on solid ground.\n\n## Where to go next\n\n- [Streams](./streams.md): the server side. How to declare a `@stream` class, the four lifecycle hooks, per-connection state, replying, and the separate `main.stream.ts` file.\n- [Channels](./channels.md): the client side. The `useChannel` React hook, the generated typed client, and a chat-style example. Also covers the planned `@channel` broadcast feature.\n\n## Related\n\n- [Compute tiers (L1 to L4)](../concepts/tiers.md): where a stream box runs on the edge.\n- [HTTP routes (`@rest`)](../backend/rest.md) and [Typed RPC](../backend/rpc.md): the plain request-and-response alternatives.\n- [Daemons](../background/daemons.md): long-lived background work that is not tied to a browser connection.\n",
66
66
  "realtime/streams.md": "# Streams (`@stream`)\n\nA `@stream` is a server class that handles one live connection from start to finish. You mark a class with `@stream`, add lifecycle hooks, and the Dacely edge keeps one instance of that class alive for as long as the browser stays connected.\n\n## What a stream is (and why it is different)\n\nWhen you write an [HTTP route](../backend/rest.md), the server builds a **fresh** handler for every request and throws it away afterward. Anything you stored on the handler's fields is gone the moment the response is sent. That is perfect for one-shot requests, but it means the handler cannot \"remember\" anything between requests on its own.\n\nA `@stream` is the opposite. It is a **resident box**: one live instance, created when a connection opens and kept alive until it closes. Because it is the *same* instance for every message on that connection, values you store on its fields **persist across messages**. That is what makes it the right tool for a conversation, a game session, or anything stateful that lasts for the life of a connection.\n\nThe word \"resident\" just means \"stays in memory and keeps running.\" The word \"box\" is toiljs's name for one sandboxed instance of your compiled server code.\n\n```ts\n@stream('echo')\nclass Echo {\n private count: i32 = 0;\n\n @connect\n onConnect(): void {\n this.count = 0; // a fresh connection starts a fresh box, so count begins at 0\n }\n\n @message\n onMessage(packet: StreamPacket): StreamOutbound {\n this.count = this.count + 1; // survives across messages: same box every time\n const text = 'pong #' + this.count.toString();\n return StreamOutbound.reply(Uint8Array.wrap(String.UTF8.encode(text)));\n }\n\n @close\n onClose(): void {\n // the box is destroyed after this hook runs\n }\n}\n```\n\nConnect, send three messages, and you get back `pong #1`, `pong #2`, `pong #3`. The advancing number is proof that the same box handled all three.\n\n## Declaring a stream\n\nMark a class with `@stream` and give it a **name**. The name becomes the route the browser connects to.\n\n```ts\n@stream('echo') // mounted at /echo\nclass Echo { /* ... */ }\n\n@stream // bare form: the route is the class name (/Echo)\nclass Echo { /* ... */ }\n```\n\nThere are three forms:\n\n- `@stream('name')`: an explicit mount name (connect at `/name`).\n- `@stream` (bare): the mount name is the class name.\n- `@stream({ ... })`: a config object (see [Configuration](#configuration) below).\n\n## The four lifecycle hooks\n\nA stream method becomes a lifecycle hook when you tag it with one of these decorators. Every hook is **optional**: declare only the ones you need, and a missing hook is simply a no-op (it does nothing, it never crashes).\n\n| Decorator | Fires when... |\n| ------------- | ----------------------------------------------------------------------- |\n| `@connect` | the connection opens (the box has just been created). |\n| `@message` | an inbound frame arrives from the browser. |\n| `@close` | the connection closes cleanly (the box is destroyed after this hook). |\n| `@disconnect` | the connection is lost abruptly (network dropped, browser killed). |\n\nA **frame** is one message: one call to `send()` on the client becomes one `@message` on the server.\n\n```mermaid\nsequenceDiagram\n participant B as Browser\n participant S as @stream box\n B->>S: open\n activate S\n Note over S: @connect runs, box is created\n B->>S: frame \"a\"\n S-->>B: @message -> reply\n B->>S: frame \"b\"\n S-->>B: @message -> reply\n B->>S: close\n Note over S: @close runs, box is destroyed\n deactivate S\n```\n\n### `@connect`\n\nRuns once, right after the box is created. Use it to set up per-connection state (reset a counter, read the requested path, decide whether to accept the connection). It can return a `StreamOutbound` to accept or reject (see below). The Echo example uses it to zero its counter.\n\n**What it receives.** `@connect` is handed a `StreamInbound`, a small read-only object the host fills in with the details of the connection that just opened. It lets you look at *where* the connection came from before you decide to keep it.\n\n| Member | Type | What it gives you |\n| ------------- | -------- | ----------------------------------------------------------------------- |\n| `streamId` | `u64` | A unique id for this connection. |\n| `transport` | `i32` | A numeric tag for the transport that carried the connection. |\n| `authority()` | `string` | The host the browser connected to (for example `example.com`). |\n| `path()` | `string` | The path the browser opened (for example `/echo`). |\n\nWatch the shape: `authority()` and `path()` are **methods** (call them with `()`), while `streamId` and `transport` are plain properties (no parentheses).\n\n```ts\n@connect\nonConnect(info: StreamInbound): StreamOutbound {\n // Inspect where the connection is headed, then decide whether to keep it.\n if (info.path() != '/echo') {\n return StreamOutbound.reject(1); // refuse: any u16 reason code\n }\n this.count = 0; // fresh connection, fresh state\n return StreamOutbound.accept(); // keep the connection open\n}\n```\n\nYou do not have to take the argument. If the hook needs none of these details, declare it with no parameter (`onConnect(): void`), exactly like the Echo example above.\n\n### `@message`\n\nRuns for every inbound frame. This is where most of your logic lives. It receives the frame and may reply. Details in [Reading and replying](#reading-and-replying-to-messages).\n\n### `@close` and `@disconnect`\n\nBoth mean \"the connection is over,\" and both are your chance to clean up. The difference is *how* it ended:\n\n- `@close` is a **graceful** close: the browser (or your server) ended it on purpose.\n- `@disconnect` is an **abrupt** loss: the network dropped or the tab was killed with no goodbye.\n\nAfter either one, the box is destroyed.\n\n**What they receive.** Both hooks are handed a `StreamConnectionEvent`, a read-only summary of the connection that just ended.\n\n| Member | Type | What it gives you |\n| -------------- | ----- | ---------------------------------------------------------- |\n| `connectionId` | `u64` | The id of the connection that ended. |\n| `reason` | `u16` | A numeric close code explaining why it ended. |\n| `durationMs` | `u64` | How long the connection stayed open, in milliseconds. |\n\nAll three are plain properties (getters), so read them without `()`.\n\n```ts\n@close\nonClose(ev: StreamConnectionEvent): void {\n // Last chance to clean up. `ev` tells you how the connection ended.\n const heldSeconds = ev.durationMs / 1000;\n // e.g. record the session length or flush a buffer here.\n}\n\n@disconnect\nonDisconnect(ev: StreamConnectionEvent): void {\n // Same shape, but this fired because the connection dropped abruptly.\n // ev.reason carries the close code the edge assigned to the drop.\n}\n```\n\nAs with `@connect`, the argument is optional: declare `onClose(): void` if you do not need it.\n\n## Per-connection state (and its limits)\n\nState on the box's fields lasts for **one connection**. It does **not** survive:\n\n- a **reconnect**: if the browser drops and reopens, it gets a brand-new box that starts clean.\n- a **different user**: every connection gets its own box, so one connection's state can never leak into another. This is a safety property, not just a convenience.\n\nSo treat box fields as **per-connection scratch space** only. For anything that must outlive the connection (a saved message, a score, who a user is across reconnects), write it to [the database](../database/README.md), not to a class field.\n\n## Reading and replying to messages\n\nBy default, a `@message` receives a `StreamPacket`, which is a thin view over the raw bytes that arrived, and returns a `StreamOutbound`, which stages the reply.\n\n```ts\n@message\nonMessage(packet: StreamPacket): StreamOutbound {\n const raw = packet.bytes(); // the inbound frame as bytes\n return StreamOutbound.reply(raw); // echo the same bytes back\n}\n```\n\n`StreamPacket` (the inbound frame):\n\n| Member | What it gives you |\n| ------------ | ---------------------------------------------------------- |\n| `bytes()` | the whole frame as a `Uint8Array` (copy it if you keep it). |\n| `length` | the number of bytes in the frame. |\n| `at(i)` | the byte at index `i`. |\n\n`StreamOutbound` (what you return):\n\n| Call | Meaning |\n| ----------------------------- | ------------------------------------------------------------------ |\n| `StreamOutbound.reply(bytes)` | send one frame back to the browser. |\n| `StreamOutbound.empty()` | accept the frame and send nothing back. |\n| `StreamOutbound.reject(code)` | refuse (used from `@connect` to turn a connection away). |\n| `StreamOutbound.accept()` | accept a connection with no reply frame. |\n\nA `@message` may also return `void` when it never replies.\n\n> **Bytes, not strings.** A frame is raw bytes on the wire. To send text, encode it with `String.UTF8.encode(...)` (server) or `new TextEncoder().encode(...)` (browser), and decode it with `new TextDecoder().decode(...)` on the other side.\n\n### Typed messages\n\nRaw bytes are flexible but fiddly. If your messages are structured, declare a [`@data`](../backend/data.md) class and pass it as the stream's `message` type. Your `@message` hook then receives the **decoded object** instead of raw bytes.\n\n```ts\n@data\nclass ChatMsg {\n text: string = '';\n constructor(text: string = '') { this.text = text; }\n}\n\n@stream({ message: ChatMsg })\nclass Chat {\n @message\n onMessage(msg: ChatMsg): StreamOutbound { // decoded @data, not raw bytes\n const echoed = 'you said: ' + msg.text;\n return StreamOutbound.reply(Uint8Array.wrap(String.UTF8.encode(echoed)));\n }\n}\n```\n\nThe reply is still raw (`StreamOutbound` deals in bytes). Only the **inbound** side is decoded for you. On the client, a typed stream lets you `send(new ChatMsg('hi'))` and toiljs encodes it for you.\n\n## Games and interactive apps\n\nA stream box remembers state for the life of a connection, which is exactly what a game session needs. Each connected player gets their **own** box, and that box holds the player's live state (position, health, score) in memory, right next to the core handling their packets. The lifecycle hooks map cleanly onto a session: `@connect` spawns the player, `@message` handles each input, `@close` and `@disconnect` remove them.\n\n```ts\n// server/streams/Match.ts\n@data\nclass Move { // the input a player sends each tick\n dx: i32 = 0;\n dy: i32 = 0;\n constructor(dx: i32 = 0, dy: i32 = 0) { this.dx = dx; this.dy = dy; }\n}\n\n@stream({ message: Move, scope: StreamScope.Regional })\nclass Match {\n // Per-connection state: this player's position. It lives in memory for the\n // whole session, on the one core that owns this connection.\n private x: i32 = 0;\n private y: i32 = 0;\n private moves: u32 = 0;\n\n @connect\n onConnect(info: StreamInbound): StreamOutbound {\n this.x = 50; // spawn point\n this.y = 50;\n this.moves = 0;\n return StreamOutbound.accept();\n }\n\n @message\n onMove(move: Move): StreamOutbound {\n // Apply the input to this player's live position, then confirm it.\n this.x = this.x + move.dx;\n this.y = this.y + move.dy;\n this.moves = this.moves + 1;\n const state = '{\"x\":' + this.x.toString() + ',\"y\":' + this.y.toString() + '}';\n return StreamOutbound.reply(Uint8Array.wrap(String.UTF8.encode(state)));\n }\n\n @disconnect\n onDrop(ev: StreamConnectionEvent): void {\n // The player left (tab closed, network died). Their box is destroyed\n // next. Persist a score here if it must outlive the session.\n }\n}\n```\n\nEvery input a player sends is one `@message`, applied to **their** box's position and confirmed straight back to them with low latency. Because the box is resident, the player's position is always there in memory, on the same core, with no database round trip per move. That is what makes fast input loops (a game tick, a cursor drag, a live editor) feel instant.\n\n**What this version does, and does not, do.** Each player has their own box, so this handles per-player input, per-player state, and instant confirmation back to that player. To make players see **each other** (the other half of multiplayer), one player's move must fan out to everyone else in the match. That cross-connection broadcast is the `@channel` feature, which is **planned, not live yet** (see [Channels](./channels.md)). Until it ships, the common patterns are to write shared match state to [the database](../database/README.md) and have clients read it, or to keep a match to a single authoritative box. The per-connection pieces above (input, state, and presence via `@connect` / `@disconnect`) work today.\n\nFor **presence** (\"who is online\"), `@connect` and `@disconnect` are your join and leave signals: bump a counter or write a row when a player connects, and undo it when they drop.\n\n## The `main.stream.ts` file (a separate tier)\n\nStreams live in their **own entry file**, `server/main.stream.ts`, separate from the request entry `server/main.ts`. Importing your `@stream` classes there pulls them into a **separate compiled artifact**, `build/server/release-stream.wasm`.\n\n```ts\n// server/main.stream.ts\nimport { revertOnError } from 'toiljs/server/runtime/abort/abort';\n\nimport './streams/Echo'; // add each @stream module here as you grow\nimport './streams/Chat';\n\n// Re-export the WASM entry points the host binds, exactly like main.ts.\nexport * from 'toiljs/server/runtime/exports';\nexport function abort(message: string, fileName: string, line: u32, column: u32): void {\n revertOnError(message, fileName, line, column);\n}\n```\n\nWhy a separate file? A stream box and a request handler are **different kinds of program** that run on different parts of the edge (see [Compute tiers](../concepts/tiers.md)). toiljs compiles each into its own `.wasm`:\n\n```sh\n$ toiljs build\n$ ls build/server/*.wasm\nbuild/server/release.wasm # L1 request (@rest / @service)\nbuild/server/release-stream.wasm # L2/L3 stream (@stream)\nbuild/server/release-cold.wasm # L4 daemon (@daemon)\n```\n\nYou do not run this by hand. `toiljs build` produces `release-stream.wasm` automatically whenever your project has a `@stream` surface, and shared helper code and `@data` types are compiled into every artifact.\n\n> **One file cannot be both a stream and an RPC surface.** A single source file may not declare both a `@stream` and a `@service` / `@remote` ([RPC](../backend/rpc.md)), because one compiled artifact cannot be two tiers at once. Keep them in separate files: `@stream` in `main.stream.ts`, `@rest` / `@service` in `main.ts`. They coexist happily side by side in the same project, just not in the same file.\n\n## Configuration\n\nThe config-object form lets you tune a stream:\n\n```ts\n@stream({\n scope: StreamScope.Regional, // where the box runs (see below)\n message: ChatMsg, // decode inbound frames into this @data type\n maxFrameBytes: 65536, // reject frames larger than this\n ingressRingBytes: 262144 // size of the inbound buffer\n})\nclass Chat { /* ... */ }\n```\n\n- **`scope`** picks how close to the user the box runs. `StreamScope.Regional` (the default) runs it at a regional node; `StreamScope.Continental` runs it at a wider continental node. See [Compute tiers](../concepts/tiers.md) for what L2 and L3 mean.\n- **`message`** is the typed-message shortcut described above.\n- **`maxFrameBytes`** and **`ingressRingBytes`** cap frame size and buffer size to protect the box from oversized or flooding input.\n\n## Reaching a stream from the browser\n\nEvery `@stream` class gets a generated, typed client at `Server.Stream.<ClassName>`, wired up for you in `shared/server.ts` (the same place the [RPC](../backend/rpc.md) client lands). Call `connect()` to open the connection:\n\n```ts\nimport '../shared/server'; // attaches globalThis.Server (browser-only)\n\nconst chat = await Server.Stream.Echo.connect();\nchat.onMessage((bytes) => { /* a reply frame, always raw bytes */ });\nchat.send(new TextEncoder().encode('hello'));\nchat.onClose((code) => { /* the connection ended */ });\nchat.close();\n```\n\nThe client is keyed by the **class name** (`Server.Stream.Echo`) and connects to the class's **mount route** (`/echo`). Inbound replies are always raw bytes. The full client walkthrough, plus the lower-level `useChannel` hook, is in [Channels](./channels.md).\n\n## How placement works (you do not manage it)\n\nOn the production edge, your box lives in **one place** for the connection's whole life. Every message from that connection is routed to the exact instance holding your box, so its in-memory state is always there. You never configure this; the edge does it automatically. In `toiljs dev` there is only one process, so this is a non-issue.\n\n## Why this scales\n\nA `@stream` box is deliberately cheap to run at scale, and the edge is built to spread a very large number of them across many machines and cities. Three design choices do the heavy lifting:\n\n- **Connections spread across the fleet in parallel.** Each connection is handled on its own, with no shared lock in the middle for connections to fight over. There is no central bottleneck every connection has to pass through, so adding machines adds capacity in a straight line.\n- **The connection survives network changes.** If a client's network changes (Wi-Fi to cellular, or a NAT rebind), the edge keeps the connection attached to the same box. The user keeps their session and their in-memory state; the box is never orphaned.\n- **Each box is isolated and bounded.** Every connection gets its own sandboxed box with its own linear memory. One tenant's boxes are capped to a slice of the node's RAM (a noisy-neighbor guard), and a single box is hard-capped (64 MiB) so a runaway connection can only fill itself, then it traps. Each lifecycle hook also runs under a hard compute cap, so a hook that loops forever is stopped instead of hogging the machine.\n\nPut together: connections spread across the fleet, run in parallel, the session sticks to its box even when the network moves, and each box is walled off from the others. That is what lets one deployment hold a very large number of live connections at once. How large depends on your hardware and message sizes; these docs do not publish a benchmarked number.\n\n> **The wider picture.** For how the edge spreads connections across the fleet and the mesh adds up to world-wide fan-out, see [Built for massive fan-out and world-wide sync](./README.md#built-for-massive-fan-out-and-world-wide-sync) in the overview.\n\n## Gotchas\n\n- **Box fields are per-connection only.** They reset on reconnect and are never shared between users. Persist anything durable to [the database](../database/README.md).\n- **Frames are bytes.** Encode and decode text yourself, or use a typed `message` so toiljs does it.\n- **Copy `packet.bytes()` if you keep it.** The inbound buffer is reused after the hook returns, so store a copy if you need the bytes later.\n- **A file cannot mix `@stream` with `@service` / `@remote`.** Keep streams in `main.stream.ts`.\n- **`@channel` is not live yet.** A stream that declares a `@channel` hook is rejected by the edge today. Broadcasting to many subscribers is a planned feature; see [Channels](./channels.md).\n\n## Related\n\n- [Realtime overview](./README.md): the big picture and when to reach for realtime.\n- [Channels](./channels.md): the client `useChannel` hook and a chat-style example.\n- [Compute tiers (L1 to L4)](../concepts/tiers.md): where the stream artifact runs.\n- [Data types (`@data`)](../backend/data.md): typed messages.\n- [The database (ToilDB)](../database/README.md): where to keep state that outlives a connection.\n",