@jimhoyd/urlcode 0.4.2 → 0.4.7

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 (214) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +57 -14
  2. package/.claude/skills/urlcode-operations/SKILL.md +4 -0
  3. package/README.md +42 -18
  4. package/SECURITY.md +5 -3
  5. package/dist/BUILD-MANIFEST.json +30 -27
  6. package/dist/agent-context.js +82 -0
  7. package/dist/agents-guide.js +42 -42
  8. package/dist/authoring.js +12 -2
  9. package/dist/body-schema.js +159 -0
  10. package/dist/build-cloudflare.js +2 -0
  11. package/dist/capabilities.js +1 -1
  12. package/dist/cli.js +28 -12
  13. package/dist/config.js +74 -6
  14. package/dist/context.js +4 -6
  15. package/dist/errors.js +3 -1
  16. package/dist/examples.js +1 -1
  17. package/dist/extensions.js +60 -2
  18. package/dist/http-policy.js +19 -4
  19. package/dist/http-response.js +2 -2
  20. package/dist/init-with.js +71 -9
  21. package/dist/mcp.js +15 -2
  22. package/dist/pattern-guard.js +32 -0
  23. package/dist/policies/security.js +0 -0
  24. package/dist/project-tests.js +35 -11
  25. package/dist/readiness.js +184 -32
  26. package/dist/recipes.js +1 -1
  27. package/dist/router.js +17 -0
  28. package/dist/runtime.js +26 -3
  29. package/dist/scaffold.js +0 -0
  30. package/dist/server.js +26 -3
  31. package/dist/site.js +0 -0
  32. package/dist/tooling.js +2 -1
  33. package/dist/types/agent-context.d.ts +44 -0
  34. package/dist/types/authoring.d.ts +3 -1
  35. package/dist/types/body-schema.d.ts +58 -0
  36. package/dist/types/config.d.ts +10 -2
  37. package/dist/types/context.d.ts +1 -1
  38. package/dist/types/errors.d.ts +9 -1
  39. package/dist/types/examples.d.ts +1 -1
  40. package/dist/types/extensions.d.ts +47 -2
  41. package/dist/types/http-policy.d.ts +3 -0
  42. package/dist/types/init-with.d.ts +10 -1
  43. package/dist/types/pattern-guard.d.ts +10 -0
  44. package/dist/types/project-tests.d.ts +9 -0
  45. package/dist/types/readiness.d.ts +68 -0
  46. package/dist/types/recipes.d.ts +1 -1
  47. package/dist/types/runtime.d.ts +4 -0
  48. package/dist/types/server.d.ts +9 -1
  49. package/dist/types/tooling.d.ts +1 -0
  50. package/dist/types/types.d.ts +13 -0
  51. package/dist/types.js +10 -2
  52. package/dist/typescript-authoring.js +5 -3
  53. package/dist/verify-deployment.js +16 -10
  54. package/examples/body-validation/README.md +16 -0
  55. package/examples/body-validation/example.yaml +17 -0
  56. package/examples/body-validation/tests/requests.json +15 -0
  57. package/examples/body-validation/urlcode.yaml +34 -0
  58. package/examples/coverage-waiver/README.md +8 -0
  59. package/examples/coverage-waiver/example.yaml +16 -0
  60. package/examples/coverage-waiver/functions/notes.mjs +2 -0
  61. package/examples/coverage-waiver/tests/requests.json +3 -0
  62. package/examples/coverage-waiver/urlcode.yaml +10 -0
  63. package/examples/data-dir/README.md +39 -0
  64. package/examples/data-dir/data/welcome.txt +1 -0
  65. package/examples/data-dir/example.yaml +22 -0
  66. package/examples/data-dir/functions/note.mjs +18 -0
  67. package/examples/data-dir/tests/requests.json +6 -0
  68. package/examples/data-dir/urlcode.yaml +6 -0
  69. package/examples/lifecycle/README.md +15 -0
  70. package/examples/lifecycle/example.yaml +19 -0
  71. package/examples/lifecycle/functions/notes.mjs +29 -0
  72. package/examples/lifecycle/tests/requests.json +12 -0
  73. package/examples/lifecycle/urlcode.yaml +29 -0
  74. package/examples/not-found/README.md +10 -0
  75. package/examples/not-found/example.yaml +17 -0
  76. package/examples/not-found/public/404.html +3 -0
  77. package/examples/not-found/public/index.html +3 -0
  78. package/examples/not-found/tests/requests.json +7 -0
  79. package/examples/not-found/urlcode.yaml +7 -0
  80. package/examples/shared-blocks/README.md +11 -0
  81. package/examples/shared-blocks/example.yaml +17 -0
  82. package/examples/shared-blocks/tests/requests.json +7 -0
  83. package/examples/shared-blocks/urlcode.yaml +37 -0
  84. package/llms-full.txt +364 -49
  85. package/llms.txt +44 -6
  86. package/package.json +15 -26
  87. package/recipes/static-page/README.md +9 -0
  88. package/recipes/static-page/public/index.html +11 -0
  89. package/recipes/static-page/recipe.yaml +21 -0
  90. package/recipes/static-page/tests/requests.json +22 -0
  91. package/recipes/static-page/urlcode.yaml +7 -0
  92. package/recipes/static-plus-api/README.md +6 -0
  93. package/recipes/static-plus-api/urlcode.yaml +4 -0
  94. package/recipes/store-crud/README.md +53 -0
  95. package/recipes/store-crud/recipe.yaml +31 -0
  96. package/recipes/store-crud/tests/requests.json +18 -0
  97. package/recipes/store-crud/urlcode.yaml +18 -0
  98. package/schemas/urlcode.schema.json +125 -60
  99. package/skills/urlcode/SKILL.md +47 -27
  100. package/starters/default/AGENTS.md +43 -43
  101. package/starters/page/README.md +14 -0
  102. package/starters/page/public/index.html +12 -0
  103. package/starters/page/tests/requests.json +17 -0
  104. package/starters/page/urlcode.yaml +6 -0
  105. package/.claude-plugin/marketplace.json +0 -18
  106. package/CONTRIBUTING.md +0 -148
  107. package/ROADMAP.md +0 -70
  108. package/docs/AI-AUTHORING.md +0 -339
  109. package/docs/ASSETS.md +0 -107
  110. package/docs/AUTH-BACKUP.md +0 -32
  111. package/docs/AWS.md +0 -86
  112. package/docs/BEST-PRACTICES.md +0 -276
  113. package/docs/BULK.md +0 -79
  114. package/docs/CAPABILITIES.md +0 -192
  115. package/docs/CAPACITY.md +0 -305
  116. package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
  117. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
  118. package/docs/CI.md +0 -147
  119. package/docs/CLOUDFLARE.md +0 -109
  120. package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
  121. package/docs/COMPLIANCE.md +0 -239
  122. package/docs/COMPOSING-A-SITE.md +0 -287
  123. package/docs/CONDITIONS.md +0 -74
  124. package/docs/CONTAINER-PROMOTION.md +0 -74
  125. package/docs/DEPLOYMENT-CHECKS.md +0 -108
  126. package/docs/DEVELOPMENT-PIPELINE.md +0 -304
  127. package/docs/EGRESS.md +0 -125
  128. package/docs/EXTENSIONS.md +0 -403
  129. package/docs/FRAMEWORK.md +0 -217
  130. package/docs/FUNCTION-SECURITY.md +0 -251
  131. package/docs/HTTP.md +0 -129
  132. package/docs/INSTALL.md +0 -128
  133. package/docs/INTERCHANGE.md +0 -134
  134. package/docs/LOAD-TESTING.md +0 -91
  135. package/docs/LOCAL-DEVELOPMENT.md +0 -102
  136. package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
  137. package/docs/MIDDLEWARE.md +0 -102
  138. package/docs/MONITORING.md +0 -115
  139. package/docs/OBSERVABILITY.md +0 -222
  140. package/docs/OPEN-DECISIONS.md +0 -149
  141. package/docs/OPERATIONAL-PROOF.md +0 -41
  142. package/docs/OPERATIONS.md +0 -201
  143. package/docs/ORGANIZATION.md +0 -135
  144. package/docs/PERFORMANCE.md +0 -72
  145. package/docs/PLUGINS.md +0 -271
  146. package/docs/POLICIES.md +0 -211
  147. package/docs/PRERENDER.md +0 -245
  148. package/docs/PROJECT-DIRECTION.md +0 -118
  149. package/docs/PROVIDER-VERIFICATION.md +0 -84
  150. package/docs/READINESS.md +0 -153
  151. package/docs/README.md +0 -93
  152. package/docs/RECIPES.md +0 -99
  153. package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
  154. package/docs/RELEASE-0.4.1.md +0 -73
  155. package/docs/RELEASE-0.4.2.md +0 -30
  156. package/docs/RELEASE-READINESS.md +0 -146
  157. package/docs/RELEASE-SECURITY.md +0 -100
  158. package/docs/RESILIENCE.md +0 -161
  159. package/docs/ROUTING.md +0 -92
  160. package/docs/SANDBOX-REVIEW.md +0 -72
  161. package/docs/SCAFFOLDING.md +0 -70
  162. package/docs/SECURITY-AUDIT.md +0 -164
  163. package/docs/SITE.md +0 -150
  164. package/docs/SPECIFICATION.md +0 -359
  165. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
  166. package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
  167. package/docs/SPIKE-CORE-LAYERING.md +0 -368
  168. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -207
  169. package/docs/STANDARDS.md +0 -311
  170. package/docs/STARTERS.md +0 -83
  171. package/docs/STATIC.md +0 -105
  172. package/docs/TOOLING.md +0 -298
  173. package/docs/TUNNELS.md +0 -72
  174. package/docs/TYPESCRIPT-AUTHORING.md +0 -87
  175. package/docs/TYPESCRIPT.md +0 -123
  176. package/docs/VERCEL.md +0 -114
  177. package/docs/VERSION-ALIGNMENT.md +0 -88
  178. package/docs/YAML-GUIDE.md +0 -57
  179. package/docs/YAML-REFERENCE.md +0 -449
  180. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
  181. package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
  182. package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
  183. package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
  184. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
  185. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
  186. package/docs/archive/2026-09-19/ROADMAP.md +0 -387
  187. package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
  188. package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
  189. package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
  190. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
  191. package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
  192. package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +0 -116
  193. package/docs/archive/README.md +0 -29
  194. package/docs/policies/agents.md +0 -182
  195. package/docs/policies/cache.md +0 -152
  196. package/docs/policies/compression.md +0 -169
  197. package/docs/policies/contract.md +0 -52
  198. package/docs/policies/hardened.md +0 -56
  199. package/docs/policies/interoperability.md +0 -169
  200. package/docs/policies/operations.md +0 -45
  201. package/docs/policies/security.md +0 -161
  202. package/docs/policies/throttle.md +0 -103
  203. package/docs/yaml/assets.md +0 -36
  204. package/docs/yaml/conditions.md +0 -20
  205. package/docs/yaml/functions.md +0 -168
  206. package/docs/yaml/middleware.md +0 -31
  207. package/docs/yaml/organization.md +0 -74
  208. package/docs/yaml/policies.md +0 -37
  209. package/docs/yaml/redirects.md +0 -64
  210. package/docs/yaml/responses.md +0 -57
  211. package/docs/yaml/site.md +0 -24
  212. package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
  213. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -130
  214. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
@@ -1,284 +0,0 @@
1
- <!-- trust-model-prose: historical-file -->
2
- <!-- This report quotes the defective wording it is reporting -- including the
3
- pre-trusted-default claims in finding 4 -- so the prose gate would read the
4
- quotations as the guidance itself. The file is a dated review of one
5
- commit and is not edited as the defects are fixed. -->
6
-
7
- # Codebase, tooling and documentation audit — 2026-09-20
8
-
9
- Reviewed commit: `bca8ac7` (core 0.4.0-alpha.2, auth 0.1.0-alpha.5,
10
- admin 0.1.0-alpha.4, UI 0.1.0-alpha.6). Local environment: macOS,
11
- Node 26.8.2. Date is UTC. This report records findings; it does not change
12
- runtime behavior or remove supported features.
13
-
14
- The trusted default is implemented correctly in the dispatch paths inspected:
15
- only a route declaring `sandbox: true` enters the QuickJS pool. Ordinary
16
- project functions and middleware run in Node. Host access by trusted code is
17
- intentional, not an audit finding. Binding grants scope injected context;
18
- they do not confine trusted code's independent filesystem, environment or
19
- network access. Opt-in sandbox isolation and provider refusals remain necessary.
20
-
21
- ## Scope and method
22
-
23
- Inventoried 966 tracked files. Reviewed the implemented specification,
24
- contribution/security instructions, runtime activation/dispatch, module loading,
25
- TypeScript authoring, inspection/manifests/diffs, extension hooks, workspace
26
- contracts, package/build/check scripts, CI/release configuration, starter,
27
- example/recipe coverage and documentation indexes. Used targeted source review,
28
- the complete local verification suite and synthetic reproductions. This is a
29
- repository-wide engineering audit, not a claim that every line or execution
30
- path received independent security review.
31
-
32
- Scanned tracked non-archive/non-spike Markdown for local links and YAML fences:
33
- 64 YAML fences, 49 route/project-shaped candidates. Added only an omitted
34
- format version or the documented route wrapper for schema checks. Classified
35
- partial illustrations separately from complete examples. Semantic constraints,
36
- external services and prose fragments require more than schema validation.
37
-
38
- Searched existing repository issues before filing. Five new issues and new
39
- evidence on existing issue 168 retain all actionable findings below.
40
-
41
- ## Findings, ordered by practical impact
42
-
43
- ### 1. Inspection hides middleware execution-mode changes — P2
44
-
45
- [Issue 199](https://github.com/jimhoyd-com/urlcode/issues/199).
46
-
47
- For a `respond` route with `middleware: [mw.mjs]`, switching `sandbox` from
48
- true to false produces identical manifest route records and an empty route
49
- diff. This changes execution from QuickJS to full Node without appearing in
50
- those review surfaces. The overall manifest digest can change; it does not
51
- explain the change to reviewers.
52
-
53
- `src/explain.ts` adds sandbox information only inside the function-handler
54
- case. `src/manifest.ts` carries that handler object forward, while
55
- `src/readiness.ts` and `src/route-diff.ts` omit execution mode from inventory.
56
- Context already exposes it at route level.
57
-
58
- Reproduction: create the same middleware/native route in both modes, call
59
- `buildManifest`, obtain `createRuntime(...).testPlan()`, then call `diffRoutes`.
60
- Observed diff: `{"added":[],"removed":[],"changed":[]}`. Add route-level
61
- execution mode/reason to explain, manifest and inventory, with compatibility
62
- handling for older inventory files and regressions for both handler kinds.
63
-
64
- ### 2. Auth/admin hook reactivation serves stale entry code — P2
65
-
66
- [Issue 198](https://github.com/jimhoyd-com/urlcode/issues/198).
67
-
68
- Both `packages/auth/src/lifecycle-hooks.ts` and
69
- `packages/admin/src/admin-hooks.ts` import an unchanged file URL. Loading a
70
- hook, editing its entry file, then loading it again in the same process returns
71
- the old hook. A synthetic decision changed on disk from allow/v1 to deny/v2;
72
- both loaders still returned allow/v1 after reactivation.
73
-
74
- Native trusted routes already give entry modules a new URL per activation.
75
- Apply an explicit hook reload policy and test it, or clearly require process
76
- restart for hook changes. This reproduction exercised the loaders directly,
77
- not the end-to-end development watcher and account flow. Transitive Node module
78
- caching is a separate, already documented limitation. Keep trusted execution
79
- as the default and reject unsupported sandbox hooks explicitly.
80
-
81
- ### 3. TypeScript authoring rejects valid mixed-trust module reuse — P2
82
-
83
- [Issue 197](https://github.com/jimhoyd-com/urlcode/issues/197).
84
-
85
- `src/typescript-authoring.ts:34` assigns each source one trust mode and rejects
86
- reuse from a different mode. Two routes sharing a pure `f.ts`, one trusted and
87
- one sandboxed, fail with “Module is imported by both a sandboxed and a trusted
88
- route”. The equivalent JavaScript project activates and both routes return
89
- 200/ok. Shared transitive helpers have the same restriction.
90
-
91
- Separate emission deduplication from sandbox reachability validation. A helper
92
- reachable by a sandboxed route must still satisfy all sandbox import/source
93
- budgets; trusted execution must not be downgraded or unnecessarily restricted.
94
- Also reconcile the documented unlimited trusted source size with the builder's
95
- 16 MiB per-file authoring cap.
96
-
97
- ### 4. Live guidance still contradicts trust by default — P2
98
-
99
- [Issue 196](https://github.com/jimhoyd-com/urlcode/issues/196).
100
-
101
- - `docs/yaml/functions.md:57` denies Node/npm/network/filesystem APIs immediately
102
- after an ordinary trusted route example.
103
- - `docs/yaml/middleware.md:26` describes a mandatory shared sandbox.
104
- - `docs/MIDDLEWARE.md:49` applies guest clone/body restrictions without scoping
105
- them to sandboxed execution. Reproduced: trusted middleware reads a native
106
- response body and returns its uppercase text; the sandboxed version rejects
107
- that operation.
108
- - `docs/SPECIFICATION.md` says `.js` ESM behavior is independent of package
109
- settings. Reproduced: ESM `f.js` under `type: commonjs` fails trusted activation
110
- and works in the sandbox. Ordinary Node resolution is the intended default;
111
- the documentation should explain `.mjs` and package type accurately.
112
- - `README.md` and `docs/FRAMEWORK.md` overstate binding grants as the only way
113
- secrets reach code. Scope that statement to injected bindings.
114
- - `docs/READINESS.md` calls the activated runtime isolated; the first test in
115
- `test/typescript-authoring.test.ts` claims QuickJS execution but omits sandbox.
116
-
117
- The prose guards pass this revision despite these contradictions. Strengthen
118
- specific regression fixtures and fix the guidance; do not impose old sandbox
119
- restrictions on trusted code to make the prose true.
120
-
121
- The POST/body-based sandbox advisory is a related cleanup candidate. It is
122
- non-blocking and documented, but suggests isolation from request shape rather
123
- than evidence about code trust. Review whether to remove or reword it while
124
- retaining explicit `sandboxReason` information. This is a recommendation, not
125
- a discovered isolation failure.
126
-
127
- ### 5. Copyable documentation examples fail validation — P2
128
-
129
- [New evidence on issue 168](https://github.com/jimhoyd-com/urlcode/issues/168#issuecomment-5747024306).
130
-
131
- - `docs/yaml/policies.md:18` uses `page.source`; the field is `page.file`.
132
- - `docs/policies/compression.md:35` uses `secret: api-key`, rejected by the
133
- external binding-name grammar; an identifier such as `api_key` is admitted.
134
- - `docs/AI-AUTHORING.md:249` omits the leading slash on `webhooks/stripe`.
135
- This also illustrates why schema checks alone cannot replace semantic route
136
- validation. The same section still lists the retired `link` handler.
137
-
138
- The first two are complete fenced projects and fail `validateDocument`.
139
- The partial profile illustration in `docs/POLICIES.md:169` was excluded from
140
- findings because it deliberately omits a handler. Add classified executable
141
- snippets: complete projects, context-dependent fragments and intentionally
142
- invalid examples. Existing cookbook tests do not validate every prose example.
143
-
144
- ### 6. Monorepo ownership, navigation and release prose need cleanup — P3
145
-
146
- [Issue 200](https://github.com/jimhoyd-com/urlcode/issues/200).
147
-
148
- - Package AGENTS files still direct findings to former package repositories.
149
- - Framework/status pages retain former source homes, old alpha combinations,
150
- private cross-repository credential instructions and peer-pin language.
151
- - Two admin README links target nonexistent package-local release workflows.
152
- - Documentation navigation duplicates cookbook entries with conflicting 25/40
153
- counts; llms.txt repeats semantics and TypeScript entries.
154
- - Hook comments claim core lacks a sandbox primitive although SandboxPool is
155
- exported. The packages still explicitly reject sandboxed hooks; update the
156
- reason and tracked work rather than claiming they already support them.
157
-
158
- Use local links and one canonical version/ownership inventory for live material;
159
- keep dated release evidence clearly historical. Consolidate the duplicate
160
- SQLite preflight predicates through development tooling without removing auth's
161
- runtime gate. Shorten repeated issue-history narration in implementation comments
162
- where a contract explanation and issue link are sufficient.
163
-
164
- Do not remove the supported UI primitive fallback, opt-in sandbox path, provider
165
- compatibility refusals, licenses or regression fixtures. No dead production module
166
- was established with enough evidence to recommend deleting it in this audit.
167
-
168
- ## Verification evidence
169
-
170
- The final sequential `npm run verify` passed, including lint, strict typecheck,
171
- syntax/JSON/catalog checks, generated-document checks, workspace links, release
172
- checks, build, core tests and all workspace suites:
173
-
174
- | Suite | Passed | Skipped | Failed/cancelled |
175
- |---|---:|---:|---:|
176
- | Core | 518 | 1 | 0 |
177
- | UI | 57 | 0 | 0 |
178
- | Auth | 207 | 0 | 0 |
179
- | Admin | 68 | 0 | 0 |
180
- | Workspace scaffold integration | 1 | 0 | 0 |
181
-
182
- Total: 851 passed, one intentional HTTPS-deployment test skip. The integration
183
- test checks scaffold composition; it is not a live provider deployment.
184
-
185
- `npm run test:package` passed installation of the packed archive and
186
- starter/cookbook/authoring-consumer checks. Separate CLI HTTP fixture runs passed
187
- for the starter, assets, cookbook, Vercel, AWS, Cloudflare, conditions (with its
188
- documented public origin) and prerender source. The cookbook count audit passed
189
- with 40 routes. Extension, fake-egress, monitoring, provider-conformance and
190
- tunnel examples are also exercised by their dedicated core tests.
191
-
192
- `npm audit --omit=dev --json`
193
- reported zero known runtime advisories at audit time. Neither substitutes for
194
- source review or establishes absence of unknown vulnerabilities.
195
-
196
- The initial restricted-environment run could not run local HTTP tests. A first
197
- unrestricted run had five test-file cancellations; those files passed alone.
198
- An overlapping build also caused transient missing-declaration errors in a
199
- workspace run. The final sequential full run passed without cancellations.
200
- These intermediate results are not reported as confirmed product defects.
201
-
202
- ## Remaining evidence boundaries
203
-
204
- No provider account was deployed, no release published, no production system
205
- probed, and no container/Windows/Linux/Node 22 or 24 run was performed locally.
206
- Live email/OIDC/passkey services, accessibility/browser/device assessment,
207
- operational recovery/soak evidence and independent sandbox review remain separate.
208
- Existing issues 58, 173, 174 and 185 already retain relevant acceptance,
209
- model-benchmark, schema-discovery and release/CI-evidence work; this audit does
210
- not close those gates.
211
-
212
- ## Follow-up: dead-code reachability
213
-
214
- [Issue 203](https://github.com/jimhoyd-com/urlcode/issues/203) records a dedicated
215
- unused-code pass requested after the initial audit. Runtime source remains the
216
- same as the reviewed revision; the intervening commit only added this report.
217
-
218
- A conservative relative-reference graph rooted at package exports and CLI entry
219
- points reached all 157 tracked production TypeScript modules. Every direct
220
- runtime dependency has a production source reference, and every root script
221
- has a named reference elsewhere in the repository. No whole production file,
222
- runtime dependency or root script was established as removable.
223
-
224
- A TypeScript symbol/reference pass excluded 501 symbols exposed by public
225
- package entrypoints. Candidates were then checked with repository-wide search
226
- and manual inspection, including local uses, CLI imports, namespace dispatch,
227
- worker URLs and dynamically loaded agent-list code. The confirmed small removals
228
- are:
229
-
230
- | Declaration/plumbing | Evidence | Proposed cleanup |
231
- |---|---|---|
232
- | `src/capability-query.ts:47`, `capabilityNameList()` | Declaration only; no caller or public entry export | Delete the unused wrapper |
233
- | `src/catalog.ts:39`, `metadataFiles` | Declaration only; no reader or public entry export | Delete the unused constant |
234
- | `src/trusted-functions.ts`, `log` option/property | Assigned but never read | Remove this unused executor plumbing, preserving runtime observer/logging behavior |
235
- | `src/mcp-authoring.ts:57`, `expandHandler(path, handler)` | `path` is never read | Remove the argument and update callers |
236
- | `src/policies/cache.ts:201`, `revalidate(state, req, result)` | `state` is never read | Remove the argument and update callers |
237
-
238
- Several live implementation helpers are unnecessarily exported: admin's
239
- `activeKit`, core's `routeState`, `forbiddenHeaders`, `normalizeRoute`,
240
- `manifestFileName`, and local scaffold/render/name helpers in `init-with.ts`.
241
- These are candidates for removing export modifiers, not deleting their bodies.
242
- Check declaration dependencies before changing exported types.
243
-
244
- An additional TypeScript check with `--noUnusedLocals --noUnusedParameters`
245
- reported six unused parameters: the two production helpers above and four test
246
- callbacks. It reported no unused local declarations. This stricter exploratory
247
- check is separate from the normal passing typecheck.
248
-
249
- Public APIs with no internal callers, types used in public signatures, registry
250
- policy hooks, dynamic imports, supported UI fallback rendering and opt-in sandbox
251
- execution are not dead code. This reachability analysis does not prove that
252
- every branch executes. No production code was removed by this follow-up.
253
-
254
- ## Follow-up: unnecessary files and distribution weight
255
-
256
- [File-level evidence on issue 200](https://github.com/jimhoyd-com/urlcode/issues/200#issuecomment-5747116642).
257
-
258
- One package-local script is obsolete: `packages/admin/scripts/peer-revisions.mjs`.
259
- It has no workflow/package-script caller and reads the deleted admin `peers.json`;
260
- executing it fails with ENOENT. Its only other named reference is historical
261
- monorepo prose. Remove it; workspace linking replaced its cross-repository
262
- revision-output mechanism. The earlier root-script scan did not cover this
263
- package-local leftover.
264
-
265
- The three package `CODE_OF_CONDUCT.md` files are byte-identical to the root copy
266
- (1,062 bytes each). Consolidate their links to the root policy before deleting
267
- the duplicates. Package governance also repeats repository-wide controls and
268
- can link to the root while retaining any package-specific information. Package
269
- licenses and attribution notices serve a different purpose and must stay.
270
-
271
- A dry-run npm package inventory includes 11 archived documentation files
272
- (179,666 bytes) and six design-spike files (184,792 bytes): about 356 KiB
273
- uncompressed combined. The audit report also ships because package.json includes
274
- all of docs. Consider excluding maintainer/history/design records from npm
275
- while retaining them in Git and keeping their references navigable. These
276
- figures are content sizes, not estimates of compressed download savings.
277
-
278
- The duplicate Claude plugin skills are intentional, checked distribution copies;
279
- cookbook/recipe copies make each project independently usable; upstream UI
280
- snapshots preserve attribution/provenance; the benchmark baseline is consumed
281
- by its gate. None is established as unwanted. No tracked build output,
282
- node_modules, tarballs, logs, backup files or TypeScript build-info files were
283
- found. Ignored local dependencies/build products are regenerable development
284
- output, not tracked repository clutter. No files were deleted in this audit.
@@ -1,239 +0,0 @@
1
- # Compliance rules
2
-
3
- A compliance rule is a standards-referenced check over what a project declares
4
- and what the runtime knows about it: the parsed YAML, the route inventory and
5
- policy table from `testPlan()`, the effective policy per route, the public
6
- origin and the logging settings the operator states for the deployment. The
7
- runtime ships three built-in profiles; an operator adds, overrides or disables
8
- rules with code kept outside the project, the same trust boundary as
9
- [plugins](PLUGINS.md) and the `--policy` binding grant; `urlcode audit` prints
10
- every finding with the reference it rests on and sets the exit code.
11
-
12
- ## What it is and is not
13
-
14
- - It checks **declared configuration and runtime facts**. No rule sends a
15
- request, reads a binding, runs guest code or inspects a running deployment.
16
- `oshp/hsts-origin` cannot know your TLS terminator; it knows the origin you
17
- declared with `--origin`, so declare it truthfully.
18
- - A passing run is **not a certification**, an audit opinion or evidence that
19
- a deployment is secure or lawful. It says the declared configuration matches
20
- a rule set whose references are listed beside each finding. Deployment,
21
- soak and recovery proof live in [release readiness](RELEASE-READINESS.md).
22
- - Rules never change behavior. The runtime serves the same responses whether a
23
- finding exists or not; a rule set is an operator's review checklist in code.
24
- - The built-in rules only check what the runtime can see. Anything a rule
25
- cannot verify (an undeclared log level, an unknown origin) is reported as an
26
- `info` finding saying so, never assumed to pass.
27
-
28
- ## The rule contract
29
-
30
- ```js
31
- export const rules = [{
32
- id: 'acme/redirect-hosts', // ^[a-z][a-z0-9-]{0,31}/[a-z][a-z0-9-]{0,63}$, unique per run
33
- title: 'Redirects only leave for approved hosts',
34
- standard: { name: 'ACME link policy', reference: 'https://example.com/policies/links', section: 'Outbound' },
35
- severity: 'high', // high | medium | low | info
36
- appliesTo: 'route', // project | route
37
- check(context) { return []; }, // findings[]; may be async
38
- }];
39
- ```
40
-
41
- `standard.reference` is a URL, an RFC number (`RFC 9110`) or a path under
42
- `docs/`. `check` returns an array of findings (an empty array, `undefined`,
43
- a single finding or an array); a thrown error fails the run with the rule
44
- named, so a rule cannot silently pass by crashing.
45
-
46
- A **project** rule runs once with:
47
-
48
- | Field | Value |
49
- |---|---|
50
- | `document` | The parsed and validated `urlcode.yaml` (includes are merged into `routes`) |
51
- | `routes` | Route configuration by pattern, as written in YAML |
52
- | `plan` | `testPlan()`: `inventory[]` (`path`, `handler`, `methods`, `middleware`, `policies`, `state`), `policies` (the per-route describe map) |
53
- | `policies` | `effectivePolicies(document, route)` by pattern: the merged configuration of every policy on each route |
54
- | `origin` | The declared public origin, or `null` |
55
- | `target` | `node` unless the caller states another |
56
- | `host` | `{ requestLog }` as declared for the deployment; `null` where undeclared |
57
-
58
- A **route** rule runs once per inventory entry and additionally receives
59
- `route` (the inventory entry), `config` (that route's YAML), `policy` (the
60
- runtime's describe map for the route: `security.emits`, `agents.deny[].revision`,
61
- `cache.cacheControl`, …) and `effective` (the merged policy configuration).
62
-
63
- A **finding** is `{ rule, severity, route?, message, remediation, standard }`.
64
- `severity` defaults to the rule's own; a check may lower or raise it for one
65
- finding (the privacy rules report an undeclared setting as `info`). The
66
- runtime fills `rule` and `standard` and the route pattern for route rules.
67
-
68
- ## Built-in profiles
69
-
70
- `strict` contains every `baseline` rule plus its own; `privacy` stands alone;
71
- `none` runs only operator rules. Check derivations reference the runtime's
72
- own code: the security profile tables in `src/policies/security.ts`, the
73
- cache and compression secrets handling in `src/policies/cache.ts` and
74
- `src/policies/compression.ts`, the `no-store` default in
75
- `src/http-response.ts`, the 16 KiB header cap in `src/http-policy.ts`.
76
-
77
- ### `baseline`
78
-
79
- | Rule | Standard | Severity | Checks | Remediation |
80
- |---|---|---|---|---|
81
- | `oshp/security-headers` | [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/) | medium | Every active route has an effective `policies.security` | Declare `policies.security: { headers: oshp }` on the project or the route |
82
- | `oshp/hsts-origin` | OWASP Secure Headers Project, Strict-Transport-Security | low | When any active route's security profile emits HSTS, the declared origin is `https:`; the runtime emits HSTS only then | Serve behind TLS and declare `--origin https://…` |
83
- | `breach/secrets-compression` | [BREACH](https://www.breachattack.com/) | high | No route binding `secrets` sets `policies.compression.allowWithSecrets` | Remove `allowWithSecrets` (the runtime then skips compression on secret routes) |
84
- | `rfc9111/secrets-no-store` | [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111) §5.2.2.5, §5.2.2.7 | medium | A route binding `secrets` declares no cache strategy, Cache-Control or handler `cacheControl` other than `no-store`/`private` | Use `no-store` or `private` on secret routes |
85
- | `rfc9111/cache-control-declared` | RFC 9111 §5.2 | low | `respond` and `redirect` routes declare a `Cache-Control` header or a cache policy; asset handlers declare `cacheControl` (otherwise the runtime defaults `no-store` / assets `no-cache`) | State the intent in `response.headers`, the handler or `policies.cache` |
86
- | `rfc6585/throttle-functions` | [RFC 6585](https://www.rfc-editor.org/rfc/rfc6585) §4 | medium | Every active function or middleware route has an effective `policies.throttle` | Declare a throttle on the route or the project |
87
- | `rfc9309/robots` | [RFC 9309](https://www.rfc-editor.org/rfc/rfc9309) | low | Some active route declares `policies.agents`, or an active `/robots.txt` `respond` route exists | Add a `/robots.txt` route or an agents deny list |
88
- | `rfc9110/expired-routes` | [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110) §15.5.11 | info | Lists routes past `expires` that still answer 410 | Remove them once the 410 window has served its purpose |
89
-
90
- ### `strict` (baseline plus)
91
-
92
- | Rule | Standard | Severity | Checks | Remediation |
93
- |---|---|---|---|---|
94
- | `oshp/csp` | OWASP Secure Headers Project, Content-Security-Policy | medium | Every active route emits `Content-Security-Policy` (security profile minus `unset`, plus `set`, or a YAML response header) | Use the `oshp` profile or set the header |
95
- | `rfc6585/throttle-all` | RFC 6585 §4 | medium | Every active non-function route has an effective throttle (function routes are covered by `throttle-functions`) | Declare `policies.throttle` at the project level |
96
- | `agents/lists-pinned` | [Bundled agent lists](policies/agents.md) | low | Every agents deny/allow list is a bundled list that carries an upstream revision; project `.json` list files report `revision: project` | Prefer bundled lists or record the revision a list was built from |
97
- | `rfc9110/redirect-https` | RFC 9110 §15.4 | medium | No active `redirect.url` starts with `http:` | Redirect to https |
98
- | `http/header-budget` | RFC 9110 §5.4 | low | YAML `response.headers` bytes plus the security profile's static bytes stay at or under 8192, half the runtime's 16 KiB response header cap | Trim headers or the security set |
99
-
100
- ### `privacy`
101
-
102
- These rules check deployment settings, so `audit` takes `--request-log` to
103
- declare the level the deployment uses (the audit process itself always logs
104
- nothing); embedders pass `host` themselves. The references are the runtime's
105
- own [logging guarantees](MONITORING.md): records carry no URL, query, header,
106
- body or binding, and `detailed` adds only the method and route pattern.
107
-
108
- | Rule | Standard | Severity | Checks | Remediation |
109
- |---|---|---|---|---|
110
- | `privacy/request-log-minimal` | [Monitoring](MONITORING.md), Log records | medium (`info` when undeclared) | `host.requestLog` is `minimal` | Use the default log unless per-route rates are required |
111
- | `privacy/detailed-log-parameters` | [Monitoring](MONITORING.md), Log records | low | With `detailed` logging, no active route takes parameters (records name the pattern and method, never values) | Keep `minimal` on parameterised deployments |
112
-
113
- ## Writing custom rules
114
-
115
- [`examples/compliance/rules.mjs`](../examples/compliance/rules.mjs) is a
116
- complete operator module; it runs against the cookbook:
117
-
118
- ```sh
119
- node src/cli.ts audit --project examples/cookbook \
120
- --compliance baseline --compliance-rules "$PWD/examples/compliance/rules.mjs" --compliance-warn
121
- ```
122
-
123
- A module exports up to three names:
124
-
125
- ```js
126
- export const rules = [ /* rules to add; an id already in the profile is an error */ ];
127
- export const disable = ['rfc9110/expired-routes']; // remove built-in or added rules by id
128
- export const override = { 'oshp/security-headers': { severity: 'high' } }; // shallow-merge fields of an existing rule
129
- ```
130
-
131
- Order is fixed: profile rules, then `rules` added, then `override` applied,
132
- then `disable` removed, then `--compliance-ignore`. The result is validated
133
- as one set, so an override cannot produce an invalid rule.
134
-
135
- The module must be an absolute path to an `.mjs`/`.js` file **outside** the
136
- audited project, checked the way `--policy` is (`realpath` of both, the file
137
- may not resolve inside the project root). It is imported as trusted operator
138
- code with the host's privileges, the same standing as a plugin: a project
139
- cannot ship its own rule set and grade itself. Rules read the context they
140
- are given; a rule that reaches for the filesystem or the network is a plugin
141
- in the wrong place.
142
-
143
- ## CLI
144
-
145
- ```
146
- urlcode audit [--project dir] [--compliance baseline|strict|privacy|none]
147
- [--compliance-rules /absolute/rules.mjs] [--compliance-ignore id,id]
148
- [--compliance-warn] [--origin https://links.example] [--request-log minimal|detailed]
149
- ```
150
-
151
- Without any compliance flag the audit report is unchanged apart from
152
- `compliance: null`. With one, the report gains a `compliance` section:
153
-
154
- ```json
155
- { "profile": "baseline", "rules": 9, "ruleIds": ["oshp/security-headers", "…"], "ignored": [],
156
- "findings": [{ "rule": "rfc6585/throttle-functions", "severity": "medium", "route": "/hello/{name}",
157
- "message": "…", "remediation": "…", "standard": { "name": "RFC 6585 …", "reference": "…", "section": "…" } }],
158
- "counts": { "high": 0, "medium": 4, "low": 11, "info": 1 }, "pass": true,
159
- "evidence": { "routes": 21, "active": 19, "policies": ["agents", "cache", "security", "throttle"],
160
- "files": ["urlcode.yaml", "routes/code.yaml"], "origin": null, "target": "node",
161
- "host": { "requestLog": "minimal" },
162
- "scope": "declared configuration and runtime facts; not a deployment or certification" } }
163
- ```
164
-
165
- `--compliance-rules` alone implies `--compliance baseline`. `--origin` and
166
- `--request-log` describe the deployment under review and are echoed in
167
- `evidence`. Findings are sorted by severity, rule and route.
168
-
169
- ### Exit codes
170
-
171
- | Condition | Exit |
172
- |---|---|
173
- | Readiness failed (`ready: false`) | 1, as before |
174
- | A `high` finding and no `--compliance-warn` | 1 |
175
- | `--compliance-warn`: findings are printed, `pass` is still `false` | 0 unless readiness failed |
176
- | Unknown profile, rules file inside the project, malformed rule or ignore id | 1 with an `error` event on stderr |
177
-
178
- ## Programmatic API
179
-
180
- ```js
181
- import { runCompliance, builtinProfiles, validateRules, resolveRules, loadComplianceRules } from '@jimhoyd/urlcode/compliance';
182
- import { createRuntime } from '@jimhoyd/urlcode';
183
-
184
- const runtime = await createRuntime('./site');
185
- const report = await runCompliance(runtime, {
186
- profile: 'strict', // baseline | strict | privacy | none
187
- rules: [], override: {}, disable: [], // as a rules module would export them
188
- ignore: ['rfc9110/expired-routes'],
189
- origin: 'https://links.example',
190
- host: { requestLog: 'minimal' }, // what the deployment is configured with
191
- });
192
- await runtime.close();
193
- ```
194
-
195
- The declarations ship with the package: `ComplianceRule` (with `ProjectRule`
196
- and `RouteRule`, and `ProjectContext`/`RouteContext` for what `check`
197
- receives), `RawFinding` and `Finding`, `ComplianceOptions`, `ComplianceReport`
198
- and `ComplianceProfileName` are all exported from `@jimhoyd/urlcode/compliance`, so a
199
- rules module written in TypeScript is checked against the same contract the
200
- runtime validates at load time:
201
-
202
- ```ts
203
- import type { ComplianceRule, ComplianceReport } from '@jimhoyd/urlcode/compliance';
204
- import { runCompliance } from '@jimhoyd/urlcode/compliance';
205
-
206
- export const rules: ComplianceRule[] = [{
207
- id: 'acme/redirect-hosts',
208
- title: 'Redirects only leave for approved hosts',
209
- standard: { name: 'ACME link policy', reference: 'https://example.com/policies/links', section: 'Outbound' },
210
- severity: 'high',
211
- appliesTo: 'route',
212
- check(context) {
213
- if (context.config.redirect?.url.startsWith('https://acme.example/')) return [];
214
- return [{ message: `${context.route.path} redirects outside the approved hosts`, remediation: 'Point the redirect at an approved host' }];
215
- },
216
- }];
217
- const report: ComplianceReport = await runCompliance(runtime, { profile: 'strict', rules });
218
- ```
219
-
220
- `runCompliance` accepts a started server from `startServer` or a runtime from
221
- `createRuntime`; it re-reads the YAML from the runtime's `root` and takes the
222
- plan from `testPlan()`, so rules see what the runtime compiled.
223
- `auditProject(app, { compliance })` runs the same and attaches the report
224
- under `compliance`. `builtinProfiles` maps profile names to their frozen rule
225
- arrays for reuse or inspection; `validateRules` checks a rule array;
226
- `resolveRules` builds the final set from a profile and operator additions.
227
-
228
- ## Extending with a plugin-style workflow
229
-
230
- Keep rule modules where you keep plugins: in the operator application, under
231
- version control, reviewed like code, outside every audited project. A shared
232
- module can export rules that read the same policy tables a plugin's
233
- `onActivate(runtime)` sees through `testPlan()`, so one review of "what does
234
- this runtime enforce" serves both. Compose organisation rules on top of a
235
- built-in profile with `override` for stricter severities and `disable` for
236
- rules that a documented decision replaces, and run `urlcode audit
237
- --compliance strict --compliance-rules …` in CI with the exit code as the
238
- gate. Record the report beside the readiness and benchmark evidence for the
239
- revision; a report proves what was declared at that commit, nothing more.