@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.
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +23 -0
- package/README.md +158 -0
- package/dist/gallery/chunk-4g8rem85.css +656 -0
- package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
- package/dist/gallery/frame/artifact.css +8424 -0
- package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
- package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
- package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
- package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
- package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
- package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
- package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
- package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
- package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
- package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
- package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
- package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
- package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
- package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
- package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
- package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
- package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
- package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
- package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
- package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
- package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
- package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
- package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
- package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
- package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
- package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
- package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
- package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
- package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
- package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
- package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
- package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
- package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
- package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
- package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
- package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
- package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
- package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
- package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
- package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
- package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
- package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
- package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
- package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
- package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
- package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
- package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
- package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
- package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
- package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
- package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
- package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
- package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
- package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
- package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
- package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
- package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
- package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
- package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
- package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
- package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
- package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
- package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
- package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
- package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
- package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
- package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
- package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
- package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
- package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
- package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
- package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
- package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
- package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
- package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
- package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
- package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
- package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
- package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
- package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
- package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
- package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
- package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
- package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
- package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
- package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
- package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
- package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
- package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
- package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
- package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
- package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
- package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
- package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
- package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
- package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
- package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
- package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
- package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
- package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
- package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
- package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
- package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
- package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
- package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
- package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
- package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
- package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
- package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
- package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
- package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
- package/dist/gallery/frame/frame.css +183 -0
- package/dist/gallery/frame/runtime/chart.js +42744 -0
- package/dist/gallery/frame/runtime/html.js +14 -0
- package/dist/gallery/frame/runtime/markdown.js +1403 -0
- package/dist/gallery/frame/runtime/mermaid.js +32 -0
- package/dist/gallery/frame/runtime/svg.js +14 -0
- package/dist/gallery/frame/runtime/tsx.js +52 -0
- package/dist/gallery/index.html +104 -0
- package/docs/reference/cli.md +163 -0
- package/docs/reference/export.md +106 -0
- package/docs/reference/html.md +215 -0
- package/docs/reference/http.md +85 -0
- package/docs/reference/mcp.md +78 -0
- package/docs/reference/security.md +119 -0
- package/docs/reference/storage.md +93 -0
- package/docs/reference/tsx.md +101 -0
- package/docs/reference/validation.md +212 -0
- package/package.json +89 -0
- package/scripts/build-gallery.ts +127 -0
- package/scripts/launch-netns.sh +67 -0
- package/skills/facet/SKILL.md +100 -0
- package/src/cli/client.ts +311 -0
- package/src/cli/commands/create.ts +49 -0
- package/src/cli/commands/doctor.ts +249 -0
- package/src/cli/commands/export.ts +179 -0
- package/src/cli/commands/instantiate.ts +35 -0
- package/src/cli/commands/list.ts +49 -0
- package/src/cli/commands/open.ts +75 -0
- package/src/cli/commands/pin.ts +39 -0
- package/src/cli/commands/promote.ts +49 -0
- package/src/cli/commands/publish.ts +122 -0
- package/src/cli/commands/read-back.ts +56 -0
- package/src/cli/commands/status.ts +114 -0
- package/src/cli/commands/watch.ts +146 -0
- package/src/cli/main.ts +522 -0
- package/src/cli/output.ts +106 -0
- package/src/cli/parser.ts +376 -0
- package/src/cli/presenter.ts +212 -0
- package/src/cli/service-metadata.ts +134 -0
- package/src/cli/spawn-service.ts +326 -0
- package/src/css.d.ts +4 -0
- package/src/gallery-web/app.ts +1581 -0
- package/src/gallery-web/export-menu.ts +121 -0
- package/src/gallery-web/export.ts +112 -0
- package/src/gallery-web/favicon.ts +54 -0
- package/src/gallery-web/frame/entries/chart.ts +6 -0
- package/src/gallery-web/frame/entries/html.ts +6 -0
- package/src/gallery-web/frame/entries/markdown.ts +6 -0
- package/src/gallery-web/frame/entries/mermaid.ts +6 -0
- package/src/gallery-web/frame/entries/svg.ts +6 -0
- package/src/gallery-web/frame/entries/tsx.ts +6 -0
- package/src/gallery-web/frame/frame-payload.ts +91 -0
- package/src/gallery-web/frame/renderer-validation.ts +13 -0
- package/src/gallery-web/frame/renderers/chart.ts +182 -0
- package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
- package/src/gallery-web/frame/renderers/html.ts +48 -0
- package/src/gallery-web/frame/renderers/markdown.ts +81 -0
- package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
- package/src/gallery-web/frame/renderers/registry.ts +260 -0
- package/src/gallery-web/frame/renderers/svg.ts +393 -0
- package/src/gallery-web/frame/renderers/tsx.ts +61 -0
- package/src/gallery-web/frame/runtime.ts +501 -0
- package/src/gallery-web/frame/styles/artifact.css +8424 -0
- package/src/gallery-web/frame/styles/frame.css +183 -0
- package/src/gallery-web/frame/styles/html-source.css +23 -0
- package/src/gallery-web/frame/view-box.ts +33 -0
- package/src/gallery-web/frame-html.ts +72 -0
- package/src/gallery-web/index.html +104 -0
- package/src/gallery-web/session.ts +107 -0
- package/src/gallery-web/sse-client.ts +212 -0
- package/src/gallery-web/styles/app.css +420 -0
- package/src/gallery-web/styles/tokens.css +112 -0
- package/src/gallery-web/styles/verdict.css +186 -0
- package/src/gallery-web/swap.ts +64 -0
- package/src/gallery-web/theme.ts +40 -0
- package/src/gallery-web/view-state.ts +93 -0
- package/src/harness-adapters/claude-code/README.md +10 -0
- package/src/harness-adapters/claude-code/facet.sh +4 -0
- package/src/harness-adapters/codex/README.md +10 -0
- package/src/harness-adapters/codex/facet.sh +4 -0
- package/src/harness-adapters/mcp/README.md +13 -0
- package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
- package/src/harness-adapters/mcp/main.ts +15 -0
- package/src/harness-adapters/mcp/server.ts +164 -0
- package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
- package/src/harness-adapters/opencode/README.md +10 -0
- package/src/harness-adapters/opencode/facet.sh +4 -0
- package/src/runtime/compiled-entrypoints.ts +69 -0
- package/src/service/dispatcher.ts +717 -0
- package/src/service/export.ts +218 -0
- package/src/service/http-utils.ts +55 -0
- package/src/service/lexical/expectations.ts +210 -0
- package/src/service/lifecycle/idle-controller.ts +125 -0
- package/src/service/lifecycle/orphan-cleanup.ts +128 -0
- package/src/service/lifecycle/process-lock.ts +235 -0
- package/src/service/main.ts +136 -0
- package/src/service/process.ts +240 -0
- package/src/service/router-guards.ts +239 -0
- package/src/service/router.ts +627 -0
- package/src/service/security/auth.ts +168 -0
- package/src/service/security/host-origin.ts +138 -0
- package/src/service/security/http-guards.ts +13 -0
- package/src/service/security/leases.ts +182 -0
- package/src/service/security/token-store.ts +138 -0
- package/src/service/server.ts +356 -0
- package/src/service/store/database.ts +61 -0
- package/src/service/store/evidence-retention.ts +234 -0
- package/src/service/store/migrations.ts +167 -0
- package/src/service/store/repository-lifecycle.ts +121 -0
- package/src/service/store/repository.ts +732 -0
- package/src/service/store/schema.ts +218 -0
- package/src/service/stored-verdict.ts +97 -0
- package/src/service/stream.ts +301 -0
- package/src/service/verdict-enrichment.ts +45 -0
- package/src/shared/build/frame-bundle-plugins.ts +61 -0
- package/src/shared/build-mode.ts +28 -0
- package/src/shared/config/evidence-read.ts +98 -0
- package/src/shared/config/limits.ts +56 -0
- package/src/shared/config/paths.ts +96 -0
- package/src/shared/contracts/artifact-types.ts +2 -0
- package/src/shared/contracts/artifact.ts +98 -0
- package/src/shared/contracts/commands/_shared.ts +59 -0
- package/src/shared/contracts/commands/guards.ts +86 -0
- package/src/shared/contracts/commands/index.ts +204 -0
- package/src/shared/contracts/commands/names.ts +35 -0
- package/src/shared/contracts/commands/requests.ts +149 -0
- package/src/shared/contracts/commands/results.ts +200 -0
- package/src/shared/contracts/envelope.ts +140 -0
- package/src/shared/contracts/events.ts +41 -0
- package/src/shared/contracts/observed-counts.ts +19 -0
- package/src/shared/contracts/renderers.ts +6 -0
- package/src/shared/contracts/validation.ts +296 -0
- package/src/shared/errors/facet-error.ts +127 -0
- package/src/shared/errors/store-error.ts +68 -0
- package/src/shared/evidence-image.ts +37 -0
- package/src/shared/export.ts +31 -0
- package/src/shared/html/policy.ts +108 -0
- package/src/shared/html/style-vocabulary.ts +352 -0
- package/src/shared/logging/logger.ts +108 -0
- package/src/shared/logging/redact.ts +49 -0
- package/src/shared/security/frozen-csp.ts +13 -0
- package/src/shared/storage-version.ts +2 -0
- package/src/shared/tsx/execution.ts +29 -0
- package/src/shared/tsx/import-policy.ts +94 -0
- package/src/shared/util/dir-permissions.ts +94 -0
- package/src/shared/util/mermaid-nodes.ts +230 -0
- package/src/shared/util/process.ts +67 -0
- package/src/shared/util/time.ts +21 -0
- package/src/shared/version.ts +3 -0
- package/src/validation/sandbox/limits.ts +41 -0
- package/src/validation/sandbox/netns.ts +177 -0
- package/src/validation/tier0/chart.ts +177 -0
- package/src/validation/tier0/dom-shim.ts +54 -0
- package/src/validation/tier0/html.ts +408 -0
- package/src/validation/tier0/markdown.ts +285 -0
- package/src/validation/tier0/mermaid.ts +98 -0
- package/src/validation/tier0/runner.ts +504 -0
- package/src/validation/tier0/svg.ts +321 -0
- package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
- package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
- package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
- package/src/validation/tier0/tsx/ast-policy.ts +181 -0
- package/src/validation/tier0/tsx/compiler.ts +156 -0
- package/src/validation/tier0/worker-dispatch.ts +211 -0
- package/src/validation/tier0/worker-entry.ts +96 -0
- package/src/validation/tier0/worker-input.ts +75 -0
- package/src/validation/tier1/browser-process.ts +147 -0
- package/src/validation/tier1/cdp-pipe.ts +281 -0
- package/src/validation/tier1/entries/chart.ts +5 -0
- package/src/validation/tier1/entries/html.ts +6 -0
- package/src/validation/tier1/entries/markdown.ts +5 -0
- package/src/validation/tier1/entries/mermaid.ts +5 -0
- package/src/validation/tier1/entries/svg.ts +5 -0
- package/src/validation/tier1/entries/tsx.ts +5 -0
- package/src/validation/tier1/frame-target.ts +162 -0
- package/src/validation/tier1/harness-entry.ts +261 -0
- package/src/validation/tier1/harness.ts +215 -0
- package/src/validation/tier1/isolated-probe.ts +94 -0
- package/src/validation/tier1/launcher.ts +177 -0
- package/src/validation/tier1/limits.ts +80 -0
- package/src/validation/tier1/nonce.ts +17 -0
- package/src/validation/tier1/protocol-probe.ts +519 -0
- package/src/validation/tier1/runner.ts +1122 -0
- package/src/validation/tier1/verdict.ts +246 -0
- package/src/validation/tier1/webp.ts +69 -0
- package/templates/README.md +88 -0
- package/templates/bar-compare.vl.json +63 -0
- package/templates/capacity-report.tsx +102 -0
- package/templates/decision-record.md +24 -0
- package/templates/deployment-state.mmd +40 -0
- package/templates/exemplar.md +91 -0
- package/templates/facet-story.tsx +799 -0
- package/templates/fleet-dashboard.html +141 -0
- package/templates/html-release-ledger.html +91 -0
- package/templates/html-status-report.html +114 -0
- package/templates/incident-console.tsx +147 -0
- package/templates/legion-boundaries.mmd +23 -0
- package/templates/legion-flow.mmd +15 -0
- package/templates/legion-sequence.mmd +14 -0
- package/templates/legion-state.mmd +7 -0
- package/templates/metric-card.svg +29 -0
- package/templates/observability-map.svg +101 -0
- package/templates/pipeline-audit.md +108 -0
- package/templates/release-metrics.vl.json +98 -0
- package/templates/service-topology.mmd +47 -0
- package/templates/status-report.md +32 -0
- package/templates/system-map.svg +53 -0
- package/templates/timeseries.vl.json +71 -0
- package/templates/tsx-interactive-counter.tsx +19 -0
- 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.
|