@jimhoyd/urlcode 0.3.0 → 0.4.0-alpha.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 (326) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +106 -0
  2. package/.claude/skills/urlcode-operations/SKILL.md +114 -0
  3. package/.claude-plugin/marketplace.json +18 -0
  4. package/CONTRIBUTING.md +30 -2
  5. package/README.md +157 -230
  6. package/ROADMAP.md +85 -2
  7. package/SECURITY.md +8 -1
  8. package/dist/BUILD-MANIFEST.json +62 -32
  9. package/dist/adapters.js +4 -23
  10. package/dist/agent-lists.js +1 -1
  11. package/dist/agents-guide.js +113 -0
  12. package/dist/authoring-files.js +60 -0
  13. package/dist/authoring.js +11 -1
  14. package/dist/aws.js +4 -3
  15. package/dist/build-cloudflare.js +11 -24
  16. package/dist/bulk.js +37 -0
  17. package/dist/capabilities.js +208 -0
  18. package/dist/capability-query.js +72 -0
  19. package/dist/catalog.js +105 -0
  20. package/dist/cli.js +154 -15
  21. package/dist/client-address.js +1 -1
  22. package/dist/compliance-rules/baseline.js +8 -8
  23. package/dist/compliance-rules/privacy.js +3 -3
  24. package/dist/compliance-rules/strict.js +5 -5
  25. package/dist/conditions.js +88 -0
  26. package/dist/config.js +60 -4
  27. package/dist/context.js +156 -0
  28. package/dist/ecosystem-cli.js +88 -0
  29. package/dist/egress.js +98 -0
  30. package/dist/examples.js +92 -0
  31. package/dist/explain-cli.js +64 -0
  32. package/dist/explain.js +132 -0
  33. package/dist/extensions.js +172 -0
  34. package/dist/function-sources.js +15 -3
  35. package/dist/index.js +37 -0
  36. package/dist/init-with.js +165 -0
  37. package/dist/interchange-cli.js +42 -0
  38. package/dist/interchange.js +189 -0
  39. package/dist/link-cli.js +1 -1
  40. package/dist/management-policy.js +0 -1
  41. package/dist/manifest.js +111 -0
  42. package/dist/match.js +2 -2
  43. package/dist/mcp-authoring.js +147 -0
  44. package/dist/mcp.js +97 -0
  45. package/dist/observability.js +6 -0
  46. package/dist/operator-host.js +29 -0
  47. package/dist/plugins.js +12 -0
  48. package/dist/policies/agents.js +2 -2
  49. package/dist/policies/compression.js +2 -1
  50. package/dist/policies/security.js +0 -0
  51. package/dist/policies.js +1 -1
  52. package/dist/policy.js +29 -7
  53. package/dist/prerender.js +100 -41
  54. package/dist/project-tests.js +3 -3
  55. package/dist/provider-verification.js +92 -0
  56. package/dist/proxy.js +44 -0
  57. package/dist/readiness.js +5 -5
  58. package/dist/recipes.js +41 -0
  59. package/dist/route-diff.js +106 -0
  60. package/dist/router.js +42 -2
  61. package/dist/runtime.js +95 -14
  62. package/dist/schema-query.js +62 -0
  63. package/dist/signals.js +24 -0
  64. package/dist/site.js +0 -0
  65. package/dist/tooling.js +96 -0
  66. package/dist/types/adapters.d.ts +7 -4
  67. package/dist/types/agent-lists.d.ts +0 -1
  68. package/dist/types/agents-guide.d.ts +17 -0
  69. package/dist/types/authoring-files.d.ts +10 -0
  70. package/dist/types/aws.d.ts +3 -1
  71. package/dist/types/build-cloudflare.d.ts +1 -0
  72. package/dist/types/bulk.d.ts +27 -0
  73. package/dist/types/capabilities.d.ts +55 -0
  74. package/dist/types/capability-query.d.ts +24 -0
  75. package/dist/types/catalog.d.ts +65 -0
  76. package/dist/types/client-address.d.ts +0 -1
  77. package/dist/types/compliance-rules/baseline.d.ts +1 -9
  78. package/dist/types/compliance-rules/privacy.d.ts +1 -4
  79. package/dist/types/compliance-rules/strict.d.ts +0 -5
  80. package/dist/types/conditions.d.ts +19 -0
  81. package/dist/types/config.d.ts +20 -2
  82. package/dist/types/context.d.ts +65 -0
  83. package/dist/types/ecosystem-cli.d.ts +17 -0
  84. package/dist/types/egress.d.ts +46 -0
  85. package/dist/types/examples.d.ts +50 -0
  86. package/dist/types/explain-cli.d.ts +11 -0
  87. package/dist/types/explain.d.ts +95 -0
  88. package/dist/types/extensions.d.ts +122 -0
  89. package/dist/types/function-sources.d.ts +5 -0
  90. package/dist/types/index.d.ts +33 -0
  91. package/dist/types/init-with.d.ts +30 -0
  92. package/dist/types/interchange-cli.d.ts +16 -0
  93. package/dist/types/interchange.d.ts +42 -0
  94. package/dist/types/link-cli.d.ts +2 -1
  95. package/dist/types/management-policy.d.ts +0 -1
  96. package/dist/types/manifest.d.ts +81 -0
  97. package/dist/types/match.d.ts +1 -0
  98. package/dist/types/mcp-authoring.d.ts +92 -0
  99. package/dist/types/mcp.d.ts +12 -0
  100. package/dist/types/observability.d.ts +2 -0
  101. package/dist/types/operator-host.d.ts +8 -0
  102. package/dist/types/plugins.d.ts +2 -0
  103. package/dist/types/policies/agents.d.ts +0 -2
  104. package/dist/types/policies/compression.d.ts +2 -0
  105. package/dist/types/policies/security.d.ts +0 -1
  106. package/dist/types/policy.d.ts +15 -4
  107. package/dist/types/project-tests.d.ts +3 -1
  108. package/dist/types/provider-verification.d.ts +53 -0
  109. package/dist/types/proxy.d.ts +21 -0
  110. package/dist/types/readiness.d.ts +1 -1
  111. package/dist/types/recipes.d.ts +30 -0
  112. package/dist/types/route-diff.d.ts +27 -0
  113. package/dist/types/runtime.d.ts +11 -0
  114. package/dist/types/schema-query.d.ts +12 -0
  115. package/dist/types/signals.d.ts +25 -0
  116. package/dist/types/site.d.ts +0 -1
  117. package/dist/types/tooling.d.ts +115 -0
  118. package/dist/types/types.d.ts +57 -0
  119. package/dist/types/typescript-authoring.d.ts +12 -0
  120. package/dist/types/vercel.d.ts +3 -1
  121. package/dist/types/verify-deployment.d.ts +47 -0
  122. package/dist/types.js +21 -2
  123. package/dist/typescript-authoring.js +104 -0
  124. package/dist/vercel.js +4 -3
  125. package/dist/verify-deployment.js +270 -0
  126. package/docs/AI-AUTHORING.md +130 -8
  127. package/docs/BULK.md +79 -0
  128. package/docs/CAPABILITIES.md +179 -0
  129. package/docs/CAPACITY.md +1 -1
  130. package/docs/CI.md +142 -0
  131. package/docs/CONDITIONS.md +74 -0
  132. package/docs/DEPLOYMENT-CHECKS.md +108 -0
  133. package/docs/DYNAMIC-LINKS.md +18 -518
  134. package/docs/EGRESS.md +125 -0
  135. package/docs/EXTENSIONS.md +226 -0
  136. package/docs/FRAMEWORK.md +182 -0
  137. package/docs/INSTALL.md +45 -7
  138. package/docs/INTERCHANGE.md +134 -0
  139. package/docs/MIDDLEWARE-EXAMPLES.md +75 -0
  140. package/docs/MIDDLEWARE.md +2 -0
  141. package/docs/NEXT-PHASE-PLAN.md +90 -0
  142. package/docs/NEXT-STEPS.md +415 -0
  143. package/docs/OBSERVABILITY.md +4 -2
  144. package/docs/OPERATIONAL-PROOF.md +4 -1
  145. package/docs/OPERATIONS.md +6 -3
  146. package/docs/PLUGINS.md +37 -0
  147. package/docs/POLICIES.md +12 -309
  148. package/docs/PRERENDER.md +40 -0
  149. package/docs/PROJECT-DIRECTION.md +42 -0
  150. package/docs/PROVIDER-VERIFICATION.md +84 -0
  151. package/docs/READINESS.md +21 -1
  152. package/docs/README.md +82 -31
  153. package/docs/RECIPES.md +99 -0
  154. package/docs/RELEASE-READINESS.md +11 -9
  155. package/docs/RELEASE-SECURITY.md +27 -4
  156. package/docs/SECURITY-AUDIT.md +1 -1
  157. package/docs/SPECIFICATION.md +95 -8
  158. package/docs/SPIKE-BUSINESS-SUITE.md +1013 -0
  159. package/docs/SPIKE-EXTENSION-MODEL.md +419 -0
  160. package/docs/SPIKE-EXTENSIONS.md +1 -0
  161. package/docs/SPIKE-LAMBDA-COMPILE.md +199 -0
  162. package/docs/STANDARDS.md +150 -142
  163. package/docs/STARTERS.md +21 -1
  164. package/docs/TOOLING.md +291 -0
  165. package/docs/TYPESCRIPT-AUTHORING.md +67 -0
  166. package/docs/TYPESCRIPT.md +1 -1
  167. package/docs/USABILITY-REVIEW.md +123 -0
  168. package/docs/YAML-GUIDE.md +18 -479
  169. package/docs/YAML-REFERENCE.md +127 -16
  170. package/docs/links/cli.md +110 -0
  171. package/docs/links/limits.md +175 -0
  172. package/docs/links/management-api.md +80 -0
  173. package/docs/links/pools.md +75 -0
  174. package/docs/links/setup.md +135 -0
  175. package/docs/policies/agents.md +1 -1
  176. package/docs/policies/contract.md +52 -0
  177. package/docs/policies/hardened.md +56 -0
  178. package/docs/policies/interoperability.md +169 -0
  179. package/docs/policies/operations.md +45 -0
  180. package/docs/yaml/assets.md +36 -0
  181. package/docs/yaml/conditions.md +20 -0
  182. package/docs/yaml/functions.md +160 -0
  183. package/docs/yaml/links.md +30 -0
  184. package/docs/yaml/middleware.md +29 -0
  185. package/docs/yaml/organization.md +74 -0
  186. package/docs/yaml/policies.md +37 -0
  187. package/docs/yaml/redirects.md +64 -0
  188. package/docs/yaml/responses.md +57 -0
  189. package/docs/yaml/site.md +24 -0
  190. package/examples/assets/example.yaml +17 -0
  191. package/examples/aws/example.yaml +20 -0
  192. package/examples/cloudflare/example.yaml +19 -0
  193. package/examples/compliance/example.yaml +11 -0
  194. package/examples/conditions/README.md +12 -0
  195. package/examples/conditions/example.yaml +19 -0
  196. package/examples/conditions/tests/requests.json +13 -0
  197. package/examples/conditions/urlcode.yaml +24 -0
  198. package/examples/cookbook/README.md +8 -4
  199. package/examples/cookbook/example.yaml +17 -0
  200. package/examples/cookbook/functions/catalog.mjs +3 -0
  201. package/examples/cookbook/functions/fail.mjs +4 -0
  202. package/examples/cookbook/functions/items.mjs +3 -0
  203. package/examples/cookbook/functions/profile.mjs +3 -0
  204. package/examples/cookbook/functions/resource.mjs +3 -0
  205. package/examples/cookbook/functions/status.mjs +3 -0
  206. package/examples/cookbook/middleware/auth.mjs +48 -0
  207. package/examples/cookbook/middleware/body.mjs +15 -0
  208. package/examples/cookbook/middleware/bucket.mjs +19 -0
  209. package/examples/cookbook/middleware/cors.mjs +21 -0
  210. package/examples/cookbook/middleware/debug.mjs +13 -0
  211. package/examples/cookbook/middleware/envelope.mjs +11 -0
  212. package/examples/cookbook/middleware/errors.mjs +11 -0
  213. package/examples/cookbook/middleware/etag.mjs +18 -0
  214. package/examples/cookbook/middleware/locale.mjs +16 -0
  215. package/examples/cookbook/middleware/maintenance.mjs +10 -0
  216. package/examples/cookbook/middleware/methods.mjs +15 -0
  217. package/examples/cookbook/middleware/negotiate.mjs +20 -0
  218. package/examples/cookbook/middleware/referer.mjs +12 -0
  219. package/examples/cookbook/middleware/request-id.mjs +16 -0
  220. package/examples/cookbook/route-index.json +676 -0
  221. package/examples/cookbook/routes/middleware.yaml +126 -0
  222. package/examples/cookbook/tests/requests.json +526 -0
  223. package/examples/cookbook/urlcode.yaml +1 -0
  224. package/examples/egress/README.md +22 -0
  225. package/examples/egress/example.yaml +19 -0
  226. package/examples/egress/urlcode.yaml +19 -0
  227. package/examples/extensions/README.md +7 -0
  228. package/examples/extensions/example.yaml +21 -0
  229. package/examples/extensions/urlcode.yaml +25 -0
  230. package/examples/live-links/example.yaml +21 -0
  231. package/examples/monitoring/example.yaml +8 -0
  232. package/examples/prerender/example.yaml +16 -0
  233. package/examples/provider-conformance/README.md +12 -0
  234. package/examples/provider-conformance/example.yaml +14 -0
  235. package/examples/provider-conformance/urlcode.yaml +34 -0
  236. package/examples/tunnel/example.yaml +8 -0
  237. package/examples/vercel/example.yaml +19 -0
  238. package/llms-full.txt +2709 -0
  239. package/llms.txt +48 -19
  240. package/package.json +29 -7
  241. package/packaging/claude-plugin/.claude-plugin/plugin.json +19 -0
  242. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +106 -0
  243. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +114 -0
  244. package/recipes/authenticated-json-api/README.md +51 -0
  245. package/recipes/authenticated-json-api/functions/profile.mjs +5 -0
  246. package/recipes/authenticated-json-api/recipe.yaml +34 -0
  247. package/recipes/authenticated-json-api/tests/requests.json +39 -0
  248. package/recipes/authenticated-json-api/urlcode.yaml +12 -0
  249. package/recipes/contact-form/README.md +25 -0
  250. package/recipes/contact-form/functions/contact.mjs +17 -0
  251. package/recipes/contact-form/recipe.yaml +33 -0
  252. package/recipes/contact-form/tests/requests.json +47 -0
  253. package/recipes/contact-form/urlcode.yaml +18 -0
  254. package/recipes/cors-api/README.md +16 -0
  255. package/recipes/cors-api/functions/items.mjs +3 -0
  256. package/recipes/cors-api/middleware/cors.mjs +21 -0
  257. package/recipes/cors-api/recipe.yaml +26 -0
  258. package/recipes/cors-api/tests/requests.json +65 -0
  259. package/recipes/cors-api/urlcode.yaml +12 -0
  260. package/recipes/health-page/README.md +13 -0
  261. package/recipes/health-page/recipe.yaml +23 -0
  262. package/recipes/health-page/tests/requests.json +36 -0
  263. package/recipes/health-page/urlcode.yaml +19 -0
  264. package/recipes/json-api/README.md +6 -0
  265. package/recipes/json-api/functions/echo.mjs +3 -0
  266. package/recipes/json-api/recipe.yaml +25 -0
  267. package/recipes/json-api/tests/requests.json +34 -0
  268. package/recipes/json-api/urlcode.yaml +12 -0
  269. package/recipes/middleware/README.md +34 -0
  270. package/recipes/middleware/functions/catalog.mjs +3 -0
  271. package/recipes/middleware/functions/fail.mjs +4 -0
  272. package/recipes/middleware/functions/items.mjs +3 -0
  273. package/recipes/middleware/functions/profile.mjs +3 -0
  274. package/recipes/middleware/functions/resource.mjs +3 -0
  275. package/recipes/middleware/functions/status.mjs +3 -0
  276. package/recipes/middleware/middleware/auth.mjs +48 -0
  277. package/recipes/middleware/middleware/body.mjs +15 -0
  278. package/recipes/middleware/middleware/bucket.mjs +19 -0
  279. package/recipes/middleware/middleware/cors.mjs +21 -0
  280. package/recipes/middleware/middleware/debug.mjs +13 -0
  281. package/recipes/middleware/middleware/envelope.mjs +11 -0
  282. package/recipes/middleware/middleware/errors.mjs +11 -0
  283. package/recipes/middleware/middleware/etag.mjs +18 -0
  284. package/recipes/middleware/middleware/locale.mjs +16 -0
  285. package/recipes/middleware/middleware/maintenance.mjs +10 -0
  286. package/recipes/middleware/middleware/methods.mjs +15 -0
  287. package/recipes/middleware/middleware/negotiate.mjs +20 -0
  288. package/recipes/middleware/middleware/referer.mjs +12 -0
  289. package/recipes/middleware/middleware/request-id.mjs +16 -0
  290. package/recipes/middleware/public/guide.txt +1 -0
  291. package/recipes/middleware/recipe.yaml +50 -0
  292. package/recipes/middleware/tests/requests.json +528 -0
  293. package/recipes/middleware/urlcode.yaml +127 -0
  294. package/recipes/protected-download/README.md +22 -0
  295. package/recipes/protected-download/files/report.txt +1 -0
  296. package/recipes/protected-download/recipe.yaml +31 -0
  297. package/recipes/protected-download/tests/requests.json +32 -0
  298. package/recipes/protected-download/urlcode.yaml +15 -0
  299. package/recipes/redirect/README.md +7 -0
  300. package/recipes/redirect/recipe.yaml +25 -0
  301. package/recipes/redirect/tests/requests.json +19 -0
  302. package/recipes/redirect/urlcode.yaml +9 -0
  303. package/recipes/static-plus-api/README.md +15 -0
  304. package/recipes/static-plus-api/functions/info.mjs +3 -0
  305. package/recipes/static-plus-api/public/assets/index.html +3 -0
  306. package/recipes/static-plus-api/public/assets/site.css +1 -0
  307. package/recipes/static-plus-api/public/index.html +8 -0
  308. package/recipes/static-plus-api/recipe.yaml +29 -0
  309. package/recipes/static-plus-api/tests/requests.json +56 -0
  310. package/recipes/static-plus-api/urlcode.yaml +23 -0
  311. package/recipes/typescript/README.md +7 -0
  312. package/recipes/typescript/functions/hello.ts +5 -0
  313. package/recipes/typescript/recipe.yaml +23 -0
  314. package/recipes/typescript/tests/requests.json +18 -0
  315. package/recipes/typescript/urlcode.yaml +5 -0
  316. package/recipes/webhook-receiver/README.md +16 -0
  317. package/recipes/webhook-receiver/functions/receive.mjs +16 -0
  318. package/recipes/webhook-receiver/recipe.yaml +26 -0
  319. package/recipes/webhook-receiver/tests/requests.json +59 -0
  320. package/recipes/webhook-receiver/urlcode.yaml +16 -0
  321. package/schemas/recipe.schema.json +138 -0
  322. package/schemas/urlcode.schema.json +656 -80
  323. package/skills/urlcode/SKILL.md +98 -0
  324. package/starters/default/.github/workflows/urlcode.yml +23 -0
  325. package/starters/default/.mcp.json +12 -0
  326. package/starters/default/AGENTS.md +79 -0
