# Vendored front-end assetsThe web front end serves all third-party JavaScript locally so it runs fullyoffline (no CDN calls) and under a strict `script-src 'self'` CSP. Each file ispinned by version and integrity hash and served from `/vendor` by Caddy.To refresh a pin, re-run the fetch below, update the version + SRI here, andupdate the `<script integrity="…">` in the templates that load it.## Inventory| File | Package | Version | License | SRI (sha384) ||------|---------|---------|---------|--------------|| `htmx.min.js` | [htmx](https://htmx.org) | 2.0.4 | 0BSD | `HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+` || `mathjax/tex-mml-chtml.js` (+ `mathjax/output/…/woff-v2/*.woff`, `mathjax/input/tex/extensions/*.js`) | [MathJax](https://www.mathjax.org) | 3.2.2 | Apache-2.0 | `Wuix6BuhrWbjDBs24bXrjf4ZQ5aFeFWBuKkFekO2t8xFU0iNaLQfp2K6/1Nxveei` (bundle; the autoloaded extensions are `'self'`, no SRI) || `codemirror/*.js` (entry `codemirror/cm6.js`) | [CodeMirror 6](https://codemirror.net) (+ lang-python/cpp/json, theme-one-dark, @lezer/\*) | 6.0.2 | MIT | ES modules — dynamic-imported, `'self'` (no SRI) || `xterm/xterm.js` | [xterm.js](https://xtermjs.org) (`@xterm/xterm`) | 5.5.0 | MIT | `M169f14mRZOXm3hD/v2Ti0ThIT/RnAQagXA9nlE15yHAtrW19gdePJh/HaTzUOe/` || `xterm/xterm-addon-fit.js` | [`@xterm/addon-fit`](https://github.com/xtermjs/xterm.js) | 0.10.0 | MIT | `iF+jqbuti4XlB64clWgFWYEscb+UnSRv3VgVikGYZu+otNFnSHr7y7NcKfBnGizn` || `xterm/xterm.css` | `@xterm/xterm` (stylesheet) | 5.5.0 | MIT | `8Xk9wy/gzEDUKrXtrmCFa2bBuK3BpjpDuL/p0SeKQX19Khl/M+lHOgD/CyYf7efP` || `hash-wasm/argon2.umd.min.js` | [hash-wasm](https://github.com/Daninet/hash-wasm) (Argon2id build) | 4.12.0 | MIT | `tP0Wy54CKmng7i9EoTlPySD0hBx6Octj0VS6MfwlnUu111MPa+JLm0CCbep6XJ1W` || `docs/presentation/fonts/newsreader.woff2`, `newsreader-italic.woff2` | [Newsreader](https://github.com/productiontype/Newsreader) (Production Type) | Google Fonts v26, Latin | OFL-1.1 | font, `'self'` (no SRI) || `docs/presentation/fonts/atkinson-hyperlegible-{400,700}.woff2` | [Atkinson Hyperlegible](https://brailleinstitute.org/freefont) (Braille Institute) | Google Fonts v12, Latin | OFL-1.1 | font, `'self'` (no SRI) || `docs/presentation/fonts/jetbrains-mono.woff2` | [JetBrains Mono](https://github.com/JetBrains/JetBrainsMono) | Google Fonts v24, Latin | OFL-1.1 | font, `'self'` (no SRI) |hash-wasm's Argon2id derives the SRP private key `x` in the browser (`/assets/srp.js`; theservice checks against `argon2-cffi`, `tests/test_srp_interop.py` pins the two together). Itis the npm tarball's `dist/argon2.umd.min.js` — tarball integrity`sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==`as published — with its WebAssembly embedded, which is why the pages that load it carry`'wasm-unsafe-eval'` in `script-src` (docs/web-server-guide.md § Content-Security-Policy).License at `hash-wasm/LICENSE`.MathJax typesets the `$…$` TeX in statements/notes (config + re-typeset-on-swapin `/assets/site.js`); the woff fonts are loaded by the bundle relative to itsown URL, so the whole tree ships together. Its full license text is at`mathjax/LICENSE`. Note: MathJax injects its stylesheet at runtime — the reason`style-src` carries `'unsafe-inline'` (docs/web-server-guide.md § Content-Security-Policy).**The TeX extensions under `mathjax/input/tex/extensions/`** ship for the samereason as the fonts: the combined bundle carries only the default packages, and the restare fetched relative to the bundle's own URL, so`/vendor/mathjax/input/tex/extensions/<name>.js`. Vendored, that request issame-origin and `script-src 'self'` admits it; *not* vendored it 404s, and the`noundefined` package then renders the macro as its own name in red — which is what astatement using `\color` used to show (problem 230).**They are preloaded, not autoloaded** (`loader.load` + `tex.packages` in`/assets/site.js`). MathJax's `autoload` fetches a package **on first use**, i.e. in themiddle of a typeset, and the swap path lost that race: a statement reached by theterminal's `show` rendered `\color[RGB]124, 192, 255` as literal text while a plainrefresh of the same URL was perfect. Preloading costs one fetch per document, of filesalready vendored, and makes the typesetter's vocabulary independent of which page thereader happened to open first. **A package added here must be added to `MJ_TEX` insite.js as well** — preloading a name whose file is missing fails MathJax *startup*, onevery page, which is worse than the autoload it replaced. A test pins both directions(`tests/test_web_verbs.py::MathJaxWiringTests`).One file per package actually used by a cached statement, found by scanning them foreach trigger macro:| Package | Macros | Statements ||---------|--------|------------|| `color` | `\color`, `\textcolor`, … | 12, e.g. 40 · 66 · 230 || `mhchem` | `\pu`, `\ce` | 5, e.g. 75 · 155 · 222 || `html` | `\style`, `\class`, `\href` | 3: 282 · 326 · 334 || `boldsymbol` | `\boldsymbol` | 2: 323 · 462 || `enclose` | `\enclose` | 1: 535 || `unicode` | `\unicode` | 1: 991 |A statement that starts using another one is the same fix: add the file, re-run thescan. There is no SRI — these are same-origin scripts MathJax injects itself, exactlylike the CodeMirror modules.**CodeMirror 6** is the code editor on the edit page (`/assets/editor.js`,lazy-imported by `site.js` only when an editor appears, so the ~630 KB graphnever loads elsewhere). It lives under `codemirror/` as a flat set of ES modulesmirrored from esm.sh (`target=es2022`) with imports rewritten to relative `./`siblings — the full transitive graph of `codemirror`, the language packages,`theme-one-dark`, `@lezer/*`, and helpers — so it loads fully offline with nobuild step. `cm6.js` is the entry (re-exports only the symbols `editor.js` uses).Pin the `codemirror` meta-package to an explicit `6.0.x`: the bare `@6` rangeresolves to the legacy CodeMirror 5 UMD, which has no `EditorView`/`basicSetup`.All `@codemirror/state` stubs re-export one `state` build (CM's single-instancerule). Like MathJax it injects its stylesheet at runtime (`style-src'unsafe-inline'`). Dynamic-imported same-origin modules, so no SRI. License at`codemirror/LICENSE`.**xterm.js** is the terminal in the right pane (`/terminal`): the UMD`xterm.js` plus the **fit** addon (it sizes the terminal to its container; theresulting `cols`×`rows` is what `terminal.js` sends over the socket as the PTY'sgeometry) and the stock stylesheet. Classic scripts with SRI — unlike CodeMirrorthey are not ES modules, so they are `<script src>`-loaded by `terminal.html` andintegrity-checked. Both files carry a `//# sourceMappingURL=` line whose `.map` is**not** vendored: harmless (only devtools ever requests it), and stripping itwould make the bytes diverge from the published ones the SRI above attests.License at `xterm/LICENSE`.**The presentation fonts** are the one entry not under this directory: they ship with thedeck they serve, in `docs/presentation/fonts/`, and the per-user service hands them out at`/story/fonts/` with the rest of the deck (web-server-guide § 11.5, The story deck). Theyare bundled for the same reason as everything here — `default-src 'self'` refuses a GoogleFonts stylesheet, and a bundled font also works offline. Each is the Latin subset as GoogleFonts serves it (the deck's text is Latin; other characters fall back to system fonts),loaded by `@font-face` in `docs/presentation/deck.css`. Fonts are not scripts, so there isno SRI; to check a refresh, compare against these sha384 digests:| File | sha384 ||------|--------|| `newsreader.woff2` | `5TsIb3Y92/m28WF/gV7mdSO1juW62ueaN4e5hRZ4uC6CQV5Igadd+oBHDfLDyCX3` || `newsreader-italic.woff2` | `NIkI1/BjDDT2w43BuJRu5j405Z8u8wvUcf6XRL2gHbDDk5sf74z1/ZmpfZXH6KMl` || `atkinson-hyperlegible-400.woff2` | `idlpjMgZTjOmx6zrv5Ae+oGIwhOzri+uCqMmDHcHbeZSD83FdoJ3vrtowWb6+kH2` || `atkinson-hyperlegible-700.woff2` | `nQ0Z7JQYR9+re+CEt4QFS/80lVM63lK+vHYQhrfJ4nQWibIMndxkRAdPQ3UA6pUw` || `jetbrains-mono.woff2` | `9wWypBPwJyfsOnJFplExHD+Wz+6/I6+rtL7ITgA/6wSPgpj+tnQovOLWM9j2rhOF` |Their license texts sit beside them (`OFL-newsreader.txt`, `OFL-atkinsonhyperlegible.txt`,`OFL-jetbrainsmono.txt`, trailing whitespace stripped for the commit hook, wording unchanged).Fetch (pinned):```bashV=2.0.4curl-sSfL-ohtmx.min.js"https://unpkg.com/htmx.org@${V}/dist/htmx.min.js"printf'sha384-%s\n'"$(openssldgst-sha384-binaryhtmx.min.js|opensslbase64-A)"V=3.2.2# MathJax: the single-file combined component + its woff-v2 fontscurl-sSfL-omathjax/tex-mml-chtml.js"https://cdn.jsdelivr.net/npm/mathjax@${V}/es5/tex-mml-chtml.js"# fonts: es5/output/chtml/fonts/woff-v2/*.woff from the same release# the autoloaded TeX extensions the cached statements use (see the table above)mkdir-pmathjax/input/tex/extensionsforeincolormhchemhtmlboldsymbolencloseunicode;docurl-sSfL-o"mathjax/input/tex/extensions/$e.js"\"https://cdn.jsdelivr.net/npm/mathjax@${V}/es5/input/tex/extensions/$e.js"doneXV=5.5.0;FV=0.10.0# xterm.js + the fit addon (the Phase-6 terminal)curl-sSfL-oxterm/xterm.js"https://unpkg.com/@xterm/xterm@${XV}/lib/xterm.js"curl-sSfL-oxterm/xterm.css"https://unpkg.com/@xterm/xterm@${XV}/css/xterm.css"curl-sSfL-oxterm/xterm-addon-fit.js"https://unpkg.com/@xterm/addon-fit@${FV}/lib/addon-fit.js"forfinxterm/xterm.jsxterm/xterm.cssxterm/xterm-addon-fit.js;doprintf'%-28s sha384-%s\n'"$f""$(openssldgst-sha384-binary"$f"|opensslbase64-A)"done# the presentation fonts (into docs/presentation/fonts/, from the repo root): the Latin# files the Google Fonts css2 API lists for these families, and the OFL textscddocs/presentation/fontsG=https://fonts.gstatic.com/scurl-sSfL-onewsreader.woff2"$G/newsreader/v26/cY9AfjOCX1hbuyalUrK4397yjA.woff2"curl-sSfL-onewsreader-italic.woff2"$G/newsreader/v26/cY9XfjOCX1hbuyalUrK439vogqCz_goCYw7oRd6JFYYzbA.woff2"curl-sSfL-oatkinson-hyperlegible-400.woff2"$G/atkinsonhyperlegible/v12/9Bt23C1KxNDXMspQ1lPyU89-1h6ONRlW45G04pIo.woff2"curl-sSfL-oatkinson-hyperlegible-700.woff2"$G/atkinsonhyperlegible/v12/9Bt73C1KxNDXMspQ1lPyU89-1h6ONRlW45G8Wbc9dCWP.woff2"curl-sSfL-ojetbrains-mono.woff2"$G/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2"forfinnewsreaderatkinsonhyperlegiblejetbrainsmono;docurl-sSfL"https://raw.githubusercontent.com/google/fonts/main/ofl/$f/OFL.txt"|sed's/[ \t]*$//'>"OFL-$f.txt"done```## Licenses### Presentation fonts — SIL Open Font License 1.1Newsreader (Copyright 2020 The Newsreader Project Authors), Atkinson Hyperlegible(Copyright 2020 Braille Institute of America, Inc.) and JetBrains Mono (Copyright 2020 TheJetBrains Mono Project Authors) are licensed under the SIL Open Font License, Version 1.1.The full text for each is in `docs/presentation/fonts/` (`OFL-*.txt`), and at<https://openfontlicense.org>.### htmx — Zero-Clause BSD (0BSD)```Permission to use, copy, modify, and/or distribute this software forany purpose with or without fee is hereby granted.THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALLWARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIESOF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLEFOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANYDAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER INAN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUTOF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.```