@jimhoyd/urlcode 0.4.0-alpha.2 → 0.4.1

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 (147) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +17 -19
  2. package/.claude/skills/urlcode-operations/SKILL.md +9 -9
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CONTRIBUTING.md +20 -16
  5. package/README.md +59 -64
  6. package/ROADMAP.md +66 -376
  7. package/dist/BUILD-MANIFEST.json +26 -25
  8. package/dist/agents-guide.js +6 -6
  9. package/dist/authoring.js +15 -1
  10. package/dist/build-static.js +2 -0
  11. package/dist/capability-query.js +0 -1
  12. package/dist/catalog.js +0 -1
  13. package/dist/cli.js +25 -9
  14. package/dist/config.js +1 -1
  15. package/dist/explain-cli.js +4 -2
  16. package/dist/explain.js +8 -2
  17. package/dist/extensions.js +1 -1
  18. package/dist/http-response.js +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/init-with.js +36 -11
  21. package/dist/manifest.js +8 -2
  22. package/dist/mcp-authoring.js +4 -4
  23. package/dist/mcp.js +1 -1
  24. package/dist/policies/cache.js +2 -2
  25. package/dist/policies.js +3 -1
  26. package/dist/prerender.js +4 -0
  27. package/dist/project-dependencies.js +305 -0
  28. package/dist/readiness.js +3 -0
  29. package/dist/route-diff.js +12 -5
  30. package/dist/runtime.js +1 -1
  31. package/dist/trusted-functions.js +4 -5
  32. package/dist/types/authoring.d.ts +9 -1
  33. package/dist/types/capability-query.d.ts +0 -1
  34. package/dist/types/catalog.d.ts +0 -4
  35. package/dist/types/config.d.ts +1 -9
  36. package/dist/types/explain.d.ts +6 -1
  37. package/dist/types/extensions.d.ts +1 -1
  38. package/dist/types/http-response.d.ts +0 -1
  39. package/dist/types/index.d.ts +1 -0
  40. package/dist/types/init-with.d.ts +7 -13
  41. package/dist/types/manifest.d.ts +5 -2
  42. package/dist/types/project-dependencies.d.ts +78 -0
  43. package/dist/types/readiness.d.ts +2 -0
  44. package/dist/types/trusted-functions.d.ts +1 -4
  45. package/dist/types/types.d.ts +8 -1
  46. package/dist/types.js +8 -1
  47. package/dist/typescript-authoring.js +18 -7
  48. package/docs/AI-AUTHORING.md +15 -7
  49. package/docs/ASSETS.md +2 -1
  50. package/docs/AUTH-BACKUP.md +32 -0
  51. package/docs/AWS.md +9 -0
  52. package/docs/BEST-PRACTICES.md +17 -9
  53. package/docs/CAPABILITIES.md +1 -1
  54. package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
  55. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
  56. package/docs/CI.md +8 -3
  57. package/docs/CODEBASE-AUDIT-2026-09-20.md +284 -0
  58. package/docs/COMPOSING-A-SITE.md +278 -0
  59. package/docs/DEVELOPMENT-PIPELINE.md +270 -0
  60. package/docs/EXTENSIONS.md +51 -11
  61. package/docs/FRAMEWORK.md +67 -48
  62. package/docs/FUNCTION-SECURITY.md +44 -0
  63. package/docs/INSTALL.md +13 -8
  64. package/docs/LOCAL-DEVELOPMENT.md +3 -0
  65. package/docs/MIDDLEWARE.md +10 -4
  66. package/docs/OPEN-DECISIONS.md +224 -212
  67. package/docs/OPERATIONAL-PROOF.md +3 -3
  68. package/docs/OPERATIONS.md +3 -3
  69. package/docs/POLICIES.md +13 -5
  70. package/docs/PRERENDER.md +23 -11
  71. package/docs/PROJECT-DIRECTION.md +3 -3
  72. package/docs/READINESS.md +10 -5
  73. package/docs/README.md +20 -44
  74. package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
  75. package/docs/RELEASE-0.4.1.md +73 -0
  76. package/docs/RELEASE-READINESS.md +6 -6
  77. package/docs/RELEASE-SECURITY.md +96 -181
  78. package/docs/RESILIENCE.md +3 -3
  79. package/docs/ROUTING.md +3 -4
  80. package/docs/SECURITY-AUDIT.md +2 -2
  81. package/docs/SPECIFICATION.md +12 -6
  82. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +6 -5
  83. package/docs/SPIKE-BUSINESS-SUITE.md +14 -6
  84. package/docs/SPIKE-CORE-LAYERING.md +49 -18
  85. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +7 -5
  86. package/docs/STARTERS.md +17 -5
  87. package/docs/STATIC.md +14 -3
  88. package/docs/TOOLING.md +10 -7
  89. package/docs/TYPESCRIPT-AUTHORING.md +6 -1
  90. package/docs/VERCEL.md +10 -2
  91. package/docs/VERSION-ALIGNMENT.md +76 -201
  92. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
  93. package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +102 -0
  94. package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +15 -5
  95. package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +15 -3
  96. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
  97. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
  98. package/docs/archive/2026-09-19/ROADMAP.md +387 -0
  99. package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +11 -0
  100. package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +25 -14
  101. package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +365 -0
  102. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +778 -0
  103. package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +12 -2
  104. package/docs/archive/README.md +28 -0
  105. package/docs/policies/agents.md +1 -1
  106. package/docs/policies/compression.md +3 -2
  107. package/docs/policies/security.md +3 -2
  108. package/docs/yaml/functions.md +10 -2
  109. package/docs/yaml/middleware.md +5 -3
  110. package/examples/assets/example.yaml +1 -1
  111. package/examples/cookbook/middleware/envelope.mjs +4 -2
  112. package/examples/cookbook/route-index.json +1 -1
  113. package/examples/cookbook/routes/middleware.yaml +1 -1
  114. package/examples/prerender/README.md +14 -6
  115. package/examples/prerender/functions/page.mjs +4 -2
  116. package/examples/prerender/middleware/template.mjs +1 -1
  117. package/examples/prerender/prerender.mjs +1 -1
  118. package/examples/prerender/urlcode.yaml +8 -4
  119. package/llms-full.txt +503 -88
  120. package/llms.txt +6 -4
  121. package/package.json +27 -4
  122. package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
  123. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +17 -19
  124. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +9 -9
  125. package/recipes/authenticated-json-api/README.md +4 -3
  126. package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
  127. package/recipes/authenticated-json-api/recipe.yaml +1 -1
  128. package/recipes/contact-form/functions/contact.mjs +2 -1
  129. package/recipes/contact-form/recipe.yaml +2 -2
  130. package/recipes/cors-api/README.md +2 -2
  131. package/recipes/cors-api/recipe.yaml +1 -1
  132. package/recipes/health-page/README.md +1 -1
  133. package/recipes/json-api/README.md +1 -1
  134. package/recipes/json-api/recipe.yaml +3 -3
  135. package/recipes/middleware/README.md +8 -4
  136. package/recipes/middleware/middleware/envelope.mjs +4 -2
  137. package/recipes/protected-download/README.md +1 -1
  138. package/recipes/protected-download/recipe.yaml +1 -1
  139. package/recipes/static-plus-api/README.md +2 -2
  140. package/recipes/static-plus-api/public/index.html +1 -1
  141. package/recipes/static-plus-api/recipe.yaml +1 -1
  142. package/recipes/static-plus-api/urlcode.yaml +1 -1
  143. package/recipes/typescript/recipe.yaml +4 -4
  144. package/skills/urlcode/SKILL.md +6 -6
  145. package/starters/default/AGENTS.md +6 -6
  146. package/docs/SPIKE-LAMBDA-COMPILE.md +0 -201
  147. package/docs/SPIKE-MONOREPO.md +0 -322
package/docs/FRAMEWORK.md CHANGED
@@ -7,26 +7,26 @@ claim here is implemented in the linked repository; nothing is roadmap.
7
7
 
8
8
  ## Four packages, one project shape
9
9
 
10
- | Package | Repository | What it adds | How a project declares it |
10
+ | Package | Source | What it adds | How a project declares it |
11
11
  |---|---|---|---|
12
- | `@jimhoyd/urlcode` | [urlcode](https://github.com/jimhoyd-com/urlcode) | The runtime: YAML routes, functions and middleware (trusted by default, `sandbox: true` opt-in), pages and assets, policies, site conventions, CLI, provider adapters, the extension contract | `urlcode.yaml` with `version: "1"` |
13
- | `@jimhoyd/urlcode-ui` | [urlcode-ui](https://github.com/jimhoyd-com/urlcode-ui) | Shared presentation: escaped templates, shadcn/ui partials, one stylesheet with light and dark, themes, translations, the `ui` extension that serves the kit's assets | `extensions.ui` plus an asset mount route |
14
- | `@jimhoyd/urlcode-auth` | [urlcode-auth](https://github.com/jimhoyd-com/urlcode-auth) | Accounts: password, passkeys, OpenID Connect, email codes, TOTP, recovery, sessions, roles, registration modes, account page, operator CLI | `extensions.auth` plus an `/account/*` mount and `policies.extensions.auth` on protected routes |
15
- | `@jimhoyd/urlcode-admin` | [urlcode-admin](https://github.com/jimhoyd-com/urlcode-admin) | Administration: users, sessions, roles, audit, registration approval, two-person cases, support impersonation, health | `extensions.admin` plus an `/admin/*` mount |
16
-
17
- The core is Apache-2.0 and released. The three extension packages are
18
- Apache-2.0 and published to npm as alphas (`@jimhoyd/urlcode-ui@0.1.0-alpha.4`,
19
- `@jimhoyd/urlcode-auth@0.1.0-alpha.2`, `@jimhoyd/urlcode-admin@0.1.0-alpha.2`,
20
- on core `0.4.0-alpha.1`). An alpha on npm is a distribution channel, not an
21
- endorsement: the source is complete, but independent review, deployment
22
- evidence and an accessibility assessment are still pending
12
+ | `@jimhoyd/urlcode` | this repository | The runtime: YAML routes, functions and middleware (trusted by default, `sandbox: true` opt-in), pages and assets, policies, site conventions, CLI, provider adapters, the extension contract | `urlcode.yaml` with `version: "1"` |
13
+ | `@jimhoyd/urlcode-ui` | [`packages/ui`](../packages/ui) | Shared presentation: escaped templates, shadcn/ui partials, one stylesheet with light and dark, themes, translations, the `ui` extension that serves the kit's assets | `extensions.ui` plus an asset mount route |
14
+ | `@jimhoyd/urlcode-auth` | [`packages/auth`](../packages/auth) | Accounts: password, passkeys, OpenID Connect, email codes, TOTP, recovery, sessions, roles, registration modes, account page, operator CLI | `extensions.auth` plus an `/account/*` mount and `policies.extensions.auth` on protected routes |
15
+ | `@jimhoyd/urlcode-admin` | [`packages/admin`](../packages/admin) | Administration: users, sessions, roles, audit, registration approval, two-person cases, support impersonation, health | `extensions.admin` plus an `/admin/*` mount |
16
+
17
+ All four are Apache-2.0. The `0.4.1` release line aligns their stable versions;
18
+ check `npm run release:status` for publication progress. A stable npm channel is
19
+ not an independent assessment: review, deployment evidence and an accessibility
20
+ assessment are still pending
23
21
  ([issue 58](https://github.com/jimhoyd-com/urlcode/issues/58)). Their status
24
- files say exactly what is built: [auth](https://github.com/jimhoyd-com/urlcode-auth/blob/main/IMPLEMENTATION-STATUS.md),
25
- [admin](https://github.com/jimhoyd-com/urlcode-admin/blob/main/IMPLEMENTATION-STATUS.md),
26
- [ui](https://github.com/jimhoyd-com/urlcode-ui/blob/main/IMPLEMENTATION-STATUS.md).
27
- Which core version each package supports, how it declares that, and the order
28
- in which a core change reaches the downstream repositories are recorded in
29
- [core version alignment](VERSION-ALIGNMENT.md).
22
+ files say exactly what is built: [auth](../packages/auth/IMPLEMENTATION-STATUS.md),
23
+ [admin](../packages/admin/IMPLEMENTATION-STATUS.md),
24
+ [ui](../packages/ui/IMPLEMENTATION-STATUS.md).
25
+ The current version of each package is its own manifest, and the peer ranges it
26
+ declares are in that manifest too; do not read a version number out of this
27
+ page. How versions, channels and release tags line up is recorded in
28
+ [package and channel alignment](VERSION-ALIGNMENT.md), and `npm run
29
+ release:status` reports the live registry and tag state.
30
30
 
31
31
  ## The ladder
32
32
 
@@ -43,8 +43,10 @@ Each rung's YAML is valid on every rung above it.
43
43
  3. **Functions and middleware.** `function` routes and ordered `middleware`
44
44
  in JavaScript, trusted and in-process by default; a route declaring
45
45
  `sandbox: true` runs isolated instead (QuickJS inside WebAssembly, fresh
46
- heap per call, no Node, filesystem or network). Secrets reach a function
47
- only through an operator grant pinned to the project revision.
46
+ heap per call, no Node, filesystem or network). The `env`/`secrets` the
47
+ runtime injects into a function come only from an operator grant pinned to
48
+ the project revision; the grant governs that injected context, not the
49
+ ambient Node environment trusted in-process code can reach on its own.
48
50
  4. **Accounts.** The `auth` extension: sign-in, registration, MFA, account
49
51
  page and protected routes. The operator installs it in a host file outside
50
52
  the project; YAML only declares the mount and configuration.
@@ -56,27 +58,29 @@ Each rung's YAML is valid on every rung above it.
56
58
  project copy, template and stylesheet overrides for kit-rendered pages.
57
59
 
58
60
  Stored short links previously sat here as a native `link` route; that handler
59
- was removed from core. A future `urlcode-dynamic-link` package (not yet
60
- published, so this one line is the exception to "nothing is roadmap" above)
61
- is expected to own them the same way `auth`/`admin` own their mounts.
61
+ was removed from core. A `urlcode-dynamic-link` package owned them the same way
62
+ `auth`/`admin` own their mounts, but it has been retired and unpublished; no
63
+ package occupies this rung today.
62
64
 
63
65
  Rungs 1 to 3 need only the core package. Rungs 4 to 6 need the extension
64
- packages, installed from npm as `0.1.0-alpha.x` prereleases, and a Node host
65
- with a patched SQLite build; see each repository's README for the exact
66
- requirement.
66
+ packages installed from npm and a Node host with a
67
+ patched SQLite build; see each package's README ([auth](../packages/auth/README.md),
68
+ [admin](../packages/admin/README.md), [ui](../packages/ui/README.md)) for the
69
+ exact requirement.
67
70
 
68
71
  ## The composition contract
69
72
 
70
- An extended project starts with the packages and one command:
73
+ After the aligned `0.4.1` packages are published, an extended project starts
74
+ with the packages and one command:
71
75
 
72
76
  ```sh
73
- npm install @jimhoyd/urlcode @jimhoyd/urlcode-ui @jimhoyd/urlcode-auth @jimhoyd/urlcode-admin
74
- urlcode init my-site --with auth,admin
77
+ npm install --save-exact @jimhoyd/urlcode@0.4.1 @jimhoyd/urlcode-ui@0.4.1 @jimhoyd/urlcode-auth@0.4.1 @jimhoyd/urlcode-admin@0.4.1
78
+ urlcode init my-site --with ui,auth,admin
75
79
  ```
76
80
 
77
- Installing from npm is the normal path; the extension repositories'
78
- `pack-sources.mjs` still builds local tarballs from a reviewed checkout for
79
- operators who install only source they have read. Three files make an extended
81
+ Installing from npm is the normal path; `scripts/pack-sources.mjs` still builds
82
+ local tarballs from a reviewed checkout for operators who install only source
83
+ they have read one revision now covers core and every extension. Three files make an extended
80
84
  project. Nothing else is discovered by convention.
81
85
 
82
86
  ```
@@ -108,7 +112,8 @@ routes:
108
112
  auth: {}
109
113
  ```
110
114
 
111
- The operator host wires the packages, once, in code the project cannot reach:
115
+ The operator host explicitly registers the packages. Registration is an
116
+ activation boundary; it does not isolate trusted application code from the host:
112
117
 
113
118
  ```js
114
119
  import { createUiExtension } from '@jimhoyd/urlcode-ui/host';
@@ -128,18 +133,18 @@ export default {
128
133
  };
129
134
  ```
130
135
 
131
- Auth and admin pages currently render through the shared primitives and a
132
- `presentation` (catalogue and `--ui-*` theme variables). The `ui` extension
133
- serves the template kit and its assets for pages an extension renders through
134
- `ui.kit`; moving the auth and admin screens onto the kit is a listed remaining
135
- item in the ui repository, so today one `presentation` restyles them and the
136
- `ui` block is optional.
136
+ Auth and admin already render their screens through `ui.kit` when the host
137
+ supplies the UI extension; their package-owned templates and catalogues must be
138
+ registered with that kit. Without it, both retain shared primitive rendering
139
+ through `presentation`. The example above uses that primitive fallback. The UI
140
+ block is optional, and a migration to the kit is not unfinished framework work.
141
+ See each package README for its complete kit registration example.
137
142
 
138
143
  ```sh
139
144
  urlcode serve --project /absolute/site --host-file /absolute/operator/host.mjs --origin https://site.example
140
145
  ```
141
146
 
142
- `urlcode init <dir> --with auth,admin` writes this layout in one step: it
147
+ `urlcode init <dir> --with ui,auth,admin` writes this layout in one step: it
143
148
  resolves each installed `@jimhoyd/urlcode-<name>` from the current directory,
144
149
  calls its `scaffold` export and merges the fragments into `app/urlcode.yaml`,
145
150
  one `host.mjs` and one `README.md`, refusing before writing anything when a
@@ -150,12 +155,24 @@ administrator from JSON on stdin. `inspectExtensionRevision(project)` prints
150
155
  the SHA-256 that `projectSha256` must carry; changing extension YAML, policies
151
156
  or mounts changes the revision and needs an explicit operator reapproval.
152
157
 
158
+ The presentation tooling composes the same way, by naming packages rather than
159
+ depending on them. `urlcode-ui` is the kit alone until
160
+ `--extensions @jimhoyd/urlcode-auth,@jimhoyd/urlcode-admin` names the packages
161
+ that ship the other namespaces: each is resolved from `--project` with Node
162
+ package resolution and imported for the namespace it exports, so `list`,
163
+ `doctor`, `eject`, `preview` and `copy --missing` cover the `auth/*` and
164
+ `admin/*` templates and copy the host registers, and a project override of an
165
+ extension template is checked against the shipped view model. A package that is
166
+ not installed is skipped. `urlcode init --with` writes the commands with the
167
+ flag already set; `@jimhoyd/urlcode-ui` still depends on neither peer.
168
+
153
169
  ## Rules an agent must follow
154
170
 
155
171
  These are the facts that keep generated projects valid. The full matrix is in
156
172
  [AI authoring](AI-AUTHORING.md); this is the short list.
157
173
 
158
- - **YAML never names code, packages, databases or credentials.** Extensions are
174
+ - **Extension YAML names logical extensions, not host packages or credentials.**
175
+ Function and middleware `source` fields do name project modules. Extensions are
159
176
  logical names; the host file chooses the implementation. There is no
160
177
  `--extension` flag, no `import` in YAML, no interpolation.
161
178
  - **One handler per route.** `redirect`, `respond`, `page`, `static`, `download`,
@@ -166,13 +183,15 @@ These are the facts that keep generated projects valid. The full matrix is in
166
183
  It runs in-process with full Node access unless the route declares
167
184
  `sandbox: true`, which isolates it to a text/JSON `Request`/`Response`
168
185
  subset, validated `args` and granted `env`, with no `fetch`, Node,
169
- filesystem or timers. Either way, `args`/`env`/`secrets` are exactly what
186
+ filesystem or general network access; bounded timers are available. Either way, `args`/`env`/`secrets` are exactly what
170
187
  the route declares and an operator grants — trust changes where code runs,
171
188
  not what it is handed. See docs/SPIKE-DEFAULT-TRUST-MODEL.md and
172
189
  docs/FUNCTION-SECURITY.md.
173
190
  - **Authentication is host processing.** Do not build login forms, session
174
- cookies or password checks in functions. Declare `policies.extensions.auth`
175
- on the route; the runtime withholds `Cookie` and `Authorization` from guests.
191
+ cookies or password checks in functions. With the auth extension declared,
192
+ prefer `auth: true` or `auth: {role: admin}`; these expand to
193
+ `policies.extensions.auth`. The runtime filters credential headers passed to
194
+ application handlers. This is not a security boundary against trusted Node code.
176
195
  - **Everything is validated before it runs.** `urlcode validate --local`,
177
196
  `urlcode test`, `urlcode audit --expect-routes N`. Unsupported features fail
178
197
  with the route named; nothing degrades silently.
@@ -191,8 +210,8 @@ These are the facts that keep generated projects valid. The full matrix is in
191
210
  | Need | Read |
192
211
  |---|---|
193
212
  | Write or change routes | [YAML guide](YAML-GUIDE.md), [field reference](YAML-REFERENCE.md), [cookbook](../examples/cookbook/README.md) |
194
- | Add accounts | [auth README](https://github.com/jimhoyd-com/urlcode-auth#readme), [auth security](https://github.com/jimhoyd-com/urlcode-auth/blob/main/SECURITY.md) |
195
- | Add administration | [admin README](https://github.com/jimhoyd-com/urlcode-admin#readme) |
196
- | Restyle every page | [ui README](https://github.com/jimhoyd-com/urlcode-ui#readme), [ui contract](https://github.com/jimhoyd-com/urlcode-ui/blob/main/CONTRACT.md) |
197
- | Write an extension | [extensions](EXTENSIONS.md), [extension model review](SPIKE-EXTENSION-MODEL.md) |
213
+ | Add accounts | [auth README](../packages/auth/README.md), [auth security](../packages/auth/SECURITY.md) |
214
+ | Add administration | [admin README](../packages/admin/README.md) |
215
+ | Restyle every page | [ui README](../packages/ui/README.md), [ui contract](../packages/ui/CONTRACT.md) |
216
+ | Write an extension | [extensions](EXTENSIONS.md), [extension model review](archive/2026-09-19/SPIKE-EXTENSION-MODEL.md) |
198
217
  | Run it | [operations](OPERATIONS.md), [install](INSTALL.md), [deployment checks](DEPLOYMENT-CHECKS.md) |
@@ -40,6 +40,35 @@ API is all it has, so its binding grant *is* effectively its whole reach into
40
40
  the environment. Trusted code's reach is not bounded that way; treat the
41
41
  grant as scoping `context`, not as scoping the process.
42
42
 
43
+ ## Migrating to the trusted default
44
+
45
+ If you are upgrading a project from a release before this change shipped:
46
+ **every existing `function` and `middleware` route silently changes execution
47
+ mode**, from sandboxed to trusted, unless it already has (or you add)
48
+ `sandbox: true`. This is a real behavior change on upgrade, not a
49
+ documentation update — a route that used to run with no filesystem or network
50
+ access will, after the upgrade, run with full Node access unless you opt it
51
+ back into the sandbox.
52
+
53
+ Before upgrading:
54
+
55
+ - List every `function` and `middleware` route in the project.
56
+ - For each one, decide whether you fully trust that code to run in-process
57
+ with full Node/filesystem/network access — the same trust you would extend
58
+ to any other code you deploy to that server.
59
+ - Add `sandbox: true` explicitly to any route whose code you do not fully
60
+ trust, that processes input from a source you don't control, or that handles
61
+ a secret binding you want isolated — before you upgrade, not after.
62
+ - Routes you do want running trusted need no change; that is now the default.
63
+
64
+ The change moves the sandbox from an unconditional guarantee to an explicit,
65
+ per-route choice, mainly for performance: the previous blanket sandbox capped
66
+ concurrency at two workers with no queue shared across every function route on
67
+ the server, which does not scale to real concurrent traffic. It also brings
68
+ first-party code in line with how the rest of the Node ecosystem treats
69
+ deployed application code. The sandbox itself is unchanged for routes that opt
70
+ into it; only the default for routes that declare neither option has changed.
71
+
43
72
  ## What "sandboxed" (`sandbox: true`) still guarantees
44
73
 
45
74
  - Function sources are parsed/snapshotted without importing them into Node.
@@ -116,6 +145,21 @@ it is handed *through `context`*. It does not change what the code can go get
116
145
  on its own once it is running; see "binding grants are unaffected" above for
117
146
  that distinction.
118
147
 
148
+ ## Trusted code, not trusted requests
149
+
150
+ "Trusted" describes the code's authorship — first-party project code you
151
+ reviewed and deployed — not the requests it handles. Every request, in either
152
+ mode, still carries client-controlled path, query, header and body data that is
153
+ exactly as adversarial as it always was. Running trusted means that code
154
+ executes with full Node access if it mishandles that input; it does not mean
155
+ the input itself became safe to trust. Declare `parameters` and `request.body`
156
+ validation in YAML, check `args` and any other request data again inside
157
+ function/middleware code, and implement your own authentication and
158
+ authorization — no route, sandboxed or trusted, adds automatic auth.
159
+ `sandbox: true` narrows what a bug or an unreviewed dependency in the *code*
160
+ can do with that same request data; it is not a substitute for validating or
161
+ authenticating the request itself.
162
+
119
163
  ## Granting selected bindings
120
164
 
121
165
  An application may request a named binding in YAML, but only an operator can
package/docs/INSTALL.md CHANGED
@@ -8,8 +8,13 @@ so the installed `urlcode` command runs `dist/cli.js` and needs no build tool.
8
8
 
9
9
  ## npm
10
10
 
11
+ The stable release target is `0.4.1`. Use these commands after publication;
12
+ `npm run release:status` in a checkout reports live registry availability. An
13
+ unversioned npm install selects the current `latest` channel, while `@alpha`
14
+ explicitly selects the separate prerelease channel.
15
+
11
16
  ```sh
12
- npm install --global @jimhoyd/urlcode
17
+ npm install --global @jimhoyd/urlcode@0.4.1
13
18
  urlcode --help
14
19
  ```
15
20
 
@@ -19,12 +24,12 @@ Which dependency list it belongs in depends on how the project uses URLCode:
19
24
  ```sh
20
25
  # Using URLCode as a tool: validate, test and build in CI, never imported by
21
26
  # the code that serves requests.
22
- npm install --save-dev @jimhoyd/urlcode
27
+ npm install --save-dev --save-exact @jimhoyd/urlcode@0.4.1
23
28
  npx urlcode validate
24
29
 
25
30
  # Embedding the runtime (see TYPESCRIPT.md): the application imports
26
31
  # @jimhoyd/urlcode at startup, so it must survive `npm ci --omit=dev`.
27
- npm install --save @jimhoyd/urlcode
32
+ npm install --save --save-exact @jimhoyd/urlcode@0.4.1
28
33
  ```
29
34
 
30
35
  A devDependency is absent from a production install, so an application that
@@ -65,7 +70,7 @@ It downloads the release tarball, verifies its SHA-256 against the release's
65
70
  `SHA256SUMS`, and installs with npm. Options:
66
71
 
67
72
  ```sh
68
- curl -fsSL .../install.sh | sh -s -- --version 0.3.0 --prefix "$HOME/.local"
73
+ curl -fsSL .../install.sh | sh -s -- --version 0.4.1 --prefix "$HOME/.local"
69
74
  ```
70
75
 
71
76
  `--prefix` avoids needing privileges for a global npm directory; add
@@ -76,12 +81,12 @@ for that moment: to inspect first, download it, read it, then run it.
76
81
 
77
82
  No image is published yet: the release job's GHCR step is gated behind the
78
83
  `PUBLISH_CONTAINER` repository variable and has not run, so there is nothing at
79
- `ghcr.io/jimhoyd-com/urlcode` to pull. Build it from a release checkout:
84
+ `ghcr.io/jimhoyd-com/urlcode` to pull. After the release tag exists, build it from that checkout:
80
85
 
81
86
  ```sh
82
- git clone --branch v0.3.0 https://github.com/jimhoyd-com/urlcode.git
83
- docker build -t urlcode:0.3.0 urlcode
84
- docker run --rm -p 127.0.0.1:3000:3000 -v "$PWD:/project:ro" urlcode:0.3.0 \
87
+ git clone --branch v0.4.1 https://github.com/jimhoyd-com/urlcode.git
88
+ docker build -t urlcode:0.4.1 urlcode
89
+ docker run --rm -p 127.0.0.1:3000:3000 -v "$PWD:/project:ro" urlcode:0.4.1 \
85
90
  serve --project /project --host 0.0.0.0
86
91
  ```
87
92
 
@@ -10,6 +10,9 @@ ship, together with the declarations; `dist` is never committed. Make is an opti
10
10
  shortcut layer; npm and the CLI work on Windows, macOS and Linux. No global
11
11
  package install, hosting account, database or Docker is needed for the local loop.
12
12
 
13
+ For the repository's prose-only checks, CI selection and release helper commands,
14
+ see [the development pipeline](DEVELOPMENT-PIPELINE.md).
15
+
13
16
  ## Try the runtime
14
17
 
15
18
  From the runtime checkout, `make dev` installs locked dependencies if needed and
@@ -48,12 +48,18 @@ Every middleware and the function share the same request and context. Validated
48
48
  with a fresh `context.state` object for this chain. State never survives the
49
49
  request. Header edits are visible downstream; editing the request does not
50
50
  reroute it or change already validated inputs. Request bodies are single-use:
51
- reading one consumes it for downstream code. There is no `clone()` or streaming
52
- API; pass parsed data through `context.state` when needed.
51
+ reading one consumes it for downstream code. On a `sandbox: true` route there is
52
+ no `clone()` or streaming API at all, so pass parsed data through
53
+ `context.state`; a trusted route receives Node's own `Request`/`Response` and so
54
+ does have `clone()`, but passing parsed data through `context.state` keeps the
55
+ chain portable between the two modes.
53
56
 
54
57
  Function responses support the existing text/JSON guest API. To transform their
55
- body, read it and return a new `Response`. Native redirect/respond/page/static/
56
- download bodies are opaque and cannot be read through `text()` or `json()`.
58
+ body, read it and return a new `Response`. On a `sandbox: true` route, native
59
+ redirect/respond/page/static/download bodies are opaque and cannot be read
60
+ through `text()` or `json()`; a trusted chain receives them as an ordinary
61
+ `Response` and can read them, so wrapping `respond: {text: hello}` and returning
62
+ `HELLO` works there and fails in the guest.
57
63
  Returning the same native response preserves original bytes, including binary
58
64
  files, ranges and HEAD lengths. You may add headers, but cannot change its
59
65
  original status or existing native headers while preserving that body. To replace