@@ -0,0 +1,415 @@
1
+ # Next steps: closing the gaps
2
+
3
+ Status: plan written 2026-09-18 from the cleanup, the
4
+ [usability review](USABILITY-REVIEW.md) and the AI-first framework brief.
5
+ Each item says what it fixes, where the work is, how it is proven, and its
6
+ size (S: a day, M: a week, L: more). Phases can overlap; order inside a phase
7
+ is the recommended sequence. The [roadmap](../ROADMAP.md) owns what ships;
8
+ this page owns how the gaps close.
9
+
10
+ ## The rule everything below serves
11
+
12
+ > Your AI should build your application, not your framework.
13
+
14
+ Agents rebuild the same routing, validation, middleware, auth plumbing,
15
+ policies, admin patterns and deployment glue on every project, and the person
16
+ ends up owning it. URLCode's job is a small, deterministic, portable vocabulary
17
+ in readable YAML, so generated code goes to the part that is the application.
18
+ The agent describes what; the runtime owns how. The three tests that decide
19
+ what gets built are in [project direction](PROJECT-DIRECTION.md#why-your-ai-should-build-your-application-not-your-framework):
20
+ the boundary test (do agents generate this across unrelated projects?), the
21
+ feature test (does it reduce what the agent must know, generate, debug or
22
+ maintain?) and the evidence test (measured repetition, not a feature list).
23
+
24
+ The order below follows from that. **Prove the thesis before building on
25
+ it.** Phase 0 is the benchmark; if it shows a large saving, the rest is worth
26
+ the work, and if it shows a small one, the abstraction is not doing enough yet
27
+ and the next phases change. Nothing here weakens the security model: guest
28
+ code stays untrusted, grants stay operator-owned, agents cannot self-authorize,
29
+ unsupported behavior fails with the route named, and inspection tooling never
30
+ becomes a privilege escalation path.
31
+
32
+ ## Phase 0: prove the thesis (M, core `benchmarks/agent/`, before anything else)
33
+
34
+ ### 0.1 The agent benchmark
35
+
36
+ Fixes: there is no evidence that URLCode saves agent effort, and the whole
37
+ plan depends on it.
38
+
39
+ - Work: ten representative tasks first, twenty later: redirect service, URL
40
+ shortener, webhook receiver, small JSON API, static site plus API,
41
+ OAuth-protected internal app, CRUD backend, admin backend, file and download
42
+ service, API proxy, contact form, authenticated endpoint. Each task has a
43
+ natural-language prompt, an acceptance test suite that is the same for both
44
+ arms, and two harness configurations: conventional (the agent picks its own
45
+ stack) and URLCode (the agent has the skill, `urlcode context` and the
46
+ recipes). Run each arm several times with the same model. Capture input,
47
+ output and total tokens, generated lines and files, agent turns, retries,
48
+ failures, wall time, tests passed, and a checklist of obvious security
49
+ mistakes. Store raw runs under `benchmarks/agent/runs/` with model, date and
50
+ harness version.
51
+ - The headline metric is the **application-specific code ratio**: generated
52
+ lines that are the idea versus generated lines that are plumbing, counted
53
+ by a documented rule (files under `functions/` and the application's own
54
+ modules count as the idea; routing, auth, sessions, middleware, validation,
55
+ headers, static serving, deployment and test scaffolding count as plumbing).
56
+ Report it beside tokens and turns.
57
+ - Proof: a reproducible runner; a README that states exactly what the
58
+ numbers are and are not. Publish only what the stored runs support.
59
+ A result like "same application, 65 percent fewer generated lines and half
60
+ the tokens" is the story; a result like 8 percent means the vocabulary is
61
+ too small or too hard to discover, and Phases 1, 3 and 4 are re-prioritized
62
+ from what the runs show the agent still had to write.
63
+
64
+ ### 0.2 Authoring regression evals (S, once 0.1 exists)
65
+
66
+ Prompts for common requests ("add a redirect", "add an authenticated
67
+ endpoint", "serve this directory", "add middleware", "create a webhook
68
+ endpoint") scored on: native functionality chosen, valid YAML, no unsupported
69
+ fields, no unnecessary JavaScript, no boundary violations, tests written,
70
+ validation run, provider limits respected. Run on a schedule; a new feature
71
+ must not lower the pass rate.
72
+
73
+ ## Phase 1: agent discovery (S each, no contract changes)
74
+
75
+ What exists: `llms.txt`, `AGENTS.md` (for working on the runtime),
76
+ `docs/AI-AUTHORING.md`, `docs/FRAMEWORK.md`, the JSON Schema, the generated
77
+ field reference, the cookbook, recipes, `validate`/`test`/`audit`, MCP
78
+ read-side tools (`inspectProject`, `validateProject`, `explainRoute`,
79
+ `getCapabilities`, `previewImport`/`previewExport`, `listRecipes`/`showRecipe`).
80
+ Keep all of it. The gap is that an agent still has to read documents to find
81
+ facts the runtime already knows.
82
+
83
+ ### 1.1 Application-level `AGENTS.md` from `urlcode init` (S, core)
84
+
85
+ Fixes: a project made with URLCode is not self-describing to Claude Code,
86
+ Codex or any repository-aware agent. Today's `AGENTS.md` explains how to work
87
+ on the runtime, not on an application.
88
+
89
+ - Work: `urlcode init` (and `init --with`, 2.3 below) writes `AGENTS.md` into
90
+ the project: inspect `urlcode.yaml` first; run `urlcode context` (1.3);
91
+ check capabilities and search recipes before writing code; prefer native
92
+ handlers; never recreate routing, validation, middleware, policies or
93
+ authentication the runtime provides; validate, test, audit; report
94
+ unsupported requirements instead of inventing fields; never create or
95
+ approve grants. Generated from the installed runtime's capability list so
96
+ it names only what that version has.
97
+ - Proof: starter test asserts the file exists and lists the commands; the
98
+ public `urlcode-template` gets the same file.
99
+
100
+ ### 1.2 `llms-full.txt` and clean Markdown for the docs site (S, core; S, urlcode-docs)
101
+
102
+ Fixes: `llms.txt` must stay a compact index, but an agent that wants complete
103
+ context has to fetch forty files.
104
+
105
+ - Work: `scripts/build-llms-full.ts` concatenates the authoring documents
106
+ (framework, AI authoring, YAML guide, field reference, specification,
107
+ routing, HTTP, middleware, assets, dynamic links, policies, extensions) in
108
+ reading order with a table of contents; checked in `npm run check` for
109
+ staleness like the field reference. The docs site serves `/docs/<page>.md`
110
+ beside `/docs/<page>` so agents never parse HTML. Do not adopt `agents.txt`
111
+ or similar until a convention settles.
112
+ - Proof: `npm run check` fails on a stale `llms-full.txt`; token estimate
113
+ recorded in the file header.
114
+
115
+ ### 1.3 `urlcode context` with a token budget (M, core)
116
+
117
+ Fixes: the central gap. An agent needs a handful of facts about this project
118
+ and this runtime and spends tens of thousands of tokens reading documentation
119
+ to get them.
120
+
121
+ - Work: `urlcode context [--project DIR] [--budget N] [--json] [--stats]`
122
+ emits deterministic YAML (or JSON): runtime and schema version; project
123
+ summary (route count, handlers used, extensions declared, policies in
124
+ effect, custom functions and middleware files, bindings requested);
125
+ constraints that matter for generation (no guest network or Node APIs, no
126
+ regex routes, path shape, one handler per route, no interpolation); target
127
+ support for the project's features; and the exact `validate`/`test`/`audit`
128
+ commands with the intentional route count filled in. `--budget` drops
129
+ sections in a fixed order (per-route detail, then target table, then
130
+ constraints prose) until the estimate fits; estimation is a documented
131
+ characters-per-token approximation, no tokenizer dependency. `--stats`
132
+ prints the estimated size of the documentation corpus versus the emitted
133
+ context, labeled as estimates. Derived from the compiled project and the
134
+ capability catalog, never from prose.
135
+ - Proof: snapshot tests for the cookbook and the starter; a test that the
136
+ same project yields byte-identical output twice; budget test that output
137
+ never exceeds the estimate; MCP tool `get_context` returns the same data.
138
+
139
+ ### 1.4 Capability and schema fragment queries (M, core)
140
+
141
+ Fixes: `urlcode capabilities` reports target support per handler; an agent
142
+ cannot ask "what does `throttle` accept, where does it run, which recipe
143
+ shows it" or "give me only the schema for `redirect`".
144
+
145
+ - Work: `urlcode capabilities <name> [--json]` extends the existing catalog
146
+ entry with the schema fragment, constraints, required grants, per-target
147
+ support, known unsupported behavior and related recipes and examples.
148
+ `urlcode schema <path>` (`route`, `redirect`, `middleware`, `policies.cache`,
149
+ `extensions`) returns only that fragment of `schemas/urlcode.schema.json`,
150
+ resolving `$ref`s. Both derive from the schema and the catalog; nothing is
151
+ hand-maintained. MCP gains `get_capability` and `get_schema`.
152
+ - Proof: a test that every capability name resolves and every fragment is
153
+ valid JSON Schema; a size test that no fragment exceeds a fixed byte cap.
154
+
155
+ ### 1.5 The URLCode agent skill (S, core `skills/urlcode/`)
156
+
157
+ Fixes: agents that support skills have no packaged instruction for URLCode.
158
+
159
+ - Work: a small skill (under 150 lines) that teaches the loop: recognize a
160
+ project by `urlcode.yaml`; run `urlcode context`; query a capability;
161
+ search recipes and examples; prefer YAML; write minimal functions; validate,
162
+ test, audit; respect grants and never approve one; report unsupported
163
+ requirements. It tells the agent how to retrieve the minimum, and links
164
+ nothing else. Ship in the package under `skills/` and reference it from
165
+ the generated `AGENTS.md`.
166
+ - Proof: the authoring evals in Phase 5 run with and without the skill.
167
+
168
+ ## Phase 2: make the ladder real (no contract changes)
169
+
170
+ ### 2.1 Publish the three extension packages (decision, S) — done
171
+
172
+ Done 2026-09-18: `@jimhoyd/urlcode-ui`, `-auth` and `-admin` are on npm as
173
+ `0.1.0-alpha.1` against core `0.4.0-alpha.1`. The alpha caveat stays: source
174
+ complete, independent review, deployment evidence and accessibility
175
+ assessment pending ([issue 58](https://github.com/jimhoyd-com/urlcode/issues/58)).
176
+
177
+ Fixed: every install step in the "add accounts" row of the usability review
178
+ except the revision pin. Before this, a person or an agent cloned three private
179
+ repositories, ran `pack-sources.mjs` with four paths and a SHA, and installed
180
+ four tarballs.
181
+
182
+ - Decide: publish `@jimhoyd/urlcode-ui`, `-auth`, `-admin` as `0.1.0-alpha.N`
183
+ to npm with provenance, from tags on `main`, keeping the "private until
184
+ reviewed" caveats in each README and status file. An alpha on npm is a
185
+ distribution channel, not an endorsement.
186
+ - Work: copy core's `release.yml` shape into each repo (candidate build, npm
187
+ audit, `npm pack`, attest, publish behind a repository variable). Drop
188
+ `"private": true` only in the release commit. Set real peer ranges.
189
+ - Proof: a clean directory installs core and auth from the registry and runs
190
+ `urlcode-auth init`, `bootstrap`, `serve`; admin's
191
+ `scripts/clean-project-acceptance.mjs` runs against the published tarballs.
192
+
193
+ ### 2.2 One place for peer revisions (S)
194
+
195
+ Fixes: three disagreeing lists of verified peer commits (the CI workflows,
196
+ `ACCEPTANCE.md` in auth and admin, the pack script's core-revision check).
197
+
198
+ - Work: `peers.json` in auth and admin; the workflows read it; the pack
199
+ script defaults from it; `ACCEPTANCE.md` links to it. 2.1 has shipped, so
200
+ the published versions (`@jimhoyd/urlcode@0.4.0-alpha.1`,
201
+ `@jimhoyd/urlcode-ui@0.1.0-alpha.1`) replace the SHAs and the file can go.
202
+
203
+ ### 2.3 `urlcode init --with auth,admin,ui` (M, core plus each extension)
204
+
205
+ Fixes: three initializers with three directory conventions; no single command
206
+ produces the layered project the framework page describes.
207
+
208
+ - Work in core: `--with a,b,c` resolves the installed
209
+ `@jimhoyd/urlcode-<name>` from the invoking directory and calls its
210
+ `scaffold` export (a small documented contract returning a YAML fragment,
211
+ host imports and entries, and a README section). Core merges fragments into
212
+ `urlcode.yaml`, writes one `host.mjs`, one `README.md`, the `AGENTS.md`
213
+ from 1.1, and prints the `inspectExtensionRevision` SHA. A missing package
214
+ refuses with the install command; core never imports the packages at build
215
+ time. Each extension exports `scaffold` built from its existing `init`.
216
+ - Proof: a core test with a fake `@jimhoyd/urlcode-demo` package in a temp
217
+ `node_modules`; each extension tests that its `scaffold` output validates
218
+ with core; admin's clean-project acceptance uses the new command.
219
+
220
+ ### 2.4 Print extension schemas: `urlcode extensions` (M, core)
221
+
222
+ Fixes: an agent cannot discover what `extensions.auth.config` accepts without
223
+ reading auth's source; `urlcode mcp` cannot serve it.
224
+
225
+ - Work: `urlcode extensions --host-file … [--json]` loads the host file as
226
+ `validate` does and prints each registration's name, contract version,
227
+ targets, configuration schema and policy schema; the same data through the
228
+ SDK and an MCP tool. Folds into `urlcode context` and `capabilities auth`
229
+ when a host file is given.
230
+ - Proof: test against the `examples/extensions` demo registry.
231
+
232
+ ## Phase 3: retrieval instead of reading (M each, core)
233
+
234
+ ### 3.1 Recipes as the vocabulary of common behavior
235
+
236
+ Fixes: four bundled recipes with a README each and no metadata; an agent
237
+ cannot search them, and nothing tells it to look before generating.
238
+
239
+ - Work: every recipe gains `recipe.yaml`: `id`, `description`,
240
+ `capabilities`, `tags`, `complexity`, required external services, grants,
241
+ configuration inputs, files, target compatibility, tests and expected
242
+ behavior. `urlcode recipes search <text>` matches id, description, tags and
243
+ capabilities locally (no AI service); `show` prints the metadata first.
244
+ MCP gains `search_recipes`. Grow the catalog from observed repetition (see
245
+ Phase 6), starting with: authenticated JSON endpoint, webhook receiver with
246
+ signature check via `proxy`/signals, contact form to a signal, protected
247
+ download, health and readiness page, CORS API, static site with API.
248
+ - Proof: schema for `recipe.yaml` checked in `npm run check`; a search test
249
+ per recipe; every recipe still validates, tests and audits.
250
+
251
+ ### 3.2 Examples become searchable the same way
252
+
253
+ - Work: `examples/*/example.yaml` with the same metadata shape; `urlcode
254
+ examples search <text>` returns the smallest matching runnable example and
255
+ its route. The cookbook's forty routes get per-route tags in one index file.
256
+ - Proof: search test; count audit unchanged.
257
+
258
+ ### 3.3 `urlcode explain` from compiled semantics
259
+
260
+ Fixes: `explainRoute` exists in the SDK and MCP; there is no CLI, and the
261
+ output repeats matching rather than effective behavior.
262
+
263
+ - Work: `urlcode explain [/route] [--json]` prints, per route, the effective
264
+ methods, handler, middleware chain, validated inputs, policies in effect
265
+ (including `extensions.auth` requirements when a host file is given),
266
+ cache and no-store outcome, bindings and target support, derived from the
267
+ compiled IR. Whole-project form lists every route in one screen.
268
+ - Proof: snapshot tests on the cookbook; a test that `explain` and the
269
+ runtime agree on methods and policies for every route.
270
+
271
+ ### 3.4 A generated semantic manifest
272
+
273
+ - Work: `urlcode manifest [--json]` (also written by `build` and offered by
274
+ MCP as `get_manifest`) emits routes, capabilities, recipe provenance,
275
+ external requirements, custom functions, requested bindings, target
276
+ compatibility and the revision digest. Generated only; never checked in as
277
+ a source of truth. `context` is a budgeted view of the same data.
278
+ - Proof: manifest equals `inspectProject` output for the same project;
279
+ digest equals `inspectExtensionRevision`.
280
+
281
+ ### 3.5 MCP authoring layer, separately authorized (M, core)
282
+
283
+ Fixes: MCP is read-only by design; an agent that wants to add a recipe or a
284
+ route still has to write files by hand.
285
+
286
+ - Work: keep the read side as is and add the new read tools above. Add an
287
+ authoring server mode enabled only by an explicit flag on the operator's
288
+ command line (`urlcode mcp --allow-authoring --project DIR`): `add_recipe`,
289
+ `create_route`, `scaffold_feature` (writes YAML and placeholder files
290
+ through the existing scaffold path), and `validate`/`test`/`audit` runners.
291
+ Every write is confined to the selected project, refuses paths outside it,
292
+ never touches operator files, grants, policies or host files, and returns
293
+ the validation verdict. Nothing in either mode reads secrets, creates
294
+ grants, deploys, or changes operator security policy.
295
+ - Proof: tests that authoring tools are absent without the flag; path
296
+ confinement tests; a write followed by `validate` in one call.
297
+
298
+ ## Phase 4: fewer lines for the common case (schema additions, `version: "1"` stays valid)
299
+
300
+ ### 4.1 Short form for function routes (M, core)
301
+
302
+ Fixes: the smallest function route is ten lines.
303
+
304
+ - Work: `function: functions/hello.mjs` as a string expands every `{param}`
305
+ to a required bounded string parameter and matching `args` entry; same for
306
+ `middleware: [functions/x.mjs]`. The long form stays the canonical IR;
307
+ `routes`, `audit`, `explain` and the field reference show the expansion.
308
+ - Proof: generated reference updated; cookbook gains a short-form route with
309
+ fixtures; a test that short and long forms compile to identical IR.
310
+
311
+ ### 4.2 Route-level `auth` as the semantic form (M, core plus auth)
312
+
313
+ Fixes: protecting a route today is `policies: { extensions: { auth: {} } }`,
314
+ which is the mechanism, not the intent. The form an agent should write is
315
+ `auth: { required: true, roles: [admin] }`.
316
+
317
+ - Work: a route-level `auth` key that expands to the `policies.extensions.auth`
318
+ requirement the auth extension validates; `roles` maps to the extension's
319
+ policy schema. Valid only when an `auth` extension is declared, refused
320
+ with the route named otherwise. The same pattern applies later to `cache:
321
+ { strategy: public, maxAge: 3600 }` over the cache policy where the policy
322
+ form is more verbose than the intent.
323
+ - Proof: a test that the short form compiles to the identical requirement;
324
+ `explain` shows both; the auth repo's HTTP tests pass unchanged.
325
+
326
+ ### 4.3 Semantic over implementation configuration (rule, ongoing)
327
+
328
+ Every new field describes intended behavior (`auth: required`), never a
329
+ provider or framework knob. Review new YAML fields against this in PR
330
+ templates and the AI authoring matrix.
331
+
332
+ ## Phase 5: keep measuring
333
+
334
+ Phase 0 runs again after each of Phases 1 to 4 lands, on the same tasks and
335
+ model, so every feature shows its effect on tokens, turns and the
336
+ application-specific code ratio. The repetition log in Phase 6 is fed from
337
+ the benchmark runs: every plumbing line the agent still wrote in the URLCode
338
+ arm is an entry.
339
+
340
+ ## Phase 6: grow from observed repetition (rule, ongoing)
341
+
342
+ When building an application with URLCode, record every place an agent still
343
+ generated commodity infrastructure and classify it: missing primitive,
344
+ policy, recipe, capability, documentation, example, integration, or
345
+ legitimately application-specific. Keep the log in `docs/REPETITION-LOG.md`
346
+ with the project, the code that was written and the classification. Candidate
347
+ areas the brief lists (CRUD, webhooks, email, uploads, jobs, pagination, API
348
+ envelopes) are built only when the log shows them repeating. The application
349
+ data question is the first entry:
350
+
351
+ ### 6.1 Spike: a declared `collection` handler with an admin view (M to write, L to build)
352
+
353
+ "Full-fledged application" today means "site with accounts". Write
354
+ `docs/SPIKE-COLLECTIONS.md` against the same principles as links: YAML
355
+ declares a collection with a JSON Schema for records, exact bounded query and
356
+ mutation routes, operator-owned SQLite, no guest queries; admin registers a
357
+ generic records screen; functions receive records as validated `args`. It
358
+ must answer portability to serverless targets, limits, schema migration and
359
+ whether links become a collection. Decide after review; build nothing first.
360
+
361
+ ## Phase 7: one presentation story (extension repos)
362
+
363
+ ### 7.1 Shared markup helpers into urlcode-ui (S)
364
+
365
+ `hiddenField`, `postForm`, `withDeadline` in the ui main entry; auth and
366
+ admin drop their copies (`admin-markup.ts`, `admin-deadline.ts`).
367
+
368
+ ### 7.2 Auth and admin render through the kit (L)
369
+
370
+ Auth exports its catalogue and templates with view models, takes an optional
371
+ `ui` from the host, and renders with `ui.kit.page` when present; admin the
372
+ same. Needs the runtime change first: an immutable-cache exception for hashed
373
+ extension assets (core, S). Proof: `urlcode-ui doctor` shows full coverage;
374
+ existing HTTP tests pass under both render paths; a themed browser
375
+ walkthrough. Retire the `presentation` option one minor version later.
376
+
377
+ ## Phase 8: evidence still missing (unchanged from issue 58)
378
+
379
+ | Gap | Plan | Owner and size |
380
+ |---|---|---|
381
+ | Browser and device WebAuthn coverage | Playwright with a virtual authenticator in auth CI; one manual pass on Safari and Android | auth, M |
382
+ | Accessibility assessment | Automated axe pass in the browser walkthroughs plus one manual screen-reader and forced-colors pass | ui, auth, admin, M |
383
+ | Soak, backup and recovery on a deployment | `operational-drills` with `URLCODE_SOAK_SECONDS=3600` on a real host; auth's recovery drill against a restored snapshot | core and auth, M |
384
+ | Provider deployments | Deploy `examples/provider-conformance` to one Vercel, AWS and Cloudflare account; run `verify-provider` | core, M, needs accounts |
385
+ | Live Google, Apple and SES | Deferred by the owner; synthetic signed fixtures stay | auth, later |
386
+ | Independent security review | `SANDBOX-REVIEW.md` is the package; commission one reviewer before any non-alpha auth release | all, L, external |
387
+
388
+ ## Phase 9: hardening left from the audit (core, S each)
389
+
390
+ - Direct tests for the sandbox pool, worker crash recovery and timeout kill
391
+ path (`src/functions.ts`, `src/function-worker.ts`, `src/guest-api.ts`).
392
+ - Tests for `src/link-store-worker.ts` behind the WAL gate.
393
+ - Re-verify the remaining line-number rows in `STANDARDS.md`; cite symbols.
394
+ - Split the three longest reference documents (YAML guide, policies, dynamic
395
+ links) into task pages under 200 lines, so retrieval returns one page.
396
+ - A pre-test check in the extension repos that fails fast with the SQLite
397
+ requirement named.
398
+
399
+ ## Sequence at a glance
400
+
401
+ ```
402
+ Phase 0 agent benchmark + code-ratio metric (decides everything after it)
403
+ Phase 1 AGENTS.md from init → llms-full → urlcode context → capability/schema queries → skill
404
+ Phase 2 publish alphas → peers.json → init --with → extensions --schema
405
+ Phase 3 recipe metadata + search → examples search → explain → manifest → MCP authoring
406
+ Phase 4 short-form function route → route-level auth
407
+ Phase 5 re-run the benchmark after each phase
408
+ Phase 6 repetition log → collections spike, then decide
409
+ Phase 7 shared helpers → kit adoption
410
+ Phase 8 proof gaps, in parallel, as people and environments allow
411
+ Phase 9 hardening, in any gap
412
+ ```
413
+
414
+ Phase 0 decides whether the rest is worth doing and in what order. Phases 1
415
+ and 3 are what an agent meets first; the rest makes that cheaper or proves it.
@@ -34,6 +34,7 @@ named.
34
34
  | `watch` | `status` `failed` | The development watcher could not fingerprint the project. |
35
35
  | `function_worker` | `status` `started`/`restarting`, `slot` integer; `attempt` and `delayMs` integers on `restarting` | A function worker became ready or is scheduled for replacement. |
36
36
  | `link_store_worker` | `status` `started`/`restarting`, `readOnly` boolean; `attempt` and `delayMs` on `restarting` | The same for a link-store connection. |
37
+ | `signal` | `outcome` (`accepted`, `delivered`, `failed`, `dropped`), positive `count` | Best-effort webhook totals; no destination, request data or secrets. |
37
38
  | `link_observer` | `status` `failed`/`dropped`/`closed`; `reason` `timeout`/`error` on `failed`; `dropped` integer on `dropped`; `queued`, `delivered`, `dropped`, `failed`, `timedOut`, `closed` on `closed` | Only with `linkEvents`: the link collector failed, the queue shed events, or the channel drained at shutdown. |
38
39
  | `link_request` | `requestId`, `collection`, `route`, `code` (only with `includeCode`), `method`, `status`, `outcome`, `durationMs` | Delivered to the `linkEvents.observe` function only, after the response is over. It never reaches the log or observers; its `outcome` is counted in the metrics. |
39
40
  | `logs_dropped` | `count` integer | The JSON logger shed records because stdout was not writable. Written by the logger itself, so observers do not see it. |
@@ -139,6 +140,7 @@ and capped at 10 000 keys.
139
140
  | `policies.agents.{denied,reported}` | counter | Agents decisions. |
140
141
  | `policies.cache.{hit,stale,miss,store}` | counter | Cache outcomes. |
141
142
  | `linkRequests.{completed,aborted,missing,disabled,expired,invalid_code,invalid_record,unavailable}` | counter | Dynamic link outcomes, counted whether or not `linkEvents` is set. |
143
+ | `signals.{accepted,delivered,failed,dropped}` | counter | Best-effort webhook outcomes; exposed as `signals_total` with outcome labels. |
142
144
  | `linkObserver.failed`, `linkObserver.dropped` | counter | Link collector failures and dropped events. |
143
145
  | `logsDropped` | counter | Records the JSON logger shed. |
144
146
  | `observers.errors` | counter | Observer hooks that threw or rejected. |
@@ -151,8 +153,8 @@ aggregation across replicas is the scraper's job.
151
153
 
152
154
  ## Prometheus exposition
153
155
 
154
- `startServer({ metrics: true })` serves `GET /_urlcode/metrics` as
155
- `text/plain; version=0.0.4`, rendered from the same snapshot by
156
+ `startServer({ metrics: true })`, or `urlcode serve --metrics` on the command
157
+ line, serves `GET /_urlcode/metrics` as `text/plain; version=0.0.4`, rendered from the same snapshot by
156
158
  `renderPrometheus(snapshot)`, a pure function you can also call yourself.
157
159
  Every metric is prefixed `urlcode_`; counters end in `_total`; the only labels
158
160
  are `status_class`, `route` and `outcome`.
@@ -20,7 +20,10 @@ nonpersistent. An I/O device failure or power cut is a different failure mode.
20
20
 
21
21
  ## Required proof on the intended deployment
22
22
 
23
- Local/CI passes do not close these gates. The deployment owner must record:
23
+ Local/CI passes do not close these gates. `urlcode verify-deployment --target`
24
+ ([deployment checks](DEPLOYMENT-CHECKS.md)) records that the deployed responses
25
+ match the project and is the first step of the rollback drill below, not a
26
+ substitute for it. The deployment owner must record:
24
27
 
25
28
  | Exercise | Acceptance evidence |
26
29
  |---|---|
@@ -35,13 +35,13 @@ The supplied image packages the runtime; it does not copy your application or
35
35
  local secret files. Build from the reviewed runtime checkout:
36
36
 
37
37
  ```sh
38
- docker build -t urlcode:0.2.0 .
38
+ docker build -t urlcode:0.3.0 .
39
39
  docker run --rm --name my-links \
40
40
  --read-only --cap-drop ALL --security-opt no-new-privileges \
41
41
  --memory 512m --cpus 1 --pids-limit 128 \
42
42
  -p 127.0.0.1:3000:3000 \
43
43
  -v "$PWD/starters/default:/project:ro" \
44
- urlcode:0.2.0
44
+ urlcode:0.3.0
45
45
  ```
46
46
 
47
47
  Replace the example mount with your app. The image uses the unprivileged `node`
@@ -163,7 +163,10 @@ this file over a network filesystem; no distributed adapter is included yet.
163
163
  1. Build a candidate from pinned runtime/application revisions and lockfiles.
164
164
  2. Validate its config/bindings and run local HTTP tests without external redirects.
165
165
  3. Start it on an alternate private port/container. Check readiness and representative
166
- redirect/function behavior through the intended proxy configuration.
166
+ redirect/function behavior through the intended proxy configuration:
167
+ `urlcode verify-deployment --project . --target https://candidate.host` compares
168
+ version, fixtures, policy headers and site files with the project
169
+ ([deployment checks](DEPLOYMENT-CHECKS.md)).
167
170
  4. Switch proxy traffic after checks pass. Drain the previous instance before stopping.
168
171
  5. If checks or observed behavior fail, route traffic back to the retained previous
169
172
  instance/image and its compatible secret bindings.
package/docs/PLUGINS.md CHANGED
@@ -42,6 +42,7 @@ const auditPlugin = {
42
42
  name: 'audit', // ^[a-z][a-z0-9-]{0,63}$, unique per runtime
43
43
  version: '1.0.0', // any string up to 64 characters
44
44
  targets: ['node', 'vercel'], // subset of node, vercel, aws, cloudflare
45
+ credentialHeaders: ['Cookie', 'Authorization'], // optional; withheld from guests
45
46
  async onActivate(runtime) {}, // may throw to refuse activation
46
47
  async onRequest(request) {}, // return a result to short-circuit
47
48
  async onResponse(request, result) { return result; }, // return the result to send
@@ -112,6 +113,42 @@ it reaches the guest: there is no sandbox handle, no deadline, no `env` or
112
113
  deadline, read or write guest state, or obtain a binding the operator policy
113
114
  did not grant to the route.
114
115
 
116
+ ## Withholding credential headers from application code
117
+
118
+ An operator plugin may declare `credentialHeaders: ['Cookie', 'Authorization']`.
119
+ Each list contains at most 64 unique HTTP header names, compared
120
+ case-insensitively, each at most 128 characters. The runtime validates the lists
121
+ and captures their union before activation hooks run. Later mutation of a plugin
122
+ object does not weaken the running boundary; a reload captures a new declaration.
123
+ No YAML key can enable, disable or override this operator setting.
124
+
125
+ The union applies to every matched route in that runtime, including public
126
+ functions and guest middleware on native routes. Before processing application
127
+ inputs, the runtime makes a separate header copy and removes those names. Guest
128
+ `Request.headers`, `ctx.inputs.header` and header-derived `ctx.args` receive no
129
+ corresponding values. Header schema defaults are also omitted from the guest
130
+ context for those names. A required protected header input without a default fails with
131
+ `Missing required parameter`, even when the original request supplied it;
132
+ projects should not declare credential inputs that the operator withholds.
133
+ Native redirect query mappings also use this sanitized application context, so
134
+ they cannot reflect a withheld header into a response visible to middleware.
135
+ Selecting `Cookie` removes the entire header, not just one named cookie.
136
+
137
+ Host request/response/error hooks, first-party policies and request-body checks
138
+ retain the original headers. The runtime does not mutate the caller's Headers
139
+ object. Existing applications behave exactly as before when no plugin declares
140
+ credential headers. Plugins still need at least one lifecycle or request hook;
141
+ a header list alone is not a complete plugin.
142
+
143
+ This is a data boundary, not authentication, authorization or cache protection.
144
+ It does not create a principal, validate a token, protect a route, or prevent
145
+ shared caching. In particular, withholding Cookie or Authorization from guests
146
+ does not make a personalized response safe to cache. Operators must separately
147
+ configure the appropriate authentication and cache behavior. It also does not
148
+ redact URL/query/body content, explicit binding grants, logs or values deliberately
149
+ reflected by trusted host plugins. Host plugins retain full access to the request
150
+ and remain trusted operator code.
151
+
115
152
  ## Results
116
153
 
117
154
  A result is what the runtime writes: