Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d4d843d
docs(pack): specify portable selected-base composition
auser Nov 10, 2026
86fe72e
feat(pack): stage selected-base composition trust boundaries
auser Nov 10, 2026
eebb114
feat(pack): stage selectable base and verified component inputs
auser Nov 11, 2026
8dcdee5
feat(pack): measure ordered component filesystem closure
auser Nov 11, 2026
1e9eb4c
refactor(bundle): keep stream and admission modules bounded
auser Nov 11, 2026
d1736ca
docs(pack): preserve local signer enrollment boundary
auser Nov 11, 2026
728a419
test(pack): sign bootable PR base and restore sealed pair fixture
auser Nov 11, 2026
9f47e4c
test(pack): add synthetic CLI guest-value recipe
auser Nov 11, 2026
59019cb
ci(pack): retain feature-built CLI for KVM witness
auser Nov 11, 2026
30042b4
ci(pack): pin PR Linux source through test fixture
auser Nov 11, 2026
527c9f8
fix(compose): preserve requested Dev posture and retained tar proof
auser Nov 11, 2026
d852d6c
docs(pack): record transient Dev composition boundary
auser Nov 11, 2026
07d602a
fix(packs): bind musl loader to component architecture
auser Nov 11, 2026
4229dd6
fix(bundle): require local signer enrollment for schema eight
auser Nov 10, 2026
db7cefd
test(bundle): refuse substituted enrolled local key
auser Nov 10, 2026
cb7333c
test(bundle): exercise pre-v8 local enrollment separately
auser Nov 11, 2026
1bfcb6d
feat(compose): stage one-run Dev bundle without installing
auser Nov 11, 2026
1cf9021
fix(compose): activate PR runtime before entrypoint readiness
auser Nov 11, 2026
5be9d21
fix(compose): seal authored entrypoint into guest filesystem
auser Nov 11, 2026
8be6429
perf(test): benchmark optimized composition CLI
auser Nov 12, 2026
c25b06a
refactor(compose): separate verified data preparation from boot staging
auser Nov 12, 2026
eb270a1
feat(machine): model local composition as data-only source
auser Nov 12, 2026
c48a202
fix(machine): pin local recipe bytes before named launch
auser Nov 12, 2026
2ca61d3
Merge origin/main into portable composition branch
auser Nov 12, 2026
af90ef5
feat(machine): admit and dispatch named local compositions
auser Nov 12, 2026
4d9e5d2
Merge remote-tracking branch 'origin/main' into feat/portable-composi…
auser Nov 12, 2026
a6f82a2
feat(machine): retain verified local base selection for named starts
auser Nov 12, 2026
7056495
Merge origin/main into portable composition branch
auser Nov 12, 2026
e8b95b4
test(composition): validate arm64 Image before PR base signing
auser Nov 12, 2026
4b95627
fix(guest): retain write-only console for detached workload
auser Nov 12, 2026
736b622
refactor(machine): isolate spec construction and restore CLI gates
auser Nov 12, 2026
c05051a
docs(composition): record named Firecracker and HVF guest witnesses
auser Nov 12, 2026
95c8bf9
fix(ci): use installed toolchain for PR evidence checks
auser Nov 12, 2026
3cef8ea
fix(runtime): guard trusted snapshot publication lifecycle
auser Nov 12, 2026
02a5f6e
docs(perf): trace named composition boot and record custody limits
auser Nov 12, 2026
8c42b35
docs(evidence): record arm64 Linux KVM composition boot
auser Nov 13, 2026
737f55f
docs(adr): define immutable warm boot read leases
auser Nov 13, 2026
9a589dd
feat(fs): seal budgeted Linux boot inputs in memfd
auser Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 265 additions & 0 deletions .github/workflows/composable-python-layer-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,283 @@ on:
- scripts/build-python-runtime-layer.py
- scripts/test_build_python_runtime_layer.py
- crates/mvm-build/src/runtime_layer.rs
- crates/mvm-build/src/pr_test_synthetic_base.rs
- crates/mvm-cli/src/commands/vm/exec.rs
- crates/mvm-cli/src/commands/vm/exec/**
- crates/mvm-hostd/src/plan_admission/composition.rs
- crates/mvm-contract/src/plan/bundle.rs
- crates/mvm-core/src/registry_pack/runtime_layer.rs
- crates/mvm-core/examples/verify-runtime-layer-fixture.rs
- crates/mvm-build/src/guest_runtime/**
- crates/mvm-build/src/guest_runtime.rs
- crates/mvm-client/src/portable_boot_assets.rs
- crates/mvm-runtime/src/runtime_evidence.rs
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
# Uses the ordinary unsigned guest builder, not a release signer. Signing
# happens only in the PR job below and nothing is published as a CLI release.
pr-guest-bins:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
uses: ./.github/workflows/guest-bins.yml
permissions:
contents: read

pr-cli-bins:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 45
permissions:
contents: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- name: Build unpublished fixture-only CLI and host helpers
env:
MVM_EMBED: "0"
# This job only builds for the hosted runner. The toolchain action
# installed 1.97.1; avoid a second toolchain-file component install.
RUSTUP_TOOLCHAIN: "1.97.1"
run: |
set -euo pipefail
cargo build --locked --profile release-witness --bin mvmctl \
--features user,pr-test-guest-runtime,mvm-cli/pr-test-synthetic-base
cargo build --locked --profile release-witness -p mvm-hostd --features pr-test-guest-runtime \
--bin mvm-host-agent --bin mvm-signer-helper --bin mvm-network-endpoint
for binary in mvmctl mvm-host-agent mvm-signer-helper mvm-network-endpoint; do
test -x "target/release-witness/$binary"
done
target/release-witness/mvmctl --version
- name: Retain unpublished CLI for an authorized KVM witness
uses: actions/upload-artifact@v7
with:
name: pr-test-cli-${{ github.run_id }}-${{ github.run_attempt }}
path: |
target/release-witness/mvmctl
target/release-witness/mvm-host-agent
target/release-witness/mvm-signer-helper
target/release-witness/mvm-network-endpoint
if-no-files-found: error
retention-days: 1

signed-pr-guest-runtime:
needs: pr-guest-bins
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
actions: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- uses: sigstore/cosign-installer@v3
- uses: actions/download-artifact@v8
with:
name: mvm-guest-bins-attempt-${{ github.run_attempt }}
path: ${{ runner.temp }}/pr-runtime
- name: Sign and verify PR-only guest runtime inputs
env:
MVM_PR_TEST_RUNTIME_DIR: ${{ runner.temp }}/pr-runtime
MVM_PR_TEST_PULL_REQUEST: ${{ github.event.pull_request.number }}
RUSTUP_TOOLCHAIN: "1.97.1"
run: |
set -euo pipefail
version="$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["workspace"]["package"]["version"])')"
archive="$MVM_PR_TEST_RUNTIME_DIR/mvm-guest-bins-v${version}.tar.gz"
test -f "$archive" && test ! -L "$archive"
test -f "${archive}.sha256" && test ! -L "${archive}.sha256"
(cd "$MVM_PR_TEST_RUNTIME_DIR" && sha256sum --check "$(basename "${archive}.sha256")")
for blob in "$archive" "${archive}.sha256"; do
cosign sign-blob --yes --new-bundle-format --bundle "${blob}.bundle" "$blob"
done
cargo test --locked -p mvm-build --features pr-test-guest-runtime --lib \
guest_runtime::evidence::tests::signed_pr_runtime_authenticates_exact_identity_and_refuses_tampering \
-- --ignored --exact
cargo test --locked -p mvm-client --features pr-test-guest-runtime --lib \
portable_boot_assets::tests::pr_test_local_acquisition_checks_assembled_runtime_independently \
-- --ignored --exact
- name: Retain synthetic PR quartet for a builder-VM CLI witness
uses: actions/upload-artifact@v7
with:
name: pr-test-guest-runtime-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/pr-runtime/mvm-guest-bins-v*.tar.gz*
if-no-files-found: error
retention-days: 1

signed-pr-base-input:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- uses: sigstore/cosign-installer@v3
- name: Authenticate only the PR's nonbootable synthetic base input
env:
MVM_EMBED: "0"
MVM_PR_TEST_BASE_DIR: ${{ runner.temp }}/pr-base
MVM_PR_TEST_PULL_REQUEST: ${{ github.event.pull_request.number }}
RUSTUP_TOOLCHAIN: "1.97.1"
run: |
set -euo pipefail
cargo test --locked -p mvm-build --features pr-test-synthetic-base --lib \
pr_test_synthetic_base::tests::write_synthetic_pr_base_fixture_for_ci \
-- --ignored --exact
cosign sign-blob --yes --new-bundle-format \
--bundle "$MVM_PR_TEST_BASE_DIR/image-set.bundle" \
"$MVM_PR_TEST_BASE_DIR/image-set.json"
cargo test --locked -p mvm-build --features pr-test-synthetic-base --lib \
pr_test_synthetic_base::tests::real_pr_signature_accepts_only_unchanged_exact_identity \
-- --ignored --exact

signed-pr-bootable-base:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
artifact_id: 13746141841
artifact_size: 503284622
artifact_digest: d12a6026d2858f1414f33a7692abf8693a4b158f547abe323fe19170eb301bfd
- arch: aarch64
artifact_id: 13745508933
artifact_size: 498828250
artifact_digest: fb0458726a70c67fc72e3948d4f9a7801549ab1d8002af0a398f84bbd9db9c5e
permissions:
contents: read
actions: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
# The emitter records this exact clean source checkout; it never builds images.
- name: Select reviewed test-only Linux source
id: test-images-ref
run: echo "ref=$(cat tests/fixtures/composable-images-source.ref)" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v6
with:
repository: tinylabscom/mvm-images
ref: ${{ steps.test-images-ref.outputs.ref }}
path: mvm-images
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- uses: sigstore/cosign-installer@v3
- name: Authenticate prebuilt Linux input and emit a local test set
env:
GH_TOKEN: ${{ github.token }}
MVM_PR_TEST_BASE_DIR: ${{ runner.temp }}/pr-bootable-base
MVM_PR_TEST_SOURCE_DIR: ${{ runner.temp }}/pr-linux-source
MVM_PR_TEST_ZIP: ${{ runner.temp }}/linux-layer-${{ matrix.arch }}.zip
run: |
set -euo pipefail
source_sha=$(cat tests/fixtures/composable-images-source.ref)
arch=${{ matrix.arch }}
artifact_id=${{ matrix.artifact_id }}
artifact_size=${{ matrix.artifact_size }}
artifact_digest=${{ matrix.artifact_digest }}
test "$(git -C mvm-images rev-parse HEAD)" = "$source_sha"
gh api repos/tinylabscom/mvm-images/actions/runs/42778535622 |
jq -e --arg sha "$source_sha" '
.id == 42778535622 and .repository.full_name == "tinylabscom/mvm-images" and
.head_repository.full_name == "tinylabscom/mvm-images" and
.event == "pull_request" and
(.pull_requests | length == 1 and .[0].number == 78) and
.path == ".github/workflows/build.yml" and .run_attempt == 1 and
.head_sha == $sha and .status == "completed" and .conclusion == "success"
' >/dev/null
gh api "repos/tinylabscom/mvm-images/actions/artifacts/$artifact_id" |
jq -e --arg sha "$source_sha" --arg digest "sha256:$artifact_digest" \
--arg arch "$arch" --argjson id "$artifact_id" --argjson size "$artifact_size" '
.id == $id and .name == ("linux-layer-" + $arch) and
.size_in_bytes == $size and .digest == $digest and .expired == false and
.workflow_run.id == 42778535622 and .workflow_run.head_sha == $sha and
.workflow_run.repository_id == 1376194514 and
.workflow_run.head_repository_id == 1376194514
' >/dev/null
gh api "repos/tinylabscom/mvm-images/actions/artifacts/$artifact_id/zip" > "$MVM_PR_TEST_ZIP"
printf '%s %s\n' "$artifact_digest" "$MVM_PR_TEST_ZIP" | sha256sum --check
mkdir -p "$MVM_PR_TEST_SOURCE_DIR"
unzip -j "$MVM_PR_TEST_ZIP" \
"default-microvm-vmlinux-$arch" \
"default-microvm-rootfs-$arch.ext4" \
"default-microvm-rootfs-$arch.tar.gz" \
-d "$MVM_PR_TEST_SOURCE_DIR"
for name in "default-microvm-vmlinux-$arch" \
"default-microvm-rootfs-$arch.ext4" "default-microvm-rootfs-$arch.tar.gz"; do
test -s "$MVM_PR_TEST_SOURCE_DIR/$name" && test ! -L "$MVM_PR_TEST_SOURCE_DIR/$name"
sha256sum "$MVM_PR_TEST_SOURCE_DIR/$name"
done
case "$arch" in
x86_64)
readelf -h "$MVM_PR_TEST_SOURCE_DIR/default-microvm-vmlinux-$arch" |
grep -q 'Machine:.*Advanced Micro Devices X86-64'
kernel_format=kernel:elf
;;
aarch64)
# The arm64 loader consumes Image, not ELF; Linux's ARMd
# magic at byte 56 identifies the selected boot bytes.
magic=$(od -An -c -j56 -N4 "$MVM_PR_TEST_SOURCE_DIR/default-microvm-vmlinux-$arch" |
tr -d ' \n')
test "$magic" = ARMd
kernel_format=kernel:image
;;
esac
python3 -m venv "$RUNNER_TEMP/local-manifest-venv"
PURE=1 "$RUNNER_TEMP/local-manifest-venv/bin/pip" install \
-r mvm-images/scripts/requirements-local-manifest.txt
"$RUNNER_TEMP/local-manifest-venv/bin/python" mvm-images/scripts/emit-local-manifest.py \
--images-checkout mvm-images --arch "$arch" --builder-cache-contract 5 \
--out "$MVM_PR_TEST_BASE_DIR" \
--artifact default_tenant_workload_kernel "$kernel_format" \
"$MVM_PR_TEST_SOURCE_DIR/default-microvm-vmlinux-$arch" \
--capability default_tenant_workload_kernel virtio_vsock \
--artifact default_tenant_workload_rootfs ext4 \
"$MVM_PR_TEST_SOURCE_DIR/default-microvm-rootfs-$arch.ext4" \
--artifact default_tenant_workload_rootfs tar_gz \
"$MVM_PR_TEST_SOURCE_DIR/default-microvm-rootfs-$arch.tar.gz" \
--capability default_tenant_workload_rootfs virtio_blk
- name: Sign and verify the bootable PR-only base
env:
MVM_EMBED: "0"
MVM_PR_TEST_BASE_DIR: ${{ runner.temp }}/pr-bootable-base
MVM_PR_TEST_PULL_REQUEST: ${{ github.event.pull_request.number }}
MVM_PR_TEST_ARCH: ${{ matrix.arch }}
RUSTUP_TOOLCHAIN: "1.97.1"
run: |
set -euo pipefail
cosign sign-blob --yes --new-bundle-format \
--bundle "$MVM_PR_TEST_BASE_DIR/image-set.bundle" \
"$MVM_PR_TEST_BASE_DIR/image-set.json"
cargo test --locked -p mvm-build --features pr-test-synthetic-base --lib \
pr_test_synthetic_base::tests::real_pr_signature_accepts_only_unchanged_exact_identity \
-- --ignored --exact
- name: Retain the signed PR base for a builder-VM composition witness
uses: actions/upload-artifact@v7
with:
name: pr-test-bootable-base-${{ matrix.arch }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/pr-bootable-base/
if-no-files-found: error
retention-days: 1

signed-input:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ template-registry-s3 = [
# in-process) needs it turned on. `mvm-runtime` is named directly too since
# this crate depends on it straight, not only through `mvm-cli`.
test-support = ["mvm-cli/test-support", "mvm-runtime/test-support"]
pr-test-guest-runtime = ["mvm-cli/pr-test-guest-runtime"]
# Debug-only Apple Silicon warm-restore validation. The runtime additionally
# requires a debug build on macOS/aarch64 before advertising the capability.
hvf-live-validation = [
Expand Down
5 changes: 5 additions & 0 deletions crates/mvm-agentd/src/bin/mvm-guest-agent/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ pub(crate) fn apply_activation(
// The telemetry signing key is the egress client's, mode 0400; read it now
// or the listener can never answer a session.
crate::telemetry::capture_session_keys_before_privilege_drop();
// The guest console is root-owned. Retain only a write-only descriptor
// before dropping to the workload identity; detached children receive a
// duplicate for output, never the agent's inherited read/write stdio.
#[cfg(target_os = "linux")]
crate::interactive::retain_detached_console_before_drop();
guest_mount::drop_guest_agent_privilege(guest_mount::WORKLOAD_UID, guest_mount::WORKLOAD_GID)?;

boot_state.set_activation(ActivationState::Activated);
Expand Down
Loading