@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,126 @@
1
+ version: "1"
2
+ # Reusable middleware patterns. Each module lives in middleware/ and is documented
3
+ # in docs/MIDDLEWARE-EXAMPLES.md. Bindings are literal values here; production
4
+ # projects use {secret: name} with an operator grant instead.
5
+ routes:
6
+ /api/private:
7
+ description: Bearer token gate with a constant-time comparison
8
+ env:
9
+ API_TOKEN: {value: cookbook-token}
10
+ middleware:
11
+ - source: middleware/auth.mjs
12
+ export: bearer
13
+ respond: {json: {private: true}}
14
+ /admin/panel:
15
+ description: HTTP Basic authentication decoded inside the guest
16
+ env:
17
+ ADMIN_USER: {value: admin}
18
+ ADMIN_PASSWORD: {value: cookbook-password}
19
+ middleware:
20
+ - source: middleware/auth.mjs
21
+ export: basic
22
+ respond: {text: Admin panel}
23
+ /cors/data:
24
+ description: CORS allowlist with preflight handled before the handler
25
+ methods: [GET, HEAD, OPTIONS]
26
+ env:
27
+ ALLOWED_ORIGINS: {value: https://app.example.com https://admin.example.com}
28
+ middleware:
29
+ - source: middleware/cors.mjs
30
+ respond: {json: {shared: true}}
31
+ /traced:
32
+ description: Caller correlation id and Server-Timing on every response
33
+ middleware:
34
+ - source: middleware/request-id.mjs
35
+ respond: {text: Traced}
36
+ /maintenance:
37
+ description: Kill switch returning 503 unless a bypass header is presented
38
+ env:
39
+ MAINTENANCE: {value: "true"}
40
+ MAINTENANCE_BYPASS: {value: let-me-in}
41
+ middleware:
42
+ - source: middleware/maintenance.mjs
43
+ respond: {text: Still here}
44
+ /fragile:
45
+ description: Error boundary turning a thrown exception into a JSON 500
46
+ parameters:
47
+ - name: fail
48
+ in: query
49
+ schema: {type: boolean, default: false}
50
+ middleware:
51
+ - source: middleware/request-id.mjs
52
+ - source: middleware/errors.mjs
53
+ function:
54
+ source: functions/fail.mjs
55
+ args:
56
+ fail: {from: query, name: fail}
57
+ /api/items:
58
+ description: JSON envelope around a function response, in the short form (a module path expands to source)
59
+ middleware: [middleware/envelope.mjs]
60
+ function: functions/items.mjs
61
+ /negotiated:
62
+ description: Accept-driven JSON or plain-text representation, 406 otherwise
63
+ middleware:
64
+ - source: middleware/negotiate.mjs
65
+ function: {source: functions/status.mjs}
66
+ /resource:
67
+ description: Method override tunneling PUT, PATCH or DELETE through POST
68
+ methods: [GET, HEAD, POST, PUT, PATCH, DELETE]
69
+ middleware:
70
+ - source: middleware/methods.mjs
71
+ export: override
72
+ function: {source: functions/resource.mjs}
73
+ /versioned:
74
+ description: Weak ETag and 304 for function output
75
+ middleware:
76
+ - source: middleware/etag.mjs
77
+ function: {source: functions/catalog.mjs}
78
+ /experiment:
79
+ description: Sticky A/B bucket cookie choosing between two destinations
80
+ env:
81
+ VARIANT_URL: {value: https://example.com/landing-b}
82
+ middleware:
83
+ - source: middleware/bucket.mjs
84
+ redirect: {url: https://example.com/landing}
85
+ /welcome:
86
+ description: Accept-Language redirect to a localized destination
87
+ env:
88
+ LOCALES: {value: en de fr}
89
+ SITE: {value: https://example.com}
90
+ middleware:
91
+ - source: middleware/locale.mjs
92
+ redirect: {url: https://example.com/en/welcome}
93
+ /downloads/report:
94
+ description: Referer allowlist in front of a native download
95
+ env:
96
+ ALLOWED_REFERERS: {value: example.com www.example.com}
97
+ middleware:
98
+ - source: middleware/referer.mjs
99
+ download:
100
+ file: public/guide.txt
101
+ filename: report.txt
102
+ contentType: text/plain
103
+ cacheControl: no-store
104
+ /profile:
105
+ description: Body validated once in middleware, parsed object passed through state
106
+ methods: [POST]
107
+ request:
108
+ body:
109
+ required: true
110
+ maxBytes: 4096
111
+ contentTypes: [application/json]
112
+ format: json
113
+ middleware:
114
+ - source: middleware/body.mjs
115
+ function: {source: functions/profile.mjs}
116
+ /inspect:
117
+ description: Authoring aid echoing inputs, args and redacted headers on demand
118
+ parameters:
119
+ - name: topic
120
+ in: query
121
+ schema: {type: string, default: routing}
122
+ env:
123
+ DEBUG: {value: "true"}
124
+ middleware:
125
+ - source: middleware/debug.mjs
126
+ respond: {text: Nothing to see}
@@ -250,5 +250,531 @@
250
250
  {
251
251
  "path": "/sitemap.xml",
252
252
  "status": 404
253
+ },
254
+ {
255
+ "path": "/api/private",
256
+ "status": 401,
257
+ "expectHeaders": {
258
+ "www-authenticate": "Bearer realm=\"cookbook\""
259
+ }
260
+ },
261
+ {
262
+ "path": "/api/private",
263
+ "status": 401,
264
+ "headers": {
265
+ "authorization": "Bearer wrong-token"
266
+ }
267
+ },
268
+ {
269
+ "path": "/api/private",
270
+ "status": 200,
271
+ "headers": {
272
+ "authorization": "Bearer cookbook-token"
273
+ },
274
+ "expectBody": "{\"private\":true}"
275
+ },
276
+ {
277
+ "path": "/api/private",
278
+ "status": 200,
279
+ "method": "HEAD",
280
+ "headers": {
281
+ "authorization": "Bearer cookbook-token"
282
+ },
283
+ "expectBody": ""
284
+ },
285
+ {
286
+ "path": "/admin/panel",
287
+ "status": 401,
288
+ "expectHeaders": {
289
+ "www-authenticate": "Basic realm=\"cookbook\", charset=\"UTF-8\""
290
+ }
291
+ },
292
+ {
293
+ "path": "/admin/panel",
294
+ "status": 401,
295
+ "headers": {
296
+ "authorization": "Basic YWRtaW46d3Jvbmc="
297
+ }
298
+ },
299
+ {
300
+ "path": "/admin/panel",
301
+ "status": 200,
302
+ "headers": {
303
+ "authorization": "Basic YWRtaW46Y29va2Jvb2stcGFzc3dvcmQ="
304
+ },
305
+ "expectBody": "Admin panel"
306
+ },
307
+ {
308
+ "path": "/admin/panel",
309
+ "status": 200,
310
+ "method": "HEAD",
311
+ "headers": {
312
+ "authorization": "Basic YWRtaW46Y29va2Jvb2stcGFzc3dvcmQ="
313
+ },
314
+ "expectBody": ""
315
+ },
316
+ {
317
+ "path": "/cors/data",
318
+ "status": 204,
319
+ "method": "OPTIONS",
320
+ "headers": {
321
+ "origin": "https://app.example.com",
322
+ "access-control-request-method": "GET"
323
+ },
324
+ "expectBody": "",
325
+ "expectHeaders": {
326
+ "access-control-allow-origin": "https://app.example.com",
327
+ "access-control-allow-methods": "GET, HEAD, OPTIONS",
328
+ "vary": "origin"
329
+ }
330
+ },
331
+ {
332
+ "path": "/cors/data",
333
+ "status": 204,
334
+ "method": "OPTIONS",
335
+ "headers": {
336
+ "origin": "https://evil.example.net"
337
+ },
338
+ "expectBody": "",
339
+ "expectHeaders": {
340
+ "vary": "origin"
341
+ }
342
+ },
343
+ {
344
+ "path": "/cors/data",
345
+ "status": 200,
346
+ "headers": {
347
+ "origin": "https://admin.example.com"
348
+ },
349
+ "expectBody": "{\"shared\":true}",
350
+ "expectHeaders": {
351
+ "access-control-allow-origin": "https://admin.example.com"
352
+ }
353
+ },
354
+ {
355
+ "path": "/cors/data",
356
+ "status": 200,
357
+ "expectBody": "{\"shared\":true}",
358
+ "expectHeaders": {
359
+ "vary": "origin"
360
+ }
361
+ },
362
+ {
363
+ "path": "/cors/data",
364
+ "status": 200,
365
+ "method": "HEAD",
366
+ "expectBody": "",
367
+ "expectHeaders": {
368
+ "vary": "origin"
369
+ }
370
+ },
371
+ {
372
+ "path": "/traced",
373
+ "status": 200,
374
+ "headers": {
375
+ "x-correlation-id": "trace-123"
376
+ },
377
+ "expectBody": "Traced",
378
+ "expectHeaders": {
379
+ "x-correlation-id": "trace-123"
380
+ }
381
+ },
382
+ {
383
+ "path": "/traced",
384
+ "status": 200,
385
+ "headers": {
386
+ "x-correlation-id": "not valid!"
387
+ },
388
+ "expectBody": "Traced"
389
+ },
390
+ {
391
+ "path": "/traced",
392
+ "status": 200,
393
+ "method": "HEAD",
394
+ "expectBody": ""
395
+ },
396
+ {
397
+ "path": "/maintenance",
398
+ "status": 503,
399
+ "expectBody": "Down for maintenance\n",
400
+ "expectHeaders": {
401
+ "retry-after": "120"
402
+ }
403
+ },
404
+ {
405
+ "path": "/maintenance",
406
+ "status": 200,
407
+ "method": "HEAD",
408
+ "headers": {
409
+ "x-maintenance-bypass": "let-me-in"
410
+ },
411
+ "expectBody": ""
412
+ },
413
+ {
414
+ "path": "/maintenance",
415
+ "status": 200,
416
+ "headers": {
417
+ "x-maintenance-bypass": "let-me-in"
418
+ },
419
+ "expectBody": "Still here"
420
+ },
421
+ {
422
+ "path": "/fragile",
423
+ "status": 200,
424
+ "headers": {
425
+ "x-correlation-id": "r-1"
426
+ },
427
+ "expectBody": "{\"healthy\":true}",
428
+ "expectHeaders": {
429
+ "x-correlation-id": "r-1"
430
+ }
431
+ },
432
+ {
433
+ "path": "/fragile?fail=true",
434
+ "status": 500,
435
+ "headers": {
436
+ "x-correlation-id": "r-2"
437
+ },
438
+ "expectBody": "{\"error\":\"Temporarily unavailable\",\"correlationId\":\"r-2\"}",
439
+ "expectHeaders": {
440
+ "cache-control": "no-store"
441
+ }
442
+ },
443
+ {
444
+ "path": "/fragile",
445
+ "status": 200,
446
+ "method": "HEAD",
447
+ "expectBody": ""
448
+ },
449
+ {
450
+ "path": "/fragile?fail=true",
451
+ "status": 500,
452
+ "method": "HEAD",
453
+ "expectBody": ""
454
+ },
455
+ {
456
+ "path": "/fragile?fail=perhaps",
457
+ "status": 400
458
+ },
459
+ {
460
+ "path": "/api/items",
461
+ "status": 200,
462
+ "expectBody": "{\"ok\":true,\"data\":[{\"id\":1,\"name\":\"first\"},{\"id\":2,\"name\":\"second\"}],\"meta\":{\"route\":\"/api/items\",\"count\":2}}"
463
+ },
464
+ {
465
+ "path": "/api/items",
466
+ "status": 200,
467
+ "method": "HEAD",
468
+ "expectBody": ""
469
+ },
470
+ {
471
+ "path": "/negotiated",
472
+ "status": 200,
473
+ "expectBody": "{\"service\":\"cookbook\",\"state\":\"ok\"}",
474
+ "expectHeaders": {
475
+ "vary": "accept"
476
+ }
477
+ },
478
+ {
479
+ "path": "/negotiated",
480
+ "status": 200,
481
+ "headers": {
482
+ "accept": "application/json"
483
+ },
484
+ "expectBody": "{\"service\":\"cookbook\",\"state\":\"ok\"}"
485
+ },
486
+ {
487
+ "path": "/negotiated",
488
+ "status": 200,
489
+ "headers": {
490
+ "accept": "text/plain, application/json;q=0.5"
491
+ },
492
+ "expectBody": "service: cookbook\nstate: ok\n",
493
+ "expectHeaders": {
494
+ "content-type": "text/plain; charset=utf-8"
495
+ }
496
+ },
497
+ {
498
+ "path": "/negotiated",
499
+ "status": 406,
500
+ "headers": {
501
+ "accept": "image/png"
502
+ }
503
+ },
504
+ {
505
+ "path": "/negotiated",
506
+ "status": 200,
507
+ "method": "HEAD",
508
+ "expectBody": ""
509
+ },
510
+ {
511
+ "path": "/resource",
512
+ "status": 200,
513
+ "expectBody": "{\"method\":\"GET\",\"tunneled\":false}"
514
+ },
515
+ {
516
+ "path": "/resource",
517
+ "status": 200,
518
+ "method": "HEAD",
519
+ "expectBody": ""
520
+ },
521
+ {
522
+ "path": "/resource",
523
+ "status": 200,
524
+ "method": "POST",
525
+ "expectBody": "{\"method\":\"POST\",\"tunneled\":false}"
526
+ },
527
+ {
528
+ "path": "/resource",
529
+ "status": 200,
530
+ "method": "POST",
531
+ "headers": {
532
+ "x-http-method-override": "delete"
533
+ },
534
+ "expectBody": "{\"method\":\"DELETE\",\"tunneled\":true}"
535
+ },
536
+ {
537
+ "path": "/resource",
538
+ "status": 405,
539
+ "method": "POST",
540
+ "headers": {
541
+ "x-http-method-override": "TRACE"
542
+ },
543
+ "expectHeaders": {
544
+ "allow": "PUT, PATCH, DELETE"
545
+ }
546
+ },
547
+ {
548
+ "path": "/resource",
549
+ "status": 200,
550
+ "method": "PUT",
551
+ "expectBody": "{\"method\":\"PUT\",\"tunneled\":false}"
552
+ },
553
+ {
554
+ "path": "/resource",
555
+ "status": 200,
556
+ "method": "PATCH",
557
+ "expectBody": "{\"method\":\"PATCH\",\"tunneled\":false}"
558
+ },
559
+ {
560
+ "path": "/resource",
561
+ "status": 200,
562
+ "method": "DELETE",
563
+ "expectBody": "{\"method\":\"DELETE\",\"tunneled\":false}"
564
+ },
565
+ {
566
+ "path": "/versioned",
567
+ "status": 200,
568
+ "expectBody": "{\"version\":3,\"items\":[\"alpha\",\"beta\"]}",
569
+ "expectHeaders": {
570
+ "etag": "W/\"b4124e8a\"",
571
+ "cache-control": "public, max-age=60"
572
+ }
573
+ },
574
+ {
575
+ "path": "/versioned",
576
+ "status": 304,
577
+ "headers": {
578
+ "if-none-match": "W/\"b4124e8a\""
579
+ },
580
+ "expectBody": "",
581
+ "expectHeaders": {
582
+ "etag": "W/\"b4124e8a\""
583
+ }
584
+ },
585
+ {
586
+ "path": "/versioned",
587
+ "status": 200,
588
+ "headers": {
589
+ "if-none-match": "W/\"stale\""
590
+ },
591
+ "expectHeaders": {
592
+ "etag": "W/\"b4124e8a\""
593
+ }
594
+ },
595
+ {
596
+ "path": "/versioned",
597
+ "status": 200,
598
+ "method": "HEAD",
599
+ "expectBody": "",
600
+ "expectHeaders": {
601
+ "etag": "W/\"b4124e8a\""
602
+ }
603
+ },
604
+ {
605
+ "path": "/experiment",
606
+ "status": 302,
607
+ "headers": {
608
+ "cookie": "bucket=a"
609
+ },
610
+ "expectHeaders": {
611
+ "location": "https://example.com/landing",
612
+ "vary": "cookie"
613
+ }
614
+ },
615
+ {
616
+ "path": "/experiment",
617
+ "status": 302,
618
+ "headers": {
619
+ "cookie": "theme=light; bucket=b"
620
+ },
621
+ "expectHeaders": {
622
+ "location": "https://example.com/landing-b",
623
+ "vary": "cookie"
624
+ }
625
+ },
626
+ {
627
+ "path": "/experiment",
628
+ "status": 302,
629
+ "expectHeaders": {
630
+ "vary": "cookie"
631
+ }
632
+ },
633
+ {
634
+ "path": "/experiment",
635
+ "status": 302,
636
+ "method": "HEAD",
637
+ "headers": {
638
+ "cookie": "bucket=a"
639
+ },
640
+ "expectHeaders": {
641
+ "location": "https://example.com/landing"
642
+ }
643
+ },
644
+ {
645
+ "path": "/welcome",
646
+ "status": 302,
647
+ "expectHeaders": {
648
+ "location": "https://example.com/en/welcome",
649
+ "vary": "accept-language"
650
+ }
651
+ },
652
+ {
653
+ "path": "/welcome",
654
+ "status": 302,
655
+ "headers": {
656
+ "accept-language": "de-DE,de;q=0.9,en;q=0.8"
657
+ },
658
+ "expectHeaders": {
659
+ "location": "https://example.com/de/welcome"
660
+ }
661
+ },
662
+ {
663
+ "path": "/welcome",
664
+ "status": 302,
665
+ "headers": {
666
+ "accept-language": "fr-CA"
667
+ },
668
+ "expectHeaders": {
669
+ "location": "https://example.com/fr/welcome"
670
+ }
671
+ },
672
+ {
673
+ "path": "/welcome",
674
+ "status": 302,
675
+ "headers": {
676
+ "accept-language": "ja"
677
+ },
678
+ "expectHeaders": {
679
+ "location": "https://example.com/en/welcome"
680
+ }
681
+ },
682
+ {
683
+ "path": "/welcome",
684
+ "status": 302,
685
+ "method": "HEAD",
686
+ "headers": {
687
+ "accept-language": "de"
688
+ },
689
+ "expectHeaders": {
690
+ "location": "https://example.com/de/welcome"
691
+ }
692
+ },
693
+ {
694
+ "path": "/downloads/report",
695
+ "status": 403
696
+ },
697
+ {
698
+ "path": "/downloads/report",
699
+ "status": 403,
700
+ "headers": {
701
+ "referer": "https://evil.example.net/page"
702
+ }
703
+ },
704
+ {
705
+ "path": "/downloads/report",
706
+ "status": 200,
707
+ "headers": {
708
+ "referer": "https://www.example.com/docs"
709
+ },
710
+ "expectBody": "URLCode guide\n",
711
+ "expectHeaders": {
712
+ "content-disposition": "attachment; filename=report.txt"
713
+ }
714
+ },
715
+ {
716
+ "path": "/downloads/report",
717
+ "status": 200,
718
+ "method": "HEAD",
719
+ "headers": {
720
+ "referer": "https://example.com/"
721
+ },
722
+ "expectBody": ""
723
+ },
724
+ {
725
+ "path": "/profile",
726
+ "status": 201,
727
+ "method": "POST",
728
+ "headers": {
729
+ "content-type": "application/json"
730
+ },
731
+ "body": "{\"name\":\"Ada\",\"age\":36}",
732
+ "expectBody": "{\"welcome\":\"Ada\",\"age\":36}"
733
+ },
734
+ {
735
+ "path": "/profile",
736
+ "status": 422,
737
+ "method": "POST",
738
+ "headers": {
739
+ "content-type": "application/json"
740
+ },
741
+ "body": "{\"name\":\"\",\"age\":-1,\"role\":\"x\"}",
742
+ "expectBody": "{\"errors\":[\"name must be 1-80 characters\",\"age must be an integer from 0 to 150\",\"unknown field role\"]}"
743
+ },
744
+ {
745
+ "path": "/profile",
746
+ "status": 400,
747
+ "method": "POST",
748
+ "headers": {
749
+ "content-type": "application/json"
750
+ },
751
+ "body": "["
752
+ },
753
+ {
754
+ "path": "/profile",
755
+ "status": 405
756
+ },
757
+ {
758
+ "path": "/inspect?topic=middleware",
759
+ "status": 200,
760
+ "expectBody": "Nothing to see"
761
+ },
762
+ {
763
+ "path": "/inspect?topic=middleware",
764
+ "status": 200,
765
+ "headers": {
766
+ "x-debug": "1",
767
+ "authorization": "Bearer hidden"
768
+ },
769
+ "expectHeaders": {
770
+ "cache-control": "no-store",
771
+ "content-type": "application/json"
772
+ }
773
+ },
774
+ {
775
+ "path": "/inspect",
776
+ "status": 200,
777
+ "method": "HEAD",
778
+ "expectBody": ""
253
779
  }
254
780
  ]
@@ -6,6 +6,7 @@ includes:
6
6
  - routes/responses.yaml
7
7
  - routes/files.yaml
8
8
  - routes/policies.yaml
9
+ - routes/middleware.yaml
9
10
  routes: {}
10
11
  # Site conventions: each key generates one native route (see docs/SITE.md).
11
12
  # sitemap is left out here because the cookbook audit runs without --origin.
@@ -0,0 +1,22 @@
1
+ # Operator-granted egress example
2
+
3
+ This executable schema example uses reserved example destinations. Replace them
4
+ with HTTPS endpoints you own before attempting requests. No test or authoring
5
+ inspection needs network access.
6
+
7
+ Generate the requested permission document with the CLI `permissions` command,
8
+ review it and save it **outside this project**, then pass it with `--policy` when
9
+ starting the runtime. The generated hash pins these routes and function sources;
10
+ regenerate and review grants after changing declarations. Keep proxy and signal
11
+ origin grants separate. An origin grant authorizes the declared destination and
12
+ its explicit path/query/header behavior; it is not a guest fetch capability.
13
+
14
+ `GET /proxy/hello?page=2` proxies to the declared items endpoint. Incoming cookies,
15
+ Authorization and unlisted query values are not forwarded. `GET /event` returns
16
+ its native response and schedules a best-effort webhook with route pattern,
17
+ method and status only. HEAD and generated readiness probes do not emit signals.
18
+ There is no retry, queue or delivery guarantee.
19
+
20
+ See [egress contract](../../docs/EGRESS.md). Integration tests execute this example
21
+ with trusted fake transport and public-address fixtures, never by allowing private
22
+ network destinations in production.
@@ -0,0 +1,19 @@
1
+ id: egress
2
+ description: A bounded HTTPS proxy route and a route with a best-effort signal, both needing revision-pinned operator egress grants.
3
+ tags: [egress, proxy, signals, outbound, https, grant, policy, operator, webhook]
4
+ complexity: advanced
5
+ capabilities: [enabled, methods, parameters, proxy, respond, signals]
6
+ targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused}
7
+ routes: 2
8
+ grants:
9
+ - kind: proxy
10
+ description: The upstream origin, granted in an operator policy generated with urlcode permissions.
11
+ - kind: signals
12
+ description: The hook origin, granted the same way; signals are best effort and never retried.
13
+ files: [urlcode.yaml, README.md]
14
+ tests:
15
+ commands:
16
+ - node src/cli.ts permissions --project examples/egress
17
+ - node src/cli.ts validate --local --project examples/egress --policy /operator/egress-policy.json
18
+ behavior:
19
+ - without the policy the project refuses to activate; nothing here reaches the network during authoring inspection