@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
@@ -0,0 +1,386 @@
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
+ # Public roadmap
12
+
13
+ URLCode is a portable runtime for programmable URL behavior, not a URL
14
+ shortener. Stored short links are an operator-installed extension, not a core
15
+ handler. The
16
+ [project direction](../../PROJECT-DIRECTION.md) explains how application
17
+ projects and provider adapters fit without redefining or restricting the free
18
+ runtime.
19
+
20
+ This is the public delivery sequence. Tests accompany every feature, not a
21
+ separate late phase. The stable 0.1 self-hosted release covers much of M0/M1 plus initial
22
+ process/container packaging and benchmarks. Provider adapters and the remaining
23
+ production-readiness gates remain open.
24
+
25
+ ## 0.4.0-alpha.2 — current alpha
26
+
27
+ `0.4.0-alpha.2` is a behavior-changing release on top of `0.4.0-alpha.1`:
28
+ **`function` and `middleware` routes now run trusted and unsandboxed by
29
+ default** — in the host process, with full Node/filesystem/network access,
30
+ exactly like any other project code — instead of always dispatching through
31
+ the QuickJS/WebAssembly worker pool. Sandboxing is now an explicit per-route
32
+ opt-in via `sandbox: true`; a route that declares it gets exactly the
33
+ isolation every earlier alpha provided, unchanged. See
34
+ [docs/SPIKE-DEFAULT-TRUST-MODEL.md](../../SPIKE-DEFAULT-TRUST-MODEL.md) for
35
+ the maintainer's decision and rationale, and
36
+ [docs/FUNCTION-SECURITY.md](../../FUNCTION-SECURITY.md) for the resulting
37
+ contract of both execution modes.
38
+
39
+ **This changes existing projects with no YAML edit.** Every `function`/
40
+ `middleware` route that does not declare `sandbox` — which, before this
41
+ release, meant every such route in every existing project — now runs trusted
42
+ instead of sandboxed the moment the runtime is upgraded to `0.4.0-alpha.2` or
43
+ later. Binding grants (`env`/`secrets`) are unaffected: a route still
44
+ receives only what it declares and an operator policy pins to the project
45
+ revision, whether trusted or sandboxed. Review which of your project's
46
+ `function`/`middleware` routes handle input or code you would not otherwise
47
+ trust with full Node/filesystem/network access, and add `sandbox: true` to
48
+ those specifically (docs/AI-AUTHORING.md's "Deciding when a route needs
49
+ `sandbox: true`") before upgrading a project that relies on the old,
50
+ always-sandboxed behavior. An operator policy pinned to a project's revision
51
+ is invalidated by this upgrade regardless of whether the project's own YAML
52
+ changed, since the project hash includes the trust-model-affecting change;
53
+ re-derive and re-approve it (`urlcode permissions`) after upgrading.
54
+
55
+ ## 0.4.0-alpha.1
56
+
57
+ `0.4.0-alpha.1` is the first alpha of the extension contract and the agent
58
+ tooling on top of the `0.3.0` self-hosted release. It carries: the
59
+ revision-pinned extension contract, capabilities and provider conformance,
60
+ strict redirect interchange, bulk import, recipes and search, TypeScript
61
+ guests, conditions, bounded proxy and signals, MCP read and authoring modes,
62
+ the `context`, `explain`, `manifest` and `schema` queries, short forms,
63
+ route-level auth, `init --with`, the agent benchmark harness, `llms-full`,
64
+ and the generated `AGENTS.md` and skill. It is an alpha: provider
65
+ deployments, soak and independent security review remain open. The
66
+ extension packages declare `@jimhoyd/urlcode >=0.4.0-alpha.1 <0.5.0` and
67
+ are published after core.
68
+
69
+ ## Extensions: accounts, administration and presentation — implemented, unreleased
70
+
71
+ The runtime now carries a generic, revision-pinned extension contract
72
+ (`@jimhoyd/urlcode/extensions`, [extensions](../../EXTENSIONS.md)): a project
73
+ declares versioned `extensions.<name>` blocks, exclusive `extension` mounts and
74
+ `policies.extensions` requirements; the operator supplies the implementations
75
+ in a host file loaded with `--host-file`, outside the project. Guest requests
76
+ never see `Cookie`, `Authorization` or declared credential headers. Cloudflare
77
+ refuses extensions until its artifact format can run them.
78
+
79
+ The implementations live in their own repositories, each with an
80
+ implementation-status file that is the authoritative feature list:
81
+ [urlcode-auth](https://github.com/jimhoyd-com/urlcode-auth) (accounts, sessions,
82
+ MFA, roles, account page, operator CLI), [urlcode-admin](https://github.com/jimhoyd-com/urlcode-admin)
83
+ (users, sessions, roles, audit, approvals, cases, impersonation) and
84
+ [urlcode-ui](https://github.com/jimhoyd-com/urlcode-ui) (escaped templates,
85
+ partials, themes, translations). All three are Apache-2.0 and published to
86
+ npm as `0.1.0-alpha.1` while first-release acceptance is reviewed; see
87
+ [issue 58](https://github.com/jimhoyd-com/urlcode/issues/58) for what remains:
88
+ browser and device WebAuthn coverage, accessibility assessment, soak, backup
89
+ and recovery drills on a deployment, live provider senders and independent
90
+ security review. [The framework](../../FRAMEWORK.md) describes how the four
91
+ packages compose, and [next steps](NEXT-STEPS.md) is the phased plan to
92
+ close the remaining gaps.
93
+
94
+ ## Capability foundation — implemented, unreleased
95
+
96
+ `urlcode capabilities [--target self-hosted|cloudflare|aws|vercel] [--json]`
97
+ and the public capability API centralize target support for handlers, bindings
98
+ and effective policies. Runtime activation and Cloudflare builds use the shared
99
+ preflight; unknown targets fail closed and unsupported requirements identify the
100
+ route and capability before resources or artifacts are created. The existing
101
+ compiled route IR is documented, not replaced. Configuration-dependent and
102
+ delegated behavior remain explicit; all provider deployments remain unverified.
103
+ See [capabilities](../../CAPABILITIES.md) and the
104
+ [next-phase review and implementation status](NEXT-PHASE-PLAN.md).
105
+
106
+ ## Portability and URL behavior — implemented, unreleased
107
+
108
+ Strict redirect interchange supports CSV/JSON/YAML, Netlify and Cloudflare
109
+ `_redirects`, a bounded Netlify TOML subset and Vercel redirects. Conversion
110
+ reports refuse unsupported semantics; provider differences require explicit
111
+ acknowledgment and are never called lossless. A synthetic conformance fixture
112
+ and bounded HTTPS runner distinguish local adapter tests from observed
113
+ provider deployments. Actual Cloudflare/AWS/Vercel deployment evidence remains
114
+ pending; this does not complete M4.
115
+
116
+ Exact query/header/cookie/origin/method conditions and disjoint conditional
117
+ redirect/response cases run in the self-hosted, AWS and Vercel runtimes.
118
+ Cloudflare refuses these until its compiler can preserve their semantics.
119
+ Self-hosted proxy and webhook signals use explicit revision-pinned operator
120
+ origin grants, connection-pinned public DNS and bounded transport. Signals
121
+ have no durable delivery or retry guarantee. Other targets refuse proxy and
122
+ signals. See [conditions](../../CONDITIONS.md), [egress](../../EGRESS.md),
123
+ [interchange](../../INTERCHANGE.md) and
124
+ [provider verification](../../PROVIDER-VERIFICATION.md).
125
+
126
+ ## Developer ecosystem — implemented, unreleased
127
+
128
+ Bundled Git-owned recipes, safe bulk imports into route includes, build-time
129
+ TypeScript guest transpilation, read-only inspection APIs and optional stdio
130
+ MCP tooling are available. The runtime still executes only JavaScript, and
131
+ TypeScript authoring adds no execution mode: transpilation happens at build
132
+ time, is not type checking, and a `sandbox: true` route's emitted JavaScript
133
+ runs inside QuickJS/WASM exactly as before. Bulk benchmarks
134
+ successfully cover 1,000, 10,000 and 100,000 routes without relaxing parser
135
+ limits. See [recipes](../../RECIPES.md), [bulk evidence](../../BULK.md),
136
+ [TypeScript authoring](../../TYPESCRIPT-AUTHORING.md) and
137
+ [SDK/MCP](../../TOOLING.md). These features do not supply durable signals,
138
+ protected downloads, a remote marketplace or provider deployment proof.
139
+
140
+ ## TypeScript source and shipped declarations — implemented, unreleased
141
+
142
+ The runtime, scripts, tests and benchmarks are TypeScript under a strict
143
+ configuration, checked by `npm run typecheck` inside `npm run verify`. The
144
+ package ships `dist/`: Node's own type stripping of the source with the
145
+ specifier extension rewritten, so it is the same JavaScript line for line,
146
+ plus `.d.ts` declarations for every export (`urlcode`, `@jimhoyd/urlcode/plugins`,
147
+ `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`,
148
+ `@jimhoyd/urlcode/prerender` and the three provider entries). `dist` is never
149
+ committed; the build runs in the digest-pinned release container, its hashes
150
+ are recorded in the manifest, and a CI job builds twice and diffs the trees.
151
+ Measured cold start, throughput and memory of `dist/` equal the previous
152
+ JavaScript (see [performance](../../PERFORMANCE.md)). Contributors need Node
153
+ 22.18+ to run the source directly; installed packages still run on 22.13+.
154
+ See [TypeScript](../../TYPESCRIPT.md).
155
+
156
+ ## Host policies and plugins — implemented, unreleased
157
+
158
+ A `policies` block in YAML, reusable `profiles` and an operator plugin API
159
+ close the gaps the [extensions spike](SPIKE-EXTENSIONS.md) ranked
160
+ highest: no cross-cutting behavior, no per-client throttle, no bot policy, no
161
+ security-header preset, no compression and no response cache. Five policies
162
+ (`throttle`, `agents`, `security`, `compression`, `cache`) run in the host
163
+ process after route match and before the route contract, all off by default,
164
+ with a per-target table that refuses at activation what a target cannot
165
+ enforce: the serverless adapters take `agents`, `security`, `cache` and
166
+ route-partitioned `throttle`; the Cloudflare build compiles `agents` and
167
+ `security` into the artifact and refuses the rest with the route named.
168
+ `--trusted-proxies` names the hops allowed to set `X-Forwarded-For` for client
169
+ identity. Plugins are host code passed to `startServer` and the adapters on
170
+ the same hook seam the policies use; nothing in YAML names one. See
171
+ [policies](../../POLICIES.md) and [plugins](../../PLUGINS.md).
172
+
173
+ This checkpoint is on a branch and not in a published release. An
174
+ interoperability review of the five policies together, the spike's `report`
175
+ mode headers on every target and the audit's policy table remain open, and no
176
+ deployment has exercised a policy on a provider.
177
+
178
+ ## Provider adapters — Vercel and AWS Lambda native handlers
179
+
180
+ `@jimhoyd/urlcode/vercel` serves a project as a Vercel Node function, reusing the
181
+ runtime's transport-agnostic handle() and a shared response writer, so a
182
+ deployment returns byte-identical status, body and headers to the self-hosted
183
+ server. Bindings arrive through a `URLCODE_POLICY` environment variable holding
184
+ the same revision-pinned grant document the operator policy file carries.
185
+
186
+ Native handlers only: `function` and `middleware` routes are refused at
187
+ activation, trusted or sandboxed alike, because they need the self-hosted Node
188
+ lifecycle and a sandboxed one would pay worker and WASM startup on every cold
189
+ start. The
190
+ `@jimhoyd/urlcode/aws` does the same for a Lambda Function URL or API Gateway HTTP API.
191
+ Payload format 2.0 only: format 1.0 supplies an already-decoded path and query,
192
+ and this runtime rejects ambiguous encoding deliberately, so rebuilding a target
193
+ from decoded parts would misrepresent the request. Response policy, including
194
+ content length, now lives in one place shared by every host rather than partly
195
+ relying on Node's implicit behaviour.
196
+
197
+ Neither adapter has been deployed; see the [Vercel](../../VERCEL.md) and
198
+ [AWS](../../AWS.md) guides, which state what stays unverified as a result.
199
+
200
+ ## Provider targets — Cloudflare Workers
201
+
202
+ Cloudflare Workers has no worker threads, no filesystem and no runtime code
203
+ generation, so it gets a compiler rather than an adapter: `urlcode build
204
+ --target cloudflare` emits a Worker, the compiled routes and Ajv standalone
205
+ validators, and `@jimhoyd/urlcode/cloudflare` serves them with the same matching, request
206
+ policy and response policy as every other host. Declarative routes only —
207
+ redirects and declared responses with parameters, defaults, validation, response
208
+ headers, `enabled` and `expires`. Functions, middleware, assets and
209
+ bindings are refused at build time with the route named, so an unsupported
210
+ project fails the build instead of the deployment. Bindings are refused even as
211
+ literals, because a build artifact must never carry a secret.
212
+
213
+ Making this possible moved request-time matching into `src/match.ts` and header
214
+ validation into `src/header-validation.ts`, both free of Node imports, so one
215
+ implementation now serves the Node server, the serverless adapters and the
216
+ Worker. `test/header-validation.test.ts` compares the header rules against
217
+ `node:http` across the full character range, because disagreeing there is header
218
+ injection, and `test/cloudflare.test.ts` asserts the Worker and the self-hosted
219
+ server return the same status, body and headers for the same project.
220
+
221
+ This has not been deployed to Cloudflare; see the
222
+ [Cloudflare guide](../../CLOUDFLARE.md) for the two request-level differences the
223
+ platform imposes and what stays unverified.
224
+
225
+ ## Installation and publication — 0.1.0
226
+
227
+ Added a tag-driven release workflow that reuses the audited candidate build path,
228
+ publishes a GitHub release with the signed tarball, SBOM, manifest, `SHA256SUMS`
229
+ and a rendered Homebrew formula, and optionally publishes to npm (with
230
+ provenance) and GHCR behind repository variables. Added a checksum-verifying
231
+ `install.sh`, a Homebrew formula template rendered only from measured bytes, and
232
+ an [installation guide](../../INSTALL.md) covering npm, Homebrew, the script, the
233
+ container and provenance verification.
234
+
235
+ 0.1.0 was released from this pipeline: the GitHub release carries the signed
236
+ tarball, SBOM, manifest, `SHA256SUMS` and Homebrew formula, and an install from
237
+ the published release was verified end to end. npm and GHCR publication stay
238
+ opt-in and remain unproven until enabled, so the Homebrew formula's registry URL
239
+ does not resolve yet.
240
+
241
+ Added tunnel and [monitoring](../../MONITORING.md) recipes, and extended
242
+ `urlcode benchmark` to measure a running deployment through its real path with
243
+ warm-up and shed/transport separation; see [load testing](../../LOAD-TESTING.md).
244
+ M3's remaining gap is sustained soak and slow-peer behaviour, which that tool
245
+ does not cover.
246
+
247
+ ## Hardening checkpoint — alpha.8
248
+
249
+ Bound HTTP admission and inactive sockets, and correct management
250
+ defaults/method responses.
251
+ The [readiness register](../../RELEASE-READINESS.md) distinguishes tested safeguards
252
+ from deployment and stable-release gates. Feature breadth does not imply stability.
253
+
254
+ ## Live short links — alpha.8 (removed from core, superseded)
255
+
256
+ Implemented an optional native `link` handler, local SQLite persistence, CLI CRUD
257
+ and a separate authenticated management API. Links became visible without YAML
258
+ changes/reloads; versioned writes prevented silent lost updates. No guest storage
259
+ or network access was added. Same-host only; distributed storage, general state,
260
+ user accounts and provider adapters remained open.
261
+
262
+ This native `link` handler, its SQLite store, CLI and management API were
263
+ removed from core in the layering work that followed. Stored short links moved
264
+ to a separate `urlcode-dynamic-link` extension package (mount-based, like
265
+ `auth`/`admin`), which was retired and unpublished in September 2026 without a
266
+ successor. A project that used `link`/`dynamicLinks` now owns that storage
267
+ itself; there is no in-core replacement and no deprecation shim.
268
+
269
+ ## Middleware — alpha.7
270
+
271
+ Implemented route-local ordered `next()` middleware around every handler, early
272
+ responses, request-local state and shared sandbox deadlines. Plain native routes
273
+ retain their fast path. Native bodies stay opaque; middleware coverage requires
274
+ explicit assertions. See [middleware](../../MIDDLEWARE.md).
275
+
276
+ ## One starter — alpha.6
277
+
278
+ `urlcode init <directory>` always creates the same function-plus-redirect project.
279
+ There is no template selector. The public urlcode-template repository mirrors
280
+ those examples with a pinned npm dependency. Richer asset examples remain under
281
+ examples/assets. Historical starter branches are not maintained.
282
+
283
+ ## Route readiness and local project benchmarks — alpha.5
284
+
285
+ Implemented route inventory, expected-count checks, active route/method coverage,
286
+ generated native probes plus explicit fixtures, and bounded assertion-aware local
287
+ benchmarks. [Readiness](../../READINESS.md) documents the gate and remaining deployment,
288
+ soak, remote-destination and business-coverage work. This does not complete M3/M4.
289
+
290
+ ## HTTP configuration and standalone starter — 0.1.0-alpha.4
291
+
292
+ Implemented bounded request body/media-type/JSON checks, literal response headers
293
+ and separate Set-Cookie values, and native text/JSON responses. See [HTTP](../../HTTP.md)
294
+ for precise scope; automatic CORS, multipart, streaming and other listed features
295
+ remain open. The public [urlcode-template](https://github.com/jimhoyd-com/urlcode-template)
296
+ provides two routes with a pinned runtime dependency and cross-platform CI.
297
+
298
+ ## Native assets — 0.1.0-alpha.3
299
+
300
+ Implemented the page/static/download portion of M2: project-contained asset
301
+ snapshots, automatic MIME types, attachment names, HEAD, cache validators and
302
+ single byte ranges. Dedicated public directories, symlink/hardlink rejection and
303
+ bounded memory are part of the contract. [Asset guide](../../ASSETS.md).
304
+ Bulk tools, recipes and best-effort signals were added in the unreleased next-phase work above; they were not part of alpha.3.
305
+
306
+ ## Security correction — 0.1.0-alpha.2
307
+
308
+ <!-- trust-model-prose: historical -->
309
+ All function code is untrusted. Node host execution has been replaced by
310
+ QuickJS/WebAssembly isolation with fresh invocation state, no ambient host or
311
+ network APIs, bounded resources, restricted module graphs and revision-pinned
312
+ operator binding policy. This protection is part of the free product. See the
313
+ [security model](../../FUNCTION-SECURITY.md). Full Fetch/Node
314
+ API compatibility and network integrations were not part of that alpha and
315
+ remain outside the 0.1 contract. That default was superseded in
316
+ `0.4.0-alpha.2`, which made this isolation the per-route `sandbox: true`
317
+ opt-in instead (see
318
+ [docs/SPIKE-DEFAULT-TRUST-MODEL.md](../../SPIKE-DEFAULT-TRUST-MODEL.md)).
319
+
320
+ ## Earlier implementation checkpoint — 0.1.0-alpha.1
321
+
322
+ Implemented: versioned strict YAML/JSON Schema subset, explicit file composition,
323
+ redirect/parameter semantics, JavaScript Request/Response functions with bounded
324
+ workers, scoped binding context, init/add/validate/dev/serve/test/doctor, indexed
325
+ snapshots, last-good reloads, graceful shutdown, health/readiness, safe request
326
+ logs, two runnable starters, ESLint and unit/HTTP/package tests. Cross-platform
327
+ CI and a non-root container build are included. See the [contract](../../SPECIFICATION.md)
328
+ and [operations guide](../../OPERATIONS.md) for exact support and evidence limits.
329
+
330
+ Still open in the early contract: host namespaces, stable identity beyond paths,
331
+ fuller parameter vocabulary. Build-time TypeScript authoring and capability planning are now implemented in the unreleased work above.
332
+ No claims of complete M0/M1 or stable production readiness. M2–M4 work continues
333
+ in the order below; a few independently useful operational foundations shipped early.
334
+
335
+
336
+ | Milestone | Scope | Completion evidence |
337
+ |---|---|---|
338
+ | M0 — contract/build loop | YAML schema, matching/input semantics, function API, composition, runtime/packaging prototype, CI | Fixture validates; invalid definitions fail; verification runs |
339
+ | 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 |
340
+ | 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 |
341
+ | 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 |
342
+ | 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 |
343
+ | 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 |
344
+
345
+ The first provider-capable public release is M4; the 0.1 self-hosted release is
346
+ useful within its documented scope. A marketplace and advanced stateful
347
+ features are not prerequisites. No web UI/TUI or framework-hosting platform.
348
+ Netlify starts as redirect interchange after the initial provider adapters.
349
+
350
+ ## Launch and stabilize the free version
351
+
352
+ Stability means repeatable installs/upgrades, dependable routing/functions,
353
+ working deployment and rollback, useful diagnostics, and serious recurring
354
+ issues from actual users addressed. Feedback begins with usable alphas, but the
355
+ free version is not stable until that evidence exists; do not invent a calendar
356
+ deadline or adoption-count threshold.
357
+
358
+ Keep the architectural direction: reusable runtime/compiler, provider adapters,
359
+ separate configuration and secrets, versioned artifacts, Git-owned definitions
360
+ and observable behavior. Avoid assumptions that would force users to rewrite
361
+ projects later. No artificial restrictions in the free version; it is licensed
362
+ under Apache-2.0.
363
+
364
+ ## Starter delivery
365
+
366
+ The [starter plan](../../STARTERS.md) makes both Git clone and CLI initialization
367
+ release requirements: one function-plus-redirect starter in M1, bulk-growth examples in
368
+ M2, and the business foundation with self-host tooling in M3. Provider recipes
369
+ follow tested M4 adapters. All use the same runtime and portable project format.
370
+
371
+ ## Quality gates
372
+
373
+ - Same fixtures pass on the local reference and each claimed runtime adapter.
374
+ A static exporter alone is not a complete function-capable adapter.
375
+ - Pure redirects avoid Lambda/per-route user functions; generated shared routing
376
+ is allowed where native provider rules cannot preserve behavior.
377
+ - CSV exports report unsupported nested content instead of silently losing it.
378
+ - 1k/10k/100k datasets measure compile/reload, memory, latency and throughput.
379
+ - Default tests run against local HTTP/fake services; ngrok is optional.
380
+ - Load tests use bounded owned targets and do not follow third-party redirects.
381
+ - Invalid reload preserves working config; secret values stay out of artifacts.
382
+ - Installation claims match tested OS/architecture packages.
383
+
384
+ Implementation runtime, exact schema/function API, first adapter order and package
385
+ format are decided through early prototypes. Do not invent performance targets
386
+ or advertise all providers before they pass tests.
@@ -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
  # Review: the extension model, its precedents and its alignment
2
12
 
3
13
  Status: review of the [auth](https://github.com/jimhoyd-com/urlcode-auth/blob/main/docs/SPIKE-AUTH.md), [admin](https://github.com/jimhoyd-com/urlcode-admin/blob/main/docs/SPIKE-ADMIN.md) and
@@ -1,19 +1,30 @@
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
  # Spike: plugins, adapters and optional runtime features
2
12
 
3
13
  > Status update: the host hook seam, the plugin API and the five policies of
4
- > section 5 are implemented; [policies](POLICIES.md) and [plugins](PLUGINS.md)
14
+ > section 5 are implemented; [policies](../../POLICIES.md) and [plugins](../../PLUGINS.md)
5
15
  > describe the shipped behavior, which takes precedence where this text differs.
6
16
  >
7
17
  > Also stale: the embedding-API row below (`openLinkStore`, `startLinkApi`)
8
18
  > and the `link` mentions in the gaps table describe core from before `link`
9
- > was extracted. Core has no native `link`/`LinkStore` API any more; it now
10
- > lives in [`urlcode-dynamic-link`](https://github.com/jimhoyd-com/urlcode-dynamic-link).
19
+ > was extracted. Core has no native `link`/`LinkStore` API any more, and the
20
+ > `urlcode-dynamic-link` package it was extracted into has since been retired,
21
+ > unpublished and deleted (September 2026).
11
22
 
12
23
  Status: exploratory. Nothing here is committed scope; it records what the
13
24
  runtime has today, how comparable tools expose the same needs, which gaps
14
25
  matter for real deployments, and a proposed shape for closing them without
15
- breaking the [project boundary](PROJECT-DIRECTION.md). The
16
- [roadmap](../ROADMAP.md) owns sequence; the [specification](SPECIFICATION.md)
26
+ breaking the [project boundary](../../PROJECT-DIRECTION.md). The
27
+ [roadmap](ROADMAP.md) owns sequence; the [specification](../../SPECIFICATION.md)
17
28
  owns what is implemented.
18
29
 
19
30
  Every feature below is **optional and off by default**. A `version: "1"`
@@ -63,10 +74,10 @@ delegating them to the provider.
63
74
 
64
75
  The docs are consistent that these belong at ingress, not in route YAML:
65
76
  rate limiting, WAF, TLS, DDoS mitigation, forwarded-header trust
66
- ([resilience](RESILIENCE.md), [capacity](CAPACITY.md)). Compression, CORS,
77
+ ([resilience](../../RESILIENCE.md), [capacity](../../CAPACITY.md)). Compression, CORS,
67
78
  content negotiation and streaming are listed as explicitly outside the
68
- [HTTP contract](HTTP.md). Caching is declarative and limited to a fixed
69
- `cacheControl` vocabulary on asset handlers ([assets](ASSETS.md)).
79
+ [HTTP contract](../../HTTP.md). Caching is declarative and limited to a fixed
80
+ `cacheControl` vocabulary on asset handlers ([assets](../../ASSETS.md)).
70
81
 
71
82
  ## 2. How comparable tools do it
72
83
 
@@ -78,7 +89,7 @@ content negotiation and streaming are listed as explicitly outside the
78
89
  | Injection / hardening | `helmet`, `express-validator` | `@fastify/helmet`, schema validation built-in | `secureHeaders`, `validator` | Built-in header directives | Header directives | Managed WAF rulesets | Header injection prevented at runtime; body JSON syntax check; **no** security-header preset, no schema body validation |
79
90
  | Compression | `compression` | `@fastify/compress` | `hono/compress` | `encode gzip zstd` | `gzip on; brotli` | Automatic at edge | None; identity only |
80
91
  | Caching | `apicache`, CDN | `@fastify/caching` | `hono/cache` | `cache` (plugin) | `proxy_cache` | Edge cache + `Cache-Control`, ISR | Asset `cacheControl` vocabulary; no response cache |
81
- | Templates | `res.render()`, view engines | `@fastify/view` | `hono/jsx`, `html` helper | `templates` directive | SSI | Framework-owned | None at request time; build-time [prerender](PRERENDER.md) only |
92
+ | Templates | `res.render()`, view engines | `@fastify/view` | `hono/jsx`, `html` helper | `templates` directive | SSI | Framework-owned | None at request time; build-time [prerender](../../PRERENDER.md) only |
82
93
  | Adapters | `serverless-http`, `@vendia`, `@hono/node-server` | `@fastify/aws-lambda` | First-party adapters for every runtime | n/a | n/a | n/a | Vercel, AWS, Cloudflare; native handlers only |
83
94
 
84
95
  Three patterns recur and are worth borrowing:
@@ -111,12 +122,12 @@ Ranked by how often a self-hosted operator hits it before the first deploy.
111
122
  | G9 | Adapters refuse functions/middleware/links | Documented and deliberate, but it means any feature built as guest middleware is also refused on serverless | Design constraint |
112
123
  | G10 | No JSON Schema body validation | `request.body.format: json` checks syntax only | Low |
113
124
  | G11 | No CORS preflight helper | Documented gap | Low |
114
- | G12 | No SPA fallback for client-routed apps | [Assets](ASSETS.md) rules it out beside directory listing and trailing-slash redirects; a host plugin cannot supply it either, because an unmatched path throws 404 before the request object or any plugin hook exists | Low |
125
+ | G12 | No SPA fallback for client-routed apps | [Assets](../../ASSETS.md) rules it out beside directory listing and trailing-slash redirects; a host plugin cannot supply it either, because an unmatched path throws 404 before the request object or any plugin hook exists | Low |
115
126
 
116
127
  ## 4. Design constraints these must respect
117
128
 
118
- From [AGENTS.md](../AGENTS.md), [project direction](PROJECT-DIRECTION.md)
119
- and [function security](FUNCTION-SECURITY.md):
129
+ From [AGENTS.md](../../../AGENTS.md), [project direction](../../PROJECT-DIRECTION.md)
130
+ and [function security](../../FUNCTION-SECURITY.md):
120
131
 
121
132
  - Route YAML describes **behavior**, not infrastructure. A throttle budget is
122
133
  behavior ("this route allows 10 requests per minute per client"); a Redis
@@ -356,7 +367,7 @@ Rules:
356
367
  ### 5.6 Templates
357
368
 
358
369
  Request-time templating conflicts with the opaque-native-body rule, and the
359
- [prerender](PRERENDER.md) helper already handles the static case. Two
370
+ [prerender](../../PRERENDER.md) helper already handles the static case. Two
360
371
  bounded options fit the boundary:
361
372
 
362
373
  1. **Build-time only (recommended first).** Promote prerender into a CLI
@@ -401,7 +412,7 @@ not offered: anything stricter is a per-project decision.
401
412
 
402
413
  1. **Network and edge.** Volumetric protection, TLS termination and
403
414
  per-client connection budgets stay with the hosting provider or the
404
- reverse proxy, as [resilience](RESILIENCE.md) already states. Runtime
415
+ reverse proxy, as [resilience](../../RESILIENCE.md) already states. Runtime
405
416
  policies are a second layer, never the first.
406
417
  2. **Ingress to origin.** Bind privately; allow only the proxy's addresses;
407
418
  pass `--trusted-proxies` so `client` partitioning uses the real peer.