@jimhoyd/urlcode 0.4.0-alpha.1 → 0.4.0-alpha.3

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 (232) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +36 -22
  2. package/.claude/skills/urlcode-operations/SKILL.md +16 -22
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CONTRIBUTING.md +20 -16
  5. package/README.md +83 -80
  6. package/ROADMAP.md +61 -331
  7. package/SECURITY.md +23 -8
  8. package/dist/BUILD-MANIFEST.json +42 -47
  9. package/dist/agents-guide.js +18 -18
  10. package/dist/build-static.js +136 -0
  11. package/dist/capabilities.js +85 -31
  12. package/dist/capability-query.js +0 -1
  13. package/dist/cli.js +24 -32
  14. package/dist/compliance-rules/baseline.js +2 -10
  15. package/dist/compliance-rules/privacy.js +5 -16
  16. package/dist/compliance-rules/shared.js +0 -2
  17. package/dist/compliance.js +6 -8
  18. package/dist/config.js +15 -8
  19. package/dist/context.js +9 -10
  20. package/dist/examples.js +2 -2
  21. package/dist/explain-cli.js +7 -5
  22. package/dist/explain.js +10 -5
  23. package/dist/extensions.js +61 -2
  24. package/dist/function-sources.js +34 -2
  25. package/dist/function-worker.js +3 -1
  26. package/dist/functions.js +84 -13
  27. package/dist/guest-api.js +29 -3
  28. package/dist/index.js +3 -6
  29. package/dist/manifest.js +11 -7
  30. package/dist/mcp-authoring.js +2 -2
  31. package/dist/mcp.js +1 -1
  32. package/dist/observability.js +1 -21
  33. package/dist/policies/cache.js +8 -3
  34. package/dist/policies.js +3 -1
  35. package/dist/policy.js +28 -9
  36. package/dist/prerender.js +4 -0
  37. package/dist/project-tests.js +3 -3
  38. package/dist/readiness.js +35 -9
  39. package/dist/route-diff.js +12 -5
  40. package/dist/router.js +5 -7
  41. package/dist/runtime.js +77 -58
  42. package/dist/sandbox.js +48 -0
  43. package/dist/scaffold.js +0 -0
  44. package/dist/scripts/operational-drills.js +12 -54
  45. package/dist/server.js +3 -29
  46. package/dist/tooling.js +1 -1
  47. package/dist/trusted-functions.js +210 -0
  48. package/dist/types/build-static.d.ts +43 -0
  49. package/dist/types/capabilities.d.ts +14 -5
  50. package/dist/types/compliance-rules/shared.d.ts +0 -2
  51. package/dist/types/compliance.d.ts +0 -3
  52. package/dist/types/config.d.ts +2 -1
  53. package/dist/types/context.d.ts +2 -1
  54. package/dist/types/examples.d.ts +1 -1
  55. package/dist/types/explain.d.ts +6 -0
  56. package/dist/types/extensions.d.ts +55 -0
  57. package/dist/types/function-sources.d.ts +4 -0
  58. package/dist/types/functions.d.ts +48 -5
  59. package/dist/types/guest-api.d.ts +1 -0
  60. package/dist/types/index.d.ts +3 -6
  61. package/dist/types/manifest.d.ts +5 -3
  62. package/dist/types/observability.d.ts +1 -14
  63. package/dist/types/project-tests.d.ts +1 -2
  64. package/dist/types/readiness.d.ts +12 -3
  65. package/dist/types/router.d.ts +2 -1
  66. package/dist/types/runtime.d.ts +0 -27
  67. package/dist/types/sandbox.d.ts +12 -0
  68. package/dist/types/scaffold.d.ts +0 -2
  69. package/dist/types/server.d.ts +1 -4
  70. package/dist/types/tooling.d.ts +3 -3
  71. package/dist/types/trusted-functions.d.ts +29 -0
  72. package/dist/types/types.d.ts +22 -8
  73. package/dist/types/verify-deployment.d.ts +2 -2
  74. package/dist/types.js +25 -5
  75. package/dist/typescript-authoring.js +67 -18
  76. package/dist/verify-deployment.js +3 -3
  77. package/docs/AI-AUTHORING.md +109 -10
  78. package/docs/ASSETS.md +2 -1
  79. package/docs/AUTH-BACKUP.md +32 -0
  80. package/docs/AWS.md +4 -4
  81. package/docs/BEST-PRACTICES.md +17 -8
  82. package/docs/CAPABILITIES.md +30 -17
  83. package/docs/CAPACITY.md +128 -31
  84. package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
  85. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
  86. package/docs/CI.md +8 -3
  87. package/docs/CLOUDFLARE.md +1 -2
  88. package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
  89. package/docs/COMPLIANCE.md +6 -9
  90. package/docs/DEPLOYMENT-CHECKS.md +1 -1
  91. package/docs/DEVELOPMENT-PIPELINE.md +181 -0
  92. package/docs/EXTENSIONS.md +193 -11
  93. package/docs/FRAMEWORK.md +53 -33
  94. package/docs/FUNCTION-SECURITY.md +173 -32
  95. package/docs/INSTALL.md +0 -5
  96. package/docs/LOAD-TESTING.md +4 -4
  97. package/docs/LOCAL-DEVELOPMENT.md +3 -0
  98. package/docs/MIDDLEWARE.md +27 -16
  99. package/docs/MONITORING.md +2 -19
  100. package/docs/OBSERVABILITY.md +7 -16
  101. package/docs/OPEN-DECISIONS.md +184 -0
  102. package/docs/OPERATIONAL-PROOF.md +26 -30
  103. package/docs/OPERATIONS.md +23 -32
  104. package/docs/POLICIES.md +27 -8
  105. package/docs/PRERENDER.md +25 -13
  106. package/docs/PROJECT-DIRECTION.md +35 -10
  107. package/docs/READINESS.md +6 -2
  108. package/docs/README.md +20 -41
  109. package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
  110. package/docs/RELEASE-READINESS.md +50 -30
  111. package/docs/RELEASE-SECURITY.md +72 -86
  112. package/docs/RESILIENCE.md +16 -15
  113. package/docs/ROUTING.md +7 -10
  114. package/docs/SANDBOX-REVIEW.md +19 -6
  115. package/docs/SCAFFOLDING.md +0 -2
  116. package/docs/SECURITY-AUDIT.md +42 -2
  117. package/docs/SPECIFICATION.md +63 -27
  118. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +288 -0
  119. package/docs/SPIKE-BUSINESS-SUITE.md +16 -0
  120. package/docs/SPIKE-CORE-LAYERING.md +368 -0
  121. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +211 -0
  122. package/docs/SPIKE-LAMBDA-COMPILE.md +12 -2
  123. package/docs/STARTERS.md +1 -1
  124. package/docs/STATIC.md +105 -0
  125. package/docs/TOOLING.md +17 -12
  126. package/docs/TUNNELS.md +0 -3
  127. package/docs/TYPESCRIPT-AUTHORING.md +32 -12
  128. package/docs/TYPESCRIPT.md +25 -4
  129. package/docs/VERCEL.md +4 -5
  130. package/docs/VERSION-ALIGNMENT.md +46 -0
  131. package/docs/YAML-GUIDE.md +0 -3
  132. package/docs/YAML-REFERENCE.md +16 -6
  133. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
  134. package/docs/{MANAGEMENT-SECURITY.md → archive/2026-09-19/MANAGEMENT-SECURITY.md} +20 -0
  135. package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +26 -8
  136. package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +274 -43
  137. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
  138. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
  139. package/docs/archive/2026-09-19/ROADMAP.md +386 -0
  140. package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +12 -2
  141. package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +28 -12
  142. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
  143. package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +18 -2
  144. package/docs/archive/README.md +27 -0
  145. package/docs/policies/agents.md +1 -1
  146. package/docs/policies/cache.md +13 -0
  147. package/docs/policies/compression.md +3 -2
  148. package/docs/policies/security.md +3 -2
  149. package/examples/assets/example.yaml +2 -2
  150. package/examples/aws/example.yaml +1 -1
  151. package/examples/cloudflare/example.yaml +1 -1
  152. package/examples/conditions/example.yaml +1 -1
  153. package/examples/cookbook/example.yaml +1 -1
  154. package/examples/cookbook/middleware/bucket.mjs +12 -2
  155. package/examples/cookbook/middleware/locale.mjs +7 -3
  156. package/examples/cookbook/route-index.json +1 -1
  157. package/examples/cookbook/routes/middleware.yaml +1 -1
  158. package/examples/egress/example.yaml +1 -1
  159. package/examples/extensions/example.yaml +1 -1
  160. package/examples/prerender/README.md +14 -6
  161. package/examples/prerender/example.yaml +1 -1
  162. package/examples/prerender/functions/page.mjs +4 -2
  163. package/examples/prerender/middleware/template.mjs +1 -1
  164. package/examples/prerender/prerender.mjs +1 -1
  165. package/examples/prerender/urlcode.yaml +8 -4
  166. package/examples/provider-conformance/example.yaml +1 -1
  167. package/examples/vercel/example.yaml +1 -1
  168. package/llms-full.txt +686 -239
  169. package/llms.txt +27 -15
  170. package/package.json +32 -5
  171. package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
  172. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +36 -22
  173. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +16 -22
  174. package/recipes/authenticated-json-api/README.md +4 -3
  175. package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
  176. package/recipes/authenticated-json-api/recipe.yaml +2 -2
  177. package/recipes/contact-form/functions/contact.mjs +2 -1
  178. package/recipes/contact-form/recipe.yaml +3 -3
  179. package/recipes/cors-api/README.md +2 -2
  180. package/recipes/cors-api/recipe.yaml +2 -2
  181. package/recipes/health-page/README.md +1 -1
  182. package/recipes/health-page/recipe.yaml +1 -1
  183. package/recipes/json-api/README.md +1 -1
  184. package/recipes/json-api/recipe.yaml +4 -4
  185. package/recipes/middleware/README.md +8 -4
  186. package/recipes/middleware/middleware/bucket.mjs +12 -2
  187. package/recipes/middleware/middleware/locale.mjs +7 -3
  188. package/recipes/middleware/recipe.yaml +1 -1
  189. package/recipes/protected-download/README.md +1 -1
  190. package/recipes/protected-download/recipe.yaml +2 -2
  191. package/recipes/redirect/recipe.yaml +1 -1
  192. package/recipes/static-plus-api/README.md +2 -2
  193. package/recipes/static-plus-api/public/index.html +1 -1
  194. package/recipes/static-plus-api/recipe.yaml +2 -2
  195. package/recipes/static-plus-api/urlcode.yaml +1 -1
  196. package/recipes/typescript/README.md +2 -1
  197. package/recipes/typescript/recipe.yaml +5 -5
  198. package/recipes/webhook-receiver/README.md +5 -1
  199. package/recipes/webhook-receiver/recipe.yaml +2 -1
  200. package/recipes/webhook-receiver/urlcode.yaml +7 -0
  201. package/schemas/recipe.schema.json +4 -3
  202. package/schemas/urlcode.schema.json +14 -41
  203. package/skills/urlcode/SKILL.md +32 -11
  204. package/starters/default/AGENTS.md +18 -18
  205. package/starters/default/urlcode.yaml +0 -1
  206. package/dist/link-api.js +0 -136
  207. package/dist/link-cli.js +0 -141
  208. package/dist/link-events.js +0 -76
  209. package/dist/link-records.js +0 -31
  210. package/dist/link-store-worker.js +0 -150
  211. package/dist/link-store.js +0 -250
  212. package/dist/management-policy.js +0 -40
  213. package/dist/sqlite-version.js +0 -6
  214. package/dist/types/link-api.d.ts +0 -30
  215. package/dist/types/link-cli.d.ts +0 -37
  216. package/dist/types/link-events.d.ts +0 -27
  217. package/dist/types/link-records.d.ts +0 -11
  218. package/dist/types/link-store-worker.d.ts +0 -1
  219. package/dist/types/link-store.d.ts +0 -130
  220. package/dist/types/management-policy.d.ts +0 -8
  221. package/dist/types/sqlite-version.d.ts +0 -1
  222. package/docs/DYNAMIC-LINKS.md +0 -61
  223. package/docs/links/cli.md +0 -110
  224. package/docs/links/limits.md +0 -175
  225. package/docs/links/management-api.md +0 -80
  226. package/docs/links/pools.md +0 -75
  227. package/docs/links/setup.md +0 -135
  228. package/docs/yaml/links.md +0 -30
  229. package/examples/live-links/README.md +0 -11
  230. package/examples/live-links/example.yaml +0 -21
  231. package/examples/live-links/tests/requests.json +0 -6
  232. package/examples/live-links/urlcode.yaml +0 -16
