Vendored front-end assets
The web front end serves all third-party JavaScript locally so it runs fully
offline (no CDN calls) and under a strict script-src 'self' CSP. Each file is
pinned 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, and
update the <script integrity="…"> in the templates that load it.
Inventory
| File | Package | Version | License | SRI (sha384) |
|---|---|---|---|---|
htmx.min.js |
htmx | 2.0.4 | 0BSD | HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+ |
mathjax/tex-mml-chtml.js (+ mathjax/output/…/woff-v2/*.woff, mathjax/input/tex/extensions/*.js) |
MathJax | 3.2.2 | Apache-2.0 | Wuix6BuhrWbjDBs24bXrjf4ZQ5aFeFWBuKkFekO2t8xFU0iNaLQfp2K6/1Nxveei (bundle; the autoloaded extensions are 'self', no SRI) |
codemirror/*.js (entry codemirror/cm6.js) |
CodeMirror 6 (+ lang-python/cpp/json, theme-one-dark, @lezer/*) | 6.0.2 | MIT | ES modules — dynamic-imported, 'self' (no SRI) |
xterm/xterm.js |
xterm.js (@xterm/xterm) |
5.5.0 | MIT | M169f14mRZOXm3hD/v2Ti0ThIT/RnAQagXA9nlE15yHAtrW19gdePJh/HaTzUOe/ |
xterm/xterm-addon-fit.js |
@xterm/addon-fit |
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 (Argon2id build) | 4.12.0 | MIT | tP0Wy54CKmng7i9EoTlPySD0hBx6Octj0VS6MfwlnUu111MPa+JLm0CCbep6XJ1W |
docs/presentation/fonts/newsreader.woff2, newsreader-italic.woff2 |
Newsreader (Production Type) | Google Fonts v26, Latin | OFL-1.1 | font, 'self' (no SRI) |
docs/presentation/fonts/atkinson-hyperlegible-{400,700}.woff2 |
Atkinson Hyperlegible (Braille Institute) | Google Fonts v12, Latin | OFL-1.1 | font, 'self' (no SRI) |
docs/presentation/fonts/jetbrains-mono.woff2 |
JetBrains Mono | 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; the
service checks against argon2-cffi, tests/test_srp_interop.py pins the two together). It
is 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-swap
in /assets/site.js); the woff fonts are loaded by the bundle relative to its
own 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 same
reason as the fonts: the combined bundle carries only the default packages, and the rest
are fetched relative to the bundle's own URL, so
/vendor/mathjax/input/tex/extensions/<name>.js. Vendored, that request is
same-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 a
statement 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 the
middle of a typeset, and the swap path lost that race: a statement reached by the
terminal's show rendered \color[RGB]124, 192, 255 as literal text while a plain
refresh of the same URL was perfect. Preloading costs one fetch per document, of files
already vendored, and makes the typesetter's vocabulary independent of which page the
reader happened to open first. A package added here must be added to MJ_TEX in
site.js as well — preloading a name whose file is missing fails MathJax startup, on
every 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 for each 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 the scan. There is no SRI — these are same-origin scripts MathJax injects itself, exactly like 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 graph
never loads elsewhere). It lives under codemirror/ as a flat set of ES modules
mirrored 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 no
build 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 range
resolves to the legacy CodeMirror 5 UMD, which has no EditorView/basicSetup.
All @codemirror/state stubs re-export one state build (CM's single-instance
rule). 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; the
resulting cols×rows is what terminal.js sends over the socket as the PTY's
geometry) and the stock stylesheet. Classic scripts with SRI — unlike CodeMirror
they are not ES modules, so they are <script src>-loaded by terminal.html and
integrity-checked. Both files carry a //# sourceMappingURL= line whose .map is
not vendored: harmless (only devtools ever requests it), and stripping it
would 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 the
deck 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). They
are bundled for the same reason as everything here — default-src 'self' refuses a Google
Fonts stylesheet, and a bundled font also works offline. Each is the Latin subset as Google
Fonts 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 is
no 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):
V=2.0.4
curl -sSfL -o htmx.min.js "https://unpkg.com/htmx.org@${V}/dist/htmx.min.js"
printf 'sha384-%s\n' "$(openssl dgst -sha384 -binary htmx.min.js | openssl base64 -A)"
V=3.2.2 # MathJax: the single-file combined component + its woff-v2 fonts
curl -sSfL -o mathjax/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 -p mathjax/input/tex/extensions
for e in color mhchem html boldsymbol enclose unicode; do
curl -sSfL -o "mathjax/input/tex/extensions/$e.js" \
"https://cdn.jsdelivr.net/npm/mathjax@${V}/es5/input/tex/extensions/$e.js"
done
XV=5.5.0; FV=0.10.0 # xterm.js + the fit addon (the Phase-6 terminal)
curl -sSfL -o xterm/xterm.js "https://unpkg.com/@xterm/xterm@${XV}/lib/xterm.js"
curl -sSfL -o xterm/xterm.css "https://unpkg.com/@xterm/xterm@${XV}/css/xterm.css"
curl -sSfL -o xterm/xterm-addon-fit.js "https://unpkg.com/@xterm/addon-fit@${FV}/lib/addon-fit.js"
for f in xterm/xterm.js xterm/xterm.css xterm/xterm-addon-fit.js; do
printf '%-28s sha384-%s\n' "$f" "$(openssl dgst -sha384 -binary "$f" | openssl base64 -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 texts
cd docs/presentation/fonts
G=https://fonts.gstatic.com/s
curl -sSfL -o newsreader.woff2 "$G/newsreader/v26/cY9AfjOCX1hbuyalUrK4397yjA.woff2"
curl -sSfL -o newsreader-italic.woff2 "$G/newsreader/v26/cY9XfjOCX1hbuyalUrK439vogqCz_goCYw7oRd6JFYYzbA.woff2"
curl -sSfL -o atkinson-hyperlegible-400.woff2 "$G/atkinsonhyperlegible/v12/9Bt23C1KxNDXMspQ1lPyU89-1h6ONRlW45G04pIo.woff2"
curl -sSfL -o atkinson-hyperlegible-700.woff2 "$G/atkinsonhyperlegible/v12/9Bt73C1KxNDXMspQ1lPyU89-1h6ONRlW45G8Wbc9dCWP.woff2"
curl -sSfL -o jetbrains-mono.woff2 "$G/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2"
for f in newsreader atkinsonhyperlegible jetbrainsmono; do
curl -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.1
Newsreader (Copyright 2020 The Newsreader Project Authors), Atkinson Hyperlegible
(Copyright 2020 Braille Institute of America, Inc.) and JetBrains Mono (Copyright 2020 The
JetBrains 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 for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.