toiljs 0.0.102 → 0.0.104
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.
- package/CHANGELOG.md +10 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/toil-docs.generated.js +8 -8
- package/docs/introduction/README.md +37 -21
- package/docs/introduction/design-principles.md +62 -36
- package/docs/introduction/distributed.md +42 -25
- package/docs/introduction/how-it-works.md +85 -38
- package/docs/introduction/hyperscale.md +37 -18
- package/docs/introduction/modern-stack.md +68 -19
- package/docs/introduction/vs-other-frameworks.md +46 -30
- package/docs/introduction/why-toil.md +34 -26
- package/package.json +1 -1
- package/src/compiler/toil-docs.generated.ts +8 -8
|
@@ -1,51 +1,70 @@
|
|
|
1
1
|
# What makes toil hyper-scalable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Hyper-scale means serving very large worldwide traffic at low latency without rebuilding your app as it grows. The test is simple. Traffic climbs from a thousand users to a hundred million across every continent. Do you rewrite the system, or just run more of it?
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Any stack can reach that scale if you spend enough: dedicated infrastructure per app, a rented vendor for each moving part, and an ops team to keep the seams from tearing. toil reaches the same scale from the other direction, cheaply. The whole design aims to make worldwide reach a default, not a budget line. This page is about cost.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Why serving the whole world is normally expensive
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Running near your users usually means paying in three places at once.
|
|
10
|
+
|
|
11
|
+
- **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 rent a lot of mostly-idle hardware.
|
|
12
|
+
- **A far-away origin.** Most stacks serve pages from everywhere but send anything real back to one origin in one region. Every write and every dynamic call pays for a round trip across the planet.
|
|
13
|
+
- **A centralized write database.** Reads scale out. Writes funnel into one primary in one city. That box is both the bottleneck and the thing you overprovision to stay ahead of.
|
|
14
|
+
|
|
15
|
+
These are cost multipliers, and they stack. toil removes all three.
|
|
16
|
+
|
|
17
|
+
## The three things that make toil cheap
|
|
18
|
+
|
|
19
|
+
Three mechanisms do the work, and they reinforce each other.
|
|
20
|
+
|
|
21
|
+
**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. It 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 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.
|
|
22
|
+
|
|
23
|
+
**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, and the 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.
|
|
24
|
+
|
|
25
|
+
**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. Every key has one home region that orders its writes, and the data replicates out for fast local reads. You get nearby reads everywhere without a single primary that every write funnels through, and without paying to overprovision one. The [distributed writes](./distributed.md) page covers the mechanism and its honest trade-off, eventual consistency.
|
|
10
26
|
|
|
11
27
|
```mermaid
|
|
12
28
|
flowchart LR
|
|
13
|
-
subgraph Origin["
|
|
29
|
+
subgraph Origin["The expensive shape: everything funnels to one origin"]
|
|
14
30
|
direction TB
|
|
15
31
|
A1["User (Tokyo)"] -->|slow| O[("Origin + DB<br/>(Virginia)")]
|
|
16
32
|
A2["User (Paris)"] -->|slow| O
|
|
17
33
|
A3["User (Sydney)"] -->|slow| O
|
|
18
34
|
end
|
|
19
|
-
subgraph Toil["
|
|
35
|
+
subgraph Toil["The cheap shape: shared edge + homed writes"]
|
|
20
36
|
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)"]
|
|
37
|
+
B1["User (Tokyo)"] --> E1["Edge box + local data (Tokyo)"]
|
|
38
|
+
B2["User (Paris)"] --> E2["Edge box + local data (Paris)"]
|
|
39
|
+
B3["User (Sydney)"] --> E3["Edge box + local data (Sydney)"]
|
|
24
40
|
E1 <-.->|"replicate"| E2
|
|
25
41
|
E2 <-.->|"replicate"| E3
|
|
26
42
|
end
|
|
27
43
|
```
|
|
28
44
|
|
|
29
|
-
The left side has one hot center every user drags a request to and back from,
|
|
45
|
+
The left side has one hot center every user drags a request to and back from, plus its dedicated fleet. No amount of caching removes that cost. 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.
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
## Why each mechanism lowers the cost
|
|
32
48
|
|
|
33
|
-
|
|
49
|
+
- **Density** splits the cost of an edge presence across many tenants instead of loading it on one app.
|
|
50
|
+
- **Edge locality** means no origin fleet to run and no cross-planet round trip on every real request.
|
|
51
|
+
- **No dedicated infrastructure** means you scale by adding interchangeable shared nodes, not by standing up a new stack per app.
|
|
34
52
|
|
|
35
|
-
|
|
53
|
+
Take any one away and a cost comes back. Without density, running near everyone is a luxury again. Without edge locality, you pay for an origin. With a centralized write path, the database caps you and gets overprovisioned to compensate.
|
|
36
54
|
|
|
37
|
-
|
|
55
|
+
## What is real today
|
|
38
56
|
|
|
39
|
-
|
|
57
|
+
This 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, and it does not repeal the speed of light between continents.
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
Some parts are honestly staged, not already everywhere.
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
- 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).
|
|
62
|
+
- ToilDB's home-region write model and its core logic are real and tested. Live multi-cell deployment, meaning WAN routing and 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.
|
|
44
63
|
|
|
45
64
|
## Related
|
|
46
65
|
|
|
47
66
|
- [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
67
|
- [Compute tiers](../concepts/tiers.md): L1 through L4, and the stateless request model.
|
|
50
68
|
- [How toil works](./how-it-works.md): the build outputs and the request lifecycle.
|
|
51
69
|
- [The database (ToilDB)](../database/README.md): families, homes, and eventual consistency.
|
|
70
|
+
- [Why toil is built this way (the RSG bar)](./design-principles.md): the efficiency check behind the hot path.
|
|
@@ -1,36 +1,85 @@
|
|
|
1
1
|
# The modern stack: what toil gives you that others do not
|
|
2
2
|
|
|
3
|
-
Most frameworks give you a way to write code
|
|
3
|
+
Most frameworks give you a way to write code, then send you shopping. A database, an auth provider, email, a rate limiter, analytics, a realtime service, a job runner. Each one is its own account, its own bill, and its own SDK, and you keep them all in sync.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
toil owns those parts instead. They ship in the framework, they are toil's own code, and they run from the first line with no configuration. Nothing third-party sits on your critical path.
|
|
6
|
+
|
|
7
|
+
The good version is the default version. A solo builder gets the same baseline a funded team would rent from ten vendors, with nothing to assemble or babysit. This page is the full catalog. For how the edge and worldwide distribution work, see [How toil works](./how-it-works.md) and [How toil is distributed](./distributed.md).
|
|
8
|
+
|
|
9
|
+
## Built-in backend features
|
|
10
|
+
|
|
11
|
+
Your TypeScript backend declares what it needs with a decorator or a one-line config flag. toil provides the machinery.
|
|
6
12
|
|
|
7
13
|
| Feature | What it is | Why it matters |
|
|
8
14
|
| --- | --- | --- |
|
|
9
|
-
|
|
|
10
|
-
| [ToilDB](../database/README.md) | A global database with no connection string and seven families
|
|
11
|
-
| [
|
|
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. |
|
|
15
|
+
| [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. |
|
|
16
|
+
| [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). |
|
|
17
|
+
| [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. |
|
|
14
18
|
| [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
|
|
16
|
-
| [Realtime streaming](../realtime/README.md) | A `@stream` class with `@connect
|
|
17
|
-
| [
|
|
18
|
-
| [
|
|
19
|
-
| [
|
|
20
|
-
|
|
|
19
|
+
| [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. |
|
|
20
|
+
| [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. |
|
|
21
|
+
| [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. |
|
|
22
|
+
| [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. |
|
|
23
|
+
| [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. |
|
|
24
|
+
| [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. |
|
|
25
|
+
| [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. |
|
|
26
|
+
|
|
27
|
+
### The seven ToilDB families
|
|
28
|
+
|
|
29
|
+
One database, seven shapes. Each family is tuned to its own access pattern instead of forced out of a single table model.
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
| Family | For |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| [Documents](../database/documents.md) | General structured records. |
|
|
34
|
+
| [Unique](../database/unique.md) | Uniqueness constraints (one email, one handle). |
|
|
35
|
+
| [Counter](../database/counters.md) | High-throughput increments (views, likes, quotas). |
|
|
36
|
+
| [Events](../database/events.md) | Append-only logs and feeds. |
|
|
37
|
+
| [Membership](../database/membership.md) | Set membership and relationships (who is in what). |
|
|
38
|
+
| [Capacity](../database/capacity.md) | Bounded resources and seat/slot allocation. |
|
|
39
|
+
| [View](../database/views.md) | Read models materialized by `@derive`. |
|
|
40
|
+
|
|
41
|
+
## Built-in frontend features
|
|
42
|
+
|
|
43
|
+
The 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.
|
|
44
|
+
|
|
45
|
+
| Feature | What it is | Why it matters |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| [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. |
|
|
48
|
+
| [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. |
|
|
49
|
+
| [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. |
|
|
50
|
+
| [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. |
|
|
51
|
+
| [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. |
|
|
52
|
+
| [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. |
|
|
53
|
+
| [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). |
|
|
54
|
+
| [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. |
|
|
55
|
+
|
|
56
|
+
## toil versus a typical stack
|
|
57
|
+
|
|
58
|
+
Everything above is toil's own code on your critical path. You can inspect it, patch it, and secure it. The table below draws the difference.
|
|
23
59
|
|
|
24
60
|
| Capability | toil (built in, zero setup) | Typical stack (you assemble it) |
|
|
25
61
|
| --- | --- | --- |
|
|
26
|
-
| Edge and transport | Frontend and backend worldwide over HTTP/3 | A separate edge product, often reads-only |
|
|
27
62
|
| Database | ToilDB: global, distributed writes, seven families | A managed database, usually single-region for writes |
|
|
28
63
|
| Auth | Post-quantum login; the server holds no password | A rented identity provider or hand-rolled hashing |
|
|
29
64
|
| 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
|
-
|
|
|
65
|
+
| Realtime and background | `@stream`, `@daemon`, `@scheduled`, `@derive` | A realtime service plus a cron server, queue, and leader election |
|
|
66
|
+
| Sessions, secrets, cookies | Owned globals, no store to provision | A session store and a secrets manager to run |
|
|
67
|
+
| Frontend integrity and SEO | Automatic SHA-384 SRI, Image/LQIP, metadata, page search | Plugins and services bolted on per concern |
|
|
32
68
|
| Critical-path ownership | The core is toil's own | A mix of vendors you cannot inspect or fix |
|
|
33
69
|
|
|
34
|
-
|
|
70
|
+
Owned means the core of a working app is toil's. It does not mean outside services are banned. You can still call a payment provider or any other API.
|
|
71
|
+
|
|
72
|
+
## Honest limits
|
|
73
|
+
|
|
74
|
+
toil grades itself on honesty. Read these before you count on anything.
|
|
75
|
+
|
|
76
|
+
- **Distributed writes are built, live multi-cell is config-gated.** The home-region model and its core logic are real and tested. 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.
|
|
77
|
+
- **Analytics is a dev stub locally.** It is real on the edge. The local dev server returns sample data.
|
|
78
|
+
- **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.
|
|
79
|
+
- **Page search indexes static metadata only.** Routes whose metadata comes from a dynamic `generateMetadata` are not in the index.
|
|
80
|
+
|
|
81
|
+
## Why these ship by default
|
|
82
|
+
|
|
83
|
+
None of this is an upgrade you unlock later. toil grades itself against [RSG](./design-principles.md), the Resilience and Scale Grade. Its one rule: your grade is your weakest axis, never the average. These built-in parts exist so no single axis quietly caps the whole.
|
|
35
84
|
|
|
36
|
-
|
|
85
|
+
The trades are real. ToilDB is not general SQL. The server language is a strict TypeScript subset. The catalog is younger than long-established platforms. Where those trades fit your project, the built-in stack earns its place. [Why toil](./why-toil.md) covers where it does not.
|
|
@@ -1,48 +1,64 @@
|
|
|
1
1
|
# toil versus other stacks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This page compares toil to the stacks you already use, axis by axis. Every tool below is good at what it was built for. The aim is to be concrete about what you gain and what you give up. toil is younger than all of them, and that shows in a few rows.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
toil trades a large, mature ecosystem for one owned framework. That framework distributes writes and ships fast, safe defaults for free. Whether the trade fits your project is the question this page answers.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## The comparison table
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Axis | A typical modern stack | toil |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| 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 |
|
|
12
|
+
| 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) |
|
|
13
|
+
| 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 |
|
|
14
|
+
| 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 |
|
|
15
|
+
| Auth | Bolt on a provider or roll your own | Post-quantum login (ML-DSA + ML-KEM) built in, enabled in about one line |
|
|
16
|
+
| Getting to production | Assemble CDN, cache, regions, hardened auth, and CI yourself | Zero config: the good version is the default version |
|
|
17
|
+
| 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 |
|
|
18
|
+
| 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 |
|
|
19
|
+
| 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 |
|
|
20
|
+
| Integration catalog | Large, mature, well documented | Smaller and younger |
|
|
21
|
+
| Single-region app | Dead simple, nothing to distribute | Distribution you may not need |
|
|
10
22
|
|
|
11
|
-
|
|
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) |
|
|
23
|
+
The top rows lean toil's way. The bottom rows lean the incumbents' way. toil wins the structural axes: one owned stack, distributed writes, a multi-tenant sandbox, end-to-end types, and built-in modern auth. It concedes the ecosystem axes that maturity buys: SQL, the Node universe, and catalog size.
|
|
19
24
|
|
|
20
|
-
##
|
|
25
|
+
## How toil compares to each stack
|
|
21
26
|
|
|
22
|
-
|
|
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**.
|
|
27
|
+
**Next.js / Vercel.** Superb React DX and global edge reads. The ceiling is the write path. Pages cache worldwide, but a write still resolves against one primary region. A comment, an order, a flash-sale click all crawl back to that box. Serverless cold starts add latency and per-invocation cost right when a spike hits. toil keeps a React-first client and moves the write to a home region near the data.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**Rails / Django.** Mature, productive, and batteries included, with a deep hiring pool. If one region suits you, 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 scale, but distributed writes are not in the model.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
**Serverless functions (Lambda, Cloud Functions).** Elastic, stateless compute that scales to zero. It still sits in front of a central database, so a write burst bottlenecks on that store. Each cold invocation bills and lags on its own.
|
|
32
32
|
|
|
33
|
-
|
|
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.
|
|
33
|
+
**Edge runtimes (Workers, Deno Deploy).** The closest in spirit to toil's compute model: your code runs near users. The catch is the database you attach. It is usually single-region, so edge compute becomes a faster front door to the same central write bottleneck.
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
Cloudflare 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, and 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.
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
**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.
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
## What incumbents do better
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
toil does not win every axis. Some gaps are real today:
|
|
42
|
+
|
|
43
|
+
- **Mature ecosystems.** More tutorials, more answered questions, more hosting options, and more people who already know the tool.
|
|
44
|
+
- **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)).
|
|
45
|
+
- **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.
|
|
46
|
+
- **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.
|
|
47
|
+
- **Single-region simplicity.** If one region already fits your users, toil's distribution is effort you do not need.
|
|
48
|
+
|
|
49
|
+
None of these are permanent, and the right tool is the one that fits the job in front of you.
|
|
50
|
+
|
|
51
|
+
## The trade toil makes
|
|
52
|
+
|
|
53
|
+
Most stacks cap in 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 refuses both. It owns the whole stack and distributes the writes, so the structural caps are designed out. The limits left are the ones your own code sets.
|
|
54
|
+
|
|
55
|
+
One caveat, to keep the mechanism honest. The home-region model and its core logic are real and tested. Live multi-region deployment is configuration-gated rather than on by default: the WAN routing and the ScyllaDB backing are opt-in, 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 honest claim is that toil is built to distribute writes worldwide and the mechanism is real. It does not mean every app is already running a live global write cluster today.
|
|
56
|
+
|
|
57
|
+
Whether the trade fits your project is the checklist in [Why toil](./why-toil.md).
|
|
58
|
+
|
|
59
|
+
## Related pages
|
|
45
60
|
|
|
46
61
|
- [Why toil? Who is it for?](./why-toil.md): the problem toil solves and the honest cases where you should not use it.
|
|
62
|
+
- [The modern stack](./modern-stack.md): the full, verified catalog of what is built in.
|
|
47
63
|
- [How toil is distributed](./distributed.md): the mechanism behind distributed writes (every key's single home region).
|
|
48
|
-
- [Why toil is built this way
|
|
64
|
+
- [Why toil is built this way](./design-principles.md): the bar toil grades itself against.
|
|
@@ -1,36 +1,44 @@
|
|
|
1
1
|
# Why toil? Who is it for?
|
|
2
2
|
|
|
3
|
-
toil is a full-stack framework
|
|
3
|
+
toil 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.
|
|
4
4
|
|
|
5
|
-
The thesis
|
|
5
|
+
The thesis is simple. toil is the modern full-stack tooling a developer actually wants: AAA-grade from the first line, and built to scale to the whole planet. Even a simple pizza site gets top-tier infrastructure with zero setup. Distributed writes are one pillar of that. The stack that just works is the core.
|
|
6
6
|
|
|
7
|
-
## The problem with
|
|
7
|
+
## The problem with modern stacks
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Shipping a good app today means fighting your own infrastructure. It fights back in five ways.
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**Reads go global, writes go to one region.** Your pages load fast from caches worldwide. A write does not. A comment, a like, or an order has to crawl back to a single database in a single region. A user in Sydney writing to Virginia pays for the whole round trip. That one region is also a single point of failure for everyone.
|
|
12
12
|
|
|
13
|
-
**
|
|
13
|
+
**You become 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 has its own account, bill, SDK, and outage. Every one on your critical path 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.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**It runs on yesterday's tooling.** The typical stack sits on a decade of 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 web platform moved on. The stack did not.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
**Every layer adds overhead.** Heavy runtimes, slow builds, serverless cold starts, oversized JavaScript in the browser, and a network hop between every service. Each layer adds latency and cost. The user feels it and you debug it.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**Fast and safe are opt-in, never 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 extra work, so most people ship the lesser one.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
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.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
## What toil delivers
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Every design decision in toil serves one goal: AAA-grade infrastructure by default, not something you assemble by hand. One framework instead of ten rented vendors. Zero configuration. No distributed-systems or networking expertise required. It works out of the box, quantum-proof login included.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Four pillars carry it.
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### AAA-grade infrastructure by default
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Top-tier infrastructure on day one, on the smallest project, with zero setup. Your code runs at the edge near users worldwide. A global database is already there. Every shipped asset is tamper-proofed automatically with SHA-384 Subresource Integrity. Login is quantum-proof in about one line.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
That 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. 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.
|
|
34
|
+
|
|
35
|
+
A 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).
|
|
36
|
+
|
|
37
|
+
### Everything built in, and owned
|
|
38
|
+
|
|
39
|
+
Auth, database, email, rate limiting, analytics, realtime streaming, and background jobs are all built in, and all toil's own. Nothing third-party sits on your critical path.
|
|
40
|
+
|
|
41
|
+
Because they are one system, the parts already fit. You are not gluing ten SDKs together and hoping they agree. Full catalog: [The modern stack](./modern-stack.md).
|
|
34
42
|
|
|
35
43
|
```mermaid
|
|
36
44
|
flowchart TB
|
|
@@ -52,19 +60,19 @@ flowchart TB
|
|
|
52
60
|
end
|
|
53
61
|
```
|
|
54
62
|
|
|
55
|
-
Honest boundary:
|
|
63
|
+
Honest 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.
|
|
56
64
|
|
|
57
|
-
###
|
|
65
|
+
### Modern tooling that just works
|
|
58
66
|
|
|
59
|
-
TypeScript end to end, one repo, one deploy, wired by types
|
|
67
|
+
Built on today's web platform, not a decade of 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. The bug surfaces at your desk as a compile error, not in production.
|
|
60
68
|
|
|
61
|
-
The toolchain is set up for you: ESLint, Prettier
|
|
69
|
+
The toolchain is set up for you: ESLint, Prettier with a plugin for toil's decorators, an editor plugin, one CLI, and a `doctor` command that fixes common problems in place. The docs are LLM-friendly too, so an AI assistant reads your current conventions instead of guessing. More in [The modern stack](./modern-stack.md).
|
|
62
70
|
|
|
63
|
-
###
|
|
71
|
+
### Hyper-scale and distributed writes
|
|
64
72
|
|
|
65
|
-
Your backend compiles to a tiny sandboxed **WebAssembly** module
|
|
73
|
+
Your backend compiles to a tiny sandboxed **WebAssembly** module, a compact locked-down binary that runs at near-native speed. That is modern tech, not a heavyweight legacy runtime. One edge box safely runs many apps, so running near everyone stays affordable.
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
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. 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).
|
|
68
76
|
|
|
69
77
|
## Who it is for
|
|
70
78
|
|
|
@@ -73,16 +81,16 @@ And the database, **ToilDB**, distributes the *writes*, not just the reads: ever
|
|
|
73
81
|
- **Global apps:** logic and data near users on every continent.
|
|
74
82
|
- **Realtime apps:** chat, presence, and live cursors on built-in streaming, not a bolted-on vendor.
|
|
75
83
|
|
|
76
|
-
Same install for the smallest project and the largest. You grow into the scale
|
|
84
|
+
Same install for the smallest project and the largest. You grow into the scale. You do not rebuild to reach it.
|
|
77
85
|
|
|
78
86
|
## When not to use toil
|
|
79
87
|
|
|
80
88
|
- **You need SQL or heavy joins.** ToilDB is seven purpose-built families, not a general SQL engine. See the [database overview](../database/README.md).
|
|
81
|
-
- **You lean on the Node ecosystem.** The server is a strict TypeScript subset compiled to WebAssembly
|
|
89
|
+
- **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.
|
|
82
90
|
- **You are happy single-region and simple.** If one region already fits, toil's distribution is effort you do not need.
|
|
83
91
|
- **You need a big integration catalog today.** toil is younger, and that catalog is smaller.
|
|
84
92
|
|
|
85
|
-
None of these are permanent
|
|
93
|
+
None of these are permanent. The right tool is the one that fits the job in front of you.
|
|
86
94
|
|
|
87
95
|
## Related
|
|
88
96
|
|
package/package.json
CHANGED