package/docs/STATIC.md ADDED
@@ -0,0 +1,105 @@
1
+ # Deploying to static hosting (S3 + CloudFront)
2
+
3
+ The `static` target is the bottom rung of the [capability
4
+ ladder](FRAMEWORK.md#the-ladder):
5
+ the same `urlcode.yaml` a self-hosted server or a serverless adapter runs, with
6
+ no server process at all. `urlcode build --target static` compiles a project
7
+ ahead of time into plain files and two small JSON manifests meant for an S3
8
+ bucket served through CloudFront — nothing here executes a request.
9
+
10
+ ```sh
11
+ urlcode build --target static --project . --out dist --origin https://links.example
12
+ ```
13
+
14
+ ## Declarative routes only, and less than that
15
+
16
+ This target serves **redirects**, **declared responses** (`respond:`) and
17
+ static files (`page`, `static`, `download`) — no path parameters, no query
18
+ passthrough or mapping, both GET and HEAD required, `respond.status: 200` only, and no `enabled: false` or `expires`
19
+ (there is no server to answer a disabled or expired route with 404/410, so the
20
+ build refuses one instead of silently serving it forever). Everything else is
21
+ refused **at build time**, with the route pattern and the reason named:
22
+
23
+ | Handler / feature | Why it is refused |
24
+ | --- | --- |
25
+ | `function`, `middleware` | no server, so no dynamic or sandboxed execution |
26
+ | `extension`, `policies.extensions` | no server, so no operator extension registry |
27
+ | `proxy`, `signals` | no server, so no bounded or fire-and-forget egress |
28
+ | `conditional`, `match` (`conditions`) | no server, so no request-time condition matching |
29
+ | `parameters`, `request.body` | no server, so no request-time validation |
30
+ | `response.headers` | no server, so no per-request headers; set them as S3 object metadata or a CloudFront response headers policy instead |
31
+ | `env`, `secrets` (`bindings`) | no server, so no per-request binding resolution |
32
+ | every `policies.*` | no server, so no runtime policy enforcement |
33
+ | a redirect with a `{parameter}` in its path | S3's per-object redirect is keyed to one exact object, not a pattern |
34
+ | a redirect with `query.pass`/`query.map` | S3's per-object redirect cannot compute a target per request |
35
+ | a redirect with a `status` other than 301 | S3's per-object website redirect always answers 301 |
36
+ | a route not admitting both GET and HEAD, or admitting other methods | the output cannot enforce a different method set |
37
+ | `respond.status` other than 200 | an uploaded response object cannot preserve a custom HTTP status |
38
+ | a route with `enabled: false` or `expires` | no server to answer a disabled/expired route; remove the route instead |
39
+
40
+ Run `urlcode capabilities --target static` for the full catalog.
41
+
42
+ ## What the build emits
43
+
44
+ `--out` (default `dist/static`) receives:
45
+
46
+ - `objects/<key>` — the actual files to upload, one per served route. A `page`
47
+ or `download` route becomes one file; a `static` mount becomes one file per
48
+ entry in its directory. Object keys match the S3 convention: the route
49
+ pattern with its leading `/` stripped, and `/` itself becomes `index.html`
50
+ (matching a bucket's own "Index document" setting). A redirect route also
51
+ gets a zero-byte placeholder object at its key, so it exists to carry
52
+ metadata.
53
+ - `objects.json` — `{ format, objects: [{ key, contentType, cacheControl?,
54
+ contentDisposition? }] }`. Plain file upload loses this metadata (S3 does not
55
+ reliably infer content type or cache headers from an extension-less key), so
56
+ a deploy step reads this file and sets each object's own `Content-Type`,
57
+ `Cache-Control` and `Content-Disposition` from it, for example with
58
+ `aws s3 cp --content-type ... --cache-control ...` per entry, or an
59
+ equivalent `aws s3 sync` post-processing step.
60
+ - `redirects.json` — `{ format, redirects: [{ key, location, status: 301 }] }`.
61
+ Each entry is applied as the S3 object's
62
+ [website redirect location](https://docs.aws.amazon.com/AmazonS3/latest/userguide/how-to-page-redirect.html)
63
+ metadata (`x-amz-website-redirect-location`), for example
64
+ `aws s3api put-object --website-redirect-location <location> --bucket ... --key <key>`.
65
+ S3 always answers a request for that key with a 301 to `location`
66
+ regardless of the object's own content — this is why a declared `status`
67
+ other than 301 is refused rather than silently downgraded.
68
+ - `manifest.json` — the same project-level semantic manifest every target
69
+ writes (see [tooling](TOOLING.md)).
70
+
71
+ None of these are edited by hand, and `dist/` belongs in `.gitignore`. This
72
+ target ships no runtime import (there is nothing to run), so there is no
73
+ `@jimhoyd/urlcode/static` entry point — the build output is the deployment.
74
+
75
+ ## What this is not: GitHub Pages
76
+
77
+ **This target's claimed support is S3 + CloudFront, not GitHub Pages.**
78
+ GitHub Pages has no per-object redirect metadata and no server-side rewrite
79
+ layer, so a `redirect` route compiled the way this target compiles it (S3
80
+ object metadata) has no equivalent there — the only options are a
81
+ meta-refresh/JavaScript page (visibly a redirect, not an HTTP one: search
82
+ engines, curl and anything that does not run JavaScript sees the source page,
83
+ not a 3xx) or a static 404-page trick, both lower fidelity than what every
84
+ other target in this project does for the same route. Rather than claim a
85
+ portability promise this platform cannot keep, GitHub Pages is out of scope
86
+ for this target. If you need Pages, treat it as a distinct target with its own
87
+ explicit fidelity caveat, not a rename of this one.
88
+
89
+ ## Verification status
90
+
91
+ This target has local build tests only (`test/static.test.ts`): the compiled
92
+ object layout, the redirect manifest, and every refusal above. **It has not
93
+ been deployed to S3 or fronted by CloudFront.** Bucket policy, CloudFront
94
+ caching behavior, TLS/domain setup and the exact `aws s3` invocations above are
95
+ unverified until a real deployment exercises them.
96
+
97
+
98
+ ## Building pages with middleware
99
+
100
+ Use [prerendering](PRERENDER.md) to execute functions and native middleware at
101
+ build time, then export the generated native page routes with this target.
102
+ Trusted Node execution is the build default; `sandbox: true` retains its
103
+ restricted imports and resource limits. Neither mode adds a request-time server
104
+ to the static output. Authentication, request-dependent headers and other
105
+ per-request middleware cannot be baked into a public file safely.
package/docs/TOOLING.md CHANGED
@@ -76,7 +76,7 @@ network. Keys always appear in this order:
76
76
  - `urlcode` (package version) and `schema` (`"1"`).
77
77
  - `project`: entry file, route count, handlers used with counts, extensions
78
78
  declared, policies in effect at the top level and the number of routes each
79
- policy applies to, requested env and secret binding names, `dynamicLinks`,
79
+ policy applies to, requested env and secret binding names,
80
80
  `site` keys, and `files` (include, function and middleware paths). With
81
81
  `--host-file`, `host` counts the operator module's extensions and plugins
82
82
  without activating them.
@@ -113,13 +113,17 @@ checks. Build output remains an explicit separate build API/CLI operation.
113
113
 
114
114
  The package root also exports existing operator-invoked workflow APIs:
115
115
  `buildCloudflare(project, options)` compiles and writes a Cloudflare artifact;
116
- `runProjectTests(project, options)` starts the local runtime, executes request
117
- fixtures and closes it; `scaffoldProject(project, {dryRun})` creates missing
118
- placeholders while preserving existing files; `initProject(destination)` creates
119
- the standard starter; and `addRedirect(project, destination, alias?)` updates
120
- project YAML under the authoring lock. `CloudflareBuildOptions`,
121
- `CloudflareBuildReport`, `ProjectTestOptions`, `ProjectTestResult`, `ScaffoldReport`
122
- and `ScaffoldUnresolved` describe these existing operations.
116
+ `buildStatic(project, options)` compiles redirects and static files into plain
117
+ objects and redirect metadata for S3 + CloudFront (see [static
118
+ hosting](STATIC.md)); `runProjectTests(project, options)` starts the local
119
+ runtime, executes request fixtures and closes it; `scaffoldProject(project,
120
+ {dryRun})` creates missing placeholders while preserving existing files;
121
+ `initProject(destination)` creates the standard starter; and
122
+ `addRedirect(project, destination, alias?)` updates project YAML under the
123
+ authoring lock. `CloudflareBuildOptions`, `CloudflareBuildReport`,
124
+ `StaticBuildOptions`, `StaticBuildReport`, `ProjectTestOptions`,
125
+ `ProjectTestResult`, `ScaffoldReport` and `ScaffoldUnresolved` describe these
126
+ existing operations.
123
127
 
124
128
  These SDK functions have explicit write or execution effects and are available
125
129
  to trusted callers only. Project tests use normal runtime activation, grants and
@@ -133,8 +137,8 @@ assistant file-write, guest-execution, deployment or network authority.
133
137
 
134
138
  `urlcode explain [/route] [--project DIR] [--target T] [--host-file F] [--json]`
135
139
  prints what `explainRoute` returns: one route in detail, or without a path a
136
- one-line-per-route table (methods, handler, state, middleware count, policies,
137
- cache outcome and target support). `--target` narrows the support columns to
140
+ one-line-per-route table (methods, handler, state, execution mode, middleware
141
+ count, policies, cache outcome and target support). `--target` narrows the support columns to
138
142
  one deployment target; `--host-file` supplies the operator registry so
139
143
  extension requirements show their provider. An unknown route exits 1 and names
140
144
  the nearest patterns. Everything comes from the compiled configuration: no
@@ -144,13 +148,14 @@ request is evaluated, no function runs and no binding is read.
144
148
  `schemaVersion`, the `urlcode` version, the entry file and its includes, the
145
149
  `revision` (the same digest `inspectExtensionRevision` returns, so an operator
146
150
  pin can be checked against it), the config `configVersion`, every route (path,
147
- methods, handler, state, middleware, inputs, policy names, extension
151
+ methods, handler, state, execution mode (`sandbox`, with `sandboxReason` when
152
+ the route declares one), middleware, inputs, policy names, extension
148
153
  requirements, cache outcome, binding names, egress origins, capabilities and
149
154
  per-target support), the union of capabilities used, extension declarations
150
155
  (version, configuration keys, mounts and protected routes), recipe provenance
151
156
  (from a `recipe.yaml` beside the entry file when one exists), external
152
157
  requirements (environment and secret names, proxy and signal origins,
153
- extensions, link-store collections, dynamic links), the function and middleware
158
+ extensions), the function and middleware
154
159
  modules with the routes that use them, and per-target compatibility. Without
155
160
  `--json` a short summary prints. The manifest is deterministic: the same
156
161
  project produces the same bytes. `urlcode build` writes the same document as
package/docs/TUNNELS.md CHANGED
@@ -65,9 +65,6 @@ scanners that find it within minutes.
65
65
  - The runtime has **no authentication and no rate limiting**. Put access control
66
66
  in the tunnel: ngrok's OAuth, OIDC or basic auth; Cloudflare Access; a
67
67
  Tailscale ACL.
68
- - **Never tunnel the link-management API.** It is a separate listener bound to
69
- loopback on purpose, holding credentials that can rewrite live links. Tunnel
70
- the application port only.
71
68
  - Prefer `serve` with a fixed snapshot, a project containing no real secrets,
72
69
  and a tunnel you shut down when finished.
73
70
 
@@ -1,8 +1,16 @@
1
1
  # Build-time TypeScript guests
2
2
 
3
3
  TypeScript guest authoring is a separate build step. Serving still accepts only
4
- JavaScript ES modules and executes them exclusively in QuickJS/WebAssembly.
5
- The build never imports application modules into Node or runs application code.
4
+ JavaScript ES modules. The build never imports application modules into Node
5
+ or runs application code, and it is `sandbox`-aware per route
6
+ (docs/SPIKE-DEFAULT-TRUST-MODEL.md): a route that declares `sandbox: true` is
7
+ transpiled under the sandbox's own module rules (relative imports only, no
8
+ dynamic import/bare specifiers, the module/size limits below), exactly as
9
+ before; a trusted (non-`sandbox: true`) route is transpiled without those
10
+ import/size restrictions, since it will run with full Node access — bare/npm
11
+ imports, dynamic `import()`, `import.meta` — once served. Both modes still
12
+ diagnose only syntax, not semantics, and neither imports application code into
13
+ the build process in a way that executes it.
6
14
 
7
15
  ```sh
8
16
  urlcode recipes add typescript --out ./hello-source
@@ -29,17 +37,29 @@ The trusted pinned TypeScript compiler transpiles ES2022/ES modules with fixed
29
37
  settings. It does not read `tsconfig.json`, package scripts, plugins, compiler
30
38
  transformers, dependency packages, Node declarations or ambient environment
31
39
  files. It diagnoses syntax errors, but does **not** perform semantic type
32
- checking. Type-only relative imports are erased without reading their targets.
33
- Bare/npm imports (including static type-only imports), CommonJS import/export
34
- syntax, dynamic runtime imports, `import.meta`, import attributes and imports
35
- outside the project are refused. No import extension inference occurs.
40
+ checking, in either mode. CommonJS import/export syntax and imports outside
41
+ the project are always refused. Type-only relative imports are erased without
42
+ reading their targets.
36
43
 
37
- The source graph is limited to 128 modules, 1 MiB per source and 4 MiB aggregate.
38
- The emitted graph must pass the runtime's own source parser and byte limits
39
- before publication. This does not execute the modules or replace normal route,
40
- policy, binding or sandbox validation at activation. Unsupported host/browser
41
- APIs remain unavailable in QuickJS, even if TypeScript accepts their names.
42
- Run `urlcode validate --local` and project tests on the output.
44
+ For a `sandbox: true` route, bare/npm imports (including static type-only
45
+ imports), dynamic runtime imports, `import.meta` and import attributes are
46
+ refused, and the source graph is limited to 128 modules, 1 MiB per source and
47
+ 4 MiB aggregate; the emitted graph must also pass the runtime's own sandboxed
48
+ source parser and byte limits before publication. For a trusted route, none of
49
+ that applies: bare/npm specifiers, dynamic `import()`, `import.meta` and
50
+ import attributes pass through unchanged (resolved by Node at serve time, not
51
+ by this build), and there is no module-count or aggregate-size ceiling — only
52
+ a generous 16 MiB per-source read cap that bounds authoring-time memory. A
53
+ module may be shared by trusted and sandboxed routes: it is emitted once, and
54
+ every module reachable from a `sandbox: true` route is still validated under
55
+ the sandbox rules and budgets above, which does not disqualify the trusted
56
+ route that also imports it. In both modes,
57
+ no import extension inference occurs — relative imports of project modules
58
+ still need an explicit `.ts`/`.js`/`.mjs` extension to be rewritten and
59
+ followed. This does not execute the modules or replace normal route, policy,
60
+ binding or sandbox validation at activation. Unsupported host/browser APIs
61
+ remain unavailable in QuickJS, even if TypeScript accepts their names. Run
62
+ `urlcode validate --local` and project tests on the output.
43
63
 
44
64
  Includes are flattened into a duplicate-checked entry document. Only referenced
45
65
  modules, page/download assets, static trees, site favicon/llms files and the
@@ -16,7 +16,7 @@ beside them, and `dist/BUILD-MANIFEST.json` with a SHA-256 per emitted file.
16
16
  | Import | Runtime | Declarations |
17
17
  |---|---|---|
18
18
  | `urlcode` | `dist/index.js` | `dist/types/index.d.ts` |
19
- | `@jimhoyd/urlcode/plugins`, `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`, `@jimhoyd/urlcode/prerender`, `@jimhoyd/urlcode/extensions` | `dist/<name>.js` | `dist/types/<name>.d.ts` |
19
+ | `@jimhoyd/urlcode/plugins`, `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`, `@jimhoyd/urlcode/prerender`, `@jimhoyd/urlcode/extensions`, `@jimhoyd/urlcode/sandbox` | `dist/<name>.js` | `dist/types/<name>.d.ts` |
20
20
  | `@jimhoyd/urlcode/aws`, `@jimhoyd/urlcode/vercel`, `@jimhoyd/urlcode/cloudflare` | `dist/<name>.js` | `dist/types/<name>.d.ts` |
21
21
  | `@jimhoyd/urlcode/schema` | `schemas/urlcode.schema.json` | — |
22
22
 
@@ -36,9 +36,7 @@ release cannot ship a declaration that does not resolve.
36
36
  ## The main exported types
37
37
 
38
38
  - `urlcode`: `Runtime`, `RuntimeOptions`, `RuntimeRequest`, `RequestTrace`,
39
- `Server`, `ServerOptions`, `HostPlugin`, `Observer`, `TestPlan`, `LinkStore`,
40
- `LinkRow`, `LinkStoreOptions`, `LinkReader`, `LinkStoreBinding`, `LinkApi`,
41
- `LinkApiOptions`, `LinkEvent`, `LinkObserverOptions`.
39
+ `Server`, `ServerOptions`, `HostPlugin`, `Observer`, `TestPlan`.
42
40
  - `@jimhoyd/urlcode/plugins`: `Plugin`, `PluginRuntime`, `PolicyRequest`,
43
41
  `HandlerResult`, `HeaderPair`, `TargetName`. See [plugins](PLUGINS.md).
44
42
  - `@jimhoyd/urlcode/policies`: `PolicyModule`, `PolicyRegistry`, `PolicyRequest`,
@@ -54,6 +52,29 @@ release cannot ship a declaration that does not resolve.
54
52
  - `@jimhoyd/urlcode/aws`, `@jimhoyd/urlcode/vercel`, `@jimhoyd/urlcode/cloudflare`: `LambdaEvent`,
55
53
  `LambdaHandler`, `LambdaHandlerOptions`; `VercelHandler`,
56
54
  `VercelHandlerOptions`; `Artifact`, `WorkerRoute`, `Validators`.
55
+ - `@jimhoyd/urlcode/sandbox`: `SandboxPool`, `SandboxEntry`, `SandboxTarget`,
56
+ `SandboxInvocation`, `SandboxPoolOptions`, `functionFile`. The public
57
+ sandboxed-execution primitive: the same QuickJS/worker-thread engine that
58
+ already backs a `sandbox: true` `function`/`middleware` route
59
+ (`FunctionPool`, internally), generalized to an explicit list of
60
+ `{source, export}` entries instead of anything route/YAML-shaped, for an
61
+ extension package that needs to run a project-supplied hook through real
62
+ isolation when the project's own config declares `sandbox: true` on it
63
+ (see [EXTENSIONS.md](EXTENSIONS.md#project-level-lifecycle-hooks) and
64
+ [FUNCTION-SECURITY.md](FUNCTION-SECURITY.md)). There is no trusted-mode
65
+ export here: a hook that does not declare `sandbox: true` is ordinary
66
+ project code the extension `import()`s directly via
67
+ `ExtensionActivation.root`, no primitive required.
68
+
69
+ ```ts
70
+ import { SandboxPool, functionFile } from '@jimhoyd/urlcode/sandbox';
71
+
72
+ const source = await functionFile(root, hookConfig.source); // root: ExtensionActivation.root
73
+ const entries = [{ source, export: hookConfig.export ?? 'default' }];
74
+ const pool = await new SandboxPool(entries, { root, workers: 1 }).start();
75
+ const result = await pool.execute({ entry: entries[0] }, request, context, undefined);
76
+ await pool.close();
77
+ ```
57
78
 
58
79
  ```ts
59
80
  import { startServer, type ServerOptions, type Observer } from '@jimhoyd/urlcode';
package/docs/VERCEL.md CHANGED
@@ -5,8 +5,8 @@ The Vercel adapter runs a URLCode project as a Node function. The same
5
5
  that is the point of the project format.
6
6
 
7
7
  **This adapter serves native handlers only:** redirects, validated responses,
8
- pages, static assets and downloads. Isolated functions, middleware and stored
9
- live links are refused at activation, not per request, so a deployment cannot
8
+ pages, static assets and downloads. `function` and `middleware` routes are
9
+ refused at activation, trusted or sandboxed alike, not per request, so a deployment cannot
10
10
  half-work. See [what is not supported](#what-this-adapter-does-not-do).
11
11
 
12
12
  A working project is in [`examples/vercel/`](../examples/vercel/).
@@ -71,9 +71,8 @@ and want it in generated URLs.
71
71
 
72
72
  | Not supported | Why |
73
73
  |---|---|
74
- | Isolated functions | Every cold start would spawn worker threads and load the WASM engine. Correctness is not the issue; predictable latency is, and it is unmeasured. |
75
- | Middleware | Runs in the same sandbox as functions. |
76
- | Stored live links | SQLite needs a durable writable file. A serverless filesystem is ephemeral and per-instance, so records would silently diverge between instances. |
74
+ | `function` routes | They need the self-hosted Node lifecycle; a `sandbox: true` route would additionally spawn worker threads and load the WASM engine on every cold start. Correctness is not the issue; predictable latency is, and it is unmeasured. |
75
+ | Middleware | Runs in the same execution mode as the route's function, and is refused with it. |
77
76
  | `urlcode serve` operational endpoints | `/_urlcode/health` and `/_urlcode/ready` describe a long-lived process. Use Vercel's own observability. |
78
77
 
79
78
  Each refusal happens at activation with a message naming the route, so you find
@@ -0,0 +1,46 @@
1
+ # Package and channel alignment
2
+
3
+ The package manifests and root lockfile are the version authority. Read live
4
+ registry and Git tag state with `npm run release:status`; do not maintain a
5
+ second table of changing version numbers in documentation.
6
+
7
+ | Package | Manifest | Version owner | Release tag |
8
+ | --- | --- | --- | --- |
9
+ | `@jimhoyd/urlcode` | `package.json` | Explicit core release PR | `v<version>` |
10
+ | `@jimhoyd/urlcode-ui` | `packages/ui/package.json` | Changesets | `@jimhoyd/urlcode-ui@<version>` |
11
+ | `@jimhoyd/urlcode-auth` | `packages/auth/package.json` | Changesets | `@jimhoyd/urlcode-auth@<version>` |
12
+ | `@jimhoyd/urlcode-admin` | `packages/admin/package.json` | Changesets | `@jimhoyd/urlcode-admin@<version>` |
13
+
14
+ Development uses workspace source. Auth and admin's `file:../..` development
15
+ links resolve core to this checkout, enforced by `check-workspace-links.ts`.
16
+ Core never imports extension packages. Release verification instead installs the
17
+ published lower bound of each declared peer range and checks resolution. A peer
18
+ floor rises when code requires a newly introduced API, not just because a sibling
19
+ published another version. Preserve the declared upper bound during Changesets
20
+ versioning; `.changeset/config.json` limits unnecessary peer rewrites.
21
+
22
+ Publishable workspace changes carry Changesets; the release PR applies them and
23
+ updates versions, changelogs and the lockfile together. Core stays an explicit
24
+ entry in that PR until a separately reviewed workspace migration. Its CLI banner
25
+ must match its manifest. `npm run release:check` rejects stale lockfile versions.
26
+ Unreleased source changes do not require moving a published tag or pretending a
27
+ new package has already shipped.
28
+
29
+ Alpha releases publish under `alpha`; they never automatically move npm
30
+ `latest`. Core's historical `latest` remains the stable 0.3.0 baseline until an
31
+ explicit stable release decision. Extensions have historical alpha versions on
32
+ `latest`; subsequent alpha publication does not keep that channel in lockstep.
33
+ Different channel values alone are not drift. Test the install combination you
34
+ recommend against peer ranges; a bare install may select an older channel.
35
+ `release:status` reports each declared peer floor and whether its current
36
+ `latest` and `alpha` satisfy the range.
37
+
38
+ The standalone `urlcode-template` is an external exact-version consumer: after a
39
+ runtime release, update its dependency and starter through its own reviewed PR.
40
+ It is not automatically released by the monorepo coordinator. The retired
41
+ `urlcode-docs`, `urlcode-middleware`, `urlcode-dynamic-link` and `urlcode-short`
42
+ repositories are not release targets.
43
+
44
+ See [the development pipeline](DEVELOPMENT-PIPELINE.md) for preparation,
45
+ publication order, immutable retries and credential scope, and
46
+ [release security](RELEASE-SECURITY.md) for provenance and limitations.
@@ -37,7 +37,6 @@ Each page holds the recipes for one task; the section numbers continue across pa
37
37
  | [Pages, static folders and downloads](yaml/assets.md) | 10. Pages, static folders, downloads and MIME |
38
38
  | [Enable, disable and expire](yaml/conditions.md) | 11. Enable, disable and expire |
39
39
  | [Bindings, split files and tests](yaml/organization.md) | 12. Environment and secret references; 13. Split files and folders; 14. Assert inputs and outputs |
40
- | [Live short-link records](yaml/links.md) | 15. Live short-link records |
41
40
  | [Policies and profiles](yaml/policies.md) | 16. Hardened profile and per-route overrides |
42
41
  | [Site conventions](yaml/site.md) | 17. Site conventions |
43
42
 
@@ -56,5 +55,3 @@ Each page holds the recipes for one task; the section numbers continue across pa
56
55
  | Automatic hot updates in `serve` | Deploy/restart or use the embedding reload API deliberately |
57
56
  | “All examples are production-ready” | Validate your security, load and deployment requirements separately |
58
57
 
59
- Live-link recipes require `dynamicLinks: true` in the entry `urlcode.yaml`; see
60
- [live short-link records](yaml/links.md) and [dynamic-link opt-in](DYNAMIC-LINKS.md#explicit-project-opt-in).
@@ -20,6 +20,8 @@ schema-valid combinations activate successfully.
20
20
  | `routes.*.methods` | array | no | default: ["GET","HEAD"]; minItems: 1; uniqueItems: true |
21
21
  | `routes.*.methods[]` | string | no | enum: ["GET","HEAD","POST","PUT","PATCH","DELETE","OPTIONS"] |
22
22
  | `routes.*.enabled` | boolean | no | — |
23
+ | `routes.*.sandbox` | boolean | no | default: false |
24
+ | `routes.*.sandboxReason` | string | no | maxLength: 500 |
23
25
  | `routes.*.expires` | string | no | — |
24
26
  | `routes.*.description` | string | no | maxLength: 1024 |
25
27
  | `routes.*.parameters` | array | no | maxItems: 64 |
@@ -113,11 +115,6 @@ schema-valid combinations activate successfully.
113
115
  | `routes.*.middleware[] (option 2)` | object | no | unknown keys rejected |
114
116
  | `routes.*.middleware[] (option 2).source` | string | yes | maxLength: 1024 |
115
117
  | `routes.*.middleware[] (option 2).export` | string | no | pattern: "^[A-Za-z_][A-Za-z0-9_]*$" |
116
- | `routes.*.link` | object | no | unknown keys rejected |
117
- | `routes.*.link.collection` | string | yes | pattern: "^[A-Za-z][A-Za-z0-9_-]{0,63}$" |
118
- | `routes.*.link.code` | object | yes | unknown keys rejected |
119
- | `routes.*.link.code.from` | constant | yes | const: "path" |
120
- | `routes.*.link.code.name` | string | yes | pattern: "^[A-Za-z_][A-Za-z0-9_]*$" |
121
118
  | `routes.*.policies` | object | no | unknown keys rejected |
122
119
  | `routes.*.policies.profile` | string | no | pattern: "^[a-z][a-z0-9-]{0,63}$" |
123
120
  | `routes.*.policies.throttle` | one of the shapes below | no | — |
@@ -268,9 +265,22 @@ schema-valid combinations activate successfully.
268
265
  | `routes.*.auth (option 2).verified` | boolean | no | — |
269
266
  | `routes.*.auth (option 2).freshWithinSeconds` | integer | no | minimum: 1; maximum: 3600 |
270
267
  | `routes.*.auth (option 2).onDeny` | number / string | no | enum: [401,403,404,"sign-in"] |
268
+ | `routes.*.cache` | object | no | unknown keys rejected |
269
+ | `routes.*.cache.strategy` | string | no | enum: ["no-store","revalidate","public","immutable","swr","sie","micro","cdn-only","private"] |
270
+ | `routes.*.cache.maxAge` | integer | no | minimum: 0; maximum: 31536000 |
271
+ | `routes.*.cache.staleWhileRevalidate` | integer | no | minimum: 0; maximum: 31536000 |
272
+ | `routes.*.cache.staleIfError` | integer | no | minimum: 0; maximum: 31536000 |
273
+ | `routes.*.cache.cdnMaxAge` | integer | no | minimum: 0; maximum: 31536000 |
274
+ | `routes.*.cache.originTtl` | integer | no | minimum: 0; maximum: 86400 |
275
+ | `routes.*.cache.vary` | array | no | maxItems: 8; uniqueItems: true |
276
+ | `routes.*.cache.vary[]` | string | no | minLength: 1; maxLength: 128 |
277
+ | `routes.*.cache.statuses` | array | no | maxItems: 16; uniqueItems: true |
278
+ | `routes.*.cache.statuses[]` | integer | no | minimum: 200; maximum: 599 |
279
+ | `routes.*.cache.maxBytes` | integer | no | minimum: 0; maximum: 16777216 |
280
+ | `routes.*.cache.maxEntries` | integer | no | minimum: 1; maximum: 1000000 |
281
+ | `routes.*.cache.force` | boolean | no | default: false |
271
282
  | `includes` | array | no | maxItems: 256; uniqueItems: true |
272
283
  | `includes[]` | string | no | maxLength: 1024 |
273
- | `dynamicLinks` | boolean | no | default: false |
274
284
  | `policies` | object | no | unknown keys rejected |
275
285
  | `policies.profile` | string | no | pattern: "^[a-z][a-z0-9-]{0,63}$" |
276
286
  | `policies.throttle` | one of the shapes below | no | — |
@@ -0,0 +1,68 @@
1
+ # Historical record
2
+
3
+ Archived 2026-09-19. This records an earlier implementation or proposal, not
4
+ current instructions. See the [current roadmap](../../../ROADMAP.md),
5
+ [current contract](../../SPECIFICATION.md) and [open decisions](../../OPEN-DECISIONS.md).
6
+ Remaining acceptance work is not declared complete by archiving this record.
7
+
8
+ <!-- trust-model-prose: historical-file -->
9
+ <!-- guidance-claims: ignore-file -->
10
+
11
+ # Extension implementation sequence
12
+
13
+ PR #54 records the auth, admin, UI and extension direction. Implementation is
14
+ incremental; the spikes are proposals, not the implemented project schema.
15
+ The implemented specification remains authoritative. This sequence incorporates
16
+ the architectural review rather than treating contradictory spike examples as
17
+ requirements.
18
+
19
+ ## Invariants
20
+
21
+ - Operators explicitly install trusted host extensions; project YAML never loads
22
+ host modules or chooses packages. Application code remains isolated in WASM.
23
+ - Authentication credentials stay in host processing. Guest Request headers and
24
+ header-derived parameters must not expose operator-declared credential headers,
25
+ including on public routes receiving a session cookie.
26
+ - Session and one-time-token changes have explicit concurrency semantics. Define
27
+ atomic operations before selecting a database abstraction; multi-record
28
+ operations cannot rely on a same-collection transaction promise.
29
+ - Passwords and bearer credentials use appropriate hashes. TOTP requires protected
30
+ recoverable key material and an operator key rotation/backup contract.
31
+ - Authentication forms and submission destinations belong to trusted extension
32
+ code. Project styling does not imply permission to replace security controls.
33
+ - Administrative authorization is enforced by the API, including subject-level
34
+ restrictions, self-promotion and last-administrator protections.
35
+ - Unsupported target capabilities fail explicitly. Live provider deployment
36
+ testing remains deferred and non-blocking; local tests do not prove deployment.
37
+
38
+ ## Delivery order
39
+
40
+ 1. **Credential boundary:** add an explicit host-plugin declaration for headers
41
+ withheld from guest requests and parameter resolution. Preserve originals for
42
+ host authentication, cache decisions and policy processing. Test functions,
43
+ middleware and routes without an authentication requirement.
44
+ 2. **Extension registration:** explicit operator registry, versioned configuration
45
+ validation, route ownership/collision rules and capability reporting. No
46
+ automatic project host-file execution. Define policy ordering before adding
47
+ an auth policy.
48
+ 3. **Session slice:** one supported login method, trusted login/logout forms,
49
+ protected page and JSON routes, session lookup/revocation, CSRF and no-store
50
+ behavior. Evaluate a maintained authentication implementation behind an
51
+ adapter before writing protocol engines. Define durable atomic store methods
52
+ and test concurrent redemption, expiry and interrupted operations.
53
+ 4. **Administrative API and CLI:** user lookup and session revocation first;
54
+ audit records and narrowly scoped permissions. Add role/recovery operations
55
+ only with object-level rules and concurrency tests.
56
+ 5. **UI and console:** extract components from real auth/admin flows; choose a
57
+ constrained renderer and asset ownership model; verify keyboard and assistive
58
+ technology behavior as well as automated accessibility checks.
59
+ 6. **Additional methods and targets:** passkeys, OIDC, MFA, recovery and imports
60
+ each receive state-machine, abuse-budget and portability tests. Preserve
61
+ verification of existing credential formats across target migrations.
62
+
63
+ ## Status
64
+
65
+ PR #54 is merged. The first implementation adds the operator-only
66
+ `Plugin.credentialHeaders` boundary, documented in [Plugins](../../PLUGINS.md).
67
+ The remaining items are pending; none of this document declares end-user auth,
68
+ admin routes, extension YAML, or a generic project store available today.
@@ -1,5 +1,25 @@
1
+ # Historical record
2
+
3
+ Archived 2026-09-19. This records an earlier implementation or proposal, not
4
+ current instructions. See the [current roadmap](../../../ROADMAP.md),
5
+ [current contract](../../SPECIFICATION.md) and [open decisions](../../OPEN-DECISIONS.md).
6
+ Remaining acceptance work is not declared complete by archiving this record.
7
+
8
+ <!-- trust-model-prose: historical-file -->
9
+ <!-- guidance-claims: ignore-file -->
10
+
1
11
  # Private management and durable mutation audit
2
12
 
13
+ > **Retired.** This page describes the management API and mutation audit of
14
+ > core's native link store. That store, its `urlcode links` CLI and this
15
+ > management listener were removed from core when `link` was extracted, and the
16
+ > `urlcode-dynamic-link` package that received them has since been retired and
17
+ > unpublished. Nothing in the current runtime exposes this surface. The page is
18
+ > kept because the security reasoning — loopback-only binding, operator-owned
19
+ > credential policy, transactional mutation audit — is referenced by
20
+ > [security audit](../../SECURITY-AUDIT.md) and applies to any component that
21
+ > reintroduces a management listener.
22
+
3
23
  Management binds only `127.0.0.1` or `::1`. Use an authenticated private tunnel
4
24
  (e.g. SSH/VPN with loopback forwarding); never publish its port through a public
5
25
  proxy or container port mapping. This is operator management, not an end-user
@@ -1,3 +1,13 @@
1
+ # Historical record
2
+
3
+ Archived 2026-09-19. This records an earlier implementation or proposal, not
4
+ current instructions. See the [current roadmap](../../../ROADMAP.md),
5
+ [current contract](../../SPECIFICATION.md) and [open decisions](../../OPEN-DECISIONS.md).
6
+ Remaining acceptance work is not declared complete by archiving this record.
7
+
8
+ <!-- trust-model-prose: historical-file -->
9
+ <!-- guidance-claims: ignore-file -->
10
+
1
11
  # Next-phase implementation status
2
12
 
3
13
  Repository review and source implementation, 2026-09-17. Phase A established
@@ -5,6 +15,13 @@ the capability catalog; the subsequent source work implements the bounded
5
15
  Phase B–D features below. This describes the source additions after `0.3.0` that ship in
6
16
  `0.4.0-alpha.1`, not a claim that provider deployments have been verified.
7
17
 
18
+ > **Update:** this document describes the `0.4.0-alpha.1` source work as it
19
+ > stood on 2026-09-17, when every `function`/`middleware` route was sandboxed.
20
+ > The default changed in `0.4.0-alpha.2`: those routes now run trusted and
21
+ > unsandboxed in the host process unless the route declares `sandbox: true`,
22
+ > which gives it exactly the QuickJS/WASM execution described below. See
23
+ > [SPIKE-DEFAULT-TRUST-MODEL.md](../../SPIKE-DEFAULT-TRUST-MODEL.md).
24
+
8
25
  ## Implementation and evidence
9
26
 
10
27
  | Phase | Implemented source | Evidence and remaining limits |
@@ -21,10 +38,10 @@ Phase B–D features below. This describes the source additions after `0.3.0` th
21
38
  | D: TypeScript guests | Fixed trusted build-time compiler, bounded relative graph, rewritten JavaScript imports, referenced-asset snapshot and safe new output | Transpilation is not type checking; no tsconfig/plugins/package execution or dotenv copying; runtime remains QuickJS JavaScript only |
22
39
  | D: consolidated SDK/MCP | Inspection, semantic validation, path explanation, compatibility, conversion previews and recipe discovery; operator-rooted stdio MCP | Read-only tooling; no arbitrary path, credential, guest execution or write authority; not a remote authenticated service |
23
40
 
24
- See [interchange](INTERCHANGE.md), [provider evidence](PROVIDER-VERIFICATION.md),
25
- [egress](EGRESS.md), [conditions](CONDITIONS.md), [recipes](RECIPES.md),
26
- [bulk measurements](BULK.md), [TypeScript authoring](TYPESCRIPT-AUTHORING.md), and
27
- [tooling/MCP](TOOLING.md) for the executable interfaces and exact restrictions.
41
+ See [interchange](../../INTERCHANGE.md), [provider evidence](../../PROVIDER-VERIFICATION.md),
42
+ [egress](../../EGRESS.md), [conditions](../../CONDITIONS.md), [recipes](../../RECIPES.md),
43
+ [bulk measurements](../../BULK.md), [TypeScript authoring](../../TYPESCRIPT-AUTHORING.md), and
44
+ [tooling/MCP](../../TOOLING.md) for the executable interfaces and exact restrictions.
28
45
 
29
46
  ## Preserved architecture
30
47
 
@@ -36,10 +53,11 @@ binding resolution and activation and distinguishes implementation support from
36
53
  actual deployment evidence. Unknown or unsupported targets fail closed.
37
54
 
38
55
  Project YAML describes route behavior. Provider infrastructure and outbound
39
- origin grants stay in operator configuration. Functions remain untrusted
40
- QuickJS/WASM guests with no host-code fallback. Existing explicit external
41
- bindings and new egress grants remain pinned to the exact configuration/source
42
- revision. Compilation and conversion do not resolve credentials or manufacture
56
+ origin grants stay in operator configuration. Through `0.4.0-alpha.1`,
57
+ functions remained untrusted QuickJS/WASM guests with no host-code fallback
58
+ (see the update note above). <!-- trust-model-prose: historical -->
59
+ Existing explicit external bindings and new egress grants remain pinned to the
60
+ exact configuration/source revision. Compilation and conversion do not resolve credentials or manufacture
43
61
  grants. File authors publish new projects without overwriting unrelated work.
44
62
 
45
63
  The five policy modules remain the authority for their target-sensitive