@legionworks/facet 1.9.0

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 (327) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +23 -0
  3. package/README.md +158 -0
  4. package/dist/gallery/chunk-4g8rem85.css +656 -0
  5. package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
  6. package/dist/gallery/frame/artifact.css +8424 -0
  7. package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
  8. package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
  9. package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
  10. package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
  11. package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
  12. package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
  13. package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
  14. package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
  15. package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
  16. package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
  17. package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
  18. package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
  19. package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
  20. package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
  21. package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
  22. package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
  23. package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
  24. package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
  25. package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
  26. package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
  27. package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
  28. package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
  29. package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
  30. package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
  31. package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
  32. package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
  33. package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
  34. package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
  35. package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
  36. package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
  37. package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
  38. package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
  39. package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
  40. package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
  41. package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
  42. package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
  43. package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
  44. package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
  45. package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
  46. package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
  47. package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
  48. package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
  49. package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
  50. package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
  51. package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
  52. package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
  53. package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
  54. package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
  55. package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
  56. package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
  57. package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
  58. package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
  59. package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
  60. package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
  61. package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
  62. package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
  63. package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
  64. package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
  65. package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
  66. package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
  67. package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
  68. package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
  69. package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
  70. package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
  71. package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
  72. package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
  73. package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
  74. package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
  75. package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
  76. package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
  77. package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
  78. package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
  79. package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
  80. package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
  81. package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
  82. package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
  83. package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
  84. package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
  85. package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
  86. package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
  87. package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
  88. package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
  89. package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
  90. package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
  91. package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
  92. package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
  93. package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
  94. package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
  95. package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
  96. package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
  97. package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
  98. package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
  99. package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
  100. package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
  101. package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
  102. package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
  103. package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
  104. package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
  105. package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
  106. package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
  107. package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
  108. package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
  109. package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
  110. package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
  111. package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
  112. package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
  113. package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
  114. package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
  115. package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
  116. package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
  117. package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
  118. package/dist/gallery/frame/frame.css +183 -0
  119. package/dist/gallery/frame/runtime/chart.js +42744 -0
  120. package/dist/gallery/frame/runtime/html.js +14 -0
  121. package/dist/gallery/frame/runtime/markdown.js +1403 -0
  122. package/dist/gallery/frame/runtime/mermaid.js +32 -0
  123. package/dist/gallery/frame/runtime/svg.js +14 -0
  124. package/dist/gallery/frame/runtime/tsx.js +52 -0
  125. package/dist/gallery/index.html +104 -0
  126. package/docs/reference/cli.md +163 -0
  127. package/docs/reference/export.md +106 -0
  128. package/docs/reference/html.md +215 -0
  129. package/docs/reference/http.md +85 -0
  130. package/docs/reference/mcp.md +78 -0
  131. package/docs/reference/security.md +119 -0
  132. package/docs/reference/storage.md +93 -0
  133. package/docs/reference/tsx.md +101 -0
  134. package/docs/reference/validation.md +212 -0
  135. package/package.json +89 -0
  136. package/scripts/build-gallery.ts +127 -0
  137. package/scripts/launch-netns.sh +67 -0
  138. package/skills/facet/SKILL.md +100 -0
  139. package/src/cli/client.ts +311 -0
  140. package/src/cli/commands/create.ts +49 -0
  141. package/src/cli/commands/doctor.ts +249 -0
  142. package/src/cli/commands/export.ts +179 -0
  143. package/src/cli/commands/instantiate.ts +35 -0
  144. package/src/cli/commands/list.ts +49 -0
  145. package/src/cli/commands/open.ts +75 -0
  146. package/src/cli/commands/pin.ts +39 -0
  147. package/src/cli/commands/promote.ts +49 -0
  148. package/src/cli/commands/publish.ts +122 -0
  149. package/src/cli/commands/read-back.ts +56 -0
  150. package/src/cli/commands/status.ts +114 -0
  151. package/src/cli/commands/watch.ts +146 -0
  152. package/src/cli/main.ts +522 -0
  153. package/src/cli/output.ts +106 -0
  154. package/src/cli/parser.ts +376 -0
  155. package/src/cli/presenter.ts +212 -0
  156. package/src/cli/service-metadata.ts +134 -0
  157. package/src/cli/spawn-service.ts +326 -0
  158. package/src/css.d.ts +4 -0
  159. package/src/gallery-web/app.ts +1581 -0
  160. package/src/gallery-web/export-menu.ts +121 -0
  161. package/src/gallery-web/export.ts +112 -0
  162. package/src/gallery-web/favicon.ts +54 -0
  163. package/src/gallery-web/frame/entries/chart.ts +6 -0
  164. package/src/gallery-web/frame/entries/html.ts +6 -0
  165. package/src/gallery-web/frame/entries/markdown.ts +6 -0
  166. package/src/gallery-web/frame/entries/mermaid.ts +6 -0
  167. package/src/gallery-web/frame/entries/svg.ts +6 -0
  168. package/src/gallery-web/frame/entries/tsx.ts +6 -0
  169. package/src/gallery-web/frame/frame-payload.ts +91 -0
  170. package/src/gallery-web/frame/renderer-validation.ts +13 -0
  171. package/src/gallery-web/frame/renderers/chart.ts +182 -0
  172. package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
  173. package/src/gallery-web/frame/renderers/html.ts +48 -0
  174. package/src/gallery-web/frame/renderers/markdown.ts +81 -0
  175. package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
  176. package/src/gallery-web/frame/renderers/registry.ts +260 -0
  177. package/src/gallery-web/frame/renderers/svg.ts +393 -0
  178. package/src/gallery-web/frame/renderers/tsx.ts +61 -0
  179. package/src/gallery-web/frame/runtime.ts +501 -0
  180. package/src/gallery-web/frame/styles/artifact.css +8424 -0
  181. package/src/gallery-web/frame/styles/frame.css +183 -0
  182. package/src/gallery-web/frame/styles/html-source.css +23 -0
  183. package/src/gallery-web/frame/view-box.ts +33 -0
  184. package/src/gallery-web/frame-html.ts +72 -0
  185. package/src/gallery-web/index.html +104 -0
  186. package/src/gallery-web/session.ts +107 -0
  187. package/src/gallery-web/sse-client.ts +212 -0
  188. package/src/gallery-web/styles/app.css +420 -0
  189. package/src/gallery-web/styles/tokens.css +112 -0
  190. package/src/gallery-web/styles/verdict.css +186 -0
  191. package/src/gallery-web/swap.ts +64 -0
  192. package/src/gallery-web/theme.ts +40 -0
  193. package/src/gallery-web/view-state.ts +93 -0
  194. package/src/harness-adapters/claude-code/README.md +10 -0
  195. package/src/harness-adapters/claude-code/facet.sh +4 -0
  196. package/src/harness-adapters/codex/README.md +10 -0
  197. package/src/harness-adapters/codex/facet.sh +4 -0
  198. package/src/harness-adapters/mcp/README.md +13 -0
  199. package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
  200. package/src/harness-adapters/mcp/main.ts +15 -0
  201. package/src/harness-adapters/mcp/server.ts +164 -0
  202. package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
  203. package/src/harness-adapters/opencode/README.md +10 -0
  204. package/src/harness-adapters/opencode/facet.sh +4 -0
  205. package/src/runtime/compiled-entrypoints.ts +69 -0
  206. package/src/service/dispatcher.ts +717 -0
  207. package/src/service/export.ts +218 -0
  208. package/src/service/http-utils.ts +55 -0
  209. package/src/service/lexical/expectations.ts +210 -0
  210. package/src/service/lifecycle/idle-controller.ts +125 -0
  211. package/src/service/lifecycle/orphan-cleanup.ts +128 -0
  212. package/src/service/lifecycle/process-lock.ts +235 -0
  213. package/src/service/main.ts +136 -0
  214. package/src/service/process.ts +240 -0
  215. package/src/service/router-guards.ts +239 -0
  216. package/src/service/router.ts +627 -0
  217. package/src/service/security/auth.ts +168 -0
  218. package/src/service/security/host-origin.ts +138 -0
  219. package/src/service/security/http-guards.ts +13 -0
  220. package/src/service/security/leases.ts +182 -0
  221. package/src/service/security/token-store.ts +138 -0
  222. package/src/service/server.ts +356 -0
  223. package/src/service/store/database.ts +61 -0
  224. package/src/service/store/evidence-retention.ts +234 -0
  225. package/src/service/store/migrations.ts +167 -0
  226. package/src/service/store/repository-lifecycle.ts +121 -0
  227. package/src/service/store/repository.ts +732 -0
  228. package/src/service/store/schema.ts +218 -0
  229. package/src/service/stored-verdict.ts +97 -0
  230. package/src/service/stream.ts +301 -0
  231. package/src/service/verdict-enrichment.ts +45 -0
  232. package/src/shared/build/frame-bundle-plugins.ts +61 -0
  233. package/src/shared/build-mode.ts +28 -0
  234. package/src/shared/config/evidence-read.ts +98 -0
  235. package/src/shared/config/limits.ts +56 -0
  236. package/src/shared/config/paths.ts +96 -0
  237. package/src/shared/contracts/artifact-types.ts +2 -0
  238. package/src/shared/contracts/artifact.ts +98 -0
  239. package/src/shared/contracts/commands/_shared.ts +59 -0
  240. package/src/shared/contracts/commands/guards.ts +86 -0
  241. package/src/shared/contracts/commands/index.ts +204 -0
  242. package/src/shared/contracts/commands/names.ts +35 -0
  243. package/src/shared/contracts/commands/requests.ts +149 -0
  244. package/src/shared/contracts/commands/results.ts +200 -0
  245. package/src/shared/contracts/envelope.ts +140 -0
  246. package/src/shared/contracts/events.ts +41 -0
  247. package/src/shared/contracts/observed-counts.ts +19 -0
  248. package/src/shared/contracts/renderers.ts +6 -0
  249. package/src/shared/contracts/validation.ts +296 -0
  250. package/src/shared/errors/facet-error.ts +127 -0
  251. package/src/shared/errors/store-error.ts +68 -0
  252. package/src/shared/evidence-image.ts +37 -0
  253. package/src/shared/export.ts +31 -0
  254. package/src/shared/html/policy.ts +108 -0
  255. package/src/shared/html/style-vocabulary.ts +352 -0
  256. package/src/shared/logging/logger.ts +108 -0
  257. package/src/shared/logging/redact.ts +49 -0
  258. package/src/shared/security/frozen-csp.ts +13 -0
  259. package/src/shared/storage-version.ts +2 -0
  260. package/src/shared/tsx/execution.ts +29 -0
  261. package/src/shared/tsx/import-policy.ts +94 -0
  262. package/src/shared/util/dir-permissions.ts +94 -0
  263. package/src/shared/util/mermaid-nodes.ts +230 -0
  264. package/src/shared/util/process.ts +67 -0
  265. package/src/shared/util/time.ts +21 -0
  266. package/src/shared/version.ts +3 -0
  267. package/src/validation/sandbox/limits.ts +41 -0
  268. package/src/validation/sandbox/netns.ts +177 -0
  269. package/src/validation/tier0/chart.ts +177 -0
  270. package/src/validation/tier0/dom-shim.ts +54 -0
  271. package/src/validation/tier0/html.ts +408 -0
  272. package/src/validation/tier0/markdown.ts +285 -0
  273. package/src/validation/tier0/mermaid.ts +98 -0
  274. package/src/validation/tier0/runner.ts +504 -0
  275. package/src/validation/tier0/svg.ts +321 -0
  276. package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
  277. package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
  278. package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
  279. package/src/validation/tier0/tsx/ast-policy.ts +181 -0
  280. package/src/validation/tier0/tsx/compiler.ts +156 -0
  281. package/src/validation/tier0/worker-dispatch.ts +211 -0
  282. package/src/validation/tier0/worker-entry.ts +96 -0
  283. package/src/validation/tier0/worker-input.ts +75 -0
  284. package/src/validation/tier1/browser-process.ts +147 -0
  285. package/src/validation/tier1/cdp-pipe.ts +281 -0
  286. package/src/validation/tier1/entries/chart.ts +5 -0
  287. package/src/validation/tier1/entries/html.ts +6 -0
  288. package/src/validation/tier1/entries/markdown.ts +5 -0
  289. package/src/validation/tier1/entries/mermaid.ts +5 -0
  290. package/src/validation/tier1/entries/svg.ts +5 -0
  291. package/src/validation/tier1/entries/tsx.ts +5 -0
  292. package/src/validation/tier1/frame-target.ts +162 -0
  293. package/src/validation/tier1/harness-entry.ts +261 -0
  294. package/src/validation/tier1/harness.ts +215 -0
  295. package/src/validation/tier1/isolated-probe.ts +94 -0
  296. package/src/validation/tier1/launcher.ts +177 -0
  297. package/src/validation/tier1/limits.ts +80 -0
  298. package/src/validation/tier1/nonce.ts +17 -0
  299. package/src/validation/tier1/protocol-probe.ts +519 -0
  300. package/src/validation/tier1/runner.ts +1122 -0
  301. package/src/validation/tier1/verdict.ts +246 -0
  302. package/src/validation/tier1/webp.ts +69 -0
  303. package/templates/README.md +88 -0
  304. package/templates/bar-compare.vl.json +63 -0
  305. package/templates/capacity-report.tsx +102 -0
  306. package/templates/decision-record.md +24 -0
  307. package/templates/deployment-state.mmd +40 -0
  308. package/templates/exemplar.md +91 -0
  309. package/templates/facet-story.tsx +799 -0
  310. package/templates/fleet-dashboard.html +141 -0
  311. package/templates/html-release-ledger.html +91 -0
  312. package/templates/html-status-report.html +114 -0
  313. package/templates/incident-console.tsx +147 -0
  314. package/templates/legion-boundaries.mmd +23 -0
  315. package/templates/legion-flow.mmd +15 -0
  316. package/templates/legion-sequence.mmd +14 -0
  317. package/templates/legion-state.mmd +7 -0
  318. package/templates/metric-card.svg +29 -0
  319. package/templates/observability-map.svg +101 -0
  320. package/templates/pipeline-audit.md +108 -0
  321. package/templates/release-metrics.vl.json +98 -0
  322. package/templates/service-topology.mmd +47 -0
  323. package/templates/status-report.md +32 -0
  324. package/templates/system-map.svg +53 -0
  325. package/templates/timeseries.vl.json +71 -0
  326. package/templates/tsx-interactive-counter.tsx +19 -0
  327. package/templates/tsx-status-report.tsx +31 -0
