From e1eef855f0ca7020cdf5505ae02225db971dbcf9 Mon Sep 17 00:00:00 2001 From: Andreas Trawoeger Date: Thu, 15 Oct 2026 09:28:46 +0200 Subject: [PATCH 1/2] fix(candy): drop the uid-1000 buildkitd socket literal so the ONE roots resolver governs candy/layer-nerdctl/charly.yml wrote a static buildkit_host = "unix:///run/user/1000/buildkit/buildkitd.sock" into /etc/nerdctl/nerdctl.toml. sdk/buildkitdrive/paths.go names that exact literal as the deviation it replaces: "it breaks on any uid other than 1000, and two conventions for one socket is one too many". SocketPath() resolves the daemon address through the ONE roots resolver instead -- storekit.Dir(storekit.RootRuntime) over the socket name, i.e. $XDG_RUNTIME_DIR/charly/buildkitd.sock, with a documented fallback to the state root when XDG_RUNTIME_DIR is unset. The key is REMOVED rather than re-expressed, and that is the point rather than a shortcut: nerdctl.toml is a STATIC image-build artefact, while the socket is a RUNTIME value. TOML does not expand $XDG_RUNTIME_DIR, so any literal here could only pin one uid again -- which is the defect. With the key gone there is exactly one authority for that slot, which is what the contract asks for. Also corrected the three places that described the removed key: the write step's name and its content comment, the candy's own description, and the skill's summary line. The ADE check that asserted "namespace + buildkit host" only ever grepped the namespace, so its description is now what it actually checks -- and a second check pins the new invariant, that the toml carries NO buildkit_host at all. Gate: charly box validate -> OK, 0 warnings, 0 errors (1 candy, 1 box, 1 deploy, 7 distros, 7 builders). Assisted-by: DeepSeek Harness ollama-cloud/deepseek-v4.1-flash (analysed on a live system) --- candy/layer-nerdctl/charly.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/candy/layer-nerdctl/charly.yml b/candy/layer-nerdctl/charly.yml index 02f6f96..2fbba5b 100644 --- a/candy/layer-nerdctl/charly.yml +++ b/candy/layer-nerdctl/charly.yml @@ -17,7 +17,9 @@ layer-nerdctl: supplies nerdctl + containerd + containerd-fuse-overlayfs-grpc + CNI + buildkit + rootlesskit. Writes /etc/nerdctl/nerdctl.toml, the rootless containerd/buildkit units, the CNI `charly` network and the cgroup2-rw + - iptables-nft posture for nested use. ADE checks: nerdctl version, nerdctl + iptables-nft posture for nested use. The BuildKit socket is deliberately NOT + pinned here: it is a runtime value resolved through the ONE roots resolver + ($XDG_RUNTIME_DIR/charly/buildkitd.sock). ADE checks: nerdctl version, nerdctl info, the rootless probe, and the fuse-overlayfs snapshotter. var: NERDCTL_FULL_VERSION: "2.1.2" @@ -84,15 +86,22 @@ layer-nerdctl: mkdir: /etc/cni/net.d run_as: root mode: "0755" - - run: write the nerdctl.toml (rootless namespace + buildkit host) + - run: write the nerdctl.toml (rootless namespace + CNI path) write: /etc/nerdctl/nerdctl.toml run_as: root mode: "0644" content: | # Managed by the opencharly layer-nerdctl candy. + # + # No `buildkit_host` here, deliberately. This file is a STATIC image-build + # artefact, and the BuildKit socket is a RUNTIME value: charly resolves it + # through the ONE roots resolver — buildkitdrive.SocketPath() over + # storekit.Dir(storekit.RootRuntime), i.e. $XDG_RUNTIME_DIR/charly/buildkitd.sock. + # A literal here could only hardcode one uid, and the deviation this drops is + # the exact one buildkitdrive/paths.go names: "it breaks on any uid other than + # 1000, and two conventions for one socket is one too many." namespace = "charly" cni_path = "/opt/cni/bin" - buildkit_host = "unix:///run/user/1000/buildkit/buildkitd.sock" - run: write the charly CNI network conflist write: /etc/cni/net.d/charly.conflist run_as: root @@ -114,9 +123,12 @@ layer-nerdctl: - check: the nerdctl binary is present on PATH command: command -v nerdctl exit_status: 0 - - check: the nerdctl.toml declares the charly namespace + buildkit host + - check: the nerdctl.toml declares the charly namespace command: grep -q 'namespace = "charly"' /etc/nerdctl/nerdctl.toml exit_status: 0 + - check: the toml carries NO uid-pinned buildkit_host (the resolver owns that slot) + command: '! grep -q "buildkit_host" /etc/nerdctl/nerdctl.toml' + exit_status: 0 - check: the charly CNI network conflist is present file: /etc/cni/net.d/charly.conflist @@ -143,7 +155,7 @@ layer-nerdctl-skill: sha256-verified `nerdctl-full` tarball (step `nerdctl-full-tarball`), which supplies nerdctl + containerd + `containerd-fuse-overlayfs-grpc` + CNI + buildkit + rootlesskit in one archive. - - `/etc/nerdctl/nerdctl.toml` (charly namespace + rootless buildkit host) and + - `/etc/nerdctl/nerdctl.toml` (charly namespace + CNI path) and the `charly` CNI network conflist at `/etc/cni/net.d/charly.conflist`. - The **nested-pod posture**: userns-scoped `cap_add: ALL`, `/dev/fuse`, `/dev/net/tun`, `unmask=/proc/*`. From 33ea858d85f9d001e421a98a1c3e72f188a7508f Mon Sep 17 00:00:00 2001 From: Andreas Trawoeger Date: Thu, 15 Oct 2026 09:35:12 +0200 Subject: [PATCH 2/2] fix(check): anchor the buildkit_host assertion to the TOML KEY, not the bare word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check added in e1eef85 used `! grep -q "buildkit_host"`, and the file it guards EXPLAINS in a comment why the key is absent -- so the pattern matched the explanation and the check failed on a correct file. The bed caught this, which is the bed doing its job: FAIL check the toml carries NO uid-pinned buildkit_host … [plan:candy:layer-nerdctl:4] and reading the artefact settled it rather than guessing: the toml INSIDE the built image is the new one, with no buildkit_host key at all. The defect was the probe's pattern, not the change's content -- a probe must isolate the OBJECT, not the word that names it. Same family as the night's other traps: a check whose output would not differ for a different object is not a check. Now anchored to a key assignment: ^[[:space:]]*buildkit_host[[:space:]]*= so a mention in prose cannot satisfy it and a real key cannot escape it. Assisted-by: DeepSeek Harness ollama-cloud/deepseek-v4.1-flash (analysed on a live system) --- candy/layer-nerdctl/charly.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/candy/layer-nerdctl/charly.yml b/candy/layer-nerdctl/charly.yml index 2fbba5b..1ed2c0d 100644 --- a/candy/layer-nerdctl/charly.yml +++ b/candy/layer-nerdctl/charly.yml @@ -126,8 +126,11 @@ layer-nerdctl: - check: the nerdctl.toml declares the charly namespace command: grep -q 'namespace = "charly"' /etc/nerdctl/nerdctl.toml exit_status: 0 - - check: the toml carries NO uid-pinned buildkit_host (the resolver owns that slot) - command: '! grep -q "buildkit_host" /etc/nerdctl/nerdctl.toml' + - check: the toml carries NO uid-pinned buildkit_host KEY (the resolver owns that slot) + # The pattern is ANCHORED to a key assignment, not the bare substring: the file + # explains in a comment why the key is absent, and a loose substring match would + # fire on that explanation. A probe must isolate the object, not the word. + command: '! grep -qE "^[[:space:]]*buildkit_host[[:space:]]*=" /etc/nerdctl/nerdctl.toml' exit_status: 0 - check: the charly CNI network conflist is present file: /etc/cni/net.d/charly.conflist