toiljs 0.0.85 → 0.0.87

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.
Files changed (132) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +2 -2
  3. package/build/cli/.tsbuildinfo +1 -1
  4. package/build/cli/index.js +303 -293
  5. package/build/compiler/.tsbuildinfo +1 -1
  6. package/build/compiler/config.d.ts +2 -0
  7. package/build/compiler/config.js +1 -0
  8. package/build/compiler/docs.js +8 -24
  9. package/build/compiler/generate.js +4 -2
  10. package/build/compiler/index.d.ts +1 -1
  11. package/build/compiler/index.js +69 -6
  12. package/build/compiler/toil-docs.generated.js +64 -22
  13. package/build/devserver/.tsbuildinfo +1 -1
  14. package/build/devserver/analytics/index.js +7 -3
  15. package/build/devserver/db/database.d.ts +4 -0
  16. package/build/devserver/db/database.js +43 -1
  17. package/build/devserver/db/index.d.ts +1 -1
  18. package/build/devserver/db/index.js +1 -1
  19. package/build/devserver/db/types.d.ts +3 -0
  20. package/build/devserver/db/types.js +2 -0
  21. package/build/devserver/runtime/module.js +4 -1
  22. package/docs/README.md +104 -65
  23. package/docs/auth/README.md +102 -0
  24. package/docs/auth/configuration.md +94 -0
  25. package/docs/auth/extending.md +202 -0
  26. package/docs/auth/how-it-works.md +138 -0
  27. package/docs/auth/usage.md +188 -0
  28. package/docs/backend/README.md +143 -0
  29. package/docs/backend/data.md +351 -0
  30. package/docs/backend/rest.md +402 -0
  31. package/docs/backend/rpc.md +226 -0
  32. package/docs/background/README.md +114 -0
  33. package/docs/background/daemons.md +230 -0
  34. package/docs/background/derive.md +179 -0
  35. package/docs/cli/README.md +377 -0
  36. package/docs/concepts/config.md +416 -0
  37. package/docs/concepts/decorators.md +127 -0
  38. package/docs/concepts/security.md +108 -0
  39. package/docs/concepts/tiers.md +166 -0
  40. package/docs/concepts/types.md +216 -0
  41. package/docs/database/README.md +143 -0
  42. package/docs/database/capacity.md +350 -0
  43. package/docs/database/counters.md +174 -0
  44. package/docs/database/documents.md +255 -0
  45. package/docs/database/events.md +307 -0
  46. package/docs/database/membership.md +246 -0
  47. package/docs/database/setup.md +155 -0
  48. package/docs/database/unique.md +216 -0
  49. package/docs/database/views.md +246 -0
  50. package/docs/frontend/README.md +101 -0
  51. package/docs/frontend/data-fetching.md +243 -0
  52. package/docs/frontend/images.md +148 -0
  53. package/docs/frontend/metadata.md +344 -0
  54. package/docs/frontend/rendering.md +236 -0
  55. package/docs/frontend/routing.md +344 -0
  56. package/docs/frontend/scripts.md +118 -0
  57. package/docs/frontend/search.md +191 -0
  58. package/docs/frontend/styling.md +147 -0
  59. package/docs/getting-started/README.md +81 -0
  60. package/docs/getting-started/create-project.md +131 -0
  61. package/docs/getting-started/deploy.md +101 -0
  62. package/docs/getting-started/first-app.md +215 -0
  63. package/docs/getting-started/installation.md +106 -0
  64. package/docs/getting-started/migrating.md +125 -0
  65. package/docs/getting-started/project-structure.md +163 -0
  66. package/docs/introduction/README.md +41 -0
  67. package/docs/introduction/design-principles.md +55 -0
  68. package/docs/introduction/distributed.md +74 -0
  69. package/docs/introduction/how-it-works.md +74 -0
  70. package/docs/introduction/hyperscale.md +51 -0
  71. package/docs/introduction/modern-stack.md +36 -0
  72. package/docs/introduction/vs-other-frameworks.md +48 -0
  73. package/docs/introduction/why-toil.md +93 -0
  74. package/docs/llms.txt +90 -0
  75. package/docs/realtime/README.md +102 -0
  76. package/docs/realtime/channels.md +211 -0
  77. package/docs/realtime/streams.md +369 -0
  78. package/docs/services/README.md +60 -0
  79. package/docs/services/analytics.md +268 -0
  80. package/docs/services/caching.md +175 -0
  81. package/docs/services/cookies.md +235 -0
  82. package/docs/services/crypto.md +209 -0
  83. package/docs/services/email.md +289 -0
  84. package/docs/services/environment.md +141 -0
  85. package/docs/services/ratelimit.md +174 -0
  86. package/docs/services/time.md +85 -0
  87. package/examples/basic/client/routes/analytics.tsx +13 -12
  88. package/examples/basic/server/models/SiteAnalytics.ts +29 -17
  89. package/examples/basic/server/routes/Analytics.ts +15 -17
  90. package/examples/basic/server/routes/UserId.ts +22 -0
  91. package/package.json +15 -11
  92. package/scripts/gen-toil-docs.mjs +24 -35
  93. package/server/auth/AuthController.ts +336 -0
  94. package/server/auth/AuthUser.ts +23 -0
  95. package/server/auth/index.ts +16 -0
  96. package/server/globals/auth.ts +31 -0
  97. package/server/globals/userid.ts +107 -0
  98. package/src/compiler/config.ts +13 -0
  99. package/src/compiler/docs.ts +16 -33
  100. package/src/compiler/generate.ts +6 -2
  101. package/src/compiler/index.ts +114 -6
  102. package/src/compiler/toil-docs.generated.ts +64 -22
  103. package/src/devserver/analytics/index.ts +10 -4
  104. package/src/devserver/db/database.ts +67 -1
  105. package/src/devserver/db/index.ts +1 -0
  106. package/src/devserver/db/types.ts +13 -0
  107. package/src/devserver/runtime/module.ts +7 -0
  108. package/test/analytics-dev.test.ts +2 -1
  109. package/test/devserver-database.test.ts +113 -0
  110. package/docs/auth-todo.md +0 -149
  111. package/docs/auth.md +0 -322
  112. package/docs/caching.md +0 -115
  113. package/docs/cli.md +0 -17
  114. package/docs/client.md +0 -39
  115. package/docs/cookies.md +0 -457
  116. package/docs/crypto.md +0 -130
  117. package/docs/daemon.md +0 -123
  118. package/docs/data.md +0 -131
  119. package/docs/derive.md +0 -159
  120. package/docs/email.md +0 -326
  121. package/docs/environment.md +0 -97
  122. package/docs/getting-started.md +0 -128
  123. package/docs/index.md +0 -30
  124. package/docs/ratelimit.md +0 -95
  125. package/docs/routing.md +0 -259
  126. package/docs/rpc.md +0 -149
  127. package/docs/server.md +0 -61
  128. package/docs/ssr.md +0 -632
  129. package/docs/streams.md +0 -178
  130. package/docs/styling.md +0 -22
  131. package/docs/tiers.md +0 -133
  132. package/docs/time.md +0 -43
