@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,177 @@
1
+ /**
2
+ * Network namespace launcher for the Tier 0 parse worker.
3
+ *
4
+ * The wrapper enforces egress denial by kernel topology, NOT by
5
+ * flagging the artifact: `unshare --map-current-user --net` creates a
6
+ * rootless network namespace in which `lo` is DOWN and no other
7
+ * interface exists. The worker cannot reach any addressable host
8
+ * because there is no route to one — `fetch`, `XMLHttpRequest`,
9
+ * `WebSocket`, raw sockets, WebRTC, and any future IP API all see the
10
+ * same empty namespace.
11
+ *
12
+ * The reference spike at `.facet-build/phase-0-spikes/egress/` proved
13
+ * this candidate blocks 0/12 known browser escape channels on the
14
+ * Chromium build; the same boundary holds for a Bun process.
15
+ *
16
+ * If `unshare` / user namespaces are unavailable in the host
17
+ * environment, `probeNetnsSupport()` returns `false` so callers can
18
+ * surface a typed `tier0_unavailable` error instead of silently
19
+ * running un-sandboxed.
20
+ */
21
+
22
+ import { spawn, spawnSync, type ChildProcess } from "node:child_process";
23
+ import { statSync } from "node:fs";
24
+
25
+ import { TIER0_MEMORY_CAP_BYTES } from "./limits";
26
+ import { InsecureLevelSchema } from "../../shared/contracts/validation";
27
+
28
+ /**
29
+ * The unshare wrapper. The shell fragment applies `ulimit -v` (RLIMIT_AS)
30
+ * so the worker cannot grow past `TIER0_MEMORY_CAP_BYTES` regardless of
31
+ * what library code inside it does, then `exec`s `unshare` so the
32
+ * `unshare` PID is reused and signals reach the inner process.
33
+ *
34
+ * `--` terminates unshare's own option parsing so a worker argv
35
+ * beginning with `-` cannot be mistaken for a wrapper flag.
36
+ */
37
+ const MEMORY_LIMIT_PREFIX = ["ulimit -v", TIER0_MEMORY_CAP_BYTES, "; "].join(" ");
38
+
39
+ // Acceptance-only seams let the proof exercise both branches on one host.
40
+ const FORCE_UNAVAILABLE_ENV = "FACET_TIER0_FORCE_NETNS_UNAVAILABLE";
41
+ const DIRECT_EXEC_MARKER_ENV = "FACET_TIER0_DIRECT_EXEC_MARKER";
42
+
43
+ export interface NetnsProbe {
44
+ /** True iff `unshare --map-current-user --net` is runnable as the current user. */
45
+ readonly available: boolean;
46
+ /**
47
+ * When `available` is `false`, a short reason suitable for an error
48
+ * body — e.g. "unshare: operation not permitted" (user namespaces
49
+ * disabled) or "unshare: not found".
50
+ */
51
+ readonly reason: string | null;
52
+ }
53
+
54
+ /**
55
+ * Run a single `unshare --map-current-user --net -- true` invocation
56
+ * to detect whether rootless user namespaces are enabled in the host.
57
+ * The result is cached per process because the probe itself is
58
+ * expensive and the capability does not change while the service runs.
59
+ */
60
+ let cachedProbe: NetnsProbe | null = null;
61
+
62
+ export function probeNetnsSupport(): NetnsProbe {
63
+ if (process.env[FORCE_UNAVAILABLE_ENV] === "1") {
64
+ return { available: false, reason: "forced unavailable for acceptance proof" };
65
+ }
66
+ if (cachedProbe !== null) return cachedProbe;
67
+ try {
68
+ // spawnSync, NOT spawn: `exit` fires on a later tick, so an async probe
69
+ // read synchronously ALWAYS observed "not resolved" and cached a false
70
+ // negative for the process lifetime — disabling Tier 0 wherever the
71
+ // capability is in fact present.
72
+ const probe = spawnSync("unshare", ["--map-current-user", "--net", "--", "/bin/true"], {
73
+ stdio: "ignore",
74
+ });
75
+ if (probe.error !== undefined)
76
+ return (cachedProbe = { available: false, reason: `unshare: ${probe.error.message}` });
77
+ if (probe.signal !== null)
78
+ return (cachedProbe = { available: false, reason: `unshare killed by ${probe.signal}` });
79
+ if (probe.status === 0) return (cachedProbe = { available: true, reason: null });
80
+ return (cachedProbe = {
81
+ available: false,
82
+ reason: `unshare exited with code ${probe.status}`,
83
+ });
84
+ } catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ return (cachedProbe = { available: false, reason: `unshare probe threw: ${message}` });
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Returns the path to the `unshare` binary, asserting it exists.
92
+ * Surfacing a typed error here (rather than letting `spawn` fail with
93
+ * ENOENT later) gives the parent a single, deterministic
94
+ * `tier0_unavailable` failure mode for "binary missing".
95
+ */
96
+ export function resolveUnsharePath(): string {
97
+ const candidates = [
98
+ "/usr/bin/unshare",
99
+ "/bin/unshare",
100
+ "/usr/local/bin/unshare",
101
+ "/home/linuxbrew/.linuxbrew/bin/unshare",
102
+ ];
103
+ for (const candidate of candidates) {
104
+ try {
105
+ if (statSync(candidate).isFile()) return candidate;
106
+ } catch {
107
+ // try next candidate
108
+ }
109
+ }
110
+ throw new Error("unshare binary not found in known locations");
111
+ }
112
+
113
+ /**
114
+ * Spawn the worker inside a rootless network namespace.
115
+ *
116
+ * The returned `ChildProcess` has STDIN, STDOUT, and STDERR piped so
117
+ * the parent can stream the input payload and parse the bounded
118
+ * result. Memory cap and netns are applied by the wrapper command;
119
+ * wall-clock + output cap are enforced by the parent runner on top of
120
+ * the returned streams.
121
+ *
122
+ * The caller keeps STDIN open for the worker's bounded NDJSON protocol
123
+ * and closes it only when tearing down the worker pool.
124
+ */
125
+ export function spawnNetnsWorker(args: readonly string[]): ChildProcess {
126
+ return spawnWorker(args, true);
127
+ }
128
+
129
+ /** Spawn the same capped worker without the network namespace boundary. */
130
+ export function spawnDirectWorker(args: readonly string[]): ChildProcess {
131
+ return spawnWorker(args, false);
132
+ }
133
+
134
+ /** Select the Tier 0 isolation boundary for the configured insecure level. */
135
+ export function resolveTier0Isolation(level: 0 | 1 | 2 | 3): "netns" | "direct" {
136
+ const parsedLevel = InsecureLevelSchema.parse(level);
137
+ return parsedLevel <= 1 ? "netns" : "direct";
138
+ }
139
+
140
+ function spawnWorker(args: readonly string[], useNetns: boolean): ChildProcess {
141
+ const bunPath = process.execPath;
142
+ // Compose the shell command. `sh -c` is required because the wrapper
143
+ // applies `ulimit` before exec-ing unshare — ulimit is a shell builtin
144
+ // and has no freestanding form. After the wrapper runs, unshare
145
+ // execs into the bun binary with the caller-supplied args, so the
146
+ // long-running worker is the bun process (not unshare or sh).
147
+ const tail = [quoteShell(bunPath), ...args.map(quoteShell)].join(" ");
148
+ const isolation = useNetns ? "unshare --map-current-user --net -- " : "";
149
+ const marker =
150
+ !useNetns && process.env[DIRECT_EXEC_MARKER_ENV] !== undefined
151
+ ? `: > ${quoteShell(process.env[DIRECT_EXEC_MARKER_ENV]!)}`
152
+ : "";
153
+ const shellCommand = `${MEMORY_LIMIT_PREFIX}${marker.length > 0 ? `${marker}; ` : ""}exec ${isolation}${tail}`;
154
+ return spawn("/bin/sh", ["-c", shellCommand], {
155
+ stdio: ["pipe", "pipe", "pipe"],
156
+ // Detach from the parent's controlling terminal so SIGINT to the
157
+ // parent does not cascade into the worker.
158
+ detached: false,
159
+ env: {
160
+ ...process.env,
161
+ // Force a deterministic locale so worker diagnostics don't pull
162
+ // in the host's UTF-8 assumptions about error message text.
163
+ LC_ALL: "C.UTF-8",
164
+ },
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Quote a single argv element for the shell command. Single-quotes
170
+ * the value and escapes embedded single quotes; safe for paths and
171
+ * flags that contain no shell metacharacters.
172
+ */
173
+ function quoteShell(value: string): string {
174
+ if (value.length === 0) return "''";
175
+ if (/^[A-Za-z0-9._/=+-]+$/.test(value)) return value;
176
+ return `'${value.replace(/'/g, `'\\''`)}'`;
177
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Tier 0 chart (Vega-Lite) parser.
3
+ *
4
+ * Two-step validation:
5
+ *
6
+ * 1. STRUCTURAL zod check on the raw spec — we forbid any `data`
7
+ * loader that references an external URL. Inline `data.values`
8
+ * and inline datasets are allowed because they cannot trigger a
9
+ * network fetch (and netns would block it anyway — but rejecting
10
+ * at Tier 0 makes the policy explicit on the wire).
11
+ *
12
+ * 2. `vega-lite.compile()` — produces a Vega spec from the input
13
+ * Vega-Lite spec WITHOUT a DOM. `compile()` is documented as a
14
+ * pure transform: it returns the compiled spec or throws a
15
+ * `vlCompileError`. We have verified empirically that `compile()`
16
+ * does not require a browser or `document` (verified against
17
+ * vega-lite@6 in this build).
18
+ *
19
+ * The Tier 0 verdict reports `graphCount: 1` for a successfully
20
+ * compiled chart (one rendered visualization) and `errorCount: 1` for
21
+ * a malformed or external-data spec.
22
+ */
23
+
24
+ import { z } from "zod";
25
+ import { compile } from "vega-lite";
26
+
27
+ import type { DiscriminativeError, VerdictObserved } from "../../shared/contracts/validation";
28
+
29
+ /**
30
+ * Vega-Lite spec surface — STRICT subset. We accept only the data
31
+ * forms that cannot reference an external URL: inline `values` and an
32
+ * unnamed inline dataset reference. A `data` property whose value is
33
+ * a string or `{ url: … }` is rejected at the zod layer.
34
+ */
35
+ const InlineDataSchema = z.union([
36
+ z.object({ values: z.array(z.unknown()) }).strict(),
37
+ z.null(),
38
+ z.undefined(),
39
+ ]);
40
+
41
+ const VegaLiteSpecSchema = z
42
+ .object({
43
+ $schema: z.string().optional(),
44
+ description: z.string().optional(),
45
+ // Leave the raw data value open here so the dedicated check below can
46
+ // report the security-specific external-data error instead of a generic
47
+ // schema failure.
48
+ data: z.unknown().optional(),
49
+ mark: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
50
+ encoding: z.record(z.string(), z.unknown()).optional(),
51
+ layer: z.array(z.record(z.string(), z.unknown())).optional(),
52
+ transform: z.array(z.record(z.string(), z.unknown())).optional(),
53
+ width: z.union([z.number(), z.record(z.string(), z.unknown())]).optional(),
54
+ height: z.union([z.number(), z.record(z.string(), z.unknown())]).optional(),
55
+ })
56
+ .passthrough();
57
+
58
+ export interface ChartParseOk {
59
+ readonly status: "ok";
60
+ readonly observed: VerdictObserved;
61
+ }
62
+
63
+ export interface ChartParseFail {
64
+ readonly status: "error";
65
+ readonly observed: VerdictObserved;
66
+ readonly errors: readonly DiscriminativeError[];
67
+ }
68
+
69
+ export type ChartParseResult = ChartParseOk | ChartParseFail;
70
+
71
+ /**
72
+ * Parse the source bytes as a Vega-Lite chart spec. First the
73
+ * structural shape is validated (zod) so any external `data` reference
74
+ * is rejected before we hand the spec to the compiler; then
75
+ * `vega-lite.compile()` is called to confirm the spec is well-formed
76
+ * Vega-Lite.
77
+ */
78
+ export function parseChart(bytes: Uint8Array): ChartParseResult {
79
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
80
+ let spec: unknown;
81
+ try {
82
+ spec = JSON.parse(text);
83
+ } catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ return {
86
+ status: "error",
87
+ observed: {
88
+ rendererRootSvgCount: 0,
89
+ graphCount: 0,
90
+ mermaidNodeCount: 0,
91
+ visibleSvgCount: 0,
92
+ errorCount: 1,
93
+ opaqueRegionCount: 0,
94
+ externalImageCount: 0,
95
+ },
96
+ errors: [{ code: "chart_json_error", message }],
97
+ };
98
+ }
99
+
100
+ // VegaLiteSpecSchema is strict about the top-level `data` shape —
101
+ // any spec whose `data` is a string or `{ url: … }` fails with a
102
+ // precise path so the wire response can point at the bad field.
103
+ const topLevel = VegaLiteSpecSchema.safeParse(spec);
104
+ if (!topLevel.success) {
105
+ return {
106
+ status: "error",
107
+ observed: {
108
+ rendererRootSvgCount: 0,
109
+ graphCount: 0,
110
+ mermaidNodeCount: 0,
111
+ visibleSvgCount: 0,
112
+ errorCount: 1,
113
+ opaqueRegionCount: 0,
114
+ externalImageCount: 0,
115
+ },
116
+ errors: topLevel.error.issues.slice(0, 5).map((issue) => ({
117
+ code: issue.code === "invalid_type" ? "chart_invalid_type" : "chart_invalid_spec",
118
+ message: `${issue.path.join(".") || "(root)"}: ${issue.message}`,
119
+ })),
120
+ };
121
+ }
122
+ // Re-check the data field specifically to surface a precise error.
123
+ const dataCheck = InlineDataSchema.safeParse(topLevel.data.data);
124
+ if (!dataCheck.success) {
125
+ return {
126
+ status: "error",
127
+ observed: {
128
+ rendererRootSvgCount: 0,
129
+ graphCount: 0,
130
+ mermaidNodeCount: 0,
131
+ visibleSvgCount: 0,
132
+ errorCount: 1,
133
+ opaqueRegionCount: 0,
134
+ externalImageCount: 0,
135
+ },
136
+ errors: [
137
+ {
138
+ code: "chart_external_data_rejected",
139
+ message:
140
+ "Vega-Lite spec references external data (data.url / data string / loader); only inline data is allowed at Tier 0",
141
+ },
142
+ ],
143
+ };
144
+ }
145
+
146
+ try {
147
+ const compiled = compile(topLevel.data as unknown as Parameters<typeof compile>[0]);
148
+ void compiled.spec;
149
+ return {
150
+ status: "ok",
151
+ observed: {
152
+ rendererRootSvgCount: 1,
153
+ graphCount: 1,
154
+ mermaidNodeCount: 0,
155
+ visibleSvgCount: 1,
156
+ errorCount: 0,
157
+ opaqueRegionCount: 0,
158
+ externalImageCount: 0,
159
+ },
160
+ };
161
+ } catch (error) {
162
+ const message = error instanceof Error ? error.message : String(error);
163
+ return {
164
+ status: "error",
165
+ observed: {
166
+ rendererRootSvgCount: 0,
167
+ graphCount: 0,
168
+ mermaidNodeCount: 0,
169
+ visibleSvgCount: 0,
170
+ errorCount: 1,
171
+ opaqueRegionCount: 0,
172
+ externalImageCount: 0,
173
+ },
174
+ errors: [{ code: "chart_compile_error", message }],
175
+ };
176
+ }
177
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * linkedom-based DOM shim for the Tier 0 worker.
3
+ *
4
+ * Mermaid and other browser-shaped libraries perform import-time
5
+ * initialization against `globalThis.document`, `window`, and the
6
+ * `implementation.createHTMLDocument` API. Bun's worker process has
7
+ * none of these by default, so a naive `import mermaid from "mermaid"`
8
+ * throws `DOMPurify.addHook is not a function` (or its equivalent)
9
+ * before the parser ever sees source.
10
+ *
11
+ * This module installs the shim at IMPORT TIME (top-level) so any
12
+ * subsequent `import "mermaid"` sees a DOM. It is structural only —
13
+ * it never executes any artifact source. The worker's netns prevents
14
+ * any library-initiated network egress.
15
+ */
16
+
17
+ import { parseHTML } from "linkedom";
18
+
19
+ let installed = false;
20
+ export let domShimDocument: Document;
21
+
22
+ function installDomShim(): void {
23
+ if (installed) return;
24
+ installed = true;
25
+ const { document, window } = parseHTML("<!DOCTYPE html><html><body></body></html>");
26
+ domShimDocument = document as unknown as Document;
27
+ // DOMPurify's isSupported check looks for `implementation.createHTMLDocument`.
28
+ // linkedom ships without an implementation object; we attach a minimal shim
29
+ // that returns a freshly-parsed document. This is structural only —
30
+ // we never serialize against this DOM.
31
+ const fakeImpl = {
32
+ createHTMLDocument: (html: string): Document => {
33
+ const r = parseHTML(html);
34
+ return r.document as unknown as Document;
35
+ },
36
+ };
37
+ Object.defineProperty(document, "implementation", { value: fakeImpl, configurable: true });
38
+ const g = globalThis as unknown as Record<string, unknown>;
39
+ g["document"] = document;
40
+ g["window"] = window;
41
+ g["Element"] = window.Element;
42
+ g["HTMLElement"] = window.HTMLElement;
43
+ g["Node"] = window.Node;
44
+ g["DocumentFragment"] = window.DocumentFragment;
45
+ g["HTMLTemplateElement"] = window.HTMLTemplateElement;
46
+ g["NodeFilter"] = window.NodeFilter;
47
+ }
48
+
49
+ // Top-level install — runs synchronously when this module is first
50
+ // imported. Any module that subsequently imports `mermaid` (which has
51
+ // import-time DOM checks) sees the structural DOM in place.
52
+ installDomShim();
53
+
54
+ export const domShimInstalled = true;