Bootstrap 5.1.3, Font Awesome 6.0.0, Socket.IO 4.7.2 and Chart.js 4.4.0 are now served from static/vendor/ at the versions the CDNs served, so every page works on a LAN with no internet. Only map tiles still need it. The Content-Security-Policy drops jsDelivr and cdnjs. Every vendored package, the map stack and login fonts included, is pinned in scripts/web-vendor/package.json and its lockfile. sync.mjs copies the listed files into static/, strips the source-map comment, empties each directory it owns first, and regenerates the version table in static/vendor/README.md; it reproduces the previously vendored files byte for byte. Font Awesome ships only its woff2 fonts, which every supported browser takes before the .ttf. Dependabot watches that directory: security fixes as advisories land, grouped minor and patch releases monthly, majors one per PR. Dependabot can't regenerate the vendored files, so vendor-refresh.yml adds them to its PRs as a second commit and dispatches the tests, since a GITHUB_TOKEN push doesn't trigger them. It runs on pull_request_target, so it uses the base branch's sync.mjs, spells out its commands instead of running the PR's npm scripts, and installs with install scripts disabled. A new CI job re-runs the sync and fails if anything under static/ changes. Tests now check that no template loads a script, stylesheet or font from another host, that every vendored file a template names exists, and that the CSP allows no CDN host. In Chrome, 12 pages load with no external request, failed load or console error.
Vendored front-end libraries
Served from here instead of a CDN so the web viewer works on a LAN with no internet access. Each file is the unmodified upstream build from npm, except that the trailing sourceMappingURL comment is removed because the source maps aren't vendored.
The versions are pinned in scripts/web-vendor/package.json and its lockfile, and scripts/web-vendor/sync.mjs lists which files each package contributes. Don't edit these files by hand: CI re-runs the sync and fails if anything here differs from it.
| Package | Version | License | Served from static/ |
|---|---|---|---|
bootstrap |
5.1.3 | MIT | vendor/bootstrap/ |
@fortawesome/fontawesome-free |
6.0.0 | (CC-BY-4.0 AND OFL-1.1 AND MIT) | vendor/fontawesome/ |
socket.io-client |
4.7.2 | MIT | vendor/socket.io/ |
chart.js |
4.4.0 | MIT | vendor/chart.js/ |
leaflet |
1.9.4 | BSD-2-Clause | vendor/leaflet/ |
maplibre-gl |
5.6.0 | BSD-3-Clause | vendor/maplibre-gl/ |
@maplibre/maplibre-gl-leaflet |
0.1.4 | ISC | vendor/maplibre-gl-leaflet/ |
vis-network |
9.1.9 | (Apache-2.0 OR MIT) | vendor/vis-network/ |
js-sha256 |
0.11.0 | MIT | vendor/js-sha256/ |
@fontsource-variable/dm-sans |
5.3.0 | OFL-1.1 | fonts/ |
@fontsource-variable/outfit |
5.3.0 | OFL-1.1 | fonts/ |
Upgrading
Dependabot opens a pull request when a package has a security advisory, and a grouped one each month for other releases. A workflow on its pull requests re-runs the sync and pushes the updated files, so the diff shows exactly what changed. Check the pages that use the library before merging: Bootstrap and Font Awesome are on every page, Chart.js on the dashboard, Contacts and Multibyte, Socket.IO on every live page, the map and graph libraries on Mesh, and js-sha256 on Real-time.
To upgrade by hand, change the version in scripts/web-vendor/package.json, then run npm install --package-lock-only --ignore-scripts && npm run vendor in that directory. If a package moves or renames a file, update its entry in sync.mjs.
The packet decoder
../js/meshcore-browser.mjs is a browser bundle of @michaelhart/meshcore-decoder 0.4.0 (MIT) with its crypto-js and @noble/ed25519 dependencies, built by scripts/meshcore-decoder-browser/build.sh from commit 60733c0 of the agessaman/meshcore-decoder fork (0.4.0 isn't on npm) plus the browser entry in that directory. Dependabot doesn't track it. To upgrade, run the script with the new commit, update the pinned default in it, and check the Real-time byte breakdown on a multibyte packet.