ADR-012: Markdown Rendering with marked.js and DOMPurify 

Status: Accepted

Date: 2026-03-17

Context 

LLM responses frequently contain Markdown: headings, bullet lists, numbered steps, code blocks, and tables. Displaying these as raw text degrades readability significantly. The rendered output must be XSS-safe: a compromised or adversarially prompted LLM could produce HTML or JavaScript in its response.

Alternatives considered:

  • Plain text only: Safe, but unreadable for structured responses.
  • Server-side Markdown-to-HTML (PHP): Requires a PHP Markdown library, adds a server round-trip for each render, and moves rendering responsibility to the server.
  • ``innerHTML`` without sanitization: Fast, but allows XSS if the LLM output contains <script> tags or event handlers.
  • marked.js + DOMPurify in the browser: Client-side rendering, no server round-trip, XSS-safe via DOMPurify sanitization after parsing.

Decision 

Vendor marked.js v15 and DOMPurify v3 as static assets (no build step, consistent with ADR-008: Lit Web Components Without a Build Step). LLM response text is parsed by marked.js into HTML, then sanitized by DOMPurify before being set as innerHTML. Both libraries are treated as untrusted-input pipelines: marked produces HTML from untrusted text, DOMPurify strips anything dangerous before it reaches the DOM.

Consequences 

  • LLM responses render as rich text (headings, lists, code blocks, tables) without a server round-trip.
  • XSS is prevented even if the LLM produces malicious HTML in its output.
  • Vendored libraries must be updated manually when security patches are released.
  • No build step is introduced (libraries are used as ES modules or UMD bundles loaded directly).