--- id: BB-RESEARCH-IPXDXOUR type: research title: Luigit v2 rich document rendering --- # Luigit v2 rich document rendering ## Question How can a future Luigit version render D2, DOT, Mermaid, SVG, and TeX while preserving one executable, hostile-input safety, bounded work, and source-first browsing? ## Scope This research covers repository document previews, not general execution. It assumes Rust, one rootless application container, no runtime helpers, no required outbound network, read-only Git inputs, optional browser enhancement, and bounded derived state. ## Facts ### A common SVG pipeline is insufficient D2, Graphviz, and Mermaid can produce SVG. That does not make renderer output trusted. A renderer may read resources or exhaust work before output validation begins. SVG validation also cannot bound the browser cost of accepted paths, filters, references, dimensions, or embedded images by itself. TeX math is different because accessible output should retain MathML. Repository-authored SVG is different because it has no trusted generator. The useful shared boundary is therefore: 1. authorize immutable Git input; 2. structurally admit a narrow feature profile; 3. render under format-specific work controls; 4. validate the final output for its exact presentation context; 5. recheck authorization whenever serving a derivative. ### Browser Workers provide the cleanest kill boundary for diagram engines A Worker can be terminated immediately. A discarded Rust future, JavaScript promise timeout, or blocking thread cannot stop uncooperative native work. Browser Workers do not provide a configurable hard heap quota and move hostile computation to the visitor. They remain preferable for optional diagrams when the alternative is an uninterruptible engine inside Luigit's only server process. Source must remain usable when JavaScript, WebAssembly, or rendering fails. ### D2 Official D2 provides a browser package containing its Go WebAssembly runtime and creates a Worker from bundled application bytes. It needs no external file load in that path. This is not a drop-in WASI module for server embedding. Use a pinned official browser build in a disposable Worker. Initially allow one built-in layout, plain text labels, fixed Luigit fonts and themes, and no imports, images, rich HTML, animation, or embedded math. Validate the generated SVG before image-only presentation. Native Go integration would enlarge Luigit's in-process failure boundary. The surveyed Rust port has compatibility gaps and no established whole-render limits. Neither is presently suitable for hostile server-side work. Sources: [D2 v0.8.2](https://github.com/d2lang/d2/releases/tag/v0.8.2), [D2 browser platform](https://github.com/d2lang/d2/blob/v0.8.2/d2js/js/src/platform.browser.js), [D2 exports](https://www.d2lang.com/tour/exports/), [d2-little snapshot](https://github.com/Actrium/supramark/tree/28c3f52895e76842f64e73fa911496e440e1dd2d/crates/d2-little). ### DOT Graphviz exposes an in-process C API, but faults, allocator pressure, and uninterruptible layout would share Luigit's process. Rust wrappers which execute `dot` violate the runtime-helper constraint. Surveyed native Rust layout alternatives do not establish Graphviz compatibility. Viz.js packages Graphviz as WebAssembly and exposes its engines and SVG output. Use a pinned Viz.js build in a disposable Worker. Initially permit only an explicit engine set and reject images, external resources, stylesheets, font paths, and unneeded HTML-label features. Apply independent source, graph, output, Worker-lifetime, and browser-paint limits. Sources: [Graphviz embedding API](https://gitlab.com/graphviz/graphviz/-/raw/main/lib/gvc/gvc.h), [Viz.js API](https://viz-js.com/api/), [Viz.js build](https://github.com/mdaines/viz-js/blob/HEAD/packages/viz/backend/Dockerfile). ### Mermaid Official Mermaid rendering depends on browser DOM measurement. Its `strict` and `sandbox` settings are sanitization and presentation policies, not computation isolation. A promise timeout or iframe removal does not prove that rendering stopped. Merman is a headless Rust Mermaid renderer with explicit source, model, layout, output, image, and recursive-work limits plus cooperative cancellation. Its documentation correctly states that hard stop semantics still require a Worker or process around opaque callbacks and encoders. The surveyed resource controls are prerelease evidence and require adversarial validation. Evaluate pinned Merman WebAssembly in a disposable Worker before either official Mermaid or native in-process Merman. Admit only tested diagram families and deployment-owned configuration. Use self-contained output without callbacks, icon loading, external resources, loose security, or repository-selected configuration. Sources: [Mermaid render implementation](https://github.com/mermaid-js/mermaid/blob/mermaid%4011.17.2/packages/mermaid/src/mermaidAPI.ts), [Merman rendering security](https://github.com/Latias94/merman/blob/b381b842f0594cab342a0178f0c7bc5013c1b9f7/docs/security/RENDERING_SECURITY.md), [Merman web integration](https://github.com/Latias94/merman/blob/b381b842f0594cab342a0178f0c7bc5013c1b9f7/platforms/web/README.md). ### SVG Repository SVG is active document syntax, not an ordinary raster image. Image processing disables scripts, interaction, and external references, but does not bound parsing, decoding, layout, filters, or paint. Direct navigation creates a different document context. Do not insert repository SVG inline or expose it through `object` or `embed`. Keep exact source and an attachment download. A preview should be a bounded PNG generated from a parser configuration with all string-resource resolution disabled. `usvg` performs no network requests, but its default string resolver may read local files. Native `resvg` is embeddable Rust but has no demonstrated hard cancellation boundary. The strongest server design is a capability-denied WebAssembly rasterizer with fixed input, memory, work, dimensions, decoded pixels, recursion, and output limits. That integration is not turnkey and remains unproven. Until it exists, SVG preview should remain unsupported rather than use unrestricted native rasterization or raw same-origin SVG. Sources: [`usvg` image resolution](https://github.com/linebender/resvg/blob/v0.48.1/crates/usvg/src/parser/image.rs), [`resvg` changes](https://github.com/linebender/resvg/blob/main/CHANGELOG.md), [SVG processing modes](https://www.w3.org/TR/SVG2/conform.html#processing-modes), [Wasmtime controls](https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html), [Wasmtime limits](https://docs.rs/wasmtime/latest/wasmtime/struct.StoreLimitsBuilder.html). ### TeX Full TeX is a programming and file-processing environment. Running TeX, Tectonic, shell escape, packages, file access, or repository-defined macros is outside a safe document-preview feature. Support must mean a bounded mathematical notation profile only. KaTeX can produce HTML plus MathML without a DOM. It exposes macro-expansion, user-size, trust, and strictness controls. Its own security guidance still recommends validating generated markup. The most credible no-JavaScript path is pinned KaTeX inside a small embedded QuickJS runtime. `rquickjs` provides an interrupt callback, memory limit, and stack limit, although its memory limit is ineffective with documented custom allocator features. Use a fresh macro state per expression, `trust: false`, finite size and expansion limits, strict parsing, local fonts, and a dedicated HTML-plus-MathML validator. The exact build must prove effective interruption and allocation limits before adoption. A native Rust math renderer remains preferable if one reaches equivalent compatibility, accessible output, maintenance, and boundedness. Do not adopt a thin wrapper which hides its JavaScript engine or control surface. Sources: [KaTeX server rendering](https://katex.org/docs/node), [KaTeX options](https://katex.org/docs/options), [KaTeX security](https://katex.org/docs/security), [`rquickjs` runtime controls](https://docs.rs/rquickjs/0.12.2/rquickjs/struct.Runtime.html), [`math-core` configuration](https://docs.rs/math-core/0.8.2/math_core/struct.MathCoreConfig.html). ## Required trust boundaries - Repository bytes are always untrusted, including generated renderer output. - No renderer receives filesystem, network, environment, module loading, plugins, host fonts, or repository configuration. - Imports and dependencies are initially forbidden. - Final SVG rejects scripts, event handlers, `foreignObject`, animation, external resources, uncontrolled CSS, unsafe links, and invalid fragment references. - Mathematical output uses a separate validator which preserves required MathML while rejecting active content, resource URLs, and arbitrary style. - Diagram SVG is presented as an image, never inserted into the document DOM. - Raw SVG is never treated as trusted because it came from a public repository. - Input bytes, tokens, nesting, nodes, edges, expansion, work, memory, stack, output bytes, dimensions, decoded pixels, recursion, queue length, concurrency, and browser paint all need finite limits. - Cancellation must terminate a Worker or interrupt a metered guest; releasing an HTTP request or async handle is insufficient. - Current visibility and object reachability are checked before every source, download, and derivative response. - CSP permits only pinned local renderer assets and the narrowly required Worker and WebAssembly capabilities. ## Derived state A cache key must include source and dependency digests, renderer build, syntax profile, engine and options, validator policy, fonts, theme, viewport or scale, and encoder settings. Publish only complete validated artifacts. Do not accept browser-produced output into a trusted shared cache. Renderer, validator, policy, font, or theme changes invalidate affected derivatives. Transient overload and cancellation are not permanent invalid-input results. Cache admission, byte accounting, eviction, and authorization freshness remain independent concerns. ## Accessibility and fallback Exact escaped source is authoritative and always available. Every preview exposes its type, state, failure or limit reason, source link, and safe download where applicable. Diagrams need an adjacent author description or bounded textual graph summary because an SVG image does not expose reliable node navigation. Math retains MathML and copyable source. Preview controls are keyboard accessible and rendering never blocks ordinary repository navigation. Nugu light and dark variants use explicit renderer colors and pinned font metrics because SVG images do not inherit page CSS or fonts reliably. Author colors are either preserved meaningfully or rejected by the syntax profile; Luigit must not silently recolor semantics. Animation is initially disabled. ## Conclusion Use one shared authorization, admission, validation, caching, and fallback envelope with format-specific renderers. Do not force every format through SVG. The provisional placement is: - D2: bundled official WebAssembly in a disposable browser Worker. - DOT: bundled Viz.js in a disposable browser Worker. - Mermaid: evaluate Merman WebAssembly in a disposable browser Worker. - SVG: source and download only until a capability-denied bounded rasterizer can produce PNG. - TeX: bounded math only, conditionally server-rendered with KaTeX in controlled QuickJS to HTML plus MathML. These are research directions, not implementation selections. Deliberate non-support remains correct until each format passes compatibility, containment, output-validation, license, accessibility, and target-hardware performance gates. ## Unresolved questions - Which exact diagram families, layouts, labels, and math commands are needed? - Which maintained validators preserve required SVG and MathML without widening Markdown privileges? - What measured CPU, memory, graph, pixel, output, concurrency, and browser-paint budgets protect klops and mobile visitors? - Can every selected Worker be terminated during initialization, parsing, layout, serialization, and browser presentation? - Can `resvg` run through a small capability-denied server WebAssembly interface with acceptable cost? - Does the exact KaTeX and QuickJS build enforce interruption and memory limits under its chosen allocator? - Are prerelease Merman behavior, Mermaid compatibility, and security controls stable enough to depend on? - What license, corresponding-source, notice, and font obligations follow from the pinned complete asset set? - Do target browser, screen-reader, font, and CSP combinations preserve descriptions, semantic math, contrast, and source access? ## Provenance Research completed 2026-09-07 from official project documentation, pinned source snapshots, web-platform specifications, and current crate APIs. Candidate versions and unreleased branches require artifact-level verification before selection.