@jimhoyd/urlcode 0.4.0-alpha.2 → 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 (115) 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 +44 -54
  6. package/ROADMAP.md +61 -376
  7. package/dist/BUILD-MANIFEST.json +15 -15
  8. package/dist/agents-guide.js +6 -6
  9. package/dist/build-static.js +2 -0
  10. package/dist/cli.js +3 -3
  11. package/dist/explain-cli.js +4 -2
  12. package/dist/explain.js +7 -1
  13. package/dist/extensions.js +1 -1
  14. package/dist/manifest.js +7 -1
  15. package/dist/mcp-authoring.js +2 -2
  16. package/dist/mcp.js +1 -1
  17. package/dist/policies.js +3 -1
  18. package/dist/prerender.js +4 -0
  19. package/dist/readiness.js +3 -0
  20. package/dist/route-diff.js +12 -5
  21. package/dist/types/explain.d.ts +6 -0
  22. package/dist/types/extensions.d.ts +1 -1
  23. package/dist/types/manifest.d.ts +5 -1
  24. package/dist/types/readiness.d.ts +2 -0
  25. package/dist/types/types.d.ts +8 -1
  26. package/dist/types.js +8 -1
  27. package/dist/typescript-authoring.js +18 -7
  28. package/docs/AI-AUTHORING.md +10 -6
  29. package/docs/ASSETS.md +2 -1
  30. package/docs/AUTH-BACKUP.md +32 -0
  31. package/docs/BEST-PRACTICES.md +17 -9
  32. package/docs/CAPABILITIES.md +1 -1
  33. package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
  34. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
  35. package/docs/CI.md +8 -3
  36. package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
  37. package/docs/DEVELOPMENT-PIPELINE.md +181 -0
  38. package/docs/EXTENSIONS.md +15 -5
  39. package/docs/FRAMEWORK.md +22 -18
  40. package/docs/FUNCTION-SECURITY.md +44 -0
  41. package/docs/LOCAL-DEVELOPMENT.md +3 -0
  42. package/docs/OPEN-DECISIONS.md +184 -212
  43. package/docs/OPERATIONAL-PROOF.md +3 -3
  44. package/docs/OPERATIONS.md +3 -3
  45. package/docs/POLICIES.md +13 -5
  46. package/docs/PRERENDER.md +23 -11
  47. package/docs/PROJECT-DIRECTION.md +3 -3
  48. package/docs/READINESS.md +6 -2
  49. package/docs/README.md +18 -41
  50. package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
  51. package/docs/RELEASE-READINESS.md +6 -6
  52. package/docs/RELEASE-SECURITY.md +68 -168
  53. package/docs/RESILIENCE.md +3 -3
  54. package/docs/ROUTING.md +3 -4
  55. package/docs/SECURITY-AUDIT.md +2 -2
  56. package/docs/SPECIFICATION.md +7 -5
  57. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +6 -5
  58. package/docs/SPIKE-BUSINESS-SUITE.md +14 -6
  59. package/docs/SPIKE-CORE-LAYERING.md +48 -17
  60. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +7 -5
  61. package/docs/SPIKE-LAMBDA-COMPILE.md +8 -0
  62. package/docs/STATIC.md +14 -3
  63. package/docs/TOOLING.md +4 -3
  64. package/docs/TYPESCRIPT-AUTHORING.md +6 -1
  65. package/docs/VERSION-ALIGNMENT.md +46 -205
  66. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
  67. package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +102 -0
  68. package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +15 -5
  69. package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +15 -3
  70. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
  71. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
  72. package/docs/archive/2026-09-19/ROADMAP.md +386 -0
  73. package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +10 -0
  74. package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +25 -14
  75. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
  76. package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +12 -2
  77. package/docs/archive/README.md +27 -0
  78. package/docs/policies/agents.md +1 -1
  79. package/docs/policies/compression.md +3 -2
  80. package/docs/policies/security.md +3 -2
  81. package/examples/assets/example.yaml +1 -1
  82. package/examples/cookbook/route-index.json +1 -1
  83. package/examples/cookbook/routes/middleware.yaml +1 -1
  84. package/examples/prerender/README.md +14 -6
  85. package/examples/prerender/functions/page.mjs +4 -2
  86. package/examples/prerender/middleware/template.mjs +1 -1
  87. package/examples/prerender/prerender.mjs +1 -1
  88. package/examples/prerender/urlcode.yaml +8 -4
  89. package/llms-full.txt +117 -45
  90. package/llms.txt +5 -4
  91. package/package.json +24 -4
  92. package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
  93. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +17 -19
  94. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +9 -9
  95. package/recipes/authenticated-json-api/README.md +4 -3
  96. package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
  97. package/recipes/authenticated-json-api/recipe.yaml +1 -1
  98. package/recipes/contact-form/functions/contact.mjs +2 -1
  99. package/recipes/contact-form/recipe.yaml +2 -2
  100. package/recipes/cors-api/README.md +2 -2
  101. package/recipes/cors-api/recipe.yaml +1 -1
  102. package/recipes/health-page/README.md +1 -1
  103. package/recipes/json-api/README.md +1 -1
  104. package/recipes/json-api/recipe.yaml +3 -3
  105. package/recipes/middleware/README.md +8 -4
  106. package/recipes/protected-download/README.md +1 -1
  107. package/recipes/protected-download/recipe.yaml +1 -1
  108. package/recipes/static-plus-api/README.md +2 -2
  109. package/recipes/static-plus-api/public/index.html +1 -1
  110. package/recipes/static-plus-api/recipe.yaml +1 -1
  111. package/recipes/static-plus-api/urlcode.yaml +1 -1
  112. package/recipes/typescript/recipe.yaml +4 -4
  113. package/skills/urlcode/SKILL.md +6 -6
  114. package/starters/default/AGENTS.md +6 -6
  115. package/docs/SPIKE-MONOREPO.md +0 -322
