План MicroVM: parity, production и релиз¶
Этот план ведёт текущий libkrun MicroVM путь к функциональному соответствию OpenShell v0.1.4 и production-релизу. Он дополняет описание MicroVM и runtime acceptance matrix. Статус пункта меняется только после реализации и проверки на целевой платформе.
Текущее состояние¶
Локальный драйвер уже умеет создавать и восстанавливать VM, импортировать OCI
rootfs и tar, хранить persistent overlay, выполнять команды и PTY, отдавать
логи, копировать файлы, проксировать policy и передавать SSH direct-tcpip.
Поддержан двухдисковый bootstrap/workload путь: явный
OPENSHELL_VM_BOOTSTRAP_IMAGE или доверенный OPENSHELL_SANDBOX_IMAGE как
bootstrap, отдельный read-only workload disk и persistent overlay. Gateway
пока блокирует создание VM до host supervisor/isolation и acceptance.
Открытые ограничения parity inventory:
vm.bootstrap-image— двухдисковый путь и digest recovery проходят macOS acceptance; в guest init добавлены веткиimage-prepдля versioned tar и OCI layout payload. Host stages tar/OCI layout, запускает prep worker на pinned bootstrap, проверяет ext4 через debugfs и только затем публикует workload disk. Live macOS acceptance проходит; статус остаётсяpartialдо Linux KVM и полного отказного/security набора.vm.gateway-create-ready— сквозной Gateway create/start/ready на VM заблокирован.- Linux KVM amd64/arm64, установленная поставка/upgrade/rollback и GPU/VFIO требуют целевых хостов; GPU/VFIO и PID limits пока не поддерживаются.
- Открытые/частичные пункты остальных
vm.*записаны вquarshell-driver-vm/vm/testdata/openshell-v0.1.4-parity.json.
Этап 1. Зафиксировать контракт и parity¶
- Сверить каждый
vm.*ID с upstream v0.1.4 и закрепить минимальную совместимую семантику: create/start/stop/delete, recovery, overlay, identity, bootstrap, OCI/tar, exec/PTY, logs/events, SSH forwarding и GPU/resource capabilities. - Для bootstrap определить отдельные поля конфигурации и их приоритет: bootstrap image — системное окружение агента и supervisor; workload image — пользовательский rootfs. Запретить неявное использование workload image как bootstrap fallback.
- Перенести двухдисковую схему OpenShell: bootstrap root disk, отдельный
read-only workload image disk в
/image-rootfs, persistent writable overlay для sandbox. Подготовку образа выполнять через bootstrap prep VM; проверять наличие/image-rootfsи guest agent digest перед публикацией disk в immutable cache. Реализация проходит macOS ARM64/HVF live acceptance; Linux KVM acceptance остаётся отдельным release gate. - Сохранить в durable state bootstrap/workload digests и ссылки на оба immutable cache entry; recovery должен проверять оба артефакта, держать оба cache lease и не загружать новый image по mutable tag.
- Проверить порядок bootstrap/workload/overlay disks fake worker-тестом;
live OCI acceptance подтверждает mount workload image, writable persistent
/sandboxoverlay через Stop/Start и recovery с тем же pinned digest после перемещения image tag. Unit tests подтверждают отказ для отсутствующего, усечённого и повреждённого ext4 workload disk. Linux KVM acceptance остаётся отдельным обязательным gate. - До запуска VM проверять ext4 superblock bootstrap/workload disks; тесты подтверждают отказ при отсутствующем, усечённом и повреждённом workload disk; guest mount и сохранение overlay проверены live OCI acceptance на macOS ARM64.
- Довести lifecycle extension registry до полной parity. Уже реализованы
и покрыты тестами registry, детерминированный порядок и обратный cleanup,
активация только зарегистрированными доверенными labels, валидация
descriptor/launch plan и required backend features (guest-init-dropins
поддержан, unsupported features отклоняются), приватное
состояние расширений, runtime environment без записи в durable state,
ConfigureLaunch/BeforeLaunch/BeforeRestore/AfterRestore/Stopped/ProcessExited/
AfterDelete hooks и исполняемые guest-init drop-ins через overlay. Live
acceptance на macOS ARM64 проверяет label activation и выполнение drop-in.
Ошибка ConfigureLaunch/digest/BeforeRestore при восстановлении теперь
запускает обратную очистку extensions с причиной
restore-failed; ошибка AfterRestore тоже запускает очистку. Регрессионные unit tests проверяют оба пути. Остаётся расширить capability resolver для новых backend features и проверить отказные сценарии на Linux KVM. - Покрыть bootstrap fallback, отказ при несовместимой OCI platform,
восстановление по digest после перемещения tag и отсутствие env/credentials в
durable state (
TestDefaultHostConfigUsesTrustedDefaultImageAsBootstrapFallback,TestOCIImageIndexRejectsMissingPlatformDescriptor, live OCI acceptance,TestCreateWithBootstrapImagePersistsSeparatePinnedDisks). - Зафиксировать OpenShell семантику
rootfs_tar_pathвместе сimage: tar становится workload filesystem, а image ref сохраняется как запрошенная identity.TestValidateSpecAllowsRootfsTarWithImageSelector, Create unit test и live acceptance покрывают этот путь; более ранний GPU отказ остаётся приоритетным. - Проверять, что workload/proxy environment и secrets отсутствуют в
durable state (
TestCreateWithBootstrapImagePersistsSeparatePinnedDisks). - Обновить parity JSON доказательствами новых unit и live acceptance tests;
сохранять
partialдо выполнения оставшихся parity и target gates.
Ожидаемый контракт конфигурации после реализации:
type MicroVMImages struct {
Bootstrap OCIImageRef // pinned linux/amd64 или linux/arm64
Workload OCIImageRef // sandbox image; независим от bootstrap
}
func (c MicroVMImages) Validate() error {
if c.Bootstrap.Digest == "" {
return errors.New("MicroVM bootstrap image must be pinned by digest")
}
if c.Workload.Digest == "" {
return errors.New("MicroVM workload image must be pinned by digest")
}
return nil
}
Это целевая форма API, не утверждение о существующей реализации. Сначала
согласовать её с текущим driver_config, Gateway API и upstream поведением.
P0: bootstrap prep VM (первый обязательный вертикальный срез)¶
До Gateway и расширений закрыть подготовку workload image тем же способом, что
OpenShell. Сегодня vm.go вызывает host-side prepareRootfsTarWorkloadDisk /
prepareOCIWorkloadDiskFromResolved, а populateRootfsFromOCI применяет слои
на хосте. Это сохраняет digest проверки, но не обеспечивает upstream границу
подготовки в VM.
Целевой интерфейс подготовки должен отделять staging, запуск и публикацию:
type PrepInput struct {
BootstrapDisk string // immutable, digest pinned
PayloadDisk string // OCI layout или rootfs tar; без registry credentials
OutputDisk string // временный ext4, только для этого запуска
SourceDigest string
Platform string // linux/amd64 или linux/arm64
}
func prepareWorkloadInGuest(ctx context.Context, in PrepInput) (string, error) {
if err := validatePrepInput(in); err != nil { return "", err }
if err := runPrepWorker(ctx, in, "CAUTEM_VM_INIT_MODE=image-prep"); err != nil {
return "", err
}
if err := verifyExt4Directory(in.OutputDisk, "/image-rootfs"); err != nil {
return "", fmt.Errorf("guest image-prep did not produce image-rootfs: %w", err)
}
return publishImmutableCache(ctx, in.OutputDisk, in.SourceDigest, in.Platform)
}
Порядок реализации и проверки:
- Добавить versioned payload format для tar и verified OCI layout. Registry auth остаётся только на host pull; в payload идут blobs/manifest и source identity, без env, token или credential helper данных.
- Добавить guest init
image-prep: смонтировать payload read-only, output disk writable, развернуть во временныйimage-rootfs.partial, применить ownership/runtime настройки, затем атомарно переименовать вimage-rootfs,sync, unmount и завершить. - Запустить выделенный libkrun worker с тем же pinned bootstrap disk; передать режим через allowlisted env. Учесть cancellation, timeout, console tail, process-group cleanup и удалить staging при любом отказе.
- Не доверять одному exit code: проверить ext4 и наличие обязательных файлов, права/метаданные и source identity до атомарной cache publication. Только затем записать workload digest в durable state.
- Включить формат payload и guest init digest в cache key; использовать cache lock/lease и проверять публикацию при конкурентной сборке.
Минимальные тестовые примеры для этого среза:
func TestPrepPublishesOnlyVerifiedImageRootfs(t *testing.T) { /* fake worker + ext4 verifier */ }
func TestPrepCancellationRemovesStagingAndDoesNotCache(t *testing.T) { /* cancel worker */ }
func TestPrepPayloadNeverContainsRegistryCredentials(t *testing.T) { /* scan staged payload */ }
Сначала unit/fake-worker tests на каждом PR; затем live acceptance на macOS
ARM64/HVF и Linux KVM amd64/arm64. Пока хотя бы один target lane не прошёл,
vm.bootstrap-image остаётся partial, а release gate закрыт.
Этап 2. Закончить host supervisor и изоляцию¶
- Запускать Gateway-managed worker через host supervisor с закрытым аутентифицированным control channel, ограниченными RPC и управляемым shutdown. Обычный VM worker и image-prep worker теперь получают закрытый parent-liveness pipe и завершают свою process group вместе с broker при аварийном выходе Gateway. Это закрывает путь orphan worker, но не заменяет отдельный managed supervisor и host filesystem isolation.
- На Linux применять private user/mount namespaces, private mount propagation, dedicated non-root host identity и явный UID/GID map; проверять их до старта broker/VM.
- На macOS ARM64 запускать подписанный и проверенный worker bundle через
отдельный supervisor path. Gateway preflight теперь требует
CAUTEM_VM_WORKER_TEAM_ID, проверяет подпись черезcodesign --verify --strictи совпадение Team ID; managed supervisor и live Gateway acceptance ещё не реализованы. - Не передавать host workspace mount. Хранить workspace в persistent guest overlay и проверять изоляцию host filesystem/network/vsock.
- Обеспечить отзыв токенов, rotation/retry, process-group cleanup и запрет возрождения завершённого workload.
- Оставить Gateway fail-closed, пока supervisor и обе целевые платформы не пройдут обязательные acceptance.
Проверка capability перед публикацией:
if !hostIsolation.Ready() || !hostSupervisor.Ready() {
return status.Error(codes.FailedPrecondition,
"MicroVM host isolation is not ready")
}
Этап 3. Gateway сквозной путь¶
- Реализовать Gateway create → driver create → start → guest readiness → Get/List status с observed workload identity.
- Проверить cancel создания, timeout, клиентский disconnect и cleanup при ошибке на каждом переходе.
- Проверить stop/delete, рестарт Gateway с running/stopped intent, потерю worker/broker, повторный Start и терминальный exit.
- Проверить extension activation только после policy-controlled label, unknown extension rejection и очистку extension state при delete.
- Проверить
WatchSandboxlogs/events, replay tail и закрытие stream. - Проверить interactive exec stdin/output/resize/signals/exit status, copy-in/out, policy proxy и SSH TCP forwarding через публичные CLI/API.
- Проверить rootfs upload ограничения, OCI auth, digest pinning и отказ при перемещении tag во время recovery.
Этап 4. Матрица тестов до релиза¶
На каждом изменении:
cd quarshell-driver && go test ./...
cd ../quarshell-gateway && go test ./...
cd ../quarshell-runtime && go test ./...
cd .. && task vm:parity-check compat:inventory docs:strict
Локальный live acceptance на macOS Apple Silicon/HVF:
Проверяет реальный worker/guest путь, lifecycle/recovery, OCI digest/auth, PTY, identity, policy и SSH TCP roundtrip. Успех на macOS не заменяет Linux KVM или Gateway acceptance.
Обязательные целевые CI runners:
# Linux amd64 и Linux arm64 runners с /dev/kvm и настроенным runtime/rootfs
task vm:live-linux
# Сквозной публичный Gateway/CLI тест с VM provider
CAUTEM_VM_GATEWAY_ACCEPTANCE=1 task vm:gateway-acceptance
vm:live-linux запускает существующий KVM acceptance script для amd64/arm64.
Workflow quarshell-driver/.github/workflows/live-vm-kvm.yml теперь запускает
оба native KVM lane на push и pull request; в настройках защиты ветки нужно
сделать оба job обязательными и настроить self-hosted runners с меткой
cautem-kvm. Gateway task
vm:gateway-acceptance пока отсутствует: его нужно реализовать и включить в CI.
Все driver test packages локально cross-compiled для linux/amd64 и
linux/arm64 (GOOS=linux GOARCH=... CGO_ENABLED=0 go test -run '^$' -exec
/usr/bin/true ./...). Это подтверждает компиляцию Linux-путей, но не исполняет
тесты и не заменяет KVM acceptance.
Обязательные gates должны завершаться ошибкой, а не skip, если host
capability отсутствует.
Ресурсные и отказные проверки: создание/удаление при заполнении cache, ограничение CPU/RAM, повторный старт после kill worker, повреждённый state, отсутствующий KVM/HVF, недоступный registry, неверные digest и отказ namespace. GPU/VFIO запускать только на выделенном совместимом runner; до этого явно advertise capability как unsupported.
Этап 5. Установка, обновление и откат¶
- Собирать отдельные подписанные артефакты для Linux amd64/arm64 и macOS arm64; фиксировать версии libkrun, guest agent, worker и layout state.
- Публиковать checksums, SBOM, provenance и инструкции проверки подписи.
- Прогнать чистую установку без локальных Go replacements и unpublished зависимостей; затем upgrade с предыдущей стабильной версии.
- Проверить миграцию state на копии данных, несовместимую версию state, interruption миграции и rollback. Миграция должна быть атомарной или сохранять восстанавливаемую резервную копию.
- Проверить install/upgrade/uninstall на чистых Linux amd64/arm64 и macOS ARM64 машинах; убедиться, что uninstall не удаляет пользовательские VM data без явного действия оператора.
- Выпустить canary, проверить ошибки старта/изоляции/восстановления и только затем продвигать тот же digest артефактов в stable.
Критерий разрешения релиза¶
Релиз разрешён, когда обязательные parity IDs реализованы либо явно объявлены вне заявленного профиля продукта; Gateway create-ready и supervisor/isolation прошли acceptance на macOS ARM64 и Linux KVM amd64/arm64; install/upgrade/ rollback пройдены на опубликованных артефактах; тесты, security checks, docs и parity inventory зелёные. GPU/VFIO можно исключить из первого профиля только если продукт не заявляет GPU и capability не рекламируется.
