chore(base): Go 1.24.1 → 1.26 (build-time patch resolve), Node 22 → 24 - #131
Merged
Conversation
Go 1.24 left Go's two-release support window, so both pins were on an EOL toolchain receiving no security fixes: - Base image pinned ARG GO_VERSION=1.24.1 -- a Mar-2025 patch, untouched for 16 months across 20+ releases. The tarball URL needs a full x.y.z, so the base now pins the MINOR line and resolves the newest patch on it at build time from go.dev/dl/?mode=json, with the ARG as offline fallback (the same build-time-latest semantics NodeSource/rustup/Bun/uv already use here). Hardened: sort -rV picks the max patch explicitly rather than inheriting go.dev's array order, and a non-numeric resolved value falls back to the pin instead of 404-ing the build. Verified under dash: both the happy path and total network failure resolve 1.26.5 with chain exit 0. - Egress proxy built FROM golang:1.24-bookworm. That floating minor tag did deliver 12 patches (through 1.24.13, pushed 2026-02-04), but once 1.24 left support the documented monthly-freshness + --pull mechanism (HF-incident Issue 3) re-resolved to an unchanged digest -- refreshing only Debian, not the Go stdlib that implements the proxy's TLS/HTTP/CONNECT I/O. Moving to golang:1.26-trixie restores actual stdlib patch delivery. CLAUDE.md's description is corrected to match this timeline. - proxy/go.mod said go 1.24.1 and CI consumes it via go-version-file, so the proxy's vet/race/fuzz/govulncheck jobs were running on an EOL toolchain. - Node 22 is Maintenance LTS (EOL Apr 2027); 24 is Active LTS (Apr 2028). NodeSource node_24.x ships the distro-agnostic nodistro suite (amd64 + arm64), so this is independent of the pending trixie base migration. New run-tests.sh guards cover the base Go block, which had no test coverage: the pin is asserted to be a supported minor (rejects a regression to 1.24), plus build-time resolution, offline fallback, max-patch selection, and the non-numeric guard. Docs synced: README, CLAUDE.md, SPECIFICATION.md (Appendix A verbatim copy + Appendix B version table). Debian bookworm -> trixie and Python 3.11 -> 3.13 are deliberately NOT here; they follow as their own change. The proxy build stage moves to trixie ahead of the base image intentionally -- it compiles a static binary into FROM scratch, so its distro never reaches the runtime image. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rappdw
added a commit
that referenced
this pull request
Aug 11, 2026
…133) Debian 12 bookworm's regular security support ended 2026-07-12 and it is now on community LTS only. Trixie (Debian 13) is current stable. Follows #131 (Go 1.26 + Node 24); together these refresh every pinned toolchain. Two apt packages were RENAMED in trixie and would have failed the build: - libpango1.0-0 -> libpango-1.0-0 (base image apt list) - libgdk-pixbuf2.0-dev -> libgdk-pixbuf-2.0-dev (all six agent generators) Note libpango1.0-dev and libcairo2-dev keep their old-style names in trixie and are deliberately unchanged. All 26 apt-installed packages across the base image and the six agent generators were verified present in trixie via Debian's madison API, not spot-checked. --pull is added to the BASE image build so debian:trixie-slim re-resolves to the current digest on rebuild -- the same HF-incident Issue 3 rationale the proxy build already uses. Deliberately NOT added to the agent, skills, or per-project builds: those build FROM a LOCAL sandy-base image, and --pull would attempt a registry fetch and fail. Python 3.11 -> 3.13 has one user-visible consequence: PYTHONUSERBASE stores packages under lib/python3.<minor>, so persistent 'pip install --user' trees from the 3.11 image are invisible to 3.13. user-setup.sh now detects a mismatched tree and prints a warn-only notice with the path and the cleanup command -- matching the existing environment-drift warnings (broken .venv symlink, foreign native modules), which likewise warn rather than auto-fix, since silently deleting a user's installed packages is the wrong default. SANDY_SANDBOX_MIN_COMPAT is deliberately NOT moved: the sandbox still works, so this is not a compat-floor event (the 1.x forward-compat promise caps it at 1.0.0 anyway, guarded by run-tests.sh §60). Existing sandboxes need no recreation. The .venv overlay is unaffected -- it uses uv-managed interpreters, independent of the container's system python3. PEP 668 is already neutralized via PIP_BREAK_SYSTEM_PACKAGES=1. Docs synced: README, CLAUDE.md (incl. a new Automatic Environment Detection bullet), SPECIFICATION.md (Appendix A verbatim copy + Appendix B table), and a stale 'Debian bookworm' reference in docs/security/nono-roadmap.md. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
rappdw
added a commit
that referenced
this pull request
Aug 12, 2026
…136) Base-image modernization plus two additive features and a LAN-allowlist fix. Every pinned toolchain was on an unsupported or maintenance-only release: - Debian 12 bookworm left regular security support 2026-07-12 -> trixie (13), which brings system Python 3.11 -> 3.13. (#133) - Go 1.24 left its two-release support window entirely, and the pin was a Mar-2025 patch untouched for 16 months -> 1.26, with the newest patch now resolved at build time instead of frozen. The egress proxy's documented monthly --pull refresh had been silently no-op since ~Feb 2026 (no further 1.24.x pushes existed), so it was refreshing Debian but not the Go stdlib implementing its TLS/HTTP/CONNECT I/O; pinning a supported minor restores it. Node 22 (Maintenance LTS) -> 24 (Active LTS). (#131) - SANDY_EFFORT pins Claude Code reasoning effort and records it in the session marker, so a run's effort is provable rather than inferred. (#115) - SANDY_SESSION_NONCE lets an operator pin the attestation nonce so a harness can prove a run is the one it launched; env-only, so a committed workspace config cannot set it. (#118) - SANDY_ALLOW_LAN_HOSTS no longer reports success when iptables rejected the rule — a silently missing hole is the worst direction to be wrong in. (#119) Additive minor per the CLAUDE.md semver rule: new keys, no retiering or renames, introspection schema_version stays 1. SANDY_SANDBOX_MIN_COMPAT stays 0.7.10 — the Python bump moves where pip --user packages live, but the sandbox still works, so this is not a compat-floor event and no sandbox needs recreating. Four user-visible upgrade consequences are documented at the top of RELEASE_NOTES.md: the first launch rebuilds every image; persistent pip --user packages become invisible to 3.13 (sandy now detects and reports the stale tree); native Node addons may need npm rebuild; and binaries built inside sandy now link glibc 2.41. Also lands the test-suite work: three macOS-only failures CI structurally cannot see (#134) — including §68 executing the real sandy binary via a backtick inside a double-quoted python3 -c string — and per-section timing, section selection, a fast-model pin, and an image warm-up preflight for the integration suite (#135). Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both Go pins were on an EOL toolchain: Go supports only the two newest majors (1.25, 1.26), so 1.24 receives no security fixes. Node 22 is Maintenance LTS. This is PR-1 of a two-part toolchain refresh.
What changed
ARG GO_VERSION=1.24.11.26, resolves newest patch at build timeFROM golang:1.24-bookwormFROM golang:1.26-trixieproxy/go.modgo 1.24.1go 1.26.0setup_22.xsetup_24.xWhy the base image now resolves at build time
The tarball URL (
go.dev/dl/go<x.y.z>.linux-<arch>.tar.gz) requires a fullx.y.z, so the base cannot float via the URL alone — but it can resolve one at build time.ARG GO_VERSIONnow pins the minor line and doubles as the offline fallback.This is a response to measured behavior, not theory:
ARG GO_VERSION=1.24.1was introduced 2026-02-21 and never touched again — through 20+ releases (v1.0.0 → v1.5.0) at roughly weekly cadence, while Go shipped 1.24.2–1.24.13 and then two entire minors. A frozen pin's failure mode is human diligence, and the observed diligence was zero. Every other toolchain in this image (NodeSource,rustup … stable, Bun, uv) already resolves at build time; Go was the odd one out only because of the URL shape.Hardened beyond the naive version:
sort -rV | head -1selects the max patch explicitly rather than inheriting go.dev's array ordering (which is newest-first only incidentally), and a non-numeric resolved value falls back to the pin instead of 404-ing the build.Verified under real
dash(DockerRUNuses/bin/sh, nopipefail) — both paths:The proxy fix restores a mechanism that had gone quiet
golang:1.24-bookwormis a floating minor tag, so it genuinely delivered 12 patch releases (through 1.24.13, pushed 2026-02-04). But once 1.24 left the support window there were no further pushes, so from ~Feb 2026 the documented monthly-freshness +--pullmechanism (HF-incident Issue 3) re-resolved to an unchanged digest — refreshing Debian but not the Go stdlib that implements the proxy's TLS/HTTP/CONNECT I/O. Pinning a supported minor is what makes that mechanism work at all. CLAUDE.md's description is corrected to match this timeline.Separately,
proxy/go.modis consumed by CI viago-version-file, so the proxy's vet/race/fuzz/govulncheck jobs had been running on an EOL toolchain too.New test coverage
The base Go block had no guard at all. Added five, including a pin-must-be-a-supported-minor assertion that would have caught this exact rot (negative control: rejects
1.24.1, accepts1.30.0), plus build-time-resolution, offline-fallback, max-patch, and non-numeric guards.Scope
Debian bookworm → trixie and Python 3.11 → 3.13 are deliberately not here (they carry apt renames and a persistent-pip consequence) and follow as their own change. The proxy build stage moves to trixie ahead of the base image on purpose: it compiles a static binary into
FROM scratch, so its build distro never reaches the runtime image.Verification
Static (no Docker available in the authoring environment):
bash -n, both doc-drift checks, §49 replayed standalone incl. the no-stderr heredoc regression check, generated Dockerfiles inspected for expansion artifacts, Appendix A confirmed byte-identical to the heredoc in the changed region. Externally verified:golang:1.26-trixieexists for amd64+arm64 and includesgit(viabuildpack-deps:trixie-scm); NodeSourcenode_24.xserves both arches; setup-go's index contains 1.26.0.go vet/build/testpass locally under an auto-downloaded 1.26.0 toolchain.Not verifiable without a build — the main residual risk: that all four npm-installed agents behave under Node 24. CI's cache-miss
--build-onlyplus a smoke launch settle it.🤖 Generated with Claude Code