@@ -0,0 +1,215 @@
1
+ # HTML reference
2
+
3
+ Static, script-free HTML artifacts. The verifier predicts structural counts
4
+ from the source bytes with a no-egress WHATWG parser, observes the rendered
5
+ DOM through Chromium, and binds the verdict to the revision SHA.
6
+
7
+ ## Publish
8
+
9
+ ```sh
10
+ facet publish --artifact-id <id> --type html --file templates/html-status-report.html
11
+ facet read-back --artifact-id <id> --revision-sha <sha> --tier 1
12
+ ```
13
+
14
+ `--type html` is a first-class type on `publish`; `--renderer` stays chart-only.
15
+
16
+ ## Static, script-free contract
17
+
18
+ The artifact has no script, no event handler, no `<style>` block, no `style=`
19
+ attribute, and no Facet marker bytes. The frame wraps the parsed body in a
20
+ single element carrying `data-facet-renderer-root` so protocol probes can
21
+ scope their observations — the marker is frame-owned, never artifact-owned.
22
+
23
+ Source export returns the published bytes byte-for-byte; the wrapper is
24
+ frame-only and never reaches storage or export. `export <artifactId>
25
+ --format source` against an HTML revision produces the exact source the
26
+ operator published.
27
+
28
+ ## Accepted semantic HTML
29
+
30
+ Every element and attribute an HTML report or dashboard reasonably uses is
31
+ permitted:
32
+
33
+ - text, headings, lists, tables, sectioning (`<section>`, `<article>`,
34
+ `<nav>`, `<header>`, `<footer>`, `<main>`, `<aside>`)
35
+ - figures, `<details>`, `<summary>`, `<mark>`, `<time>`, `<abbr>`, `<cite>`,
36
+ `<q>`, `<dfn>`
37
+ - inline semantics (`<span>`, `<em>`, `<strong>`, `<code>`, `<kbd>`,
38
+ `<samp>`, `<var>`, `<br>`, `<wbr>`)
39
+ - images (`<img>`), source (`<source>`), anchors (`<a>`)
40
+ - canvases (`<canvas>`)
41
+
42
+ ## Denied elements and attributes
43
+
44
+ | kind | refused |
45
+ | --------------- | --------------------------------------------------------------------- |
46
+ | script-tagged | `script`, `iframe`, `object`, `embed`, `form`, `link`, `meta`, `base` |
47
+ | styling surface | `style`, `style=` (no CSS-injection surface anywhere) |
48
+ | event handlers | every `on*=` attribute |
49
+ | bad URL schemes | `http:`, protocol-relative (`//host/...`), `javascript:` |
50
+
51
+ `cleartext http:`, protocol-relative, and `javascript:` URLs fail closed
52
+ with `html_denied_url_scheme`. Image anchors accept `https:` and `data:`;
53
+ regular anchors accept `https:` and `mailto:`.
54
+
55
+ ## Verdict claim
56
+
57
+ `ok` on an HTML artifact means: counts agree across the Tier 0 parse5
58
+ prediction and the Tier 1 Chromium observation, every count group is
59
+ exercised, no discriminative error fired, and the layout pass is
60
+ observable. The observable is a structural count vector:
61
+
62
+ | key | what it counts |
63
+ | -------------------- | ------------------------------------------------------------------ |
64
+ | `rendererRootCount` | frame-owned `data-facet-renderer-root` wrappers in the document |
65
+ | `headingCount` | `<h1>`–`<h6>` |
66
+ | `tableCount` | `<table>` |
67
+ | `listCount` | `<ul>`, `<ol>` |
68
+ | `imageCount` | `<img>` |
69
+ | `canvasCount` | `<canvas>` |
70
+ | `externalImageCount` | `<img>` and `<source>` whose `src` / `srcset` resolves to `https:` |
71
+
72
+ The marker-anchored structure is the smallest claim that catches blank
73
+ renders, truncated trees, or structure the source never declared — and
74
+ the largest claim that survives legitimate parse5-versus-Chromium
75
+ recovery differences (see [unsupported recovery families](#unsupported-recovery-families)).
76
+
77
+ ## Verdict precedence
78
+
79
+ Status is decided in exactly one place (`src/validation/tier1/verdict.ts`).
80
+ For HTML specifically:
81
+
82
+ 1. `tampered` — predicted counts disagree with the Tier 1 observation.
83
+ 2. `error` — discriminative error fired (`html_denied_element`,
84
+ `html_denied_attribute`, `html_denied_url_scheme`,
85
+ `html_encoding_unsupported`, `html_nesting_depth_exceeded`,
86
+ `html_recovery_unsupported`).
87
+ 3. `partial:opaque_content` — a `<canvas>` exists; structure beneath it
88
+ is unobservable. MUST carry a screenshot or typed `screenshotError`.
89
+ 4. `partial:external_resources` — an HTTPS image was referenced; the
90
+ no-egress verifier never loaded it. MUST carry a screenshot or typed
91
+ `screenshotError`.
92
+ 5. `ok` — counts agree, no discriminative error, no opaque region, no
93
+ external image.
94
+
95
+ `partial:` is not a degraded `ok`. The screenshot is mandatory evidence
96
+ so a human or re-verifier can see what the verifier saw.
97
+
98
+ ## HTTPS image and the no-connect rule
99
+
100
+ The frozen CSP widened from `img-src data:` to `img-src data: https:` to
101
+ let reports link real screenshots without inflating the source cap. The
102
+ no-connect and no-script rules are unchanged:
103
+
104
+ | directive | value | verdict or display? |
105
+ | ------------- | --------------------------- | -------------------- |
106
+ | `script-src` | `'nonce-<BOOTSTRAP_NONCE>'` | verdict |
107
+ | `connect-src` | `'none'` | verdict |
108
+ | `frame-src` | `'none'` | verdict |
109
+ | `object-src` | `'none'` | verdict |
110
+ | `base-uri` | `'none'` | verdict |
111
+ | `form-action` | `'none'` | verdict |
112
+ | `worker-src` | `'none'` | verdict |
113
+ | `img-src` | `data: https:` | display-time privacy |
114
+ | `font-src` | `data:` | display-time privacy |
115
+
116
+ The first six protect the VERDICT — a page that can run attacker code
117
+ or open a network socket can forge a verdict or exfiltrate, so they stay
118
+ closed. `img-src` and `font-src` protect PRIVACY at display time only;
119
+ the verifier never follows the URLs, so widening `img-src` to `https:`
120
+ cannot weaken the verdict.
121
+
122
+ → [Security reference](security.md) for the full frozen-CSP contract.
123
+
124
+ ## Vendored styling
125
+
126
+ Styling comes exclusively from the frame's offline-vendored stylesheet.
127
+ Gallery frames map resolved light/dark themes to daisyUI's `winter` and
128
+ `night` themes. The gallery preference defaults to `system` and resolves from
129
+ the user's light/dark preference; Tier 1 deliberately fixes dark/night parity
130
+ for deterministic structural comparison. Artifact-authored HTML still cannot
131
+ supply `<style>` or `style=`; gallery theming does not widen that policy.
132
+ Tailwind utilities come from a deterministic corpus that
133
+ covers the templates, this reference, and common layout, spacing, typography,
134
+ and color classes.
135
+
136
+ `HTML_TAILWIND_CLASSES` and `HTML_DAISY_COMPONENTS` in
137
+ `src/shared/html/style-vocabulary.ts` are recommendations, not a styling
138
+ ceiling. Use them for predictable artifact output. A valid daisyUI class or
139
+ an included Tailwind utility outside this list can still render.
140
+
141
+ <!-- VOCABULARY:START -->
142
+
143
+ ### Recommended Tailwind utilities
144
+
145
+ `block`, `flex`, `grid`, `inline-flex`, `flex-col`, `flex-wrap`, `items-center`, `justify-between`, `justify-center`, `grid-cols-1`, `grid-cols-2`, `grid-cols-3`, `gap-2`, `gap-3`, `gap-4`, `gap-6`, `p-2`, `p-3`, `p-4`, `p-6`, `px-3`, `px-4`, `py-2`, `py-3`, `m-0`, `mt-2`, `mt-4`, `mb-2`, `mb-4`, `w-full`, `max-w-prose`, `max-w-2xl`, `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `font-medium`, `font-semibold`, `font-bold`, `leading-relaxed`, `text-left`, `text-center`, `text-right`, `text-legion-ink`, `text-legion-muted`, `text-legion-cyan`, `bg-legion-paper`, `bg-legion-ink`, `bg-legion-cyan`, `border`, `border-2`, `border-legion-line`, `rounded`, `rounded-box`, `overflow-x-auto`, `table`, `table-zebra`.
146
+
147
+ 59 recommended utilities.
148
+
149
+ ### Recommended daisyUI components
150
+
151
+ `alert`, `badge`, `btn`, `card`, `stat`, `table`.
152
+
153
+ 6 recommended components · 64 documented recommendations.
154
+
155
+ <!-- VOCABULARY:END -->
156
+
157
+ ### Unknown classes
158
+
159
+ No error, no `partial:`, no trust downgrade. A class that is neither a real
160
+ daisyUI class nor present in the Tailwind corpus has no styling effect.
161
+
162
+ ## Pinned styling packages
163
+
164
+ | package | version | loaded under `script-src 'nonce-…'`? |
165
+ | ------------------ | ------- | ------------------------------------ |
166
+ | `@tailwindcss/cli` | 4.3.3 | no (build-time only) |
167
+ | `tailwindcss` | 4.x | no (vendored stylesheet, no runtime) |
168
+ | `daisyui` | 5.7.16 | no (vendored CSS, no runtime JS) |
169
+
170
+ Tailwind is bundled at build time (`bun scripts/build-html-styles.ts`)
171
+ against the deterministic build corpus in `src/shared/html/style-vocabulary.ts`;
172
+ no Tailwind runtime runs in the frame. `daisyui` 5.7.16 ships CSS-var themes
173
+ and components only — the vendored bundle contains zero `<script>` and zero
174
+ `javascript:` URLs. The `img-src` and `font-src` widenings do not load any
175
+ runtime JS because the packages carry none.
176
+
177
+ ## Starter template
178
+
179
+ A neutral status / report page that uses only the shipped vocabulary:
180
+
181
+ ```sh
182
+ facet publish --artifact-id <id> --type html --file templates/html-status-report.html
183
+ ```
184
+
185
+ → [`templates/html-status-report.html`](../../templates/html-status-report.html) ·
186
+ [`templates/README.md`](../../templates/README.md) for the starter index.
187
+
188
+ ## Unsupported recovery families
189
+
190
+ The differential corpus (parse5 prediction vs Chromium observation over real
191
+ documents) discovered three recovery shapes where the WHATWG parser and
192
+ Chromium disagree. Static HTML reports do not need these, so the accepted
193
+ input set shrinks instead of weakening the comparison:
194
+
195
+ | family | rejected as |
196
+ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
197
+ | UTF-8 encoding ambiguity (`0xFF` mid-stream) | `html_encoding_unsupported` |
198
+ | `<select>` containing `<table>` / `<tr>` / `<td>` / `<th>` / `<tbody>` / `<thead>` / `<tfoot>` / `<caption>` / `<colgroup>` / `<col>` (including the two-`<select>` variant where the second `<select>` carries the table markup, and any `<noscript>`-nested instance) | `html_recovery_unsupported` |
199
+
200
+ A fourth bound — `html_nesting_depth_exceeded` — fires when source nesting
201
+ exceeds the cap in `MAX_HTML_NESTING_DEPTH`. The cap protects Tier 0 and
202
+ Tier 1 from pathological inputs.
203
+
204
+ These error codes are stable wire values; downstream tooling can match on
205
+ them without parsing message text.
206
+
207
+ ## Tier 0 prediction source
208
+
209
+ Tier 0 parses the source bytes with `parse5@8.0.1` (`scriptingEnabled:
210
+ false`, matching Chromium's `DOMParser`) inside the existing netns worker.
211
+ No CSS sanitizer, no DOM mutation, no event-loop. The structural counts
212
+ that come out of Tier 0 are bound to the revision SHA and never mutate.
213
+
214
+ → [Validation reference](validation.md) for tier evidence and the
215
+ revision-binding guarantee.
@@ -0,0 +1,85 @@
1
+ # HTTP surface
2
+
3
+ Facet binds loopback only. The CLI is the supported client; these routes are the
4
+ service and gallery contract.
5
+
6
+ ## Routes
7
+
8
+ | method | route | purpose | success |
9
+ | ------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------: |
10
+ | `POST` | `/api/v1/commands` | Parse and dispatch a versioned command envelope. | `200` |
11
+ | `GET` | `/api/v1/stream` | Stream committed revisions for one leased artifact as SSE. | `200` |
12
+ | `POST` | `/api/v1/gallery/bootstrap` | Consume the one-shot `open` capability and return the artifact, revision, bearer, and lease. | `200` |
13
+ | `POST` | `/api/v1/gallery/release` | Release a gallery lease. | `204` |
14
+ | `GET` | `/api/v1/gallery/source?revisionSha=<sha>` | Read source bytes and the latest stored verdict for the leased revision. | `200` |
15
+ | `GET` | `/api/v1/gallery/evidence?revisionSha=<sha>` | Read retained Tier 1 screenshot bytes for the leased revision without rerendering. | `200` |
16
+ | `GET` | `/gallery` and `/gallery/*` | Serve the built gallery shell and static assets. | `200` |
17
+ | `GET` | `/gallery/frame?nonce=<32 hex>&type=<type>&theme=<theme>` | Return a frame document for `markdown`, `mermaid`, `svg`, `chart`, `html`, or `tsx`. | `200` |
18
+ | `GET` | `/gallery/frame/bootstrap/*` and `/gallery/frame/chunks/*` | Serve bundled frame scripts. | `200` |
19
+
20
+ The gallery source route requires a non-empty `revisionSha`. It returns
21
+ `artifactId`, the bound SHA, `artifactType`, `renderer`, UTF-8 `source`, and
22
+ `verdict` (or `null`); TSX source responses also carry `execution`.
23
+ The source and stream routes match both the lease ID and the artifact ID,
24
+ preventing a valid lease for one artifact from reading another.
25
+
26
+ The evidence route requires the same lease and artifact headers as the source
27
+ route. It serves retained screenshot bytes with a content type sniffed from
28
+ the bytes: `image/webp` or `image/png`. `screenshot_format` metadata does not
29
+ override the file signature, and the route never rerenders the artifact.
30
+
31
+ `theme` is validated as `dark` or `light` when supplied. It selects resolved
32
+ frame display state; it is not an authorization or validation control.
33
+
34
+ ## Authentication
35
+
36
+ `/api/v1/commands` accepts the install bearer or operator bearer. `promote`
37
+ requires the operator bearer. Mutations require
38
+ `Content-Type: application/json`.
39
+
40
+ The stream requires:
41
+
42
+ ```text
43
+ Authorization: Bearer <install-token>
44
+ X-Gallery-Lease: <lease-id>
45
+ X-Gallery-Artifact: <artifact-id>
46
+ ```
47
+
48
+ The release and source routes use the same bearer plus the two gallery headers.
49
+ The bootstrap route uses the one-shot capability returned by `open`; it does
50
+ not accept a reusable query-string lease. `X-Gallery-Lease` is deliberately a
51
+ header because URL tokens leak through logs, referrers, and browser history.
52
+
53
+ ## Host and CSRF
54
+
55
+ Requests must use the loopback `Host` value issued by the service. Missing or
56
+ foreign hosts are rejected. Missing `Host` is `421`; a foreign host is `400`.
57
+ State-changing routes require an authenticated loopback request with `Origin`
58
+ absent or equal to the service origin and `Sec-Fetch-Site` absent,
59
+ `same-origin`, or `none`. Cross-site mutations return `403`. Browser origins
60
+ are not trusted as authorization.
61
+
62
+ ## Status codes
63
+
64
+ | status | meaning |
65
+ | -----: | ---------------------------------------------------------------------------------------------------------------------- |
66
+ | `200` | Successful envelope, JSON response, SSE stream, gallery file, or frame. |
67
+ | `204` | Gallery lease released. |
68
+ | `400` | Invalid JSON/envelope/request, unsupported frame input, invalid `revisionSha`, invalid content type, or host mismatch. |
69
+ | `401` | Missing or invalid bearer, lease, artifact header, or one-shot bootstrap capability. |
70
+ | `403` | Cross-site mutation or operator-only command attempted with the install bearer. |
71
+ | `404` | Unknown route, missing gallery file, artifact, revision, or template. |
72
+ | `405` | `GET` sent to the command endpoint. |
73
+ | `409` | Store constraint, duplicate or immutable revision, unsupported artifact type, or pinned revision capacity. |
74
+ | `413` | Raw request body exceeds the 16 MiB HTTP cap. |
75
+ | `422` | Tier 0 worker timeout, protocol error, death, or output cap. |
76
+ | `503` | Tier 0 sandbox is unavailable. |
77
+ | `500` | Unhandled internal error or gallery build failure. |
78
+
79
+ ## Status shape
80
+
81
+ Status reports artifact-scoped `latestRevisionSha`, `revisionCount`,
82
+ `pinnedCount`, and `templateCount` alongside `state` (`dormant` or `active`), `process` (`pid`, `uptimeMs`,
83
+ `rssBytes`, `pssBytes`), `dbBytes`, `evidenceBytes`, `activeLeases`,
84
+ `activeJobs`, `browserJobs`, `idleDeadline`, `version`, and `contractVersion`.
85
+ Unavailable memory values are `null`; RSS from multiple processes is not summed.
@@ -0,0 +1,78 @@
1
+ # MCP adapter
2
+
3
+ On harnesses with shell access, the CLI is the integration; the MCP adapter is for structured-tool-only environments. If an agent can run a shell, use the CLI and the Facet skill — this adapter buys no capability there.
4
+
5
+ Facet's npm package includes the `facet-mcp` bin. It needs Bun `1.4.0`; npm and pnpm install the package, but Bun remains the runtime.
6
+
7
+ Run the adapter without a checkout:
8
+
9
+ ```sh
10
+ bunx -p @legionworks/facet facet-mcp
11
+ ```
12
+
13
+ The adapter resolves the CLI in this order: `FACET_CLI`, then `bun <adapter-relative-repository>/src/cli/main.ts`, then `facet` on `PATH`. Set `FACET_CLI` to an absolute CLI executable when the adapter should use another installation.
14
+
15
+ ## Register the server
16
+
17
+ OpenCode config:
18
+
19
+ ```json
20
+ {
21
+ "mcp": {
22
+ "facet": {
23
+ "type": "local",
24
+ "command": ["bunx", "-p", "@legionworks/facet", "facet-mcp"],
25
+ "enabled": true
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Claude Code project config (`.mcp.json`):
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "facet": {
37
+ "command": "bunx",
38
+ "args": ["-p", "@legionworks/facet", "facet-mcp"]
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Codex config (`~/.codex/config.toml`):
45
+
46
+ ```toml
47
+ [mcp_servers.facet]
48
+ command = "bunx"
49
+ args = ["-p", "@legionworks/facet", "facet-mcp"]
50
+ ```
51
+
52
+ Set `FACET_HOME` in the host configuration when the adapter must use a non-default Facet runtime directory. Set `FACET_CLI` only for an alternate CLI executable.
53
+
54
+ ## Tools
55
+
56
+ | Tool | Inputs | Effect |
57
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
58
+ | `facet_publish` | `artifactId`, `type`, exactly one of `sourceText` or `file`; optional `execution`, `renderer`, `note`, `parentRevisionId` | Publishes inline source through CLI stdin or reads the named local file. |
59
+ | `facet_read_back` | `artifactId`; optional `revisionSha`, `tier` (`0` \| `1` \| `visual`) | Reads the latest or named revision. Tier 1 and visual need browser evidence. |
60
+ | `facet_status` | optional `artifactId`, `start` | Reads status. Set `start` only when activation is intended. |
61
+ | `facet_export` | `artifactId`, `format` (`source` \| `render`), `outDir`; optional `revisionSha`, `force`, `includeBytes` | Writes the CLI's normal export and sidecar beneath `outDir`. |
62
+ | `facet_open_url` | `artifactId`; optional `revisionSha` | Returns a gallery `frameUrl` without launching a browser. |
63
+
64
+ `facet_open_url` always adds `--no-launch`. It is the MCP-safe form of `facet open`; it never invokes `xdg-open` or another desktop launcher.
65
+
66
+ Exactly one of `sourceText` or `file` is required. The adapter returns `invalid_request` when both or neither are supplied.
67
+
68
+ The MCP surface is the five artifact tools; run `facet doctor` through the CLI.
69
+
70
+ ## Result and error handling
71
+
72
+ Every tool returns one text content item containing the complete versioned Facet envelope. `ok: true` means the CLI command completed at the transport boundary. For publish and read-back, inspect `data.verdict.status` separately: a stored verdict can be `error` even when the envelope is successful.
73
+
74
+ Typed Facet failures return that same envelope with `isError: true`. The JSON body preserves `error.code`, `error.message`, `error.retryable`, and `error.details`. The adapter converts malformed CLI stdout and subprocess failures into typed `invalid_envelope` errors instead of throwing raw process text through MCP.
75
+
76
+ ## Boundary
77
+
78
+ The adapter only shells out to `facet` and parses the shared wire envelope. It does not import service, validation, or gallery code. The boundary checker permits only the MCP SDK, Zod, Node builtins, adapter-local modules, shared contracts, and the shared product version in `src/harness-adapters/mcp/`.
@@ -0,0 +1,119 @@
1
+ # Security reference
2
+
3
+ Facet uses two bearer capabilities:
4
+
5
+ • The install token authorizes ordinary agent/service commands.
6
+ • The distinct operator promote capability authorizes `promote`.
7
+
8
+ The operator token is supplied first by `FACET_PROMOTE_TOKEN`, otherwise by
9
+ `FACET_HOME/secrets/promote.token` (or the configured runtime token path).
10
+ Promotion requires that token and records the operator identity and timestamp.
11
+ Token values never appear in argv, envelopes, logs, URLs, artifacts, notes, or
12
+ fixtures. Structured logs contain request, artifact, revision, and timestamp
13
+ identifiers only; source bytes and bearer tokens are redacted and never logged.
14
+
15
+ TTY presence is not authorization. An agent can allocate a PTY; only the distinct operator token can promote. Promotion changes retention and audit state, not validation tier or sandbox trust.
16
+
17
+ Gallery theme choice is display state, not a validation or security control.
18
+
19
+ ## Static HTML artifact policy
20
+
21
+ HTML artifacts are static, script-free, and carry no `<style>` block or
22
+ `style=` attribute. The verifier rejects:
23
+
24
+ - the short known-dangerous element set: `script`, `iframe`, `object`,
25
+ `embed`, `form`, `link`, `meta`, `base`, `style`
26
+ - every `on*=` event handler attribute
27
+ - non-https, protocol-relative, and `javascript:` URLs on `<a>`, `<img>`,
28
+ and `<source>` (`<a>` allows `mailto:`; `<img>` and `<source>` allow
29
+ `data:` and `https:`)
30
+
31
+ This is not the same surface the other artifact types police. Static
32
+ HTML is the only type that can carry text-format risk inside its body,
33
+ so the policy applies at parse time in Tier 0 and again in the Tier 1
34
+ renderer. The verdict is bound to the bytes the operator published; the
35
+ frame never injects script or styles into the artifact, only wraps the
36
+ sanitized body in a marker-bearing wrapper that never reaches storage
37
+ or export.
38
+
39
+ ### The two CSP jobs
40
+
41
+ The frozen CSP at `src/shared/security/frozen-csp.ts` does two unrelated
42
+ jobs, and the HTML widening widens only one of them:
43
+
44
+ | job | directives |
45
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | Verdict protection | `script-src 'nonce-<BOOTSTRAP_NONCE>'`, `connect-src 'none'`, `frame-src 'none'`, `object-src 'none'`, `base-uri 'none'`, `form-action 'none'`, `worker-src 'none'`, `default-src 'none'` |
47
+ | Display-time privacy | `img-src`, `font-src` |
48
+
49
+ A page that can run attacker code or open a network socket can forge a
50
+ verdict or exfiltrate, so the verdict-protection directives stay closed
51
+ on every artifact type. `img-src` and `font-src` protect PRIVACY at
52
+ display time only — the verifier never follows these URLs, so widening
53
+ `img-src` to `https:` (from `data:`) cannot weaken the verdict. The
54
+ widening was an over-restriction being corrected: artifacts are authored
55
+ by the operator's own agents, which hold bash and unrestricted network,
56
+ so the artifact channel is strictly weaker than its author.
57
+
58
+ The full frozen CSP, verbatim:
59
+
60
+ ```
61
+ default-src 'none';
62
+ script-src 'nonce-<BOOTSTRAP_NONCE>';
63
+ style-src 'unsafe-inline';
64
+ img-src data: https:;
65
+ font-src data:;
66
+ worker-src 'none';
67
+ connect-src 'none';
68
+ object-src 'none';
69
+ base-uri 'none';
70
+ form-action 'none';
71
+ frame-src 'none';
72
+ media-src 'none'
73
+ ```
74
+
75
+ `style-src 'unsafe-inline'` exists for the vendored Tailwind/daisyUI
76
+ stylesheet, which is loaded into the frame by `src/gallery-web/frame/styles/html-source.css`
77
+ under the per-frame nonce. A custom `data:` font ships in the bundle as
78
+ `font-src data:` allows; no remote font ever loads.
79
+
80
+ → [HTML reference](html.md) for the full HTML contract.
81
+
82
+ ## TSX execution policy
83
+
84
+ TSX is executable artifact code, so interactive bundles run only inside the
85
+ artifact's gallery frame, under that frame's own restrictive CSP (`self`, plus `blob:` for the
86
+ compiled module import, plus inline styles for renderer-injected theme
87
+ blocks) — a display-time policy,
88
+ separate from the frozen nonce-only CSP the Tier 1 verifier enforces during
89
+ validation. Tier 0 rejects direct capability use for typed author feedback,
90
+ but the compilation-time runtime boundary is authoritative: netns blocks
91
+ egress during `Bun.build` compilation. The AST policy is intentionally not
92
+ complete under aliasing; the vendored-module allowlist (`src/shared/tsx/import-policy.ts`)
93
+ is what makes indirect import forms unreachable at compile time.
94
+
95
+ Facet mounts the default export. Artifact source must not self-mount.
96
+
97
+ ## Insecure mode
98
+
99
+ Insecure mode is never enabled by default. It is boot-only: set `FACET_INSECURE=1`,
100
+ `2`, or `3`, then restart the service. Environment changes do not alter a live
101
+ service.
102
+
103
+ | level | contract |
104
+ | ----: | --------------------------------------------------------------------------------------------- |
105
+ | `0` | Secure defaults. Tier 0 and Tier 1 use their normal isolation. |
106
+ | `1` | Removes Tier 1 network-namespace isolation only. Real Tier 0 and Tier 1 validators still run. |
107
+ | `2` | Removes Tier 0 and Tier 1 network-namespace isolation. Real validators still run. |
108
+ | `3` | Performs no validation and records `insecure:unvalidated`. |
109
+
110
+ Levels compose as a forced floor: the effective level is never below the
111
+ operator's `FACET_INSECURE` value. `FACET_INSECURE_AUTO=1` may raise a level when
112
+ startup probes fail, but it never selects level 3. With auto mode off, hard
113
+ `tier*_unavailable` errors remain hard errors.
114
+
115
+ Every insecure-level verdict carries its `Verdict.insecure` marker. L1 and L2
116
+ statuses are real validator results — do not call them unvalidated. L3 also
117
+ carries the marker and owns `insecure:unvalidated`. Startup, the service-ready
118
+ envelope, CLI output, and gallery badge are intentionally loud. The CLI emits
119
+ an `INSECURE` line.
@@ -0,0 +1,93 @@
1
+ # Storage reference
2
+
3
+ Facet stores source bytes in SQLite and keeps render evidence on disk. The
4
+ service does not parse or render source while writing it.
5
+
6
+ ## Runtime paths and permissions
7
+
8
+ `computeFacetPaths` (`src/shared/config/paths.ts`) uses `FACET_HOME` when set:
9
+
10
+ | path | location under `FACET_HOME` | XDG default |
11
+ | ------------- | --------------------------- | -------------------------------------------- |
12
+ | database | `db/facet.sqlite` | `$XDG_DATA_HOME/facet/db/facet.sqlite` |
13
+ | evidence | `evidence/` | `$XDG_STATE_HOME/facet/evidence/` |
14
+ | promote token | `secrets/promote.token` | `$XDG_DATA_HOME/facet/secrets/promote.token` |
15
+ | lock | `run/facet.lock` | `$XDG_STATE_HOME/facet/run/facet.lock` |
16
+ | metadata | `metadata.json` | `$XDG_CONFIG_HOME/facet/metadata.json` |
17
+
18
+ `openDatabase` enables SQLite WAL mode, a 1,000 ms default busy timeout, and
19
+ foreign keys. `hardenDatabaseFiles` applies mode `0600` to the database and its
20
+ `-wal` and `-shm` sidecars. Owner-only directories use mode `0700`, including
21
+ the evidence root and every per-run evidence directory.
22
+
23
+ ## Schema migrations
24
+
25
+ `runMigrations` records applied versions in `schema_migrations` and applies
26
+ additive fragments in order. The current schema is v9:
27
+
28
+ | version | change |
29
+ | ------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | v1 | Creates `projects`, `artifacts`, `revisions`, `render_runs`, and `templates`. Revision source is a BLOB; artifact types are `markdown`, `mermaid`, `svg`, and `chart`. |
31
+ | v2 | Adds `render_runs.retained`, exempting selected evidence from cleanup. |
32
+ | v3 | Adds `revisions.renderer`, constrained to `svg` or `canvas`. `canvas` is a chart renderer, not an artifact type. |
33
+ | v4 | Adds `render_runs.screenshot_error_json` for typed transient screenshot-capture failures. |
34
+ | v5 | Adds `render_runs.insecure_json` for the effective insecure execution marker and reason. |
35
+ | v6 | Adds static `html` revisions and HTML observations. |
36
+ | v7 | Backfills HTML observation defaults. |
37
+ | v8 | Adds `tsx`, declared revision execution, and nullable `render_runs.compiled_path`. |
38
+ | v9 | Adds `render_runs.screenshot_format`, recorded as `png` or `webp` for retained evidence. |
39
+
40
+ Migrations are additive and transactional. Existing revisions are not rewritten
41
+ when a later schema version is applied.
42
+
43
+ ## Revisions
44
+
45
+ Each artifact keeps a ring of at most 50 revisions. Publication evicts the
46
+ oldest revision that is neither pinned nor bound to a template. If every
47
+ retained revision is pinned or template-bound, publication fails with
48
+ `revision_capacity_pinned`; protected history is never deleted.
49
+
50
+ `pin` changes retention metadata only. It does not copy or rewrite source bytes.
51
+ Unpinning makes a revision eligible for future ring eviction.
52
+
53
+ Each revision has an immutable source BLOB, SHA-256, revision number, optional
54
+ parent revision, note, artifact type, renderer, and timestamps. A revision SHA
55
+ is unique per artifact. Read-back looks up `(artifactId, revisionSha)` before
56
+ reading verdict rows, so verdicts cannot cross revisions.
57
+
58
+ ## Render evidence
59
+
60
+ Tier 0 stores its row and, for successful TSX compilation, derived compiled
61
+ bytes at `compiled_path`. Tier 1 stores the row plus a deterministic per-run directory:
62
+
63
+ ```text
64
+ <evidence>/tier1/<revisionSha>/<runId>/
65
+ ├── screenshot.webp
66
+ ├── console.txt
67
+ └── protocol-observation.json
68
+ ```
69
+
70
+ New captures use `screenshot.webp`; legacy retained rows may keep
71
+ `screenshot.png`. The v9 `render_runs.screenshot_format` value is `webp` or
72
+ `png`, but bytes are authoritative on read and export: the service sniffs PNG
73
+ and WebP signatures and treats unknown signatures as unavailable rather than
74
+ trusting stale metadata. The service stores and serves bytes; it does not
75
+ encode screenshots.
76
+
77
+ The row also points to `protocol-observation.json` when present. The evidence
78
+ root and each run directory are mode `0700`. `EVIDENCE_LAST_N_PER_ARTIFACT`
79
+ defaults to `10`: the write path keeps the ten newest non-retained runs for an
80
+ artifact and unlinks older screenshot and console files. Rows marked
81
+ `retained = 1` are exempt. Cleanup is best-effort; the database row remains the
82
+ authority and the orphan sweep can recover from stale files.
83
+
84
+ ## Templates
85
+
86
+ A template records one immutable revision ID. Later publication to the source
87
+ artifact cannot change that revision's SHA or bytes. Promotion records
88
+ `promoted_by` and `promoted_at`. Promotion and instantiation require the
89
+ operator capability.
90
+
91
+ Instantiation creates a new artifact and publishes a byte-for-byte copy of the
92
+ template revision with the same artifact type. The new revision is independent
93
+ of the source artifact.