Policy: "ALL external references must be pinned by cryptographic hash...
Version tags are server-mutable and therefore remote code execution
vulnerabilities... There are zero exceptions to this rule."
The Dockerfile and the Gitea workflow comply. The developer-facing install
path does not:
script/bootstrap:141-143 installs golangci-lint from whatever the OS
package manager offers — nix, apt, brew, or apk — with no version
constraint at all. This is not merely unpinned, it is unversioned.
Makefile:36: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
— mutable tag.
Makefile:51 (devprereqs): go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2
— mutable tag, and a second, weaker source of truth competing with script/bootstrap.
script/bootstrap never installs gofumpt or prettier, both of which script/fmt requires. A developer who follows make setup cannot run make fmt.
This is not theoretical. On this machine script/bootstrap produced
golangci-lint v2.10.1 while the repo pins v2.12.2, and the two
disagree by 10 findings. That skew already caused a green PR to look red
locally, and it will keep costing review time until it is fixed.
Definition of done
script/bootstrap installs the exact pinned golangci-lint version the
repo targets, verified by hash, on every supported platform — not
"whatever the package manager has".
gofumpt and prettier are installed by script/bootstrap at pinned
versions, so make fmt works on a fresh clone after make setup.
Makefile:36 and the devprereqs target either install by content hash
or are deleted in favour of script/bootstrap being the single source of
truth. Prefer deletion — two install paths is the actual defect.
Every pinned version carries a # tool vX.Y.Z, YYYY-MM-DD comment, per
policy.
The pinned golangci-lint version appears in exactly one place that the Makefile, Dockerfile, and script/bootstrap all derive from, so they
cannot drift again.
Running script/bootstrap on a machine with a different golangci-lint
already on PATH results in the pinned version being used by script/lint — not the pre-existing one.
make check passes and docker build . succeeds. TODO.md updated in
the same commit.
Implementation requirements
Never curl | sh. Download a specific release archive, verify a hardcoded
sha256 with the existing verify_sha256 helper in script/bootstrap, then
install. That helper already exists and is used for nvm; follow it.
go install pkg@v1.2.3 is not hash pinning — the tag is mutable. If
you keep a go install path, it must resolve through a go.sum-verified
mechanism (a tools.go with the tool as a module dependency, built with
the repo's own go.sum), which is content-verified. Otherwise fetch a
release archive and verify its hash.
script/bootstrap must stay POSIX sh, set -eu, no bashisms, and stay
idempotent — re-running it must not reinstall or fail.
Per-platform hashes are required for a release-archive approach; detect
OS/arch and select the right one. Do not pin only the Linux amd64 hash and
silently fall through to an unverified install elsewhere.
If script/lint should assert the linter version matches the pin, add
that check and make the mismatch message tell the developer to run make bootstrap.
Commit title must end with (closes #68).
## Context
Policy: "ALL external references must be pinned by cryptographic hash...
Version tags are server-mutable and therefore remote code execution
vulnerabilities... There are zero exceptions to this rule."
The `Dockerfile` and the Gitea workflow comply. The developer-facing install
path does not:
- `script/bootstrap:141-143` installs `golangci-lint` from whatever the OS
package manager offers — nix, apt, brew, or apk — with **no version
constraint at all**. This is not merely unpinned, it is unversioned.
- `Makefile:36`: `go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1`
— mutable tag.
- `Makefile:51` (`devprereqs`): `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2`
— mutable tag, and a second, weaker source of truth competing with
`script/bootstrap`.
- `script/bootstrap` never installs `gofumpt` or `prettier`, both of which
`script/fmt` requires. A developer who follows `make setup` cannot run
`make fmt`.
This is not theoretical. On this machine `script/bootstrap` produced
golangci-lint **v2.10.1** while the repo pins **v2.12.2**, and the two
disagree by 10 findings. That skew already caused a green PR to look red
locally, and it will keep costing review time until it is fixed.
## Definition of done
- `script/bootstrap` installs the exact pinned `golangci-lint` version the
repo targets, verified by hash, on every supported platform — not
"whatever the package manager has".
- `gofumpt` and `prettier` are installed by `script/bootstrap` at pinned
versions, so `make fmt` works on a fresh clone after `make setup`.
- `Makefile:36` and the `devprereqs` target either install by content hash
or are deleted in favour of `script/bootstrap` being the single source of
truth. Prefer deletion — two install paths is the actual defect.
- Every pinned version carries a `# tool vX.Y.Z, YYYY-MM-DD` comment, per
policy.
- The pinned `golangci-lint` version appears in exactly one place that the
`Makefile`, `Dockerfile`, and `script/bootstrap` all derive from, so they
cannot drift again.
- Running `script/bootstrap` on a machine with a different `golangci-lint`
already on `PATH` results in the pinned version being used by
`script/lint` — not the pre-existing one.
- `make check` passes and `docker build .` succeeds. `TODO.md` updated in
the same commit.
## Implementation requirements
- Never `curl | sh`. Download a specific release archive, verify a hardcoded
sha256 with the existing `verify_sha256` helper in `script/bootstrap`, then
install. That helper already exists and is used for nvm; follow it.
- `go install pkg@v1.2.3` is **not** hash pinning — the tag is mutable. If
you keep a `go install` path, it must resolve through a `go.sum`-verified
mechanism (a `tools.go` with the tool as a module dependency, built with
the repo's own `go.sum`), which is content-verified. Otherwise fetch a
release archive and verify its hash.
- `script/bootstrap` must stay POSIX sh, `set -eu`, no bashisms, and stay
idempotent — re-running it must not reinstall or fail.
- Per-platform hashes are required for a release-archive approach; detect
OS/arch and select the right one. Do not pin only the Linux amd64 hash and
silently fall through to an unverified install elsewhere.
- If `script/lint` should assert the linter version matches the pin, add
that check and make the mismatch message tell the developer to run
`make bootstrap`.
- Commit title must end with ` (closes #68)`.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:40:18 +02:00
Scope additions folded in from the PR #88 review, so they are fixed
alongside the bootstrap work they belong with rather than as a separate
issue.
The prettier half of this issue is already done. PR #88 added package.json / yarn.lock pinning prettier 3.9.6 by sha512, and script/bootstrap now installs node, yarn, and the locked JS deps. That
pin is verified enforced, not merely asserted — corrupting the lockfile
hash makes docker build . fail at Integrity check failed for "prettier".
So what remains here is: gofumpt, golangci-lint, and the Makefile Go tool installs. Adjust the definition of done accordingly;
do not redo the prettier work.
Four additional items to fix as part of this issue:
Make the prettier version check a hard failure.script/prettier
currently prefers node_modules/.bin/prettier but falls back to a PATH prettier of unknown version with only a stderr warning. Different
prettier versions format differently, so this is the same version-skew
trap that already cost a review cycle when a local golangci-lint v2.10.1
disagreed with the pinned v2.12.2 by ten findings. Compare the resolved
binary's --version against the pin in package.json and fail hard on
mismatch, naming script/bootstrap in the message.
Apply the identical treatment to gofumpt and golangci-lint in script/fmt, script/fmt-check-go, and script/lint — those are
currently invoked with no version check at all, which is worse. That
consistency is the reason the prettier fallback was not treated as
blocking on #88.
Correct the --frozen-lockfile explanation. The comment in script/bootstrap (and the #88 commit message) states that --frozen-lockfile enforces the integrity hash. It does not — the flag
only guards lockfile-versus-manifest consistency; integrity verification
happens at fetch time regardless. The behaviour is right, the stated
reason is wrong, and a wrong reason in a comment about supply-chain
pinning will mislead the next person to touch it.
Remove the dead make fmt-check-md target, or wire it up. It is
currently unreachable.
Record the Dockerfile lint-stage deviation in TODO.md. The lint
stage runs make fmt-check-go rather than the canonical policy
Dockerfile's make fmt-check, because the golangci/golangci-lint
image has no node and the markdown check lives in its own mdfmt stage.
That is deliberate and correct, but it is a visible divergence from the
template — without a note, a future policy audit will "correct" it back
and silently drop the markdown gate.
Also cosmetic, fix while you are in there: package.json invents "version": "0.1.0" for a private manifest that has no version semantics,
and script/fmt-check prints its "Checking formatting" banner twice.
Scope additions folded in from the PR #88 review, so they are fixed
alongside the bootstrap work they belong with rather than as a separate
issue.
**The prettier half of this issue is already done.** PR #88 added
`package.json` / `yarn.lock` pinning prettier 3.9.6 by `sha512`, and
`script/bootstrap` now installs node, yarn, and the locked JS deps. That
pin is verified enforced, not merely asserted — corrupting the lockfile
hash makes `docker build .` fail at `Integrity check failed for "prettier"`.
So what remains here is: **gofumpt**, **golangci-lint**, and the
**Makefile Go tool installs**. Adjust the definition of done accordingly;
do not redo the prettier work.
Four additional items to fix as part of this issue:
1. **Make the prettier version check a hard failure.** `script/prettier`
currently prefers `node_modules/.bin/prettier` but falls back to a
`PATH` prettier of unknown version with only a stderr warning. Different
prettier versions format differently, so this is the same version-skew
trap that already cost a review cycle when a local golangci-lint v2.10.1
disagreed with the pinned v2.12.2 by ten findings. Compare the resolved
binary's `--version` against the pin in `package.json` and fail hard on
mismatch, naming `script/bootstrap` in the message.
Apply the identical treatment to `gofumpt` and `golangci-lint` in
`script/fmt`, `script/fmt-check-go`, and `script/lint` — those are
currently invoked with no version check at all, which is worse. That
consistency is the reason the prettier fallback was not treated as
blocking on #88.
2. **Correct the `--frozen-lockfile` explanation.** The comment in
`script/bootstrap` (and the #88 commit message) states that
`--frozen-lockfile` enforces the integrity hash. It does not — the flag
only guards lockfile-versus-manifest consistency; integrity verification
happens at fetch time regardless. The behaviour is right, the stated
reason is wrong, and a wrong reason in a comment about supply-chain
pinning will mislead the next person to touch it.
3. **Remove the dead `make fmt-check-md` target**, or wire it up. It is
currently unreachable.
4. **Record the Dockerfile lint-stage deviation in `TODO.md`.** The lint
stage runs `make fmt-check-go` rather than the canonical policy
Dockerfile's `make fmt-check`, because the `golangci/golangci-lint`
image has no node and the markdown check lives in its own `mdfmt` stage.
That is deliberate and correct, but it is a visible divergence from the
template — without a note, a future policy audit will "correct" it back
and silently drop the markdown gate.
Also cosmetic, fix while you are in there: `package.json` invents
`"version": "0.1.0"` for a private manifest that has no version semantics,
and `script/fmt-check` prints its "Checking formatting" banner twice.
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.
Context
Policy: "ALL external references must be pinned by cryptographic hash...
Version tags are server-mutable and therefore remote code execution
vulnerabilities... There are zero exceptions to this rule."
The
Dockerfileand the Gitea workflow comply. The developer-facing installpath does not:
script/bootstrap:141-143installsgolangci-lintfrom whatever the OSpackage manager offers — nix, apt, brew, or apk — with no version
constraint at all. This is not merely unpinned, it is unversioned.
Makefile:36:go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1— mutable tag.
Makefile:51(devprereqs):go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2— mutable tag, and a second, weaker source of truth competing with
script/bootstrap.script/bootstrapnever installsgofumptorprettier, both of whichscript/fmtrequires. A developer who followsmake setupcannot runmake fmt.This is not theoretical. On this machine
script/bootstrapproducedgolangci-lint v2.10.1 while the repo pins v2.12.2, and the two
disagree by 10 findings. That skew already caused a green PR to look red
locally, and it will keep costing review time until it is fixed.
Definition of done
script/bootstrapinstalls the exact pinnedgolangci-lintversion therepo targets, verified by hash, on every supported platform — not
"whatever the package manager has".
gofumptandprettierare installed byscript/bootstrapat pinnedversions, so
make fmtworks on a fresh clone aftermake setup.Makefile:36and thedevprereqstarget either install by content hashor are deleted in favour of
script/bootstrapbeing the single source oftruth. Prefer deletion — two install paths is the actual defect.
# tool vX.Y.Z, YYYY-MM-DDcomment, perpolicy.
golangci-lintversion appears in exactly one place that theMakefile,Dockerfile, andscript/bootstrapall derive from, so theycannot drift again.
script/bootstrapon a machine with a differentgolangci-lintalready on
PATHresults in the pinned version being used byscript/lint— not the pre-existing one.make checkpasses anddocker build .succeeds.TODO.mdupdated inthe same commit.
Implementation requirements
curl | sh. Download a specific release archive, verify a hardcodedsha256 with the existing
verify_sha256helper inscript/bootstrap, theninstall. That helper already exists and is used for nvm; follow it.
go install pkg@v1.2.3is not hash pinning — the tag is mutable. Ifyou keep a
go installpath, it must resolve through ago.sum-verifiedmechanism (a
tools.gowith the tool as a module dependency, built withthe repo's own
go.sum), which is content-verified. Otherwise fetch arelease archive and verify its hash.
script/bootstrapmust stay POSIX sh,set -eu, no bashisms, and stayidempotent — re-running it must not reinstall or fail.
OS/arch and select the right one. Do not pin only the Linux amd64 hash and
silently fall through to an unverified install elsewhere.
script/lintshould assert the linter version matches the pin, addthat check and make the mismatch message tell the developer to run
make bootstrap.(closes #68).Scope additions folded in from the PR #88 review, so they are fixed
alongside the bootstrap work they belong with rather than as a separate
issue.
The prettier half of this issue is already done. PR #88 added
package.json/yarn.lockpinning prettier 3.9.6 bysha512, andscript/bootstrapnow installs node, yarn, and the locked JS deps. Thatpin is verified enforced, not merely asserted — corrupting the lockfile
hash makes
docker build .fail atIntegrity check failed for "prettier".So what remains here is: gofumpt, golangci-lint, and the
Makefile Go tool installs. Adjust the definition of done accordingly;
do not redo the prettier work.
Four additional items to fix as part of this issue:
Make the prettier version check a hard failure.
script/prettiercurrently prefers
node_modules/.bin/prettierbut falls back to aPATHprettier of unknown version with only a stderr warning. Differentprettier versions format differently, so this is the same version-skew
trap that already cost a review cycle when a local golangci-lint v2.10.1
disagreed with the pinned v2.12.2 by ten findings. Compare the resolved
binary's
--versionagainst the pin inpackage.jsonand fail hard onmismatch, naming
script/bootstrapin the message.Apply the identical treatment to
gofumptandgolangci-lintinscript/fmt,script/fmt-check-go, andscript/lint— those arecurrently invoked with no version check at all, which is worse. That
consistency is the reason the prettier fallback was not treated as
blocking on #88.
Correct the
--frozen-lockfileexplanation. The comment inscript/bootstrap(and the #88 commit message) states that--frozen-lockfileenforces the integrity hash. It does not — the flagonly guards lockfile-versus-manifest consistency; integrity verification
happens at fetch time regardless. The behaviour is right, the stated
reason is wrong, and a wrong reason in a comment about supply-chain
pinning will mislead the next person to touch it.
Remove the dead
make fmt-check-mdtarget, or wire it up. It iscurrently unreachable.
Record the Dockerfile lint-stage deviation in
TODO.md. The lintstage runs
make fmt-check-gorather than the canonical policyDockerfile's
make fmt-check, because thegolangci/golangci-lintimage has no node and the markdown check lives in its own
mdfmtstage.That is deliberate and correct, but it is a visible divergence from the
template — without a note, a future policy audit will "correct" it back
and silently drop the markdown gate.
Also cosmetic, fix while you are in there:
package.jsoninvents"version": "0.1.0"for a private manifest that has no version semantics,and
script/fmt-checkprints its "Checking formatting" banner twice.