Run #4256 finally cleared every preceding obstacle and reached
git_sanity_test's per-submodule verification phase. sq-git authenticated
every commit signature in the chain — that part is working perfectly —
but failed at:
ERROR: Untagged commit in: qubes/qubes-template-kicksecure
INFO: As a developer or advanced user you might want to use:
WARNING: This can be insecure if you cannot audit the changes.
--allow-untagged true --allow-uncommitted true
git_sanity_test runs two orthogonal checks:
1. signatures (sq-git, verified ✅)
2. tagged-commit-only mode (verified ❌ for one submodule)
The pinned upstream tag (18.1.7.4-developers-only — the name itself
flags the intent) deliberately ships with some submodule pointers at
intermediate / merge commits rather than release tags. parse-cmd
documents `--allow-untagged true` and `--allow-uncommitted true` for
exactly this case. Signatures remain verified; we're only relaxing the
release-tag check, which is appropriate when we've deliberately pinned
to a developer tag.
If/when we move to a redistributable upstream tag in M1.10+ (signing
ceremony milestone), these flags should come back out.
No image rebuild needed — script-only change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run #4255 reached deeper into 1100_sanity-tests, finished its apt-get
phase, and then died at the supply-chain verification step:
/workspace/.../help-steps/git_sanity_test: line 184: sq-git: command not found
ERROR: sq-git verification failed: main repo
INFO: If this is intentional, configure your own sq-git policy file.
See 'buildconfig.d/30_signing_key.conf'.
derivative-maker uses sq-git (sequoia-git) to authenticate the commit
chain against an OpenPGP policy file before building. The policy file
itself ships in the upstream repo (./openpgp-policy.toml) and the
trust-root defaults are correctly configured by help-steps/variables
(line 232 + 290) for non-redistributable builds — i.e. the verification
machinery is fully wired and just needs the binary.
Aligns with the upstream container's package list at
linux/build/derivative-maker/docker/derivative-maker-docker-setup.
Changes:
- Dockerfile.builder: add sq, sqv, sqop, sequoia-git,
sequoia-chameleon-gnupg, gpg-agent. All available in trixie main.
- build.sh: BUILDER_IMAGE digest re-pinned to sha256:c1490bab…5c97
(rebuilt on 10.0.0.51, sq-git binary verified present at /usr/bin/sq-git).
No reproducibility implications — image rebuilds against the same
pinned snapshot timestamp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run #4254 finally got past every harness issue and into derivative-
maker's actual sanity-tests, where it died with:
You are attempting to build on an unsupported operating system or version.
detected operating system codename: 'bookworm'
expected operating system codename: 'trixie'
The pinned derivative-maker tag (18.1.7.4-developers-only) requires
Debian 13 (trixie) as the build host. Upstream's own
linux/build/derivative-maker/docker/Dockerfile uses
`FROM debian:trixie-slim`. We picked bookworm originally and the tag
mismatch wasn't caught until the build actually ran.
Changes:
- Dockerfile.builder: FROM debian:bookworm-slim →
debian:trixie-slim @ sha256:cedb1ef4…2c5a (resolved 2026-05-07 on
the runner host). sources.list suite names follow:
`bookworm` → `trixie`, `bookworm-security` → `trixie-security`.
snapshot.debian.org pin (20260415T000000Z) is unchanged — snapshots
are date-keyed, so the same timestamp resolves trixie's dists/.
- silvermetal-base.conf: DERIVATIVE_DIST `bookworm` → `trixie` for
consistency (the value isn't passed to derivative-maker — there's
no --dist option — but it's referenced by the build.sh prologue
and we shouldn't have a stale codename floating around).
- build.sh: BUILDER_IMAGE digest re-pinned to sha256:7d893178…1890
(rebuilt natively on 10.0.0.51 against the new base, pushed).
The reproducibility guarantee is unchanged in shape — same snapshot
timestamp, same source-date-epoch derivation, just a different stable
host OS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run #4253 finally got past all the harness failures and into
derivative-maker's actual build steps, where 1100_sanity-tests
rejected our invocation with:
unknown option (1): '--build'
The CLI we'd been passing was built from invented flag names rather
than the real grammar in derivative-maker/help-steps/parse-cmd.
Concretely:
- `--build` is not a real option (just wrong)
- `--flavour` should be `--flavor` (upstream uses American spelling)
- `--dist` is not a real option; dist is implicit from `--flavor`
(kicksecure-cli ⇒ bookworm)
- `--config` is not a real option; the silvermetal-base.conf is
sourced into env above the invocation, no flag needed
- `--freedom true|false` was missing entirely; parse-cmd requires it
for `--arch amd64` (line 70 in parse-cmd) — the script
exits if neither is set
Fix: build-inner.sh now invokes
./derivative-maker --flavor … --target … --arch … --freedom …
which is the minimal valid form per parse-cmd's case-branches.
Set DERIVATIVE_FREEDOM=false in silvermetal-base.conf, matching
Kicksecure's own public-ISO choice — `--freedom true` would omit
firmware-nonfreedom and the resulting ISO wouldn't initialise wifi /
many GPUs / Intel microcode on most hardware. Privacy/functionality
trade-off documented inline; the hardening overlay in M1.2+ can
revisit if that conversation becomes useful.
Verified: bash -n on both scripts. No image rebuild needed — pure
script and config changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run #4252 died at:
runuser: failed to execute /workspace/SilverLABS/SilverMetal/linux/build/scripts/build-inner.sh:
Permission denied
The script was created on the WSL/Windows side (/mnt/c) where every
file appears world-rwx regardless of git's index, so the local
`chmod +x` was a no-op as far as git was concerned and the file got
committed at mode 100644 like any other regular file. Sibling scripts
(build.sh, verify-reproducibility.sh, diagnose-divergence.sh) all
correctly carry 100755 in the index.
Fix: `git update-index --chmod=+x` to set the bit in the index
explicitly, independent of the working-tree perms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run #4251 advanced past checkout and into derivative-maker, then died
immediately:
ERROR: This must NOT be run as root (sudo)!
ERROR: Exiting ./derivative-maker with non-zero exit code 1.
Errors Detected: 0. Execution Time: 00:00:00.
Kicksecure's derivative-maker explicitly refuses to run as root — it
expects a regular user with passwordless sudo and uses sudo internally
for the privileged operations (debootstrap, mksquashfs, chroot mounts).
Our minimal debian-slim builder image had a `builder` user (uid 1000)
but no sudo, no sudoers entry, and the container ran as root.
Aligns with the upstream Kicksecure container pattern at
linux/build/derivative-maker/docker/derivative-maker-docker-setup
(uses USER=user with `${USER} ALL=(ALL) NOPASSWD:ALL`).
Changes:
- Dockerfile.builder: install `sudo` (and `fakeroot` while we're here —
upstream sanity-tests pulls this in via apt at build time, but having
it baked avoids a snapshot.debian.org round-trip every run); add
passwordless sudoers entry for builder; correct the misleading
comment that claimed root was needed.
- New scripts/build-inner.sh: the inner derivative-maker invocation
pulled out of build.sh's heredoc. Once we needed to drop privileges
via runuser, the nested-heredoc / nested-quoting situation became
unmaintainable; a regular script with normal quoting is far cleaner.
- build.sh: inner heredoc now just chowns the workspace to builder and
runuser's into build-inner.sh. ${REPO_ROOT} and ${BUILD_DIR} continue
to be forwarded into the container via -e.
- build.sh: BUILDER_IMAGE digest re-pinned to sha256:f8f0db37…1bedc
(rebuilt and pushed natively on 10.0.0.51 — never on the WSL/aarch64
dev box, see reference_silvermetal_runner.md memory).
Verified: bash -n on both scripts; image builds and pushes cleanly.
Pushing this commit triggers a fresh CI run that will exercise it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
build.sh ran fine locally but failed in Gitea Actions on the first
reproducibility-gated run (#4250) with:
bash: line 3: /work/linux/build/config/silvermetal-base.conf:
No such file or directory
Root cause: classic Docker-out-of-Docker confusion. build.sh runs
inside the act_runner job container, which talks to the host's docker
daemon via the mounted /var/run/docker.sock. The "-v ${REPO_ROOT}:/work"
flag was being interpreted by the host daemon against the host
filesystem, where /workspace/SilverLABS/SilverMetal does not exist;
docker silently auto-created an empty dir there and mounted that as
/work, so the config source target was missing.
Fix: detect GITHUB_ACTIONS and use --volumes-from "$(hostname)" in CI
to inherit the parent job container's /workspace mount intact. Locally
we keep a bind mount, but use the same path inside and outside
(${REPO_ROOT}:${REPO_ROOT}) so the inner heredoc is identical in both
modes. Inner script now references "${REPO_ROOT}/..." and
"${BUILD_DIR}/..." instead of the synthetic /work and /out paths.
No reproducibility implications — bind topology doesn't affect bytes
inside the ISO.
Verified locally: bash -n passes; structural change only, behaviour
preserved for the non-CI path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two corrections to f9e606d:
1. Registry hostname: docker-registry:5000 isn't DNS-resolvable on the
SLAB docker host (verified). The fleet-wide convention is the canonical
docker-registry.silverlabs.uk URL, registered as an insecure-registry
in /etc/docker/daemon.json on every docker host.
2. Architecture: the original push from WSL2-on-aarch64 produced an arm64
image that won't run on the amd64 runner. Rebuilt natively on the docker
host. New manifest digest (amd64-only):
sha256:9e7161f9f180483f434074d7f32c27c907955232bd0c44efe6dc0ee1d9e56ae0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Image built from Dockerfile.builder@36f7672 was pushed to both
docker-registry:5000 (internal) and docker-registry.silverlabs.uk
(external) under tags m1.1-bootstrap + latest. Both URLs serve the
same registry, so the manifest digest is identical:
sha256:cedef039425e0b0f5901c1023eda820c7aa38ab4b81c2bb1e12d64cadb3d6c85
Default points at the internal hostname for CI; external dev overrides
via BUILDER_IMAGE env var.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vendors Kicksecure derivative-maker as a pinned submodule (18.1.7.4),
adds the wrapper + verify + diagnose scripts, the pinned builder image,
and the reproducibility-gated Gitea Actions workflow. Base flavour only —
no hardening overlay (that's M1.2).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>