@@ -0,0 +1,163 @@
1
+ # Project structure
2
+
3
+ A 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?**
4
+
5
+ ## Why this matters
6
+
7
+ toiljs 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.
8
+
9
+ ```mermaid
10
+ flowchart TD
11
+ subgraph Browser["Runs in the browser"]
12
+ C["client/*"]
13
+ end
14
+ subgraph Edge["Runs on the edge (WebAssembly)"]
15
+ S["server/* -> release.wasm"]
16
+ end
17
+ subgraph Generated["Generated glue (types + config)"]
18
+ SH["shared/server.ts"]
19
+ CFG["toil.config.ts / toilconfig.json"]
20
+ end
21
+ C -. "typed calls" .-> SH
22
+ SH -. "HTTP / realtime" .-> S
23
+ S --> DB[("ToilDB")]
24
+ ```
25
+
26
+ ## The top level
27
+
28
+ These files sit in your project root.
29
+
30
+ | File | What it is | Runs where |
31
+ | --- | --- | --- |
32
+ | `package.json` | Scripts (`dev`, `build`, `lint`, `typecheck`, `format`) and dependencies (`toiljs`, `react`, `toilscript`, ...) | tooling only |
33
+ | `toil.config.ts` | **Client and build config.** Uses `defineConfig` to set SEO, images, page transitions, and dev options. | tooling only |
34
+ | `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 |
35
+ | `tsconfig.json` | TypeScript config for the client (`client/`, `shared/`, `emails/`). Extends `toiljs/tsconfig`. | tooling only |
36
+ | `eslint.config.js` | Linting preset (`toiljs/eslint`). | tooling only |
37
+ | `.prettierrc` | Formatting preset (`toiljs/prettier`). | tooling only |
38
+ | `.prettierignore` | Files Prettier should skip (generated files). | tooling only |
39
+ | `.gitignore` | Ignores `build/`, `.toil/`, generated files, and your `.env` files. | tooling only |
40
+ | `.vscode/settings.json` | Tells VS Code to use the project's TypeScript so the toilscript editor plugin loads. | editor only |
41
+ | `toil-env.d.ts` | **Generated** editor types for client globals like `Toil.Link` and `Toil.Image`. Do not edit. | editor only |
42
+ | `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 |
43
+ | `README.md` | Your project's readme. | docs |
44
+ | `CLAUDE.md`, `AGENTS.md`, etc. | Optional AI-assistant hint files that point tools at the toiljs docs. | docs |
45
+
46
+ Two folders you may also see at the root:
47
+
48
+ - **`.toil/`** is a working directory toiljs manages (a build cache and a copy of the docs). It is gitignored. You never edit it.
49
+ - **`.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).
50
+
51
+ ## `client/` (runs in the browser)
52
+
53
+ This is a normal React app. You can use React libraries and browser APIs here freely.
54
+
55
+ ```text
56
+ client/
57
+ toil.tsx the entry point; mounts your app
58
+ layout.tsx the root layout wrapping every page
59
+ 404.tsx the not-found page
60
+ global-error.tsx the top-level error page
61
+ routes/ file-based pages
62
+ components/ shared React components
63
+ styles/ global stylesheets
64
+ public/ static files served as-is
65
+ ```
66
+
67
+ - **`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.
68
+ - **`layout.tsx`** is your root layout: the header, footer, and page shell that wrap every route. Its `children` prop is the current page.
69
+ - **`404.tsx`** renders when no route matches. **`global-error.tsx`** renders when a route throws.
70
+ - **`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).
71
+ - **`components/`** holds React components you reuse across pages. Nothing here is a route.
72
+ - **`styles/`** holds your global CSS (or Sass, Less, or Stylus, if you chose one). See [Styling](../frontend/styling.md).
73
+ - **`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.
74
+
75
+ A 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`.
76
+
77
+ ## `server/` (runs on the edge, as WebAssembly)
78
+
79
+ This 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).
80
+
81
+ ```text
82
+ server/
83
+ main.ts the entry: wires the handler + imports your modules
84
+ tsconfig.json server-only TS config (loads the toilscript editor plugin)
85
+ toil-server-env.d.ts generated editor types for server globals
86
+ core/ your request handler and shared logic
87
+ models/ @data classes
88
+ routes/ @rest controllers (HTTP)
89
+ services/ @service / @remote (typed RPC)
90
+ migrations/ ToilDB schema migrations
91
+ scheduled/ reserved for scheduled tasks
92
+ ```
93
+
94
+ - **`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.
95
+ - **`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.
96
+ - **`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).
97
+ - **`routes/`** holds your `@rest` controllers: classes decorated with `@rest`, `@get`, and `@post` that expose HTTP endpoints. See [HTTP routes](../backend/rest.md).
98
+ - **`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).
99
+ - **`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).
100
+ - **`scheduled/`** is reserved for scheduled tasks. New decorated files anywhere under `server/` are picked up automatically by the build.
101
+ - **`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.
102
+
103
+ ### How the build discovers your server code
104
+
105
+ You 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.
106
+
107
+ ## `shared/` (generated glue)
108
+
109
+ ```text
110
+ shared/
111
+ server.ts GENERATED typed client (do not edit)
112
+ ```
113
+
114
+ **`shared/server.ts` is written for you** by the server build. It contains:
115
+
116
+ - A typed `Server` object the browser uses to call your backend: `Server.REST.*` for HTTP routes and `Server.<service>.*` for RPC.
117
+ - The client-side codecs for every `@data` class, so responses come back as real typed objects.
118
+ - A `getUser()` helper for reading the signed-in user on the client.
119
+
120
+ Because 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.
121
+
122
+ ## `build/` (compiled output)
123
+
124
+ ```text
125
+ build/
126
+ server/release.wasm your compiled backend (+ release.wat, a readable text form)
127
+ client/ the bundled React app (from Vite)
128
+ ```
129
+
130
+ This is what actually ships. It is gitignored and recreated by `toiljs build`. You do not edit anything here.
131
+
132
+ ## Putting it together: one request
133
+
134
+ ```mermaid
135
+ sequenceDiagram
136
+ participant B as Browser (client/)
137
+ participant SH as shared/server.ts
138
+ participant W as server.wasm (server/)
139
+ participant DB as ToilDB
140
+ B->>SH: Server.REST.likes.like()
141
+ SH->>W: HTTP POST /likes
142
+ W->>DB: counter.add(key, 1)
143
+ W-->>SH: typed LikeCount response
144
+ SH-->>B: { count }
145
+ ```
146
+
147
+ The 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.
148
+
149
+ ## Gotchas and notes
150
+
151
+ - **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.
152
+ - **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.
153
+ - **One `@data` type per file** under `models/` keeps things tidy and matches the convention the tooling expects.
154
+ - **`build/` and `.toil/` are disposable.** Delete them and the next build recreates them.
155
+
156
+ ## Related
157
+
158
+ - [Your first app](./first-app.md)
159
+ - [Frontend overview](../frontend/README.md) and [Routing](../frontend/routing.md)
160
+ - [Backend overview](../backend/README.md)
161
+ - [Database overview](../database/README.md)
162
+ - [Configuration](../concepts/config.md)
163
+ - [Decorators reference](../concepts/decorators.md)
@@ -0,0 +1,41 @@
1
+ # Understanding toil
2
+
3
+ This 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."
4
+
5
+ ## The one big idea
6
+
7
+ Almost 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.
8
+
9
+ toil 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.
10
+
11
+ ```mermaid
12
+ flowchart TB
13
+ subgraph Ordinary["The ordinary web"]
14
+ U1["User in Tokyo"] -->|fast| E1["Edge (pretty part)"]
15
+ E1 -->|slow, one region| DB1[("Database in Virginia")]
16
+ end
17
+ subgraph Toil["toil"]
18
+ U2["User in Tokyo"] -->|fast| E2["Edge near Tokyo<br/>(frontend + backend + data)"]
19
+ end
20
+ ```
21
+
22
+ That is the entire pitch. The rest of these pages is how toil pulls it off, and why almost nobody else does.
23
+
24
+ ## Read these in order
25
+
26
+ 1. **[Why toil? Who is it for?](./why-toil.md)** The problem with today's stacks, who benefits most, and the honest cases against.
27
+ 2. **[The modern stack](./modern-stack.md)** The full catalog of modern tech baked in with zero setup.
28
+ 3. **[How toil works](./how-it-works.md)** The whole machine end to end: React client, WebAssembly backend, the edge, ToilDB, the four compute tiers.
29
+ 4. **[What makes toil hyper-scalable](./hyperscale.md)** What "hyper-scale" means, and the mechanisms that let one small program serve the planet.
30
+ 5. **[How toil is distributed](./distributed.md)** The hardest problem in web infrastructure, distributing the writes, and how ToilDB solves it.
31
+ 6. **[toil versus other frameworks](./vs-other-frameworks.md)** An honest comparison with Next.js, Rails, Django, serverless, edge runtimes, and backend-as-a-service.
32
+ 7. **[Why toil is built this way (the RSG bar)](./design-principles.md)** The rubric toil grades itself against.
33
+
34
+ ## The short version
35
+
36
+ - **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).
37
+ - **Why it is fast:** the code runs next to the user, with no slow trip to a central origin. See [Hyper-scale](./hyperscale.md).
38
+ - **Why it is different:** it distributes the writes, not just the reads. See [Distributed](./distributed.md).
39
+ - **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).
40
+
41
+ When you are ready to build, jump to [Getting started](../getting-started/README.md).
@@ -0,0 +1,55 @@
1
+ # Why toil is built this way (the RSG bar)
2
+
3
+ toil is opinionated on purpose. Almost every design decision traces back to one internal rubric it
4
+ holds itself against, and this page shows the rubric and the choice each axis forces.
5
+
6
+ ## The rubric in a paragraph
7
+
8
+ **RSG** (Resilience and Scale Grade) is toil's own internal rubric for how resilient, distributed,
9
+ fast, lean, and secure an app really is, scored as a single letter from AAA down to D. It grades
10
+ nine axes, and the one rule that matters is this: **your grade is your weakest axis**, never the
11
+ average and never the best. To earn AAA, all nine must be AAA at the same time. A globally edged
12
+ frontend on a single-region database is capped by the database. A worldwide system serving a
13
+ one-second app is capped by latency. The lowest column sets the grade, every time. RSG is not an
14
+ external certification and no auditor issues it; it is a design mirror the team holds up to find
15
+ the weakest link and fix it first. The full rubric lives at the repository root in
16
+ [`RSG.md`](../../RSG.md).
17
+
18
+ The reason for the weakest-link rule is the most common lie in this space: calling something
19
+ "global scale" because the read path is global, while the write path is one box in one region.
20
+ Averaging would let that one strong axis hide the weak one. The minimum refuses to.
21
+
22
+ ## The nine axes, and the one choice each one forces
23
+
24
+ Each axis names a way a system can be weak. Each row is the single design decision toil makes so
25
+ that axis cannot be the thing that caps it.
26
+
27
+ | RSG axis | What it grades | The toil design choice that hits it |
28
+ | --- | --- | --- |
29
+ | **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. |
30
+ | **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. |
31
+ | **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). |
32
+ | **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. |
33
+ | **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. |
34
+ | **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. |
35
+ | **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). |
36
+ | **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. |
37
+ | **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. |
38
+
39
+ ## The punchline
40
+
41
+ toil is opinionated because being AAA on **every** axis at once forces these choices. You cannot
42
+ reach the top grade with a fast frontend on a centralized database, or a global system running
43
+ slow code, or a modern stack that only works on the newest browser. The weakest-link rule closes
44
+ every one of those escape hatches. Any framework that lets a single axis slip is, by its own
45
+ honest scoring, not AAA. Holding all nine at once is the whole reason toil looks the way it does.
46
+
47
+ ## Related
48
+
49
+ - [How toil is distributed](./distributed.md): the data-path axis in depth, and why distributing
50
+ writes is the hard one.
51
+ - [What makes toil hyper-scalable](./hyperscale.md): the topology, latency, and program axes in
52
+ practice.
53
+ - [Security](../concepts/security.md): the security axis and its hard caps.
54
+ - [`RSG.md`](../../RSG.md) at the repository root: the full rubric, the internal mirror this page
55
+ summarizes.
@@ -0,0 +1,74 @@
1
+ # How toil is distributed
2
+
3
+ Distributing 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.
4
+
5
+ ## Reads are easy, writes are hard
6
+
7
+ A 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.
8
+
9
+ A 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**.
10
+
11
+ You 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.
12
+
13
+
14
+ ## So almost everyone centralizes the write database
15
+
16
+ Faced 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.
17
+
18
+ It is a reasonable choice, and it hides two costs the [RSG rubric](./design-principles.md) flags on its data-path axis:
19
+
20
+ - **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.
21
+ - **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.
22
+
23
+ The 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.
24
+
25
+ ## ToilDB's answer: every key has a home
26
+
27
+ ToilDB 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.
28
+
29
+ Two things follow, and together they are the whole trick:
30
+
31
+ - **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.
32
+ - **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.
33
+
34
+ ```mermaid
35
+ flowchart TB
36
+ WA["write user:aiko"] --> KA["home near Tokyo<br/>key: user:aiko"]
37
+ WB["write user:bruno"] --> KB["home near Paris<br/>key: user:bruno"]
38
+ WC["write room:general"] --> KC["home near Ohio<br/>key: room:general"]
39
+ KA -.->|"replicate for<br/>local reads"| KB
40
+ KA -.-> KC
41
+ ```
42
+
43
+ Reads 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.
44
+
45
+ Which 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.
46
+
47
+ ## The seven families pick the right consistency tool
48
+
49
+ One "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:
50
+
51
+ | Family | What it gives |
52
+ | --- | --- |
53
+ | [Documents](../database/documents.md) | A record you look up by id |
54
+ | [Counters](../database/counters.md) | Conflict-free tallies: adds from anywhere merge, no lost updates |
55
+ | [Unique](../database/unique.md) | A one-of-a-kind claim (a username); the home picks exactly one winner |
56
+ | [Capacity](../database/capacity.md) | Limited stock (tickets); reserve/confirm/cancel holds prevent overselling |
57
+ | [Events](../database/events.md) | An append-only log in one agreed order |
58
+ | [Membership](../database/membership.md) | Sets of who belongs to what |
59
+ | [View](../database/views.md) | A read-optimized result a background job builds |
60
+
61
+ Distributing writes is not one problem with one answer; each family is the right tool for one shape.
62
+
63
+ ## The hard machinery toil provides so you do not have to
64
+
65
+ The 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.
66
+
67
+ **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.
68
+
69
+ ## Related
70
+
71
+ - [The database (ToilDB)](../database/README.md): families, keys and values, and eventual consistency in depth.
72
+ - [Compute tiers](../concepts/tiers.md): where your code runs, the compute side of the same story.
73
+ - [What makes toil hyper-scalable](./hyperscale.md): the mechanisms that let one small program serve the planet.
74
+ - [Why toil is built this way (the RSG bar)](./design-principles.md): the rubric behind the data-path axis.
@@ -0,0 +1,74 @@
1
+ # How toil works
2
+
3
+ What your project compiles into, and what happens when a user makes a request. Every term is defined as it appears.
4
+
5
+ ## What "build" produces
6
+
7
+ `toiljs build` turns your one TypeScript project into three outputs, because your code has two homes: the browser and the edge.
8
+
9
+ - **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.
10
+ - **`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.
11
+ - **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.
12
+
13
+ **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).)
14
+
15
+ ## The request lifecycle
16
+
17
+ It all happens on the **edge node nearest the user**, with no trip to a central origin.
18
+
19
+ Two terms first:
20
+
21
+ - **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).
22
+ - 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.
23
+
24
+ ```mermaid
25
+ sequenceDiagram
26
+ participant U as User's browser
27
+ participant E as Nearest edge node
28
+ participant W as server.wasm (your handler)
29
+ participant DB as ToilDB (local copy)
30
+
31
+ U->>E: Request (page or API call)
32
+ alt Path is a page/asset it can serve
33
+ E-->>U: Prerendered / SSR page or static file
34
+ else Path is a dynamic API call
35
+ E->>W: Decode bytes, call your handler
36
+ W->>DB: Read / write (a local, nearby copy)
37
+ DB-->>W: Result
38
+ W-->>E: Response
39
+ E-->>U: Response
40
+ end
41
+ ```
42
+
43
+ 1. **The request lands on the closest edge node.** The network routes the user there automatically.
44
+ 2. **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.
45
+ 3. **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.
46
+ 4. **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.
47
+ 5. **Your handler returns a `Response`,** toil encodes it back to bytes, and the edge sends it to the browser.
48
+
49
+ The mental model for your backend: a function of the request. Bytes in, bytes out, one request at a time.
50
+
51
+ ### Stateless by default
52
+
53
+ A 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).
54
+
55
+ ## The pieces
56
+
57
+ Five parts make up a running toil app; you have now met all of them.
58
+
59
+ | Piece | What it is | Where it runs |
60
+ | --- | --- | --- |
61
+ | **React client** | Your frontend UI, the client bundle from the build. | The user's browser |
62
+ | **toilscript backend** | Your TypeScript backend compiled to `server.wasm`. | The edge |
63
+ | **The Dacely edge** | The Rust runtime that terminates the connection, serves pages, and runs your WASM. | Servers in many cities |
64
+ | **ToilDB** | The globally distributed database, replicated next to your code. | The edge |
65
+ | **The four tiers** | Where and for how long a piece of backend code lives. | L1 nearest, up to L4 worldwide |
66
+
67
+ Most 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).
68
+
69
+ ## Related
70
+
71
+ - [Backend overview](../backend/README.md): the request/response model and the sandbox in depth.
72
+ - [The database (ToilDB)](../database/README.md): where persistent, shared state lives.
73
+ - [Compute tiers](../concepts/tiers.md): L1 request, L2/L3 stream, L4 daemon.
74
+ - [What makes toil hyper-scalable](./hyperscale.md): why this design serves the planet cheaply.
@@ -0,0 +1,51 @@
1
+ # What makes toil hyper-scalable
2
+
3
+ **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?
4
+
5
+ Most 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.
6
+
7
+ ## The mechanisms
8
+
9
+ **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.
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ subgraph Origin["Everything funnels to one origin"]
14
+ direction TB
15
+ A1["User (Tokyo)"] -->|slow| O[("Origin + DB<br/>(Virginia)")]
16
+ A2["User (Paris)"] -->|slow| O
17
+ A3["User (Sydney)"] -->|slow| O
18
+ end
19
+ subgraph Toil["Edge + distributed DB scales out"]
20
+ direction TB
21
+ B1["User (Tokyo)"] --> E1["Edge + data (Tokyo)"]
22
+ B2["User (Paris)"] --> E2["Edge + data (Paris)"]
23
+ B3["User (Sydney)"] --> E3["Edge + data (Sydney)"]
24
+ E1 <-.->|"replicate"| E2
25
+ E2 <-.->|"replicate"| E3
26
+ end
27
+ ```
28
+
29
+ The 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.
30
+
31
+ **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.
32
+
33
+ **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)).
34
+
35
+ **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).
36
+
37
+ **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.
38
+
39
+ The 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.
40
+
41
+ ## An honest note on numbers
42
+
43
+ This 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.
44
+
45
+ ## Related
46
+
47
+ - [How toil is distributed](./distributed.md): distributing the writes, the hard problem this rests on.
48
+ - [Why toil is built this way (the RSG bar)](./design-principles.md): the efficiency check behind the hot path.
49
+ - [Compute tiers](../concepts/tiers.md): L1 through L4, and the stateless request model.
50
+ - [How toil works](./how-it-works.md): the build outputs and the request lifecycle.
51
+ - [The database (ToilDB)](../database/README.md): families, homes, and eventual consistency.
@@ -0,0 +1,36 @@
1
+ # The modern stack: what toil gives you that others do not
2
+
3
+ Most 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).
4
+
5
+ ## What is built in
6
+
7
+ | Feature | What it is | Why it matters |
8
+ | --- | --- | --- |
9
+ | 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. |
10
+ | [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). |
11
+ | [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. |
12
+ | 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. |
13
+ | [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. |
14
+ | [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. |
15
+ | [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. |
16
+ | [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). |
17
+ | [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. |
18
+ | [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. |
19
+ | [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. |
20
+ | 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. |
21
+
22
+ ## Built in versus assemble it yourself
23
+
24
+ | Capability | toil (built in, zero setup) | Typical stack (you assemble it) |
25
+ | --- | --- | --- |
26
+ | Edge and transport | Frontend and backend worldwide over HTTP/3 | A separate edge product, often reads-only |
27
+ | Database | ToilDB: global, distributed writes, seven families | A managed database, usually single-region for writes |
28
+ | Auth | Post-quantum login; the server holds no password | A rented identity provider or hand-rolled hashing |
29
+ | Email, rate limiting, analytics | Built-in primitives, one call each | A separate SDK or vendor per capability |
30
+ | Realtime and background | `@stream`, `@daemon`, `@derive` | A realtime service plus a cron server, queue, and leader election |
31
+ | Asset integrity and toolchain | Automatic SRI, ESLint/Prettier/editor plugins, one CLI | Manual or skipped; configured and maintained by you |
32
+ | Critical-path ownership | The core is toil's own | A mix of vendors you cannot inspect or fix |
33
+
34
+ ## Why it is all a default
35
+
36
+ None 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.
@@ -0,0 +1,48 @@
1
+ # toil versus other stacks
2
+
3
+ An 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.
4
+
5
+ We 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).
6
+
7
+ RSG 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.
8
+
9
+ ## Where each stack caps
10
+
11
+ | Stack | Great at | Typical binding axis | Why it caps there |
12
+ | --- | --- | --- | --- |
13
+ | **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 |
14
+ | **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 |
15
+ | **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 |
16
+ | **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) |
17
+ | **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 |
18
+ | **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) |
19
+
20
+ ## One line each
21
+
22
+ - **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.
23
+ - **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.
24
+ - **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.
25
+ - **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.
26
+ - **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.
27
+ - **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**.
28
+
29
+ ## Being honest about toil's own limits
30
+
31
+ RSG grades toil by the same weakest-link rule, and some axes are not handed to you for free:
32
+
33
+ - **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.
34
+ - **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.
35
+ - **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)).
36
+ - **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.
37
+
38
+ ## toil's bet
39
+
40
+ Every 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.
41
+
42
+ Whether that bet fits *your* project is the honest checklist in [Why toil](./why-toil.md).
43
+
44
+ ## Related
45
+
46
+ - [Why toil? Who is it for?](./why-toil.md): the problem toil solves and the honest cases where you should not use it.
47
+ - [How toil is distributed](./distributed.md): the mechanism behind distributed writes (every key's single home region).
48
+ - [Why toil is built this way (the RSG bar)](./design-principles.md): the weakest-link rubric this comparison uses.