@substrat-run/adapter-cloudflare 0.2.0 → 0.3.0

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 (2) hide show
  1. package/README.md +91 -0
  2. package/package.json +4 -4
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @substrat-run/adapter-cloudflare
2
+
3
+ The **Durable-Object scope host** for [Substrat](https://github.com/substrat-run/substrat) —
4
+ the production backing for the same scope-host contract the
5
+ [pure-SQLite adapter](https://npmjs.com/package/@substrat-run/adapter-sqlite) implements.
6
+
7
+ One SQLite-backed Durable Object per scope, a durable control-plane DO for the directory,
8
+ and a stateless coordinator that mints capability stubs. Both adapters pass the **same**
9
+ [conformance suite](https://npmjs.com/package/@substrat-run/contract-tests) unchanged
10
+ (decision 14), so a vertical developed and CI-tested on pure SQLite runs on Cloudflare with
11
+ no code change.
12
+
13
+ **Full documentation: https://substrat.ahlstrand.es/reference/adapter-cloudflare**
14
+
15
+ ```sh
16
+ pnpm add @substrat-run/adapter-cloudflare
17
+ ```
18
+
19
+ ## The pieces
20
+
21
+ - **`ScopeDO`** — one scope = one SQLite-backed Durable Object. `defineScopeDO([…modules])`
22
+ bundles the kernel, engines, and the vertical's modules into the DO at build time (a DO
23
+ cannot receive handler closures over RPC). Each operation runs inside
24
+ `ctx.storage.transaction(async …)` — the DO analogue of `BEGIN IMMEDIATE … COMMIT`, which
25
+ rolls back on a throw *across `await`s* — with strict per-scope serialization, lazy
26
+ migrations on wake, manifest guards, and the outbox→consumer dispatch loop.
27
+ - **`ControlPlaneDO`** — the durable directory: tenants, scope lifecycle, roles,
28
+ entitlements, identities, tenant-level tuples, and the append-only admin audit log.
29
+ - **`CloudflareScopeHost`** — the coordinator. Stateless (rebuilt per request); it validates
30
+ and gates against the control plane, then mints a `ScopeStub` that RPCs `invoke` into the
31
+ scope's DO. Implements the same `ScopeHost` contract as the pure adapter.
32
+
33
+ ## Usage (a Worker)
34
+
35
+ ```ts
36
+ import { defineScopeDO, ControlPlaneDO, CloudflareScopeHost } from '@substrat-run/adapter-cloudflare';
37
+ import { workorderModule } from '@substrat-run/engine-workorder';
38
+
39
+ export const ScopeDO = defineScopeDO([workorderModule /*, …engines, vertical */]);
40
+ export { ControlPlaneDO };
41
+
42
+ export default {
43
+ async fetch(req, env) {
44
+ const host = new CloudflareScopeHost({ scope: env.SCOPE, controlPlane: env.CONTROL_PLANE });
45
+ // authenticate → getScope → invoke
46
+ const stub = await host.getScope(principal, tenantId, scopeId);
47
+ return Response.json(await stub.invoke('workorder/list', {}));
48
+ },
49
+ };
50
+ ```
51
+
52
+ `wrangler.jsonc` binds `SCOPE` + `CONTROL_PLANE` as SQLite-backed Durable Objects
53
+ (`new_sqlite_classes`). Run it on real `workerd` with `wrangler dev` (no account needed);
54
+ deploy with `wrangler deploy` (DO SQLite needs a Workers Paid plan).
55
+
56
+ A demo vertical — [ServiceCo](https://github.com/substrat-run/substrat/tree/main/demos/fsm) —
57
+ runs deployed on it today, behind Better Auth.
58
+
59
+ ## How the semantics map
60
+
61
+ | Contract guarantee | Implementation here |
62
+ |---|---|
63
+ | strict serialization per scope (K-6) | per-DO operation queue — the DO input gate over-delivers, so serialization is enforced explicitly |
64
+ | scope storage isolation | one SQLite-backed DO per scope |
65
+ | transactional operation + rollback (K-4) | `ctx.storage.transaction(async …)` — commits on success, rolls back on a throw across `await`s |
66
+ | structured-clone boundary | the coordinator→DO RPC boundary itself |
67
+ | fail-closed addressing + lifecycle gates | validated in the `ControlPlaneDO` before the stub is minted |
68
+ | permission checks | tuple checker — scope tuples local to the DO, tenant tuples + roles via the control plane |
69
+
70
+ ## Related packages
71
+
72
+ - [`@substrat-run/kernel`](https://npmjs.com/package/@substrat-run/kernel) — the scope-host
73
+ contract this implements
74
+ - [`@substrat-run/adapter-sqlite`](https://npmjs.com/package/@substrat-run/adapter-sqlite) —
75
+ the pure-SQLite adapter for local dev, CI, and self-host
76
+ - [`@substrat-run/contract-tests`](https://npmjs.com/package/@substrat-run/contract-tests) —
77
+ the conformance suite both adapters pass unchanged
78
+
79
+ ## Status
80
+
81
+ Milestone 1: the shared contract suites run **green in workerd against real Durable
82
+ Objects** (one runtime-late-registration test is skipped — a deployed DO bundle is
83
+ code-time), the control plane is durable, and a vertical is deployed. Deferred, honestly:
84
+
85
+ - the directory is a single control-plane DO; the tenant-root-DO + global-D1 split is a
86
+ later scaling/blast-radius refinement;
87
+ - per-jurisdiction DO ids (K-7) and the `hostname → (tenant, scope, vertical)` router /
88
+ Workers-for-Platforms multi-vertical dispatch are not built yet;
89
+ - Shape B (DO control plane fronting per-tenant D1) is not built.
90
+
91
+ Pre-release (0.x): surfaces change without notice until the first vertical ships.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@substrat-run/adapter-cloudflare",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Cloudflare Durable-Object scope host (D-14): DO-per-scope, real kernel semantics on Workers",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@substrat-run/kernel": "^0.2.0",
26
- "@substrat-run/contracts": "^0.2.0"
25
+ "@substrat-run/kernel": "^0.3.0",
26
+ "@substrat-run/contracts": "^0.3.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@cloudflare/vitest-pool-workers": "^0.8.19",
@@ -31,7 +31,7 @@
31
31
  "wrangler": "^4.110.0",
32
32
  "typescript": "^5.6.0",
33
33
  "vitest": "^3.0.0",
34
- "@substrat-run/contract-tests": "^0.2.0"
34
+ "@substrat-run/contract-tests": "^0.3.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"