package/ROADMAP.md CHANGED
@@ -1,376 +1,61 @@
1
- # Public roadmap
2
-
3
- URLCode is a portable runtime for programmable URL behavior, not a URL
4
- shortener. Stored short links are an operator-installed extension, not a core
5
- handler. The
6
- [project direction](docs/PROJECT-DIRECTION.md) explains how application
7
- projects and provider adapters fit without redefining or restricting the free
8
- runtime.
9
-
10
- This is the public delivery sequence. Tests accompany every feature, not a
11
- separate late phase. The stable 0.1 self-hosted release covers much of M0/M1 plus initial
12
- process/container packaging and benchmarks. Provider adapters and the remaining
13
- production-readiness gates remain open.
14
-
15
- ## 0.4.0-alpha.2 current alpha
16
-
17
- `0.4.0-alpha.2` is a behavior-changing release on top of `0.4.0-alpha.1`:
18
- **`function` and `middleware` routes now run trusted and unsandboxed by
19
- default** in the host process, with full Node/filesystem/network access,
20
- exactly like any other project code — instead of always dispatching through
21
- the QuickJS/WebAssembly worker pool. Sandboxing is now an explicit per-route
22
- opt-in via `sandbox: true`; a route that declares it gets exactly the
23
- isolation every earlier alpha provided, unchanged. See
24
- [docs/SPIKE-DEFAULT-TRUST-MODEL.md](docs/SPIKE-DEFAULT-TRUST-MODEL.md) for
25
- the maintainer's decision and rationale, and
26
- [docs/FUNCTION-SECURITY.md](docs/FUNCTION-SECURITY.md) for the resulting
27
- contract of both execution modes.
28
-
29
- **This changes existing projects with no YAML edit.** Every `function`/
30
- `middleware` route that does not declare `sandbox` — which, before this
31
- release, meant every such route in every existing project — now runs trusted
32
- instead of sandboxed the moment the runtime is upgraded to `0.4.0-alpha.2` or
33
- later. Binding grants (`env`/`secrets`) are unaffected: a route still
34
- receives only what it declares and an operator policy pins to the project
35
- revision, whether trusted or sandboxed. Review which of your project's
36
- `function`/`middleware` routes handle input or code you would not otherwise
37
- trust with full Node/filesystem/network access, and add `sandbox: true` to
38
- those specifically (docs/AI-AUTHORING.md's "Deciding when a route needs
39
- `sandbox: true`") before upgrading a project that relies on the old,
40
- always-sandboxed behavior. An operator policy pinned to a project's revision
41
- is invalidated by this upgrade regardless of whether the project's own YAML
42
- changed, since the project hash includes the trust-model-affecting change;
43
- re-derive and re-approve it (`urlcode permissions`) after upgrading.
44
-
45
- ## 0.4.0-alpha.1
46
-
47
- `0.4.0-alpha.1` is the first alpha of the extension contract and the agent
48
- tooling on top of the `0.3.0` self-hosted release. It carries: the
49
- revision-pinned extension contract, capabilities and provider conformance,
50
- strict redirect interchange, bulk import, recipes and search, TypeScript
51
- guests, conditions, bounded proxy and signals, MCP read and authoring modes,
52
- the `context`, `explain`, `manifest` and `schema` queries, short forms,
53
- route-level auth, `init --with`, the agent benchmark harness, `llms-full`,
54
- and the generated `AGENTS.md` and skill. It is an alpha: provider
55
- deployments, soak and independent security review remain open. The
56
- extension packages declare `@jimhoyd/urlcode >=0.4.0-alpha.1 <0.5.0` and
57
- are published after core.
58
-
59
- ## Extensions: accounts, administration and presentation implemented, unreleased
60
-
61
- The runtime now carries a generic, revision-pinned extension contract
62
- (`@jimhoyd/urlcode/extensions`, [extensions](docs/EXTENSIONS.md)): a project
63
- declares versioned `extensions.<name>` blocks, exclusive `extension` mounts and
64
- `policies.extensions` requirements; the operator supplies the implementations
65
- in a host file loaded with `--host-file`, outside the project. Guest requests
66
- never see `Cookie`, `Authorization` or declared credential headers. Cloudflare
67
- refuses extensions until its artifact format can run them.
68
-
69
- The implementations live in their own repositories, each with an
70
- implementation-status file that is the authoritative feature list:
71
- [urlcode-auth](https://github.com/jimhoyd-com/urlcode-auth) (accounts, sessions,
72
- MFA, roles, account page, operator CLI), [urlcode-admin](https://github.com/jimhoyd-com/urlcode-admin)
73
- (users, sessions, roles, audit, approvals, cases, impersonation) and
74
- [urlcode-ui](https://github.com/jimhoyd-com/urlcode-ui) (escaped templates,
75
- partials, themes, translations). All three are Apache-2.0 and published to
76
- npm as `0.1.0-alpha.1` while first-release acceptance is reviewed; see
77
- [issue 58](https://github.com/jimhoyd-com/urlcode/issues/58) for what remains:
78
- browser and device WebAuthn coverage, accessibility assessment, soak, backup
79
- and recovery drills on a deployment, live provider senders and independent
80
- security review. [The framework](docs/FRAMEWORK.md) describes how the four
81
- packages compose, and [next steps](docs/NEXT-STEPS.md) is the phased plan to
82
- close the remaining gaps.
83
-
84
- ## Capability foundation — implemented, unreleased
85
-
86
- `urlcode capabilities [--target self-hosted|cloudflare|aws|vercel] [--json]`
87
- and the public capability API centralize target support for handlers, bindings
88
- and effective policies. Runtime activation and Cloudflare builds use the shared
89
- preflight; unknown targets fail closed and unsupported requirements identify the
90
- route and capability before resources or artifacts are created. The existing
91
- compiled route IR is documented, not replaced. Configuration-dependent and
92
- delegated behavior remain explicit; all provider deployments remain unverified.
93
- See [capabilities](docs/CAPABILITIES.md) and the
94
- [next-phase review and implementation status](docs/NEXT-PHASE-PLAN.md).
95
-
96
- ## Portability and URL behavior — implemented, unreleased
97
-
98
- Strict redirect interchange supports CSV/JSON/YAML, Netlify and Cloudflare
99
- `_redirects`, a bounded Netlify TOML subset and Vercel redirects. Conversion
100
- reports refuse unsupported semantics; provider differences require explicit
101
- acknowledgment and are never called lossless. A synthetic conformance fixture
102
- and bounded HTTPS runner distinguish local adapter tests from observed
103
- provider deployments. Actual Cloudflare/AWS/Vercel deployment evidence remains
104
- pending; this does not complete M4.
105
-
106
- Exact query/header/cookie/origin/method conditions and disjoint conditional
107
- redirect/response cases run in the self-hosted, AWS and Vercel runtimes.
108
- Cloudflare refuses these until its compiler can preserve their semantics.
109
- Self-hosted proxy and webhook signals use explicit revision-pinned operator
110
- origin grants, connection-pinned public DNS and bounded transport. Signals
111
- have no durable delivery or retry guarantee. Other targets refuse proxy and
112
- signals. See [conditions](docs/CONDITIONS.md), [egress](docs/EGRESS.md),
113
- [interchange](docs/INTERCHANGE.md) and
114
- [provider verification](docs/PROVIDER-VERIFICATION.md).
115
-
116
- ## Developer ecosystem — implemented, unreleased
117
-
118
- Bundled Git-owned recipes, safe bulk imports into route includes, build-time
119
- TypeScript guest transpilation, read-only inspection APIs and optional stdio
120
- MCP tooling are available. The runtime still executes only JavaScript, and
121
- TypeScript authoring adds no execution mode: transpilation happens at build
122
- time, is not type checking, and a `sandbox: true` route's emitted JavaScript
123
- runs inside QuickJS/WASM exactly as before. Bulk benchmarks
124
- successfully cover 1,000, 10,000 and 100,000 routes without relaxing parser
125
- limits. See [recipes](docs/RECIPES.md), [bulk evidence](docs/BULK.md),
126
- [TypeScript authoring](docs/TYPESCRIPT-AUTHORING.md) and
127
- [SDK/MCP](docs/TOOLING.md). These features do not supply durable signals,
128
- protected downloads, a remote marketplace or provider deployment proof.
129
-
130
- ## TypeScript source and shipped declarations — implemented, unreleased
131
-
132
- The runtime, scripts, tests and benchmarks are TypeScript under a strict
133
- configuration, checked by `npm run typecheck` inside `npm run verify`. The
134
- package ships `dist/`: Node's own type stripping of the source with the
135
- specifier extension rewritten, so it is the same JavaScript line for line,
136
- plus `.d.ts` declarations for every export (`urlcode`, `@jimhoyd/urlcode/plugins`,
137
- `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`,
138
- `@jimhoyd/urlcode/prerender` and the three provider entries). `dist` is never
139
- committed; the build runs in the digest-pinned release container, its hashes
140
- are recorded in the manifest, and a CI job builds twice and diffs the trees.
141
- Measured cold start, throughput and memory of `dist/` equal the previous
142
- JavaScript (see [performance](docs/PERFORMANCE.md)). Contributors need Node
143
- 22.18+ to run the source directly; installed packages still run on 22.13+.
144
- See [TypeScript](docs/TYPESCRIPT.md).
145
-
146
- ## Host policies and plugins — implemented, unreleased
147
-
148
- A `policies` block in YAML, reusable `profiles` and an operator plugin API
149
- close the gaps the [extensions spike](docs/SPIKE-EXTENSIONS.md) ranked
150
- highest: no cross-cutting behavior, no per-client throttle, no bot policy, no
151
- security-header preset, no compression and no response cache. Five policies
152
- (`throttle`, `agents`, `security`, `compression`, `cache`) run in the host
153
- process after route match and before the route contract, all off by default,
154
- with a per-target table that refuses at activation what a target cannot
155
- enforce: the serverless adapters take `agents`, `security`, `cache` and
156
- route-partitioned `throttle`; the Cloudflare build compiles `agents` and
157
- `security` into the artifact and refuses the rest with the route named.
158
- `--trusted-proxies` names the hops allowed to set `X-Forwarded-For` for client
159
- identity. Plugins are host code passed to `startServer` and the adapters on
160
- the same hook seam the policies use; nothing in YAML names one. See
161
- [policies](docs/POLICIES.md) and [plugins](docs/PLUGINS.md).
162
-
163
- This checkpoint is on a branch and not in a published release. An
164
- interoperability review of the five policies together, the spike's `report`
165
- mode headers on every target and the audit's policy table remain open, and no
166
- deployment has exercised a policy on a provider.
167
-
168
- ## Provider adapters — Vercel and AWS Lambda native handlers
169
-
170
- `@jimhoyd/urlcode/vercel` serves a project as a Vercel Node function, reusing the
171
- runtime's transport-agnostic handle() and a shared response writer, so a
172
- deployment returns byte-identical status, body and headers to the self-hosted
173
- server. Bindings arrive through a `URLCODE_POLICY` environment variable holding
174
- the same revision-pinned grant document the operator policy file carries.
175
-
176
- Native handlers only: `function` and `middleware` routes are refused at
177
- activation, trusted or sandboxed alike, because they need the self-hosted Node
178
- lifecycle and a sandboxed one would pay worker and WASM startup on every cold
179
- start. The
180
- `@jimhoyd/urlcode/aws` does the same for a Lambda Function URL or API Gateway HTTP API.
181
- Payload format 2.0 only: format 1.0 supplies an already-decoded path and query,
182
- and this runtime rejects ambiguous encoding deliberately, so rebuilding a target
183
- from decoded parts would misrepresent the request. Response policy, including
184
- content length, now lives in one place shared by every host rather than partly
185
- relying on Node's implicit behaviour.
186
-
187
- Neither adapter has been deployed; see the [Vercel](docs/VERCEL.md) and
188
- [AWS](docs/AWS.md) guides, which state what stays unverified as a result.
189
-
190
- ## Provider targets — Cloudflare Workers
191
-
192
- Cloudflare Workers has no worker threads, no filesystem and no runtime code
193
- generation, so it gets a compiler rather than an adapter: `urlcode build
194
- --target cloudflare` emits a Worker, the compiled routes and Ajv standalone
195
- validators, and `@jimhoyd/urlcode/cloudflare` serves them with the same matching, request
196
- policy and response policy as every other host. Declarative routes only —
197
- redirects and declared responses with parameters, defaults, validation, response
198
- headers, `enabled` and `expires`. Functions, middleware, assets and
199
- bindings are refused at build time with the route named, so an unsupported
200
- project fails the build instead of the deployment. Bindings are refused even as
201
- literals, because a build artifact must never carry a secret.
202
-
203
- Making this possible moved request-time matching into `src/match.ts` and header
204
- validation into `src/header-validation.ts`, both free of Node imports, so one
205
- implementation now serves the Node server, the serverless adapters and the
206
- Worker. `test/header-validation.test.ts` compares the header rules against
207
- `node:http` across the full character range, because disagreeing there is header
208
- injection, and `test/cloudflare.test.ts` asserts the Worker and the self-hosted
209
- server return the same status, body and headers for the same project.
210
-
211
- This has not been deployed to Cloudflare; see the
212
- [Cloudflare guide](docs/CLOUDFLARE.md) for the two request-level differences the
213
- platform imposes and what stays unverified.
214
-
215
- ## Installation and publication — 0.1.0
216
-
217
- Added a tag-driven release workflow that reuses the audited candidate build path,
218
- publishes a GitHub release with the signed tarball, SBOM, manifest, `SHA256SUMS`
219
- and a rendered Homebrew formula, and optionally publishes to npm (with
220
- provenance) and GHCR behind repository variables. Added a checksum-verifying
221
- `install.sh`, a Homebrew formula template rendered only from measured bytes, and
222
- an [installation guide](docs/INSTALL.md) covering npm, Homebrew, the script, the
223
- container and provenance verification.
224
-
225
- 0.1.0 was released from this pipeline: the GitHub release carries the signed
226
- tarball, SBOM, manifest, `SHA256SUMS` and Homebrew formula, and an install from
227
- the published release was verified end to end. npm and GHCR publication stay
228
- opt-in and remain unproven until enabled, so the Homebrew formula's registry URL
229
- does not resolve yet.
230
-
231
- Added tunnel and [monitoring](docs/MONITORING.md) recipes, and extended
232
- `urlcode benchmark` to measure a running deployment through its real path with
233
- warm-up and shed/transport separation; see [load testing](docs/LOAD-TESTING.md).
234
- M3's remaining gap is sustained soak and slow-peer behaviour, which that tool
235
- does not cover.
236
-
237
- ## Hardening checkpoint — alpha.8
238
-
239
- Bound HTTP admission and inactive sockets, and correct management
240
- defaults/method responses.
241
- The [readiness register](docs/RELEASE-READINESS.md) distinguishes tested safeguards
242
- from deployment and stable-release gates. Feature breadth does not imply stability.
243
-
244
- ## Live short links — alpha.8 (removed from core, superseded)
245
-
246
- Implemented an optional native `link` handler, local SQLite persistence, CLI CRUD
247
- and a separate authenticated management API. Links became visible without YAML
248
- changes/reloads; versioned writes prevented silent lost updates. No guest storage
249
- or network access was added. Same-host only; distributed storage, general state,
250
- user accounts and provider adapters remained open.
251
-
252
- This native `link` handler, its SQLite store, CLI and management API were
253
- removed from core in the layering work that followed: stored short links are
254
- moving to a future `urlcode-dynamic-link` extension package (mount-based, like
255
- `auth`/`admin`, not yet published). A project that used `link`/`dynamicLinks`
256
- needs that extension once it ships; there is no in-core replacement or
257
- deprecation shim.
258
-
259
- ## Middleware — alpha.7
260
-
261
- Implemented route-local ordered `next()` middleware around every handler, early
262
- responses, request-local state and shared sandbox deadlines. Plain native routes
263
- retain their fast path. Native bodies stay opaque; middleware coverage requires
264
- explicit assertions. See [middleware](docs/MIDDLEWARE.md).
265
-
266
- ## One starter — alpha.6
267
-
268
- `urlcode init <directory>` always creates the same function-plus-redirect project.
269
- There is no template selector. The public urlcode-template repository mirrors
270
- those examples with a pinned npm dependency. Richer asset examples remain under
271
- examples/assets. Historical starter branches are not maintained.
272
-
273
- ## Route readiness and local project benchmarks — alpha.5
274
-
275
- Implemented route inventory, expected-count checks, active route/method coverage,
276
- generated native probes plus explicit fixtures, and bounded assertion-aware local
277
- benchmarks. [Readiness](docs/READINESS.md) documents the gate and remaining deployment,
278
- soak, remote-destination and business-coverage work. This does not complete M3/M4.
279
-
280
- ## HTTP configuration and standalone starter — 0.1.0-alpha.4
281
-
282
- Implemented bounded request body/media-type/JSON checks, literal response headers
283
- and separate Set-Cookie values, and native text/JSON responses. See [HTTP](docs/HTTP.md)
284
- for precise scope; automatic CORS, multipart, streaming and other listed features
285
- remain open. The public [urlcode-template](https://github.com/jimhoyd-com/urlcode-template)
286
- provides two routes with a pinned runtime dependency and cross-platform CI.
287
-
288
- ## Native assets — 0.1.0-alpha.3
289
-
290
- Implemented the page/static/download portion of M2: project-contained asset
291
- snapshots, automatic MIME types, attachment names, HEAD, cache validators and
292
- single byte ranges. Dedicated public directories, symlink/hardlink rejection and
293
- bounded memory are part of the contract. [Asset guide](docs/ASSETS.md).
294
- Bulk tools, recipes and best-effort signals were added in the unreleased next-phase work above; they were not part of alpha.3.
295
-
296
- ## Security correction — 0.1.0-alpha.2
297
-
298
- <!-- trust-model-prose: historical -->
299
- All function code is untrusted. Node host execution has been replaced by
300
- QuickJS/WebAssembly isolation with fresh invocation state, no ambient host or
301
- network APIs, bounded resources, restricted module graphs and revision-pinned
302
- operator binding policy. This protection is part of the free product. See the
303
- [security model](docs/FUNCTION-SECURITY.md). Full Fetch/Node
304
- API compatibility and network integrations were not part of that alpha and
305
- remain outside the 0.1 contract. That default was superseded in
306
- `0.4.0-alpha.2`, which made this isolation the per-route `sandbox: true`
307
- opt-in instead (see
308
- [docs/SPIKE-DEFAULT-TRUST-MODEL.md](docs/SPIKE-DEFAULT-TRUST-MODEL.md)).
309
-
310
- ## Earlier implementation checkpoint — 0.1.0-alpha.1
311
-
312
- Implemented: versioned strict YAML/JSON Schema subset, explicit file composition,
313
- redirect/parameter semantics, JavaScript Request/Response functions with bounded
314
- workers, scoped binding context, init/add/validate/dev/serve/test/doctor, indexed
315
- snapshots, last-good reloads, graceful shutdown, health/readiness, safe request
316
- logs, two runnable starters, ESLint and unit/HTTP/package tests. Cross-platform
317
- CI and a non-root container build are included. See the [contract](docs/SPECIFICATION.md)
318
- and [operations guide](docs/OPERATIONS.md) for exact support and evidence limits.
319
-
320
- Still open in the early contract: host namespaces, stable identity beyond paths,
321
- fuller parameter vocabulary. Build-time TypeScript authoring and capability planning are now implemented in the unreleased work above.
322
- No claims of complete M0/M1 or stable production readiness. M2–M4 work continues
323
- in the order below; a few independently useful operational foundations shipped early.
324
-
325
-
326
- | Milestone | Scope | Completion evidence |
327
- |---|---|---|
328
- | M0 — contract/build loop | YAML schema, matching/input semantics, function API, composition, runtime/packaging prototype, CI | Fixture validates; invalid definitions fail; verification runs |
329
- | M1 — local alpha | CLI, aliases, redirects, parameters, custom functions, env/secrets, indexed routes and reload | Redirect plus function tested locally without accounts/DB; invalid reload preserves working version |
330
- | M2 — bulk/reusable behavior | CSV/YAML/JSON, safe bulk edits/checks, templates, best-effort async signals, simple pages/static/downloads | 10k-route example, local side-effect tests, safe file serving, benchmark datasets |
331
- | M3 — self-hosted beta | Process/container deployment, domain/TLS guide, packages/Homebrew, ngrok, monitoring and load tools | Install, test, deploy, observe and roll back a real project |
332
- | M4 — provider public release | Cloudflare/AWS/Vercel adapters, provider conversion, capability/limit checks and stable docs | Baseline redirect/parameter/function fixtures on each advertised initial target; additional capability gaps explicit |
333
- | M5 — advanced public features | Bounded proxies, protected/one-time downloads, durable signals/state, broader catalog and API/SDK/MCP | Feature-specific guarantees, tests and portable capability reports |
334
-
335
- The first provider-capable public release is M4; the 0.1 self-hosted release is
336
- useful within its documented scope. A marketplace and advanced stateful
337
- features are not prerequisites. No web UI/TUI or framework-hosting platform.
338
- Netlify starts as redirect interchange after the initial provider adapters.
339
-
340
- ## Launch and stabilize the free version
341
-
342
- Stability means repeatable installs/upgrades, dependable routing/functions,
343
- working deployment and rollback, useful diagnostics, and serious recurring
344
- issues from actual users addressed. Feedback begins with usable alphas, but the
345
- free version is not stable until that evidence exists; do not invent a calendar
346
- deadline or adoption-count threshold.
347
-
348
- Keep the architectural direction: reusable runtime/compiler, provider adapters,
349
- separate configuration and secrets, versioned artifacts, Git-owned definitions
350
- and observable behavior. Avoid assumptions that would force users to rewrite
351
- projects later. No artificial restrictions in the free version; it is licensed
352
- under Apache-2.0.
353
-
354
- ## Starter delivery
355
-
356
- The [starter plan](docs/STARTERS.md) makes both Git clone and CLI initialization
357
- release requirements: one function-plus-redirect starter in M1, bulk-growth examples in
358
- M2, and the business foundation with self-host tooling in M3. Provider recipes
359
- follow tested M4 adapters. All use the same runtime and portable project format.
360
-
361
- ## Quality gates
362
-
363
- - Same fixtures pass on the local reference and each claimed runtime adapter.
364
- A static exporter alone is not a complete function-capable adapter.
365
- - Pure redirects avoid Lambda/per-route user functions; generated shared routing
366
- is allowed where native provider rules cannot preserve behavior.
367
- - CSV exports report unsupported nested content instead of silently losing it.
368
- - 1k/10k/100k datasets measure compile/reload, memory, latency and throughput.
369
- - Default tests run against local HTTP/fake services; ngrok is optional.
370
- - Load tests use bounded owned targets and do not follow third-party redirects.
371
- - Invalid reload preserves working config; secret values stay out of artifacts.
372
- - Installation claims match tested OS/architecture packages.
373
-
374
- Implementation runtime, exact schema/function API, first adapter order and package
375
- format are decided through early prototypes. Do not invent performance targets
376
- or advertise all providers before they pass tests.
1
+ # Roadmap
2
+
3
+ URLCode lets people and agents describe URL behavior in YAML and write application
4
+ code only where the framework cannot express it. The free Apache-2.0 runtime
5
+ stays useful on its own. [Project direction](docs/PROJECT-DIRECTION.md) owns the
6
+ principles; [the specification](docs/SPECIFICATION.md) owns implemented behavior.
7
+
8
+ ## What works now
9
+
10
+ The source at `db375bf` provides declarative routing, responses, assets, policies,
11
+ conditions, proxy/signals, trusted Node functions and middleware, and opt-in
12
+ `sandbox: true` isolation. Target support differs: use `urlcode capabilities`
13
+ before promising a deployment. Stored short links have no supported package.
14
+
15
+ Auth, admin, UI and middleware are separate optional packages. Auth/admin already
16
+ render through the shared UI kit when configured. Core includes scaffolding,
17
+ searchable recipes/examples, compact context, schema queries, a semantic manifest,
18
+ and MCP inspection with separately enabled authoring. These are implemented,
19
+ not future phases. See [the framework](docs/FRAMEWORK.md).
20
+
21
+ ## Next work
22
+
23
+ Monorepo work is starting now. Move middleware in as a separate package before
24
+ folding it into core; coordinate the guidance cleanup with that migration.
25
+
26
+ 1. **Make the existing product coherent.** Keep docs, examples, generated LLM
27
+ resources, installed skills and the standalone template consistent with their
28
+ runtime version. Resolve the [open decisions](docs/OPEN-DECISIONS.md).
29
+ [Issue 168](https://github.com/jimhoyd-com/urlcode/issues/168) tracks checking
30
+ schema-invalid documentation examples beyond the existing prose checks.
31
+ [Issue 174](https://github.com/jimhoyd-com/urlcode/issues/174) retains the
32
+ extension-schema retrieval proposal.
33
+ 2. **Measure the agent experience.** The benchmark harness and authoring evals
34
+ exist, but the committed baseline is a stub, not a real-model measurement.
35
+ [Issue 173](https://github.com/jimhoyd-com/urlcode/issues/173) tracks the
36
+ measurement. Run the existing tasks, retain the raw results and use observed friction to
37
+ choose improvements. See [the benchmark](benchmarks/agent/README.md) and
38
+ [the broader experiment proposal](docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md).
39
+ 3. **Close release evidence gaps.** Browser/device coverage, accessibility,
40
+ independent security review, deployed recovery/soak tests and real provider
41
+ verification remain distinct from source implementation and local tests.
42
+ [Issue 58](https://github.com/jimhoyd-com/urlcode/issues/58) and
43
+ [release readiness](docs/RELEASE-READINESS.md) retain those gates. Live
44
+ Google/Apple/SES checks remain explicitly deferred.
45
+ 4. **After the monorepo move, consolidate middleware into core.** Move the
46
+ existing middleware package first, preserving behavior.
47
+ [issue 172](https://github.com/jimhoyd-com/urlcode/issues/172) tracks the later consolidation
48
+ and consumer migration; package retirement is not part of the initial move. Static targets continue rejecting
49
+ request-time middleware.
50
+ 5. **Choose expansion from evidence.** Collections, a business application suite,
51
+ and per-route Lambda compilation are proposals,
52
+ not available features. Decide scope before implementing them.
53
+
54
+ ## Records and ownership
55
+
56
+ Track actionable defects and feature gaps in the owning repository's issues.
57
+ This page explains sequence; package contracts explain behavior; dated evidence
58
+ states what was actually tested. Completed and superseded plans live in the
59
+ [archive](docs/archive/README.md), including the previous release chronology
60
+ and detailed phase plan. Archiving an implementation plan does not close its
61
+ remaining operational acceptance gates.
@@ -3,18 +3,18 @@
3
3
  "files": {
4
4
  "dist/adapters.js": "a7153ec52f2ed7e0cdd8ed7d433504815f402ec53b2b1c3d38d0cf0e2fcf6509",
5
5
  "dist/agent-lists.js": "35ab484198897501d011cb6cc00b5a6787192e19df29c97646e8c118fcc19eef",
6
- "dist/agents-guide.js": "a76e9818ec995a349085a53e8badd3ef49734275557b50bc7dcbe995ce3d0e9f",
6
+ "dist/agents-guide.js": "0b19909aee3f8a1039c34c43ff58116ec1bc3f1875e3dbc34b3b01655273201c",
7
7
  "dist/assets.js": "0833b093d2fe457ce68281110dc07d47914992753607741c13b339d92c76cf5c",
8
8
  "dist/authoring-files.js": "ad32814f9ae9c549c6247396f982aa700d13a293cc0a589609eccc68f8965550",
9
9
  "dist/authoring.js": "ec5240e8686c124f4601229b45f89ce112d9eeb9d35e3059fe55e93056c45209",
10
10
  "dist/aws.js": "7d8e3a97b4f6dbefbd68da058f05192469d1efcaed5458b284863ebe462e060e",
11
11
  "dist/build-cloudflare.js": "af4f49ad45287d7c4105bb84b1592b5b8e76c930f7159505338298f7c5fd5651",
12
- "dist/build-static.js": "a2d2fcef3099805392da9140096d9843226bfb582dc046acf9a77f37b8a0d528",
12
+ "dist/build-static.js": "fe01ef4fcc66d83d8be7a787538b55974e84fd3d7c89e26f0a2f1a152762d565",
13
13
  "dist/bulk.js": "aac88d422bd9a7a341a421fb4e29ff5580b28d39250b7f1f5315ba20ae92da8a",
14
14
  "dist/capabilities.js": "28dba05b3851b8b12b3c1b809af87b732f84d601c737afe9225abe429886e691",
15
15
  "dist/capability-query.js": "ba148b27c6a60f8c895f13f26468b16c18ae0a5cddea26b602965c8efbba90ae",
16
16
  "dist/catalog.js": "ab1c01c9296cc277d2bd4fd682aeb053656a7d9f91c61974f4b78feea0211de8",
17
- "dist/cli.js": "f6097c246c6b9006df58e3463648013ec8837ab3296a8c294809313503be167a",
17
+ "dist/cli.js": "ff3e583bd43044bdc06122e33c6137f400f677967ebff1ab49900b517682531c",
18
18
  "dist/client-address.js": "9d0d01466aab23124330605c5a0e0f981f87c897858acfad909f546a63df165b",
19
19
  "dist/cloudflare.js": "17b43b0a6b6a0ef8a893415c989e4ae3976d97cc966adbf897b785f9adc472e3",
20
20
  "dist/compliance-rules/baseline.js": "6296dea6bcb9f9bb80ed6ed5ab2f456e7f74e6de2be5d2d98ca8c983fb4df3ab",
@@ -30,9 +30,9 @@
30
30
  "dist/egress.js": "2ae29fe2cb4590fd2f715abe6817db47fed2f50dd31f37946ecf42eafbb0dd9e",
31
31
  "dist/errors.js": "a908e66496afebbdd632a8c159a6e14d23f8f38798a0b4342ca122f2511c6cde",
32
32
  "dist/examples.js": "89ae2b764a688fe52acfb9b94d3d262d7742401ebd0ef10c9ebfc76edfa5631c",
33
- "dist/explain-cli.js": "dd342b21a96fc97a11488ac3528793ba0f5d4744b74a1d99085e142c4737b69a",
34
- "dist/explain.js": "9450d2699cbe65e5c078c443ba7a6cba95c748e75d455e05264083b3c93c92a2",
35
- "dist/extensions.js": "fed2b394466a7d406f9edeaedb71d9da81e50e3150eb7d01f2e7659104afeac7",
33
+ "dist/explain-cli.js": "f0f8f3b5046430d03c6abb629b38651c9c734b03456040735ce9d7e582b2bcd5",
34
+ "dist/explain.js": "dff1ed9a1bd818906ea867f4fb6d4601397943f9bdc4fceccf1809a365f95168",
35
+ "dist/extensions.js": "0506ec6289fa0582399d8c6d27727ffeb629bd442264248c921c63cc5b46ff01",
36
36
  "dist/function-sources.js": "16fad4abc81c7ee07b6cbcef2d23a9fc50e97a17cb4dde1db53469bbb555c96f",
37
37
  "dist/function-worker.js": "35771790ada4e1b36d447d4967a5e6cf0b543f32944e4c904f5fbf8998e824a5",
38
38
  "dist/functions.js": "0f09d1bd27eb655ceb5900a4a5e2b760d2028b02cb637a6a89a4eb5940828008",
@@ -45,10 +45,10 @@
45
45
  "dist/interchange-cli.js": "35bd70ba8077af5c3e39404ff0e5d28707632090a59a90645141252449fd3f04",
46
46
  "dist/interchange.js": "26789420af33344d9a08c00fe6b2708aa71994224df527b3075295be2151611e",
47
47
  "dist/logging.js": "8ec57fea26983aa071b05fe15d680d6550b03488101163ed9666da674118cf31",
48
- "dist/manifest.js": "977f0f1cbe4466f7fbd9f2a0745e016cb9ebae640bc264fba04b4d02f4dbaca9",
48
+ "dist/manifest.js": "aa450ff88d44b184ed1ad3331d1f81005a844a9f971e9e12dfa039952839bc75",
49
49
  "dist/match.js": "53ebcc2cda529a8d07fb83f8cb69a1446036ffc1641af3c4bd45a55851bded28",
50
- "dist/mcp-authoring.js": "5030f52ef9c7ebc7b97305fcce687eb0a0d6164e34fda87f8278135a1e6f3d65",
51
- "dist/mcp.js": "e2d70f63481dc40aae60f7c27128e6a09ec370b69fd68aee8890bcc990f4f304",
50
+ "dist/mcp-authoring.js": "4987b0ba08999309e07aea1b8bb31f4600cec22d79998e6982e56f765070587a",
51
+ "dist/mcp.js": "fa44f5f9e0bc4ca9790d1d1eb461147673c1539cf1704a4daae9bad30e4120eb",
52
52
  "dist/observability.js": "f4b1ab496f051fe3ef2ed36b2b2e469c63a8940700e2df852e6ff2d9cccdd5ae",
53
53
  "dist/operator-host.js": "e3dac9d43a83beb775202b4ab9eeeba7ac63cebfc5dcc669be38d825be587bd0",
54
54
  "dist/plugins.js": "a61aa933cd9e1b41020b86fae78aa3a7f45f5313124aa11b50cb57898722385e",
@@ -57,15 +57,15 @@
57
57
  "dist/policies/compression.js": "800f3ae3b1a4c1fe58c3d9908364ea9d78f655ff50c7f7589f584378e7f194b0",
58
58
  "dist/policies/security.js": "2b3bfdf82140156fea4b162adc28ee43ccd85c402fb200680dbb8d8706417420",
59
59
  "dist/policies/throttle.js": "bbb0cb1c24cf70d0a1ac32fc0618efb6c44e5b5d42564842f9c585ff7e501603",
60
- "dist/policies.js": "23fa019b7bf1cdedcc5f7772d2f157e4b381a554bce9e84594c2be4a0f8f76b8",
60
+ "dist/policies.js": "3750a7032ec7326aa544f4808683ad9eb91d4f2c5afe79904420cd8a355710a9",
61
61
  "dist/policy.js": "33c527008fafe4af18243ba668d069517c20659150f9f1d1a5a1d076e8724f89",
62
- "dist/prerender.js": "0386ea2411e00b9563b5349133f1d723947b01f837fa92d4c61a699c9cfb5cdb",
62
+ "dist/prerender.js": "6210999e848ab86c87f265943960ef63716896da7a40e248221a6a30425c56b8",
63
63
  "dist/project-tests.js": "445eaa139d2f1a2534369cfdeef081ef518de03218a467e7c5c2059caf840270",
64
64
  "dist/provider-verification.js": "ffcf9f05e8cb5f71ea46a97e279e8a613fdfdf9435ab02bd57af11e2680a9107",
65
65
  "dist/proxy.js": "34107e59ac0dbbea090efa3cf5badf3226c5302ba25e8f8ff9b2b5596ed80e50",
66
- "dist/readiness.js": "6ce69a005d7c45cf0ef97fa5eaba34279d43ebf28124eca18359d9db4f599808",
66
+ "dist/readiness.js": "90b54bc0cf60dd155d05789a1aad767bd67483d5fa6064ae8b1143f8cf9e06b2",
67
67
  "dist/recipes.js": "11853f23e63c3c65c5bed03a278cde0349d28c9fa23266c9907a2df318bb5fc3",
68
- "dist/route-diff.js": "eebac076a886ddfb2bf5b97ea09d81f888e7bb0063d482eddb8e5fef9acea107",
68
+ "dist/route-diff.js": "8d71d50d332ffaaed588656bd32aa36763260d1ad87bb62d7ea1c8fb23383969",
69
69
  "dist/router.js": "cf831025d5ed6778d1d6a0e541177799794e76766c82084ded81d3e87317ca1e",
70
70
  "dist/runtime.js": "0470dcab0774d2da3336e9ce76a4853757bf96081e52bd741f6b44c1bdd4161f",
71
71
  "dist/sandbox.js": "c88c5aa5dd95b4f55f1ba62da7969ad66dbd97c30f72e5b5f2817a2098ac1c65",
@@ -76,8 +76,8 @@
76
76
  "dist/site.js": "c40a06a6f9e2d6d3bea94727783c115fed4309bb0190787f58434eed64cbde84",
77
77
  "dist/tooling.js": "7409db98422325cd4181d073d645ea57debca0e54cb151123474b85f4d4ed7dc",
78
78
  "dist/trusted-functions.js": "befd256848be786f97e372c6af0e05ac08a0c93baed38bf2470ea49bc00dd3e4",
79
- "dist/types.js": "a1213d033b6a347c63e94685ab9056d8cd7a301d9c83150142afe0f3ab2c6536",
80
- "dist/typescript-authoring.js": "27829098603029ab565174e540fb1d5ef68c1a923272b7186f64a362f5244872",
79
+ "dist/types.js": "2fe7664dbb5695ec72114197abde72de4031df9c3b211740eacefa68e9b21844",
80
+ "dist/typescript-authoring.js": "2e98498a15bdbb76f36fbee84afb7c42ca95bbf80a2e6dd4040147f3b1642a29",
81
81
  "dist/vercel.js": "a0da20c095d0c7fa8f579ed4dd5951e7a34191f8a72a57c5e782c9eab39f6f08",
82
82
  "dist/verify-deployment.js": "0c2e68f3cf258a364728c801411df5284343bc01b75a64fe275ff84e397e998b",
83
83
  "dist/scripts/operational-drills.js": "c26c406d28013cc554b25a5b6bf8baf4e71d5e67e50d866fae546f538a192317"
@@ -74,12 +74,12 @@ invitation to reimplement it.
74
74
 
75
75
  ## Functions and middleware are trusted by default; sandbox is opt-in
76
76
 
77
- A route's \`function\`/\`middleware\` code runs trusted, in-process, with full
78
- Node/filesystem/\`fetch\` access, receiving only the declared/granted \`args\`
79
- and \`env\`/\`secrets\`. Add \`sandbox: true\` when code warrants isolation
80
- (untrusted input, an unreviewed contribution, an especially sensitive
81
- secret): that route then gets a text/JSON subset only, no Node/filesystem/
82
- outside imports — use \`proxy\`/a binding instead, and say why in \`description\`.
77
+ A route's \`function\`/\`middleware\` runs trusted, in-process, with full
78
+ Node/filesystem/\`fetch\` access, given only declared \`args\`/\`env\`/\`secrets\`. Add
79
+ \`sandbox: true\` when that code warrants isolation (unreviewed code, a sensitive
80
+ secret, complex logic) — not merely for untrusted input, which both modes share.
81
+ A \`sandbox: true\` route gets a text/JSON subset only: use \`proxy\`/a binding, and
82
+ say why in \`sandboxReason\`.
83
83
 
84
84
  ## Checks that count as evidence
85
85
 
@@ -61,6 +61,8 @@ export async function buildStatic(project , { out = 'dist/static', origin
61
61
  assert(route.enabled !== false, `${route.pattern}: static hosting has no server to answer a disabled route with 404; remove the route instead of disabling it`);
62
62
  assert(route.expiresAt === undefined, `${route.pattern}: static hosting has no server to answer an expired route with 410; remove the route when it expires`);
63
63
  assert(route.methods.every(method => defaultMethods.has(method)), `${route.pattern}: static hosting only ever answers GET/HEAD; this target refuses declared methods ${route.methods.join(', ')}`);
64
+ assert(defaultMethods.size === route.methods.length && [...defaultMethods].every(method => route.methods.includes(method)), `${route.pattern}: static hosting cannot preserve a GET-only or HEAD-only method restriction; declare both GET and HEAD`);
65
+ if (route.reply) assert(route.reply.status === 200, `${route.pattern}: static hosting serves response objects with status 200; declared status ${route.reply.status} cannot be preserved`);
64
66
  if (route.redirect) {
65
67
  assert(!route.names.length, `${route.pattern}: static hosting cannot redirect a path pattern; only an exact literal path can carry an S3 per-object redirect`);
66
68
  const query = route.redirect.query;
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import { loadComplianceRules, profileNames as complianceProfiles } from './compl
27
27
  import { parseRouteSnapshot, diffRoutes, renderRouteDiff } from './route-diff.js';
28
28
  import { readFile } from 'node:fs/promises';
29
29
 
30
- const usage = `URLCode 0.4.0-alpha.2 — local/self-hosted runtime
30
+ const usage = `URLCode 0.4.0-alpha.3 — local/self-hosted runtime
31
31
  urlcode init <directory> [--with auth,admin] # --with: layered site from installed @jimhoyd/urlcode-<name> packages
32
32
  urlcode scaffold [--project directory] [--dry-run]
33
33
  urlcode validate [--project directory] [--local] [--origin https://links.example] # origin: absolute URLs in site.* files
@@ -74,7 +74,7 @@ const usage = `URLCode 0.4.0-alpha.2 — local/self-hosted runtime
74
74
  # compact facts for an authoring agent from the compiled project; --stats compares estimated tokens with the docs
75
75
  urlcode doctor
76
76
  serve/dev/validate/test/routes/audit/benchmark/explain/context/extensions/mcp: --host-file /absolute/operator/host.mjs (trusted code outside project)
77
- Dev loads .env.local and watches; serve does neither. Functions run in WASM isolation; external bindings require --policy outside the project.
77
+ Dev loads .env.local and watches; serve does neither. Functions run trusted and in-process by default; a route declaring sandbox: true runs in WASM isolation. External bindings require --policy outside the project.
78
78
  `;
79
79
  const print = (value ) => process.stdout.write(typeof value === 'string' ? value : JSON.stringify(value) + '\n');
80
80
  const options = {
@@ -286,7 +286,7 @@ try {
286
286
  print(result); if (result.failed) process.exitCode = 1; break;
287
287
  }
288
288
  case 'doctor':
289
- print({ node:process.version, platform:process.platform, architecture:process.arch, runtime:'node-process', functionSandbox:'quickjs-wasm', network:false, filesystem:false, guestNetwork:false, hostEgress:'revision-pinned-origin-grants', tooling:['recipes','examples','bulk-import','build-typescript','mcp','verify-provider'], providers:[], capabilityTargets:getCapabilities().targets, policies:Object.keys(policyRegistry), license:'Apache-2.0' }); break;
289
+ print({ node:process.version, platform:process.platform, architecture:process.arch, runtime:'node-process', functionDefault:'trusted-in-process', sandboxEngine:'quickjs-wasm', trustedFilesystem:true, trustedNetwork:true, sandboxedFilesystem:false, sandboxedNetwork:false, hostEgress:'revision-pinned-origin-grants', tooling:['recipes','examples','bulk-import','build-typescript','mcp','verify-provider'], providers:[], capabilityTargets:getCapabilities().targets, policies:Object.keys(policyRegistry), license:'Apache-2.0' }); break;
290
290
  case 'dev': case 'serve': {
291
291
  const port = Number(values.port);
292
292
  if (!/^\d+$/.test(values.port) || !Number.isInteger(port) || port < 0 || port > 65535) throw new ConfigError('Invalid port');
@@ -12,7 +12,7 @@ const targetsOf=(target ) =>target===undefined
12
12
  function summary(explanation ,targets ) {
13
13
  const handler=explanation.handler,detail=handler.kind==='function'?`${handler.source }#${handler.export }`:handler.kind==='redirect'?`${handler.status } ${handler.url }`:handler.kind==='extension'?handler.name :handler.kind==='page'||handler.kind==='download'?handler.file :handler.kind==='static'?handler.directory :handler.kind==='proxy'?handler.url :handler.kind==='respond'?String(handler.status):'';
14
14
  const support=targets.map(target=>explanation.targets[target].compatible?target:`${target}:refused`).join(',');
15
- return [explanation.path,explanation.methods.join(','),`${handler.kind}${detail?` ${detail}`:''}`,explanation.state,String(explanation.middleware.length),explanation.policies.names.join(',')||'-',explanation.cache.outcome,support].join('\t');
15
+ return [explanation.path,explanation.methods.join(','),`${handler.kind}${detail?` ${detail}`:''}`,explanation.state,explanation.sandbox?'sandboxed':'trusted',String(explanation.middleware.length),explanation.policies.names.join(',')||'-',explanation.cache.outcome,support].join('\t');
16
16
  }
17
17
  function detail(explanation ,targets ) {
18
18
  const lines =[`route: ${explanation.path}`];
@@ -20,6 +20,8 @@ function detail(explanation ,targets )
20
20
  if(explanation.generated)lines.push(`generated: site.${explanation.generated}`);
21
21
  lines.push(`state: ${explanation.state}${explanation.expires?` (expires ${explanation.expires})`:''}`,`methods: ${explanation.methods.join(', ')}`,`handler: ${JSON.stringify(explanation.handler)}`);
22
22
  lines.push(`middleware: ${explanation.middleware.length?explanation.middleware.map(item=>`${item.source}#${item.export}`).join(' -> '):'none'}`);
23
+ // Route-level, so it is printed for a native handler with middleware too.
24
+ lines.push(`execution: ${explanation.sandbox?'sandboxed (QuickJS)':'trusted (in-process)'}${explanation.sandboxReason?`; ${explanation.sandboxReason}`:''}`);
23
25
  lines.push(`inputs: ${explanation.inputs.parameters.length?explanation.inputs.parameters.map(p=>`${p.in}:${p.name}${p.required?'':'?'}`).join(', '):'none'}${explanation.inputs.body?`; body ${JSON.stringify(explanation.inputs.body)}`:''}`);
24
26
  lines.push(`policies: ${explanation.policies.names.length?explanation.policies.names.join(', '):'none'}`);
25
27
  for(const [name,entry] of Object.entries(explanation.policies.inventory))lines.push(` ${name}: ${JSON.stringify(entry)}`);
@@ -52,7 +54,7 @@ export async function runExplainCommand(command ,route
52
54
  if(route===undefined){
53
55
  const report=await explainProject(options.project,base);
54
56
  if(options.json){print(report);return 0;}
55
- print(table([['route','methods','handler','state','mw','policies','cache','targets'],...report.routes.map(item=>summary(item,targets).split('\t'))]));return 0;
57
+ print(table([['route','methods','handler','state','execution','mw','policies','cache','targets'],...report.routes.map(item=>summary(item,targets).split('\t'))]));return 0;
56
58
  }
57
59
  if(!route.startsWith('/'))throw new ConfigError('Provide an absolute route path such as /docs');
58
60
  const explanation=await explainRoute(options.project,route,base);