fix(runtime): the seed copy ran as root too, ~3200 files per mission

The uid fix landed and the CHECKOUT came back completely clean — 0 non-65532
files under `repo/` after a benchmark run that builds and tests Rust. But the
same mission still held 3247 root-owned files, all under `runtime-data/`.

`seed_runtime_data` spawns a throwaway container to `cp -a` the runtime seed
into the mission's directory and never set `user`, so it ran as root — the
identical absent-`user` omission `container_exec` had, in a container create
instead of an exec. The seed source is 65532-owned and the destination is
created by the server (which itself runs as 65532), so the copy never had a
reason to out-rank either.

This is the tree a gateway GC has to be able to delete, and a GC running as
65532 cannot remove root-owned files — the cleanup-that-cannot-clean-up shape,
found before writing the GC rather than after.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-09 10:55:18 -07:00
co-authored by Claude Opus 5
parent dcd9514622
commit 8ddea454d1
2 changed files with 8 additions and 1 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ pub fn connect() -> Result<Docker, String> {
/// `target/` directories appearing inside a checkout that uid 65532 then could /// `target/` directories appearing inside a checkout that uid 65532 then could
/// not delete, `root_copy` existing at all, and a cleanup path that had to /// not delete, `root_copy` existing at all, and a cleanup path that had to
/// re-enter the container as root to undo what it had just done. /// re-enter the container as root to undo what it had just done.
const MISSION_UID: &str = "65532:65532"; pub(crate) const MISSION_UID: &str = "65532:65532";
/// Environment a non-root exec needs, because the image gives uid 65532 no /// Environment a non-root exec needs, because the image gives uid 65532 no
/// writable `HOME` and no writable `CARGO_HOME`. /// writable `HOME` and no writable `CARGO_HOME`.
+7
View File
@@ -402,6 +402,13 @@ async fn seed_runtime_data(
let name = format!("cm-seed-{}", Uuid::now_v7().simple()); let name = format!("cm-seed-{}", Uuid::now_v7().simple());
let config = ContainerCreateBody { let config = ContainerCreateBody {
image: Some(image.to_string()), image: Some(image.to_string()),
// As 65532, like every other writer into the missions tree. Left unset,
// this `cp -a` ran as root and laid down ~3200 root-owned files per
// mission under `runtime-data/` — the same absent-`user` omission that
// `container_exec` had, in a container create rather than an exec. The
// seed source is 65532-owned and the destination is created by the
// server (also 65532), so the copy has no reason to out-rank either.
user: Some(crate::container_exec::MISSION_UID.to_string()),
entrypoint: Some(vec!["/bin/sh".to_string()]), entrypoint: Some(vec!["/bin/sh".to_string()]),
cmd: Some(vec!["-c".to_string(), copy_script()]), cmd: Some(vec!["-c".to_string(), copy_script()]),
host_config: Some(HostConfig { host_config: Some(HostConfig {