diff --git a/AGENTS.md b/AGENTS.md index 7c7e823..fd704f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,7 +81,7 @@ make format # Format Rust source make test # Run locked Rust tests make check-rust-release-manifest # Validate release-manifest fixtures offline make release-candidate # Create and locally prove one atomic candidate -make release-tool-images # Build the local Ubuntu and Fedora release tool images +make release-images # Build the local Ubuntu and Fedora release build/proof images make release-candidate-prove # Resume only missing package proofs make release-candidate-recover # Read-only retained-candidate validation make ci # Host evidence: Rust format, lint, tests, offline policy diff --git a/Makefile b/Makefile index dc9795d..ea7729f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # solstone-linux Makefile # Standalone Linux desktop observer for solstone -.PHONY: all bootstrap install format test check-observer-contract check-rust-release-manifest ci audit update-deps shellcheck install-service uninstall-service service-restart service-status service-logs versions clean clean-install release release-tool-images release-candidate release-candidate-prove release-candidate-recover legacy-python-bootstrap legacy-python-install legacy-python-format legacy-python-test legacy-python-test-only legacy-python-ci legacy-python-release legacy-python-release-test check-toolchain-env establish-toolchain rust-preflight check-cargo-deny +.PHONY: all bootstrap install format test check-observer-contract check-rust-release-manifest ci audit update-deps shellcheck install-service uninstall-service service-restart service-status service-logs versions clean clean-install release release-images release-candidate release-candidate-prove release-candidate-recover legacy-python-bootstrap legacy-python-install legacy-python-format legacy-python-test legacy-python-test-only legacy-python-ci legacy-python-release legacy-python-release-test check-toolchain-env establish-toolchain rust-preflight check-cargo-deny APP := solstone-linux UNIT := solstone-linux.service @@ -18,6 +18,9 @@ CARGO_LOCKED := --locked CARGO_DENY_VERSION := 0.20.2 CARGO_DEB_VERSION := 3.7.0 CARGO_GENERATE_RPM_VERSION := 0.21.0 +# Proof roles are provisioned images now, so keep their immutable stock bases explicit. +UBUNTU_STOCK_BASE := sha256:b8e6b596a32475661d9fcaf4a212fcc7736e0d8d1494973aefdbcc71c442d890 +FEDORA_STOCK_BASE := sha256:1eea7f82474ec19ef359ee5a5896014df434cd44c0d6ba2b937ffbe0697dec56 SHELLCHECK_SCRIPTS := scripts/build-release.sh scripts/install.sh VENV := .venv @@ -163,20 +166,18 @@ versions: rust-preflight check-cargo-deny release: release-candidate -release-tool-images: - @# The proof roles pin the stock bases from which build-tool images are provisioned. - @UBUNTU_STOCK_BASE=$$(sed -n 's/^proof_debian = "\([^"]*\)"/\1/p' packaging/release-policy.toml); \ - FEDORA_STOCK_BASE=$$(sed -n 's/^proof_rpm = "\([^"]*\)"/\1/p' packaging/release-policy.toml); \ - test -n "$$UBUNTU_STOCK_BASE" && test -n "$$FEDORA_STOCK_BASE" || { echo "error: stock image policy mismatch: expected proof_debian and proof_rpm, actual missing" >&2; exit 1; }; \ +release-images: + @# Build and proof images share the same explicitly pinned stock bases. podman build --pull=never --no-cache --file packaging/Containerfile.tools --target ubuntu-tools --tag localhost/solstone-linux-build-ubuntu \ - --build-arg "UBUNTU_STOCK_BASE=$$UBUNTU_STOCK_BASE" --build-arg "FEDORA_STOCK_BASE=$$FEDORA_STOCK_BASE" \ + --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" \ --build-arg "RUST_VERSION=$(RUST_VERSION)" --build-arg "CARGO_DEB_VERSION=$(CARGO_DEB_VERSION)" . - @UBUNTU_STOCK_BASE=$$(sed -n 's/^proof_debian = "\([^"]*\)"/\1/p' packaging/release-policy.toml); \ - FEDORA_STOCK_BASE=$$(sed -n 's/^proof_rpm = "\([^"]*\)"/\1/p' packaging/release-policy.toml); \ - test -n "$$UBUNTU_STOCK_BASE" && test -n "$$FEDORA_STOCK_BASE" || { echo "error: stock image policy mismatch: expected proof_debian and proof_rpm, actual missing" >&2; exit 1; }; \ podman build --pull=never --no-cache --file packaging/Containerfile.tools --target fedora-tools --tag localhost/solstone-linux-build-fedora \ - --build-arg "UBUNTU_STOCK_BASE=$$UBUNTU_STOCK_BASE" --build-arg "FEDORA_STOCK_BASE=$$FEDORA_STOCK_BASE" \ + --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" \ --build-arg "RUST_VERSION=$(RUST_VERSION)" --build-arg "CARGO_GENERATE_RPM_VERSION=$(CARGO_GENERATE_RPM_VERSION)" . + podman build --pull=never --no-cache --file packaging/Containerfile.tools --target ubuntu-proof --tag localhost/solstone-linux-proof-ubuntu \ + --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" . + podman build --pull=never --no-cache --file packaging/Containerfile.tools --target fedora-proof --tag localhost/solstone-linux-proof-fedora \ + --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" . release-candidate: rust-preflight @test -n "$(strip $(EXPECTED_RELEASE_COMMIT))" || { echo "error: expected release commit mismatch: expected EXPECTED_RELEASE_COMMIT, actual missing" >&2; echo "repair: make release-candidate EXPECTED_RELEASE_COMMIT= ADVISORY_DESCRIPTOR=" >&2; exit 1; } diff --git a/RELEASING.md b/RELEASING.md index b1893d3..a301d50 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -20,33 +20,41 @@ Four evidence activities remain deliberately separate: Use a clean checkout at the exact release commit. The compiler authority is `rust-toolchain.toml`; Cargo operations use `Cargo.lock`. -Regenerate the Ubuntu and Fedora build-tool images from the locally pinned stock +Regenerate all four Ubuntu and Fedora release images from the locally pinned stock bases, without pulling, then inspect their bare local image IDs: ```bash -make release-tool-images +make release-images podman image inspect --format '{{.Id}}' localhost/solstone-linux-build-ubuntu podman image inspect --format '{{.Id}}' localhost/solstone-linux-build-fedora +podman image inspect --format '{{.Id}}' localhost/solstone-linux-proof-ubuntu +podman image inspect --format '{{.Id}}' localhost/solstone-linux-proof-fedora ``` -Commit those IDs as `sha256:` in the `build_ubuntu` and `build_fedora` -roles of `packaging/release-policy.toml`. Rebuild the tool images when +Commit those IDs as `sha256:` in the matching build and proof roles of +`packaging/release-policy.toml`. Rebuild the build-tool images when `Cargo.lock`; dependency-affecting workspace or member manifests; `rust-toolchain.toml`; the Rust, cargo-deb, or cargo-generate-rpm pins; the stock base references; or `packaging/Containerfile.tools` changes. The Ubuntu image carries Cargo state warmed for the committed lockfile. The Fedora image deliberately carries no warmed Cargo registry because its offline metadata and RPM -generation path does not require one. +generation path does not require one. Rebuild the proof images when their stock +base references, runtime dependency closure, or `packaging/Containerfile.tools` +changes. Locally regenerated tool images are not guaranteed to be bit-for-bit reproducible: distro repositories, rustup distribution content, and tool build environments can change while declared versions remain the same. Prove each freshly regenerated image offline before committing its observed ID. -The `proof_debian`, `proof_rpm`, and `proof_tar` roles deliberately remain the -stock Ubuntu and Fedora bases; they are not placeholders for the build-tool -images. Keep all five policy images present locally before transaction entry. For -each proof image, observe and commit the exact normalized OS release, +The `proof_debian`, `proof_rpm`, and `proof_tar` roles use clean OS images carrying +only the runtime dependency closure, with no compiler, Rust toolchain, Cargo, or +other build tooling. Stock bases cannot install or execute the dynamically linked +observer because it requires GLib, GStreamer, and PulseAudio libraries. Debian and +RPM proofs install into the disposable proof container's live root, so their +declared dependencies are genuinely enforced; the tar proof retains its dedicated +`/proof-root`. Keep all four images backing the five policy roles present locally +before transaction entry. For each proof image, observe and commit the exact normalized OS release, package-manager output, install argv, version argv, executable path, and executable mode. Commit those values before selecting `EXPECTED_RELEASE_COMMIT`. The proof producer observes the same values inside the selected image and fails closed when diff --git a/crates/rust-release-manifest/src/candidate.rs b/crates/rust-release-manifest/src/candidate.rs index 274e335..b3e10a1 100644 --- a/crates/rust-release-manifest/src/candidate.rs +++ b/crates/rust-release-manifest/src/candidate.rs @@ -882,7 +882,7 @@ pub(crate) fn emit_lane_handoff_in( ubuntu_glibc: command_evidence("getconf", &["GNU_LIBC_VERSION"])?, ubuntu_gzip: command_first_line("gzip", &["--version"])?, ubuntu_image_digest: image_id.into(), - ubuntu_linker: command_first_line("ld", &["--version"])?, + ubuntu_linker: linker_identity()?, ubuntu_os: os_pretty_name()?, ubuntu_rustc: two_word_identity(&rustc_verbose, "rustc")?, ubuntu_tar: command_first_line("tar", &["--version"])?, @@ -949,6 +949,16 @@ fn command_evidence(program: &str, args: &[&str]) -> Result { } pub(crate) fn command_first_line(program: &str, args: &[&str]) -> Result { + let first_line = raw_command_first_line(program, args)?; + let field = if program == "rustc" && args == ["--version", "--verbose"] { + "lane rustc verbose" + } else { + program + }; + normalize_command_evidence(field, first_line) +} + +fn raw_command_first_line(program: &str, args: &[&str]) -> Result { let output = Command::new(program) .args(args) .output() @@ -966,12 +976,34 @@ pub(crate) fn command_first_line(program: &str, args: &[&str]) -> Result .filter(|line| !line.is_empty()) .ok_or_else(|| Error::new(format!("{program} identity mismatch: expected output")))? .to_owned(); - let field = if program == "rustc" && args == ["--version", "--verbose"] { - "lane rustc verbose" + Ok(first_line) +} + +pub(crate) fn linker_identity_from_line(line: &str) -> Result { + let (prefix, remainder) = if let Some(value) = line.strip_prefix("GNU ld (") { + ("GNU ld ", value) + } else if let Some(value) = line.strip_prefix("ld (") { + ("ld ", value) } else { - program + return Err(Error::new(format!( + "ld identity mismatch: expected vendor parenthetical and trailing version, actual {line}" + ))); }; - normalize_command_evidence(field, first_line) + let (_, version) = remainder.rsplit_once(") ").ok_or_else(|| { + Error::new(format!( + "ld identity mismatch: expected vendor parenthetical and trailing version, actual {line}" + )) + })?; + if version.is_empty() || version.chars().any(char::is_whitespace) { + return Err(Error::new(format!( + "ld identity mismatch: expected vendor parenthetical and trailing version, actual {line}" + ))); + } + normalize_command_evidence("ld", format!("{prefix}{version}")) +} + +fn linker_identity() -> Result { + linker_identity_from_line(&raw_command_first_line("ld", &["--version"])?) } fn two_word_identity(value: &str, expected_name: &str) -> Result { @@ -1633,7 +1665,7 @@ pub(crate) fn run_success_owned( let stderr = sanitize_process_stderr(&output.stderr); let context = (!stderr.is_empty()).then(|| format!("\nstderr: {stderr}")); return Err(Error::new(format!( - "{program} command mismatch: expected success, actual {}{}\nrepair: run make release-tool-images", + "{program} command mismatch: expected success, actual {}{}\nrepair: run make release-images", output.status, context.unwrap_or_default() ))); diff --git a/crates/rust-release-manifest/src/candidate_tests.rs b/crates/rust-release-manifest/src/candidate_tests.rs index 401e712..817aee4 100644 --- a/crates/rust-release-manifest/src/candidate_tests.rs +++ b/crates/rust-release-manifest/src/candidate_tests.rs @@ -613,7 +613,21 @@ fn container_build_failure_reports_sanitized_container_stderr() { assert!(error.contains( "stderr: error: Rust compiler mismatch: expected 1.97.1 x86_64-unknown-linux-gnu, actual different\\x01" )); - assert!(error.contains("repair: run make release-tool-images")); + assert!(error.contains("repair: run make release-images")); +} + +#[test] +fn linker_identity_canonicalizes_vendor_parenthetical_and_fails_closed() { + let identity = linker_identity_from_line("GNU ld (GNU Binutils for Ubuntu) 2.38").unwrap(); + assert_eq!(identity, "GNU ld 2.38"); + validate_identity("ubuntu_linker", &identity).unwrap(); + + let error = linker_identity_from_line("GNU ld GNU Binutils for Ubuntu 2.38") + .unwrap_err() + .to_string(); + assert!( + error.contains("ld identity mismatch: expected vendor parenthetical and trailing version") + ); } #[test] diff --git a/crates/rust-release-manifest/src/transaction.rs b/crates/rust-release-manifest/src/transaction.rs index f8f30d0..654c522 100644 --- a/crates/rust-release-manifest/src/transaction.rs +++ b/crates/rust-release-manifest/src/transaction.rs @@ -1422,12 +1422,6 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { )); } let root = Path::new("/proof-root"); - if root.symlink_metadata().is_ok() { - return Err(Error::new( - "proof isolation root mismatch: expected absent, actual present", - )); - } - fs::create_dir(root).map_err(display_error)?; let artifact_name = input .artifact .file_name() @@ -1447,7 +1441,6 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { "debian-amd64" => { let command = vec![ "dpkg".into(), - "--root=/proof-root".into(), "--install".into(), input.artifact.display().to_string(), ]; @@ -1455,7 +1448,7 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { ( command, "/usr/bin/solstone-linux", - root.join("usr/bin/solstone-linux"), + Path::new("/usr/bin/solstone-linux").to_path_buf(), command_line("dpkg", &["--version"])?, "amd64", None, @@ -1463,16 +1456,8 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { ) } "rpm-x86_64" => { - run_exact(&[ - "rpm".into(), - "--root".into(), - "/proof-root".into(), - "--initdb".into(), - ])?; let command = vec![ "rpm".into(), - "--root".into(), - "/proof-root".into(), "--install".into(), input.artifact.display().to_string(), ]; @@ -1480,7 +1465,7 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { ( command, "/usr/bin/solstone-linux", - root.join("usr/bin/solstone-linux"), + Path::new("/usr/bin/solstone-linux").to_path_buf(), command_line("rpm", &["--version"])?, "x86_64", None, @@ -1488,7 +1473,11 @@ pub fn emit_proof_handoff(input: &ProofHandoffInput<'_>) -> Result<()> { ) } "tar-x86_64" => { - fs::remove_dir(root).map_err(display_error)?; + if root.symlink_metadata().is_ok() { + return Err(Error::new( + "proof isolation root mismatch: expected absent, actual present", + )); + } let script = Path::new("/input/install.sh"); let dry_command = vec![ script.display().to_string(), diff --git a/packaging/Containerfile.tools b/packaging/Containerfile.tools index 495e288..82bc433 100644 --- a/packaging/Containerfile.tools +++ b/packaging/Containerfile.tools @@ -4,7 +4,7 @@ ARG UBUNTU_STOCK_BASE ARG FEDORA_STOCK_BASE -# Build tooling starts from the proof role because that role pins the stock base. +# All release images start from explicit stock-base digests supplied by Make. FROM ${UBUNTU_STOCK_BASE} AS ubuntu-tools-base ARG DEBIAN_FRONTEND=noninteractive @@ -13,7 +13,7 @@ ARG CARGO_DEB_VERSION RUN test -n "$RUST_VERSION" \ && test -n "$CARGO_DEB_VERSION" \ || { echo "error: Ubuntu tool version mismatch: expected nonempty RUST_VERSION and CARGO_DEB_VERSION, actual empty" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } # ca-certificates: rustup and Cargo TLS failed without a trusted certificate set. # curl: the stock image had no rustup download client. @@ -57,11 +57,11 @@ RUN rustc --version --verbose > /tmp/rustc-verbose \ && grep -Fx "release: ${RUST_VERSION}" /tmp/rustc-verbose \ && grep -Fx "host: x86_64-unknown-linux-gnu" /tmp/rustc-verbose \ || { echo "error: Rust compiler mismatch: expected ${RUST_VERSION} x86_64-unknown-linux-gnu verbose banner, actual different" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } RUN actual="$(cargo deb --version)" \ && test "$actual" = "cargo-deb ${CARGO_DEB_VERSION}" \ || { echo "error: cargo-deb mismatch: expected 'cargo-deb ${CARGO_DEB_VERSION}', actual '${actual:-unavailable}'" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } FROM ubuntu-tools-base AS ubuntu-cargo-fetch WORKDIR /src @@ -71,7 +71,7 @@ RUN cargo fetch --locked FROM ubuntu-tools-base AS ubuntu-tools COPY --from=ubuntu-cargo-fetch /root/.cargo /root/.cargo -# Build tooling starts from the proof role because that role pins the stock base. +# All release images start from explicit stock-base digests supplied by Make. FROM ${FEDORA_STOCK_BASE} AS fedora-tools ARG RUST_VERSION @@ -79,7 +79,7 @@ ARG CARGO_GENERATE_RPM_VERSION RUN test -n "$RUST_VERSION" \ && test -n "$CARGO_GENERATE_RPM_VERSION" \ || { echo "error: Fedora tool version mismatch: expected nonempty RUST_VERSION and CARGO_GENERATE_RPM_VERSION, actual empty" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } # ca-certificates: rustup and Cargo require a trusted TLS certificate set. # curl: rustup requires a download client. @@ -104,11 +104,35 @@ RUN rustc --version --verbose > /tmp/rustc-verbose \ && grep -Fx "release: ${RUST_VERSION}" /tmp/rustc-verbose \ && grep -Fx "host: x86_64-unknown-linux-gnu" /tmp/rustc-verbose \ || { echo "error: Rust compiler mismatch: expected ${RUST_VERSION} x86_64-unknown-linux-gnu verbose banner, actual different" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } RUN actual="$(cargo-generate-rpm --version)" \ && test "$actual" = "cargo-generate-rpm ${CARGO_GENERATE_RPM_VERSION}" \ || { echo "error: cargo-generate-rpm mismatch: expected 'cargo-generate-rpm ${CARGO_GENERATE_RPM_VERSION}', actual '${actual:-unavailable}'" >&2; \ - echo "repair: run make release-tool-images" >&2; exit 1; } + echo "repair: run make release-images" >&2; exit 1; } # Fedora packaging only reads manifests and the Ubuntu-built executable; a cold # Cargo registry was empirically verified, so no warmed Cargo state belongs here. + +# Proof images are deliberately runtime-only; build tooling does not belong here. +FROM ${UBUNTU_STOCK_BASE} AS ubuntu-proof +ARG DEBIAN_FRONTEND=noninteractive +# libglib2.0-0: the observer dynamically links GLib. +# libgstreamer1.0-0: the observer dynamically links GStreamer core. +# libpulse0: the observer dynamically links PulseAudio. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libglib2.0-0 \ + libgstreamer1.0-0 \ + libpulse0 \ + && rm -rf /var/lib/apt/lists/* + +# Proof images are deliberately runtime-only; build tooling does not belong here. +FROM ${FEDORA_STOCK_BASE} AS fedora-proof +# glib2: the observer dynamically links GLib. +# gstreamer1: the observer dynamically links GStreamer core. +# pulseaudio-libs: the observer dynamically links PulseAudio. +RUN dnf install -y \ + glib2 \ + gstreamer1 \ + pulseaudio-libs \ + && dnf clean all diff --git a/packaging/release-policy.toml b/packaging/release-policy.toml index a4000dd..d64d183 100644 --- a/packaging/release-policy.toml +++ b/packaging/release-policy.toml @@ -1,31 +1,31 @@ build_ubuntu = "sha256:73180481993bc9b41b5a03267a446a2d8cdcefcd7164a378324d5bb3bdd9f1a5" build_fedora = "sha256:12362ddec42ba597c99ac5b25341bc5d674d4f3dee7819ff2faf787309c5e5f4" -proof_debian = "sha256:b8e6b596a32475661d9fcaf4a212fcc7736e0d8d1494973aefdbcc71c442d890" -proof_rpm = "sha256:1eea7f82474ec19ef359ee5a5896014df434cd44c0d6ba2b937ffbe0697dec56" -proof_tar = "sha256:b8e6b596a32475661d9fcaf4a212fcc7736e0d8d1494973aefdbcc71c442d890" +proof_debian = "sha256:b75fef7c2910a6f21e068dae7efe1747a4efd51a95e0b5aaa468202df09f24b9" +proof_rpm = "sha256:a8f1a1871d8030a9e5fbf464b1b9a9d9a726faf2567da29c0feda91d27f68a77" +proof_tar = "sha256:b75fef7c2910a6f21e068dae7efe1747a4efd51a95e0b5aaa468202df09f24b9" # Operator-owned values: verify these inside the provisioned digest images and # commit any corrections before selecting EXPECTED_RELEASE_COMMIT. [debian-amd64] -image_digest = "sha256:b8e6b596a32475661d9fcaf4a212fcc7736e0d8d1494973aefdbcc71c442d890" +image_digest = "sha256:b75fef7c2910a6f21e068dae7efe1747a4efd51a95e0b5aaa468202df09f24b9" os_release = "Ubuntu 22.04.5 LTS" package_manager_version = "Debian 'dpkg' package management program version 1.21.1 (amd64)." -install_command = ["dpkg", "--root=/proof-root", "--install", "/input/solstone-linux_1.0.0-1_amd64.deb"] +install_command = ["dpkg", "--install", "/input/solstone-linux_1.0.0-1_amd64.deb"] version_command = ["/usr/bin/solstone-linux", "--version"] executable_path = "/usr/bin/solstone-linux" executable_mode = 493 [rpm-x86_64] -image_digest = "sha256:1eea7f82474ec19ef359ee5a5896014df434cd44c0d6ba2b937ffbe0697dec56" +image_digest = "sha256:a8f1a1871d8030a9e5fbf464b1b9a9d9a726faf2567da29c0feda91d27f68a77" os_release = "Fedora Linux 42 (Container Image)" package_manager_version = "RPM version 4.20.1" -install_command = ["rpm", "--root", "/proof-root", "--install", "/input/solstone-linux-1.0.0-1.x86_64.rpm"] +install_command = ["rpm", "--install", "/input/solstone-linux-1.0.0-1.x86_64.rpm"] version_command = ["/usr/bin/solstone-linux", "--version"] executable_path = "/usr/bin/solstone-linux" executable_mode = 493 [tar-x86_64] -image_digest = "sha256:b8e6b596a32475661d9fcaf4a212fcc7736e0d8d1494973aefdbcc71c442d890" +image_digest = "sha256:b75fef7c2910a6f21e068dae7efe1747a4efd51a95e0b5aaa468202df09f24b9" os_release = "Ubuntu 22.04.5 LTS" package_manager_version = "installer portable-tar" install_command = ["/input/install.sh", "--prefix", "/proof-root", "/input/solstone-linux-1.0.0-linux-x86_64.tar.gz"]