Skip to content

refactor: official golden only; custom images as-is; delete the curated bake - #427

Merged
Bnjoroge1 merged 10 commits into
mainfrom
refactor/official-golden-only
Oct 9, 2026
Merged

Bnjoroge1 merged 10 commits into
mainfrom
refactor/official-golden-only

Conversation

@Bnjoroge1

@Bnjoroge1 Bnjoroge1 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

One golden source survives: the published packed official golden (the official GitHub runner image with preloop-runner baked in). Everything curated is deleted. A configured image is baked as-is plus the GitHub-runner contract.

Decisions

  • Official golden is the default image. runs-on: ubuntu-latest/ubuntu-24.04, and any pool with no image configured, resolve to the digest-pinned packed official golden (PRELOOP_GOLDEN_OCI_REF overrides it per arch; PRELOOP_GOLDEN_URL still selects a release-asset mirror). A missing or unverifiable golden FAILS the job: no local bake, no stock-Ubuntu fallback, no direct-create fallback.
  • ubuntu-22.04 maps nowhere — it keeps the configured image (the official golden when nothing is configured) instead of silently selecting a 24.04 base. ubuntu-slim resolves the same way. The AgentENV template mapping is out of scope here.
  • User OCI images are used AS-IS. At golden-build time Preloop adds only the GitHub-runner machinery: the runner account (uid 1001, home, _work, passwordless sudo) unless the image already has one; ownership of home/_work fixed once via an owner-only walk (find … ! -user 1001 -exec chown -h 1001:1001 {} +, which leaves a runner-owned file's group alone); a writable /opt/hostedtoolcache with RUNNER_TOOL_CACHE/AGENT_TOOLSDIRECTORY in /etc/environment; and the /etc/preloop-bake.json build record. The one enforced requirement is a glibc dynamic loader — the bake fails with a clear message if it is missing. Nothing else is checked: a missing bash/git/docker fails the step, exactly like GitHub-hosted runners. No toolchains, packages, PATH or env overrides.
  • Follow-up (out of scope): label-based routing to multiple custom goldens.

Deleted / kept, file-level

Total: 24 files, +1673 / −3649.

Deleted outright

  • .github/workflows/apt-indices-refresh.yml (−140)
  • crates/preloop-orchestrator/tests/golden_fidelity.rs (−436)
  • scripts/write-golden-provenance.py apt-index wiring (−14)
  • environment.rs −705/+157: ToolchainLayer, base_install_script, the apt/pin baseline, the per-runs-on environment goldens, the curated classification (898 → 350 lines).
  • lib.rs −1654/+843 (10351 → 9540): toolchain-layer plumbing, environment-golden fan-out and retry loop, the per-VM chown -R prelude and reconcile script, RUSTUP_HOME/CARGO_HOME and /usr/local/{cargo,go}/bin PATH overrides, the packed-artifact fallback to a local stock bake, the direct-create fallback.
  • versions.toml 203 → 47 lines (−166/+10): 90 keys → 5.

Kept / renamed

  • GoldenRegistry → GoldenCache; prepare_golden_for_env → prepare_fork_base, now the single golden entry point: the file-pack backend unpacks the official packed artifact, or packs a golden baked from the configured custom image via build_golden_artifact; the non-file-pack (AgentENV) backend boots the image, applies golden_contract_script in-guest and freezes.
  • PRELOOP_GOLDEN_URL (release-asset mirror), PRELOOP_GOLDEN_OCI_REF (new override), PRELOOP_RUNNER_BASE_IMAGE, [golden] base_image, the fork pool and machine fork --freeze-source behavior.
  • The new golden contract: glibc check, runner account with _work + passwordless sudo, one owner-only ownership walk at build time, writable toolcache + /etc/environment, /etc/preloop-bake.json; preloop build-golden --base-image resolving to the configured image.

Migration notes (what operators must change)

  • PRELOOP_USE_PACKED_GOLDEN is gone — a file-pack backend always uses a packed golden. Remove it from host configs.
  • PRELOOP_GOLDEN_URL, if set, must be a mirror of the published golden release assets; the OCI reference (digest-pinned, per arch) is the default source. ubuntu-22.04 no longer selects a 22.04 golden.
  • versions.toml overrides: the curated keys (ubuntu_24_04_base, ubuntu_22_04_base, toolchain pins, apt pins, apt_indices_max_age_days, github_runner_image_version) no longer exist. The 5 remaining keys are runner_version, smolvm_min_version, smolvm_golden_version, node externals, and the runner-image docker/buildx pins.
  • preloop build-golden no longer has a default base: pass --base-image <ref> or configure PRELOOP_RUNNER_BASE_IMAGE / [golden] base_image. The official golden is published packed and cannot (and need not) be built locally.
  • preloop init "official" now stores the choice as a complete answer (kind alone; no base image, nothing to re-ask).

Interactions

Rebased on main @ 5e9778d. Overlaps #426 (which calls prepare_golden_for_env/GoldenRegistry and keeps two is_packed_disabled() guards — this PR renames/deletes those) and #425 (hosted-runtime parity; its limits and guest_hosted_runtime_init_script call in provisioning/as_runner_user are preserved). Whoever merges second rebases.

Verification (macstudio, branch b3b4096, CARGO_TARGET_DIR=$HOME/pr-work/target-curated)

  • ✅ cargo fmt --all -- --check — clean
  • ✅ cargo clippy --workspace --all-targets -- -D warnings — clean
  • ✅ zizmor .github/workflows/ — no findings
  • ✅ cargo build --locked -p preloop-cli -p preloop-runner-client and cargo zigbuild --locked -p preloop-runner --target aarch64-unknown-linux-gnu — both succeed
  • 🟡 cargo test --locked --workspace (scratch Postgres on 127.0.0.1:54931, PROPTEST_CASES=8): 837 passed, 2 failed, 3 ignored in the preloop-runner-server lib binary; every other test binary green. Both failures — snapshots::remote_checkout_cache_tests::lfs_private_without_credential_is_not_fetched and event_feed::tests::dirty_marks_are_coalesced_into_one_notification — pass in isolation on the same build, sit in files this PR does not touch, and are timing-sensitive (the box ran at load average 20–40 with ~15 concurrent VMs). CI's sharded nextest run is the arbiter.
  • 🟡 Smoke 1 (official golden, private engine on 47931, pool of 2, fork): the 9.6 GB payload was placed at the engine's golden-path and unpacked (31.7 GB layers-cs), but the golden VM's first start failed at krun_start_enter returned: -22 (EINVAL) with the host at load average 41; the pool retries. Not yet green.
  • 🟡 Smoke 2 (lean custom image): scripted and chained behind smoke 1 in the same private home; the smoke build with feat(control): serve the checkout REST archive endpoint #420 cherry-picked is ready. Not yet run.
  • 🟡 CI: control 16/17/18 pass; rust shards, rust-lint, conformance, supply-chain, zizmor, pullfrog queued (run ids: e9ff62b2, 42809822, 19cb8c4b, a0775b70, b6355f16, d7579d47).

Follow-ups (not in this PR)

  • The other campaign harnesses (benchmarks/real-world/conformance-10repos.sh, conformance-new5repos.sh, benchmarks/substrates/{e2e,project}-bench.sh) still export the removed PRELOOP_USE_PACKED_GOLDEN and pin the old runner-images base; they need the same official-sentinel rewrite conformance-5repos.sh got.
  • Label-based routing to multiple custom goldens.

Devin Review


Summary by cubic

Collapses golden sources to exactly two: the published packed official golden — the default for ubuntu-latest/ubuntu-24.04 and any pool with no image configured — and the configured custom image, baked as-is plus the GitHub-runner contract. The curated stock-Ubuntu bake (toolchain layers, apt baseline, package pins) is deleted, and a failed official-golden download or verification now fails the job instead of falling back to a local stock bake or direct create. A deadlock in the golden checksum-probe download is fixed, and apt indices are refreshed at bake time and when an unpacked official pack has none, so bare sudo apt-get install steps work like on GitHub-hosted runners. Startup cleanup also retires goldens and artifact payloads keyed on the removed stock-Ubuntu environments.

Migration

  • Remove PRELOOP_USE_PACKED_GOLDEN; the file-pack backend always uses a packed golden.
  • preloop build-golden has no default base: pass --base-image <ref> or set PRELOOP_RUNNER_BASE_IMAGE / [golden] base_image. The official golden is published packed and cannot be built locally.
  • The release-verify workflow and benchmark scripts now read the per-arch official base from official-image.toml instead of versions.toml.

Written for commit 6f57554. Summary will update on new commits.

View guided diff

…mages

The curated stock-Ubuntu bake is deleted. Two golden sources remain: the
published packed official golden (ubuntu-latest/ubuntu-24.04 and the
unconfigured default; a download or verify failure is fatal), and a
configured image, baked as-is plus the GitHub-runner contract.

Deleted: ToolchainLayer, base_install_script and the apt/pin baseline,
the apt-index marker and refresh, environment goldens
(prepare_golden_for_env's fan-out, GoldenRegistry's packed fallback, the
env-golden retry loop), the local stock bake, the direct-create fallback,
the per-VM ownership walk and reconcile script, RUSTUP_HOME/CARGO_HOME and
the /usr/local/{cargo,go}/bin PATH entries.

Added: the lean golden contract (glibc check, runner account with _work and
passwordless sudo, one owner-only ownership walk at build time, writable
toolcache + /etc/environment, /etc/preloop-bake.json), official-only label
mapping, and `preloop build-golden --base-image` resolving to the configured
image.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8a80d7e0-12bc-4c68-93bc-ea0d638f5626

📥 Commits

Reviewing files that changed from the base of the PR and between f484f98 and f5bf5f0.


📒 Files selected for processing (28)
  • .github/workflows/apt-indices-refresh.yml
  • .github/workflows/official-golden.yml
  • .github/workflows/release-golden.yml
  • .github/workflows/release.yml
  • .github/workflows/smolvm-release-verify.yml
  • CHANGELOG.md
  • benchmarks/real-world/conformance-10repos.sh
  • benchmarks/real-world/conformance-5repos.sh
  • benchmarks/real-world/conformance-new5repos.sh
  • benchmarks/substrates/e2e-bench.sh
  • benchmarks/substrates/project-bench.sh
  • crates/preloop-cli/src/init.rs
  • crates/preloop-cli/src/main.rs
  • crates/preloop-orchestrator/src/environment.rs
  • crates/preloop-orchestrator/src/lib.rs
  • crates/preloop-orchestrator/tests/golden_fidelity.rs
  • crates/preloop-orchestrator/tests/runner_pool_lifecycle.rs
  • crates/preloop-runner-server/src/config.rs
  • docs/build-goldens.md
  • docs/cli_reference.md
  • docs/fidelity-gap.md
  • docs/self-hosting.md
  • docs/vm-images.md
  • docs/vm-substrates.md
  • scripts/build-golden-x86_64.sh
  • scripts/write-golden-provenance.py
  • skills/preloop/SKILL.md
  • versions.toml

 ______________________________________
< Hold on, let me under-engineer this! >
 --------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 11 potential issues.

🐛 7 issues in files not directly in the diff

🐛 Default AgentENV pool cannot start

With AgentENV and no configured image, bake_golden_in_guest boots the official sentinel as an OCI image. AgentENV requires a real image reference, so the pool never starts.


🐛 Existing runner account retains the wrong UID

If a custom image already has runner at another UID, runner_account_script skips creation but changes its home to UID 1001. Job processes then use the mismatched account and can lose access to their home.


🐛 Official toolchains vanish from job PATH

On the official runner image, guest_runner_path removes the existing Cargo and Go binary directories. Steps invoking those preinstalled tools without setup actions now fail command lookup.


🐛 Non-Ubuntu Mac goldens fail at apt

On Apple Silicon, bake_golden_in_guest runs the Ubuntu Rosetta installer for every custom image. A glibc image without Ubuntu apt sources fails before the runner contract runs.


⚠️ Shutdown waits for golden preparation

When shutdown arrives during prepare_fork_base, RunnerPool::run cannot observe it until the full download or bake finishes. An interrupted golden preparation can delay engine shutdown for the entire transfer.


🟥 Runner username injects root shell commands

When PRELOOP_RUNNER_USER contains shell syntax, runner_account_script embeds it unquoted in the root-run bake script. The injected commands execute with golden-builder privileges.


🟨 Unchecked mirror payload becomes job image

If PRELOOP_GOLDEN_URL has no valid checksum sidecar, download_release_asset installs its payload anyway. The mirror's bytes become the golden without integrity verification.

Devin Review

Comment on lines +174 to 178
// fingerprint untouched would keep a golden whose runner demands
// the previous version, so every JS action step fails with
// `bundled nodeXX is missing` against a bundle that is itself
// perfectly valid at the new pin.
"node_externals": crate::node_externals::expected_runtimes()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Account changes reuse the wrong golden

Changing PRELOOP_RUNNER_USER or PRELOOP_RUNNER_UID leaves EnvironmentSpec's fingerprint unchanged. The pool reuses an artifact baked for the old account, so jobs run against mismatched home ownership.

Learn more

A configured golden's fingerprint is computed from the base and the default runner contract, hardcoded to runner and UID 1001. Actual baking uses apply_golden_contract, which takes the configured user and UID, while as_runner_user also uses those configured values. On a restart with the same image but a different account, ensure_golden_payload accepts the existing artifact and the new job runs on the old ownership.

Example: Bake with runner/1001, then restart with PRELOOP_RUNNER_UID=2000. The existing fingerprint is reused, while the job runs as UID 2000 against a home owned by 1001.

Recommended fix: Include the effective runner user and UID in the configured-image artifact fingerprint at every path computing it, including the golden-path CLI. Keep the runtime and bake using the same account values.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +174 to +176
// fingerprint untouched would keep a golden whose runner demands
// the previous version, so every JS action step fails with
// `bundled nodeXX is missing` against a bundle that is itself

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Mirror changes leave the old golden active

Changing PRELOOP_GOLDEN_URL does not change the official golden's fingerprint. ensure_golden_payload accepts the existing pack, so the new mirror is never fetched.

Learn more

The official artifact's path is derived from EnvironmentSpec::for_base, which hashes the default or overridden OCI reference. download_prebaked_golden_with_space selects PRELOOP_GOLDEN_URL instead of OCI when set, but that URL does not enter the fingerprint. ensure_golden_payload returns immediately for an existing path, so a change to the selected release mirror cannot invalidate its previous contents.

Example: Fetch the default OCI artifact, then restart with PRELOOP_GOLDEN_URL=https://mirror.example/new-pack. The original pack remains at the same path and the mirror receives no request.

Recommended fix: Include the effective source URL or immutable content identity in the official cache key, and use the same resolution in both the pool and golden-path.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +643 to +672
/// The official golden is downloaded packed and is never baked locally.
///
/// The deleted stock-Ubuntu bake used to stand in when the official golden
/// could not be fetched, which silently ran jobs on a different image than the
/// one `runs-on: ubuntu-latest` names. There is no substitute now: an
/// unreachable pack fails the pool at startup, and the error names the
/// official golden so an operator knows to point
/// `PRELOOP_GOLDEN_OCI_REF`/`PRELOOP_GOLDEN_URL` at a reachable one.
#[tokio::test]
async fn official_golden_download_failure_is_a_startup_error() {
let fixture = Fixture::new("official-fatal", false);
let mut config = fixture.config.clone();
config.base_image = preloop_orchestrator::environment::OFFICIAL_GOLDEN.to_owned();
let provider = Arc::new(RecordingVmProvider::with_machines(&[], vec![]));
let pool = RunnerPool::new(provider.clone(), config).unwrap();

let error = pool
.run(CancellationToken::new())
.await
.expect_err("an unreachable official golden must fail the pool, not bake a substitute");
let message = error.to_string();
assert!(
message.contains("official golden"),
"the error must name the official golden: {message}"
);
assert!(
provider.snapshot().await.events.is_empty(),
"no substitute golden may be created for the official sentinel"
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Golden fixes lack before-and-after evidence

The new lifecycle tests cover changed golden behavior, but the PR provides no failing output from the base revision. The repository review guide requires that evidence for bug fixes.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread docs/vm-images.md
Comment on lines +7 to +13
A golden comes from exactly one of two sources. The **official packed golden**
is the published official GitHub runner image with `preloop-runner` baked in;
Preloop downloads it per architecture, digest-pinned, and verifies it before
use. A **configured image** (`PRELOOP_RUNNER_BASE_IMAGE`, or the `[golden]
base_image` that `preloop init` records) is baked as it is, plus the
GitHub-runner machinery described below. There is no third source and no local
fallback: a golden download that cannot complete fails the job that needed it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Older golden instructions remain discoverable

The new two-source guide coexists with older current-sounding bake instructions elsewhere. Check the operational runbooks for recipes that now describe removed behavior.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

codex and others added 9 commits October 8, 2026 00:32
# Conflicts:
#	.github/workflows/apt-indices-refresh.yml
#	.github/workflows/release-golden.yml
#	.github/workflows/release.yml
#	CHANGELOG.md
The previous run was failed by an engine restart (deploy of the migrated build), not by this tree.
# Conflicts:
#	crates/preloop-orchestrator/src/lib.rs
#	crates/preloop-orchestrator/tests/golden_fidelity.rs
#	crates/preloop-orchestrator/tests/runner_pool_lifecycle.rs
The runner-image dump ships with /var/lib/apt/lists wiped, so a step's
bare `sudo apt-get install <pkg>` fails with 'Unable to locate package'
unless it runs apt-get update first — unlike a GitHub-hosted runner.
Refresh the indices once at bake time (best-effort, 5-minute bound,
skipped on images without apt) and once when an unpacked official pack
has none, before it is frozen; forks inherit the result.

Also:

- startup_cleanup removes goldens keyed on retired environments
  (neither the official golden nor the configured image resolves to
  them, so no fingerprint rotation retires them) along with their
  fingerprint records;
- the artifact sweep reclaims payload files still named for the
  retired stock Ubuntu stems, which no fingerprint under the current
  stem reaches;
- golden_contract_script("root", …) no longer emits `; ;`, which
  `sh -n` rejected — the root contract never actually parsed;
- TestProvider::list reports the machines it created so the stale-
  machine cleanup is testable in-process.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants