Follow-up to #145, which was fixed by taking option 1 (fetch Alpine at build time under a verified sha256) rather than option 3 (drop it). Not a defect: the build-time fetch fully satisfies the hash-pinning policy. This is the optional further step, deliberately deferred out of the 1.0 cycle.
The argument for it: Alpine is 44758 bytes of third-party code in the admin's browser, and the UI's actual use of it is small — 19 directive uses across templates/navbar.html, templates/source_detail.html and templates/source_logs.html. Writing those directly removes the dependency and its supply-chain surface outright, rather than pinning it.
The argument against doing it in 1.0, and the reason it was deferred: it means hand-rewriting working interactive UI in three pages at the end of a release cycle, trading a solved supply-chain problem for an untested regression risk.
If taken up:
Rewrite the 19 directive uses in plain JS in static/js/app.js.
Remove the <script defer src="/s/js/alpine.min.js"> loader from templates/base.html.
Drop the Alpine entry from static/vendor.sha256 and the Alpine block from script/fetch-assets. Both are built to survive that: static/vendor_test.go iterates the manifest, and .gitignore/.dockerignore entries for the artifact would go with it.
Cover each rewritten interaction with a test, since the point of deferring was regression risk.
Follow-up to https://git.eeqj.de/sneak/webhooker/issues/145, which was fixed by taking option 1 (fetch Alpine at build time under a verified sha256) rather than option 3 (drop it). Not a defect: the build-time fetch fully satisfies the hash-pinning policy. This is the optional further step, deliberately deferred out of the 1.0 cycle.
The argument for it: Alpine is 44758 bytes of third-party code in the admin's browser, and the UI's actual use of it is small — 19 directive uses across `templates/navbar.html`, `templates/source_detail.html` and `templates/source_logs.html`. Writing those directly removes the dependency and its supply-chain surface outright, rather than pinning it.
The argument against doing it in 1.0, and the reason it was deferred: it means hand-rewriting working interactive UI in three pages at the end of a release cycle, trading a solved supply-chain problem for an untested regression risk.
If taken up:
- Rewrite the 19 directive uses in plain JS in `static/js/app.js`.
- Remove the `<script defer src="/s/js/alpine.min.js">` loader from `templates/base.html`.
- Drop the Alpine entry from `static/vendor.sha256` and the Alpine block from `script/fetch-assets`. Both are built to survive that: `static/vendor_test.go` iterates the manifest, and `.gitignore`/`.dockerignore` entries for the artifact would go with it.
- Cover each rewritten interaction with a test, since the point of deferring was regression risk.
drop the hash and just fetch the file and vendor it into our repo. there was never any reason to go to all this trouble about verifying the hash and repeatedly fetching - it's small, just fetch it once and commit it.
drop the hash and just fetch the file and vendor it into our repo. there was never any reason to go to all this trouble about verifying the hash and repeatedly fetching - it's small, just fetch it once and commit it.
Recorded: Alpine.js stays, as a file committed to the repo. Fetch it once at
the version pinned today and commit it; drop the sha256 check and the
build-time fetch. Retitled to match.
Definition of done
The Alpine.js file the pages load is committed in the repo, at the version script/fetch-assets pins today.
The build fetches nothing for Alpine: its block in script/fetch-assets, its
entry in static/vendor.sha256 and the .gitignore and .dockerignore
entries for it are gone. Whatever then has nothing left to do (the script, the
manifest, static/vendor_test.go) is removed too. Any other file the script
fetches stays as it is, and the PR body names it.
Every document that describes the fetch or the hash says what is true now.
script/cibuild green on a fresh clone; one PR into next ending its title
with (closes#163), passing an independent review before it is squash-merged.
Model: opus-5-5
Recorded: Alpine.js stays, as a file committed to the repo. Fetch it once at
the version pinned today and commit it; drop the sha256 check and the
build-time fetch. Retitled to match.
**Definition of done**
- The Alpine.js file the pages load is committed in the repo, at the version
`script/fetch-assets` pins today.
- The build fetches nothing for Alpine: its block in `script/fetch-assets`, its
entry in `static/vendor.sha256` and the `.gitignore` and `.dockerignore`
entries for it are gone. Whatever then has nothing left to do (the script, the
manifest, `static/vendor_test.go`) is removed too. Any other file the script
fetches stays as it is, and the PR body names it.
- Every document that describes the fetch or the hash says what is true now.
- `script/cibuild` green on a fresh clone; one PR into `next` ending its title
with (closes #163), passing an independent review before it is squash-merged.
Model: opus-5-5
clawbot
self-assigned this 2026-10-25 03:07:20 +01:00
clawbot
changed title from Consider dropping Alpine.js and writing its 19 directive uses directly to Commit Alpine.js to the repo instead of fetching and hash-checking it at build time2026-10-25 03:07:28 +01:00
Plan. Since this issue was written, #345 removed the download and the hash check. The npm package file 3p/alpinejs-csp-3.14.9.tgz is committed, and script/assets unpacks static/js/alpine.min.js from it on every build. What is left is to commit the file the pages load:
Commit static/js/alpine.min.js, byte for byte the package/dist/cdn.min.js in that package (@alpinejs/csp 3.14.9, the build the pages load today). Keep it out of prettier and ESLint, so it stays exactly as published.
Remove what then has nothing to do: script/assets, the assets make target and its uses in make build, make dev, Dockerfile and Dockerfile.browser, the .gitignore and .dockerignore entries, and the package file in 3p/.
Make every comment and document that describes unpacking the file or 3p/ say what is true: README.md, static/static.go, eslint.config.mjs, internal/server/static_assets_test.go, the Dockerfile comments.
Judgement call: the package file in 3p/ goes too. The ruling here is to commit the file itself, and nothing would read the package afterwards. The package ships no licence file (its package.json says MIT), so nothing is lost with it.
Model: opus-5-5
Plan. Since this issue was written, https://git.eeqj.de/sneak/webhooker/issues/345 removed the download and the hash check. The npm package file `3p/alpinejs-csp-3.14.9.tgz` is committed, and `script/assets` unpacks `static/js/alpine.min.js` from it on every build. What is left is to commit the file the pages load:
- Commit `static/js/alpine.min.js`, byte for byte the `package/dist/cdn.min.js` in that package (`@alpinejs/csp` 3.14.9, the build the pages load today). Keep it out of prettier and ESLint, so it stays exactly as published.
- Remove what then has nothing to do: `script/assets`, the `assets` make target and its uses in `make build`, `make dev`, `Dockerfile` and `Dockerfile.browser`, the `.gitignore` and `.dockerignore` entries, and the package file in `3p/`.
- Make every comment and document that describes unpacking the file or `3p/` say what is true: `README.md`, `static/static.go`, `eslint.config.mjs`, `internal/server/static_assets_test.go`, the Dockerfile comments.
Judgement call: the package file in `3p/` goes too. The ruling here is to commit the file itself, and nothing would read the package afterwards. The package ships no licence file (its `package.json` says MIT), so nothing is lost with it.
Model: opus-5-5
Done in #564. static/js/alpine.min.js is committed, byte for byte the package/dist/cdn.min.js of @alpinejs/csp 3.14.9 from the package file that was in 3p/. Nothing in the build unpacks it any more: script/assets, the assets make target, its Dockerfile steps, the ignore entries for the file and the package file are removed.
Model: opus-5-5
Done in https://git.eeqj.de/sneak/webhooker/pulls/564. `static/js/alpine.min.js` is committed, byte for byte the `package/dist/cdn.min.js` of `@alpinejs/csp` 3.14.9 from the package file that was in `3p/`. Nothing in the build unpacks it any more: `script/assets`, the `assets` make target, its Dockerfile steps, the ignore entries for the file and the package file are removed.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #145, which was fixed by taking option 1 (fetch Alpine at build time under a verified sha256) rather than option 3 (drop it). Not a defect: the build-time fetch fully satisfies the hash-pinning policy. This is the optional further step, deliberately deferred out of the 1.0 cycle.
The argument for it: Alpine is 44758 bytes of third-party code in the admin's browser, and the UI's actual use of it is small — 19 directive uses across
templates/navbar.html,templates/source_detail.htmlandtemplates/source_logs.html. Writing those directly removes the dependency and its supply-chain surface outright, rather than pinning it.The argument against doing it in 1.0, and the reason it was deferred: it means hand-rewriting working interactive UI in three pages at the end of a release cycle, trading a solved supply-chain problem for an untested regression risk.
If taken up:
static/js/app.js.<script defer src="/s/js/alpine.min.js">loader fromtemplates/base.html.static/vendor.sha256and the Alpine block fromscript/fetch-assets. Both are built to survive that:static/vendor_test.goiterates the manifest, and.gitignore/.dockerignoreentries for the artifact would go with it.drop the hash and just fetch the file and vendor it into our repo. there was never any reason to go to all this trouble about verifying the hash and repeatedly fetching - it's small, just fetch it once and commit it.
Recorded: Alpine.js stays, as a file committed to the repo. Fetch it once at
the version pinned today and commit it; drop the sha256 check and the
build-time fetch. Retitled to match.
Definition of done
script/fetch-assetspins today.script/fetch-assets, itsentry in
static/vendor.sha256and the.gitignoreand.dockerignoreentries for it are gone. Whatever then has nothing left to do (the script, the
manifest,
static/vendor_test.go) is removed too. Any other file the scriptfetches stays as it is, and the PR body names it.
script/cibuildgreen on a fresh clone; one PR intonextending its titlewith (closes #163), passing an independent review before it is squash-merged.
Model: opus-5-5
Consider dropping Alpine.js and writing its 19 directive uses directlyto Commit Alpine.js to the repo instead of fetching and hash-checking it at build timePlan. Since this issue was written, #345 removed the download and the hash check. The npm package file
3p/alpinejs-csp-3.14.9.tgzis committed, andscript/assetsunpacksstatic/js/alpine.min.jsfrom it on every build. What is left is to commit the file the pages load:static/js/alpine.min.js, byte for byte thepackage/dist/cdn.min.jsin that package (@alpinejs/csp3.14.9, the build the pages load today). Keep it out of prettier and ESLint, so it stays exactly as published.script/assets, theassetsmake target and its uses inmake build,make dev,DockerfileandDockerfile.browser, the.gitignoreand.dockerignoreentries, and the package file in3p/.3p/say what is true:README.md,static/static.go,eslint.config.mjs,internal/server/static_assets_test.go, the Dockerfile comments.Judgement call: the package file in
3p/goes too. The ruling here is to commit the file itself, and nothing would read the package afterwards. The package ships no licence file (itspackage.jsonsays MIT), so nothing is lost with it.Model: opus-5-5
Done in #564.
static/js/alpine.min.jsis committed, byte for byte thepackage/dist/cdn.min.jsof@alpinejs/csp3.14.9 from the package file that was in3p/. Nothing in the build unpacks it any more:script/assets, theassetsmake target, its Dockerfile steps, the ignore entries for the file and the package file are removed.Model: opus-5-5