diff --git a/architecture/compute-runtimes.md b/architecture/compute-runtimes.md
index ec42277ab..1f5ae1283 100644
--- a/architecture/compute-runtimes.md
+++ b/architecture/compute-runtimes.md
@@ -22,6 +22,26 @@ drive client provisioning UI, the driver attaches the shared
clients to parse Kubernetes reasons, VM cache states, or other driver-local
reason strings.
+## VM Rootfs Preparation Boundary
+
+The VM driver prepares an image rootfs and then launches it with the selected
+VM runtime. `openshell-driver-vm::provisioning` makes that hand-off explicit:
+
+```text
+existing VM image acquisition -> UnpackedImage -> VmRootfsImage
+ |
+ v
+ existing VM launch path
+```
+
+`VmRootfsMaterializer` adapts the existing rootfs preparation and ext4 image
+construction functions. Both cached-image paths use it before handing the
+resulting ext4 image to the existing VM launch path.
+
+Local image acquisition identifies Docker and Podman as distinct
+Docker-compatible image sources, while preserving the existing Docker-first,
+Podman-fallback selection policy. Registry acquisition remains a separate path.
+
The capability RPC reports driver identity, version, and the default sandbox
image used by the gateway. GPU availability stays driver-local and is validated
when a sandbox create request asks for GPU resources.
diff --git a/crates/openshell-driver-vm/src/driver.rs b/crates/openshell-driver-vm/src/driver.rs
index 90f546792..937e8f95d 100644
--- a/crates/openshell-driver-vm/src/driver.rs
+++ b/crates/openshell-driver-vm/src/driver.rs
@@ -8,10 +8,10 @@ use crate::lifecycle::{
BackendFeature, GuestInitDropin, LaunchAbortReason, LaunchPlan, LifecycleExtensionRegistry,
RestoreContext, extension_state_dir,
};
+use crate::provisioning::{RootfsMaterializationRequest, UnpackedImage, VmRootfsMaterializer};
use crate::rootfs::{
- clone_or_copy_sparse_file, create_ext4_image_from_dir_with_size, create_rootfs_image_from_dir,
- extract_rootfs_archive_to, prepare_sandbox_rootfs_from_image_root, sandbox_guest_init_path,
- set_rootfs_image_file_mode, write_rootfs_image_file,
+ clone_or_copy_sparse_file, create_ext4_image_from_dir_with_size, extract_rootfs_archive_to,
+ sandbox_guest_init_path, set_rootfs_image_file_mode, write_rootfs_image_file,
};
use crate::runtime::VmBackend;
use bollard::Docker;
@@ -203,8 +203,39 @@ struct GuestImagePayload {
#[derive(Debug, Clone)]
enum GuestImagePayloadSource {
- RegistryOciLayout { layout_dir: PathBuf },
- LocalDocker { rootfs_archive: PathBuf },
+ RegistryOciLayout {
+ layout_dir: PathBuf,
+ },
+ LocalContainer {
+ engine: LocalContainerEngine,
+ rootfs_archive: PathBuf,
+ },
+}
+
+/// The local container engine that supplied an image to the VM driver.
+///
+/// Both engines implement the Docker-compatible API used for inspect and
+/// export, but retaining their identity keeps source selection and progress
+/// reporting accurate.
+#[derive(Debug, Clone, Copy)]
+enum LocalContainerEngine {
+ Docker,
+ Podman,
+}
+
+impl LocalContainerEngine {
+ const fn image_source(self) -> &'static str {
+ match self {
+ Self::Docker => "local_docker",
+ Self::Podman => "local_podman",
+ }
+ }
+}
+
+/// A local image source backed by a Docker-compatible container engine.
+struct LocalContainerImageSource {
+ client: Docker,
+ engine: LocalContainerEngine,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -1953,7 +1984,7 @@ impl VmDriver {
async fn resolve_local_container_image(
&self,
image_ref: &str,
- ) -> Result