From 296a2d597c2c4f98518a4fac2dafcfe80edad7e4 Mon Sep 17 00:00:00 2001 From: japabu Date: Tue, 13 Oct 2026 06:01:08 +0200 Subject: [PATCH 1/3] CI's Debian image comes from ToyOSOrg's GHCR copy, pulled with the run's token, by the same digest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Public ECR refused `guest / suite`'s container pull in the merge queue with `toomanyrequests: Data limit exceeded`: ECR Public gives unauthenticated clients 500 GB of data a month, and GitHub-hosted runners share their addresses. Docker Hub had refused it before for its own anonymous limit. The image, Docker Hub's `sid-20260918` (digest `a2aa4626…`), is copied once, every manifest byte for byte, to `ghcr.io/toyosorg/debian`, and both containers pull it with `container.credentials` from `github.token`, the callers granting `packages: read`. GitHub documents data transfer to Actions under `GITHUB_TOKEN` as free; the digest stays the pin, so the registry is no part of the provenance. nightly's `portability-linux` takes the same digest, since a copy made once cannot follow `sid`'s tag; its archive stays `sid` as it stands. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy --- .github/workflows/ci.yml | 4 ++++ .github/workflows/guest.yml | 12 ++++++++---- .github/workflows/nightly.yml | 16 +++++++++++++--- ...uild-runs-host-tools-outside-rust-and-qemu.md | 2 +- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d23d5844aa..9df8632f46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,10 @@ jobs: guest: needs: toolchain if: ${{ !cancelled() }} + # Its container's pull. + permissions: + contents: read + packages: read uses: ./.github/workflows/guest.yml with: kvm: true diff --git a/.github/workflows/guest.yml b/.github/workflows/guest.yml index 2879f7e9b4..8c81f3b1d0 100644 --- a/.github/workflows/guest.yml +++ b/.github/workflows/guest.yml @@ -24,11 +24,15 @@ jobs: timeout-minutes: 60 # The digest is the instrument's one pin: a dated image names the snapshot # archive it was built from, and `deps` installs QEMU from that archive. - # ECR Public's copy of Docker Hub's image: Docker Hub's anonymous pull limit - # refused it there, and the digest holds whichever registry serves it. - # The node ships `crw-rw---- root:kvm` and root opens it. + # ToyOSOrg's copy of Docker Hub's `sid-20260918`, pulled with the run's + # token, which its caller grants `packages: read`: Docker Hub's and ECR + # Public's anonymous limits refused it, and the digest holds whichever + # registry serves it. The node ships `crw-rw---- root:kvm` and root opens it. container: - image: public.ecr.aws/docker/library/debian@sha256:a2aa46262453eba3f464d8b1c7a8c31db85eb15af180ae34dd400615d7208547 + image: ghcr.io/toyosorg/debian@sha256:a2aa46262453eba3f464d8b1c7a8c31db85eb15af180ae34dd400615d7208547 + credentials: + username: ${{ github.actor }} + password: ${{ github.token }} options: ${{ inputs.kvm && '--device=/dev/kvm' || '' }} steps: # Before the checkout, which wants git. Three attempts, because the diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index bf99891b4a..1d87b05941 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -75,19 +75,29 @@ jobs: tcg: needs: toolchain if: ${{ !cancelled() }} + # Its container's pull. + permissions: + contents: read + packages: read uses: ./.github/workflows/guest.yml with: kvm: false sysroot-key: ${{ needs.toolchain.outputs.sysroot-key }} sysroot-path: ${{ needs.toolchain.outputs.sysroot-path }} - # `cargo run -- --build-only` from a fresh machine. `sid` as it stands, - # image and archive both, and no cache — a fresh machine is the premise. + # `cargo run -- --build-only` from a fresh machine: `sid`'s archive as it + # stands, on guest.yml's image, and no cache — a fresh machine is the premise. portability-linux: runs-on: ubuntu-24.04 timeout-minutes: 350 + permissions: + contents: read + packages: read container: - image: public.ecr.aws/docker/library/debian:sid + image: ghcr.io/toyosorg/debian@sha256:a2aa46262453eba3f464d8b1c7a8c31db85eb15af180ae34dd400615d7208547 + credentials: + username: ${{ github.actor }} + password: ${{ github.token }} steps: - name: deps run: | diff --git a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md index ec45e066c5..9d91114429 100644 --- a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md +++ b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md @@ -24,7 +24,7 @@ arrives and is not one. M4 and M5 are stages of `issues/toyos-builds-itself.md`. | the toolchain's own `clang`, `llvm-ar`, `rust-lld` and `llvm-config`, built from `ToyOSOrg/llvm-project` | rustc links every guest binary with `rust-lld`; `clang` compiles the C corpus (`tests/common/compile.rs`) and, with `llvm-ar`, through `cc::Build` the C of every guest crate that has any (`build/clang.rs`, `build/build.rs`'s `GuestEnv`): doomgeneric, and ring's C and assembly in a test crate's `https_get` and `ring_kat`; rustc's bootstrap asks `llvm-config` how to link LLVM | admitted: our fork's C++, which ToyOS can one day build and run; no Rust tool compiles C, `cc::Build` archives with an `ar`, bootstrap reads LLVM through `llvm-config`, and `CLAUDE.md` links everything with `rust-lld` | M5: no host in the loop | | `ovmf-generic`, `qemu-efi-aarch64` | the x86-64 and AArch64 UEFI firmware of CI's guest containers (`build/firmware.rs`), packaged by Debian apart from QEMU | admitted: QEMU's own firmware, and no Rust firmware does its job | the instrument's QEMU carries its own firmware | | `ca-certificates` | the trust store `git` and `curl` verify against in CI's containers | admitted: data both of them need | goes when neither runs there | -| Debian's `sid` image, pulled anonymously from ECR Public's copy of Docker Hub's | the container of `guest.yml`'s suite, pinned to digest `a2aa4626…`, whose dated snapshot archive `deps` installs QEMU from; and of `nightly.yml`'s `portability-linux`, `sid` as it stands | admitted: the runner's Ubuntu 24.04 publishes QEMU 8.2.2 (Launchpad, noble, every pocket) and `build/ci/qemu-version` declares 11.1.1, which that snapshot archive carries | the runner's own OS carries the declared QEMU, or the build system builds it | +| Debian's `sid` image, Docker Hub's `sid-20260918` copied once to `ghcr.io/toyosorg/debian` and pulled with the run's token | the container of `guest.yml`'s suite, pinned to digest `a2aa4626…`, whose dated snapshot archive `deps` installs QEMU from; and of `nightly.yml`'s `portability-linux`, by the same digest, on `sid`'s archive as it stands | admitted: the runner's Ubuntu 24.04 publishes QEMU 8.2.2 (Launchpad, noble, every pocket) and `build/ci/qemu-version` declares 11.1.1, which that snapshot archive carries | the runner's own OS carries the declared QEMU, or the build system builds it | | Docker on the runner | Actions pulls and runs every `container:` image with it, those two jobs' | refused: Go, not C or C++ ToyOS can one day build and run, and Actions runs a `container:` with Docker alone | the container goes with the image row | | the T14's Ubuntu and every tool `tests/metal/src/lib.rs` runs on it over `ssh` | the metal loop, on the T14 and never on a development host | outside the rule: recovery equipment on a test machine, not the build's host | they leave with Ubuntu (`issues/the-machine-updates-itself-without-ubuntu.md`) | | `sh` running `rust/x`, and Python running `x.py` and `bootstrap.py` | every toolchain build (`build/toolchain.rs`) | refused: a Rust tool does it, upstream's bootstrap binary, which builds with stable cargo, fetches its own stage0 (`rust/src/bootstrap/src/core/download.rs`) and needs no Python | `build/toolchain.rs` runs the bootstrap binary | From 6f4c227bc105e0d0af6621de79cd3f9cdb9a560e Mon Sep 17 00:00:00 2001 From: japabu Date: Tue, 13 Oct 2026 06:27:59 +0200 Subject: [PATCH 2/3] CI: portability-linux stays on sid as it stands; skopeo is declared The review of #903 found that moving nightly's portability-linux onto the guest's 2026-09-18 copy breaks its premise, a fresh sid machine: deps installs without a full upgrade, so the base would drift ever further from sid and the job could pass where a fresh machine fails. It never hit ECR's limit, so it goes back to ECR Public's moving tag, and the issue row says so again. The GHCR copy was made with skopeo, which Arrivals asks declared: Go, so refused, with its exit. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy --- .github/workflows/nightly.yml | 12 +++--------- ...he-build-runs-host-tools-outside-rust-and-qemu.md | 3 ++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1d87b05941..d4bdf6bba9 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -85,19 +85,13 @@ jobs: sysroot-key: ${{ needs.toolchain.outputs.sysroot-key }} sysroot-path: ${{ needs.toolchain.outputs.sysroot-path }} - # `cargo run -- --build-only` from a fresh machine: `sid`'s archive as it - # stands, on guest.yml's image, and no cache — a fresh machine is the premise. + # `cargo run -- --build-only` from a fresh machine. `sid` as it stands, + # image and archive both, and no cache — a fresh machine is the premise. portability-linux: runs-on: ubuntu-24.04 timeout-minutes: 350 - permissions: - contents: read - packages: read container: - image: ghcr.io/toyosorg/debian@sha256:a2aa46262453eba3f464d8b1c7a8c31db85eb15af180ae34dd400615d7208547 - credentials: - username: ${{ github.actor }} - password: ${{ github.token }} + image: public.ecr.aws/docker/library/debian:sid steps: - name: deps run: | diff --git a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md index 9d91114429..f78476136d 100644 --- a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md +++ b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md @@ -24,8 +24,9 @@ arrives and is not one. M4 and M5 are stages of `issues/toyos-builds-itself.md`. | the toolchain's own `clang`, `llvm-ar`, `rust-lld` and `llvm-config`, built from `ToyOSOrg/llvm-project` | rustc links every guest binary with `rust-lld`; `clang` compiles the C corpus (`tests/common/compile.rs`) and, with `llvm-ar`, through `cc::Build` the C of every guest crate that has any (`build/clang.rs`, `build/build.rs`'s `GuestEnv`): doomgeneric, and ring's C and assembly in a test crate's `https_get` and `ring_kat`; rustc's bootstrap asks `llvm-config` how to link LLVM | admitted: our fork's C++, which ToyOS can one day build and run; no Rust tool compiles C, `cc::Build` archives with an `ar`, bootstrap reads LLVM through `llvm-config`, and `CLAUDE.md` links everything with `rust-lld` | M5: no host in the loop | | `ovmf-generic`, `qemu-efi-aarch64` | the x86-64 and AArch64 UEFI firmware of CI's guest containers (`build/firmware.rs`), packaged by Debian apart from QEMU | admitted: QEMU's own firmware, and no Rust firmware does its job | the instrument's QEMU carries its own firmware | | `ca-certificates` | the trust store `git` and `curl` verify against in CI's containers | admitted: data both of them need | goes when neither runs there | -| Debian's `sid` image, Docker Hub's `sid-20260918` copied once to `ghcr.io/toyosorg/debian` and pulled with the run's token | the container of `guest.yml`'s suite, pinned to digest `a2aa4626…`, whose dated snapshot archive `deps` installs QEMU from; and of `nightly.yml`'s `portability-linux`, by the same digest, on `sid`'s archive as it stands | admitted: the runner's Ubuntu 24.04 publishes QEMU 8.2.2 (Launchpad, noble, every pocket) and `build/ci/qemu-version` declares 11.1.1, which that snapshot archive carries | the runner's own OS carries the declared QEMU, or the build system builds it | +| Debian's `sid` image, Docker Hub's `sid-20260918` copied once to `ghcr.io/toyosorg/debian` and pulled with the run's token | the container of `guest.yml`'s suite, pinned to digest `a2aa4626…`, whose dated snapshot archive `deps` installs QEMU from; and, pulled anonymously from ECR Public's copy of Docker Hub's, of `nightly.yml`'s `portability-linux`, `sid` as it stands | admitted: the runner's Ubuntu 24.04 publishes QEMU 8.2.2 (Launchpad, noble, every pocket) and `build/ci/qemu-version` declares 11.1.1, which that snapshot archive carries | the runner's own OS carries the declared QEMU, or the build system builds it | | Docker on the runner | Actions pulls and runs every `container:` image with it, those two jobs' | refused: Go, not C or C++ ToyOS can one day build and run, and Actions runs a `container:` with Docker alone | the container goes with the image row | +| `skopeo` | `skopeo copy --all --preserve-digests` copied Docker Hub's `sid-20260918` index, every manifest by its digest, to `ghcr.io/toyosorg/debian`, once, from a `run:` step of a workflow that ran on a branch and is not in `main`; a new pin is a new copy made the same way | refused: Go, not C or C++ ToyOS can one day build and run | a Rust tool makes the copy, or the container goes with the image row | | the T14's Ubuntu and every tool `tests/metal/src/lib.rs` runs on it over `ssh` | the metal loop, on the T14 and never on a development host | outside the rule: recovery equipment on a test machine, not the build's host | they leave with Ubuntu (`issues/the-machine-updates-itself-without-ubuntu.md`) | | `sh` running `rust/x`, and Python running `x.py` and `bootstrap.py` | every toolchain build (`build/toolchain.rs`) | refused: a Rust tool does it, upstream's bootstrap binary, which builds with stable cargo, fetches its own stage0 (`rust/src/bootstrap/src/core/download.rs`) and needs no Python | `build/toolchain.rs` runs the bootstrap binary | | `curl` in rustc's bootstrap | fetches the stage0 `rust/src/stage0` pins, for a compiler or LLVM build whose build directory lacks it, whichever bootstrap runs | refused: a Rust tool does it, rustup installs the dated beta the pin names, and bootstrap takes a stage0 through `build.rustc` and `build.cargo`, as `build/sysroot.rs` hands it one | no toolchain build fetches with `curl` | From b572aacb322c9017098049e8053a63d27fdbbf8e Mon Sep 17 00:00:00 2001 From: japabu Date: Tue, 13 Oct 2026 06:33:25 +0200 Subject: [PATCH 3/3] issues: the copy a re-pin replaces is deleted The package is private; a second copy doubles its charge to the private allowance, and whoever re-pins reads this row. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy --- issues/the-build-runs-host-tools-outside-rust-and-qemu.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md index f78476136d..076ed879e5 100644 --- a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md +++ b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md @@ -26,7 +26,7 @@ arrives and is not one. M4 and M5 are stages of `issues/toyos-builds-itself.md`. | `ca-certificates` | the trust store `git` and `curl` verify against in CI's containers | admitted: data both of them need | goes when neither runs there | | Debian's `sid` image, Docker Hub's `sid-20260918` copied once to `ghcr.io/toyosorg/debian` and pulled with the run's token | the container of `guest.yml`'s suite, pinned to digest `a2aa4626…`, whose dated snapshot archive `deps` installs QEMU from; and, pulled anonymously from ECR Public's copy of Docker Hub's, of `nightly.yml`'s `portability-linux`, `sid` as it stands | admitted: the runner's Ubuntu 24.04 publishes QEMU 8.2.2 (Launchpad, noble, every pocket) and `build/ci/qemu-version` declares 11.1.1, which that snapshot archive carries | the runner's own OS carries the declared QEMU, or the build system builds it | | Docker on the runner | Actions pulls and runs every `container:` image with it, those two jobs' | refused: Go, not C or C++ ToyOS can one day build and run, and Actions runs a `container:` with Docker alone | the container goes with the image row | -| `skopeo` | `skopeo copy --all --preserve-digests` copied Docker Hub's `sid-20260918` index, every manifest by its digest, to `ghcr.io/toyosorg/debian`, once, from a `run:` step of a workflow that ran on a branch and is not in `main`; a new pin is a new copy made the same way | refused: Go, not C or C++ ToyOS can one day build and run | a Rust tool makes the copy, or the container goes with the image row | +| `skopeo` | `skopeo copy --all --preserve-digests` copied Docker Hub's `sid-20260918` index, every manifest by its digest, to `ghcr.io/toyosorg/debian`, once, from a `run:` step of a workflow that ran on a branch and is not in `main`; a new pin is a new copy made the same way, and the copy it replaces is deleted | refused: Go, not C or C++ ToyOS can one day build and run | a Rust tool makes the copy, or the container goes with the image row | | the T14's Ubuntu and every tool `tests/metal/src/lib.rs` runs on it over `ssh` | the metal loop, on the T14 and never on a development host | outside the rule: recovery equipment on a test machine, not the build's host | they leave with Ubuntu (`issues/the-machine-updates-itself-without-ubuntu.md`) | | `sh` running `rust/x`, and Python running `x.py` and `bootstrap.py` | every toolchain build (`build/toolchain.rs`) | refused: a Rust tool does it, upstream's bootstrap binary, which builds with stable cargo, fetches its own stage0 (`rust/src/bootstrap/src/core/download.rs`) and needs no Python | `build/toolchain.rs` runs the bootstrap binary | | `curl` in rustc's bootstrap | fetches the stage0 `rust/src/stage0` pins, for a compiler or LLVM build whose build directory lacks it, whichever bootstrap runs | refused: a Rust tool does it, rustup installs the dated beta the pin names, and bootstrap takes a stage0 through `build.rustc` and `build.cargo`, as `build/sysroot.rs` hands it one | no toolchain build fetches with `curl` |