Repository navigation
Releases: zyvorai/zyvor-guestkit
Releases · zyvorai/zyvor-guestkit
Release list
v1.2.5
[1.2.5] - 2026-10-03
Fixed
- Online snapshots failed at the filesystem freeze, and the agent claimed it had frozen.
guestkitdruns aszyvor-agentwith an empty capability set, but the QGA handlersguest-fsfreeze-freeze/-thawranfsfreezethemselves, so every freeze failed withOperation not permittedand KubeVirt failed the snapshot of any VM with PVC-backed disks (measured on Ubuntu 24.04 under KubeVirt 1.9: a freshly installed agent cannot freeze; withCAP_SYS_ADMINit can). The unit file already says privileged operations run inguestkitd-exec; freeze and thaw now do: a newfsfreezeaction in the helper (freeze/thawonly, always/) andexecutor_ipc::fsfreeze()for the agent, falling back to an in-process call only when no helper is running (agent run as root). Also,snapshot_hooks::freeze_filesystems/thaw_filesystemstreated a successfully spawnedfsfreezeas success (status().is_ok()), sosnapshot.preparereportedfs_frozen: truewhile nothing was frozen; they now use the same checked path. Covered by a test that starts the realguestkitd-execwith a fakefsfreeze. Requires the helper service (zyvor-guest-agent-exec.service, enabled by the packages) to be running. - Linux agent never connected to the hypervisor channel on stock distros. The virtio port (
/dev/virtio-ports/org.qemu.guest_agent.0) isroot:root 0600butguestkit-agent.serviceruns aszyvor-agent, so the agent started, loggedfailed to open virtio channel, and KubeVirt reported no guest agent. The DEB, RPM and tarball now ship60-zyvor-guest-agent.rules(groupzyvor-agent, mode 0660 fororg.qemu.guest_agent.0andcom.zyvor.guestkit.0) and reload/trigger udev on install. Verified on Ubuntu 24.04 under KubeVirt 1.9: with the rule the agent connects (agentConnected), without it it does not. - Concurrent NBD allocate+connect —
NbdDevice::connectholds a cross-processflock(/run/lock/guestkit-nbd.lock, fallback/tmp) across free-device selection andqemu-nbd -c, and retries the next free index on failure. Fixes races where two mounts claimed the same/dev/nbdN(fluxvm#104:/dev/nbd0p1 already mounted, wrong guest-agent token).new()no longer claims a device index before connect.
Added
- Offline inject on
run_migrate_repair— optionalinject_jsonappends hostname, network files, users, services, first-boot scripts, cloud-init user-data, Active Directory rejoin, Windows KMS reactivation, and RDP enable to the repair plan. Empty or"null"is a no-op.guestkit migrate-repairhas no inject flag; h2kvm passes the JSON from Python. - Live guest fix helpers —
live_fix_commands()returns shell lines (regenerate initramfs, update GRUB, optionally removeopen-vm-tools).run_live_plan(commands, dry_run=False)runs those lines on the machine where Python is executing. It does not SSH. - Docs: README is now a landing page — the detailed sections moved into
docs/(quick-start,who-does-what,capabilities,oss-vs-enterprise,platform-layout,repository-layout,documentation-map, ...); every earlier README anchor still resolves.
v1.2.4
[1.2.4] - 2026-09-18
Changed
- Docs — install instructions use the GitHub Release and
pip install zyvor-guestkit. GHCR examples pinv1.2.4. Serial console withoutgrubbyis documented. crates.ioguestkitis still 0.3.2.
v1.2.3
[1.2.3] - 2026-09-18
Fixed
- Serial console without
grubby— Photon and other guests have nogrubby. Kernel console args are written into the bootloader the guest already uses (grub2, BLS, syslinux, extlinux, zipl) instead of failing first-boot with exit 127. - XFS remount — pick a valid remount option set after
norecoveryinspect mounts. - Guest root resolution — path resolve uses the real guest
/, notsda1/sda2. - Windows agent inject — mount the Windows root read-write for offline
guestkitdinject.
Changed
- PyPI — Python distribution is
zyvor-guestkit, owned by pypi.org/user/zyvor.hypersdk-guestkitis retired. Import name staysguestkit. Wheel artifact:zyvor_guestkit-*.whl. - GHCR registry — container images publish/pull from
ghcr.io/zyvorai/{zyvor-ui,zyvor-api,guestkit-worker}(wasghcr.io/hypersdk). - Docs — customer wording is now users; CE vs Enterprise licensing is spelled out; docs site is Docusaurus.
Removed
- Zyvor company terms and the distribution acceptance gate.
v1.2.2
[1.2.2] - 2026-09-06
Added
- Web Image Vault TUI parity — OSS
deploy/uirenders inspect inventory tabs, Assurance (doctor / migration plan / passport / repair preview + gated apply), Profiles/Issues, and read-only Files browse (guestkit.explore). Docs: using-the-dashboard.md. POST /api/v1/vms/:id/profileandPOST /api/v1/vms/:id/explore— enqueue existing/new worker ops; repair-plan acceptsdry_runquery (default true).- OpenAPI documents profile, explore, and repair
dry_run.
Changed
- Inspect worker summary — larger package/service samples (200/100) for UI lists.
v1.0.1
[1.0.1] - 2026-08-15
Fixed
guestkit-worker's Docker release build failed for every release past
the0.xline —crates/guestkit-worker/Cargo.tomlpinned its own
guestkitpath dependency toversion = "0.3.3"; the worker's
Dockerfilenever copies aCargo.lockin, so each build freshly
resolves dependencies and cargo enforces the version requirement even
against apathdependency —1.0.0didn't satisfy^0.3.3and the
Publish GHCR Release Imagesjob inv1.0.0's release run failed.
Dropped the version pin (path-only, matchingzyvor-apiand
zyvor-guest-agent's existing pattern for the same in-workspace
dependency).
v1.0.0
[1.0.0] - 2026-08-15
Added
- GitHub Action for the Passport CI gate (
action.yml) — reusable
composite action wrappingdoctor → migrate-plan → passport emit → passport verifyas a single CI step; installs a checksum-verified
release binary, no build step. Dogfooded against a real disk image by
.github/workflows/passport-gate-demo.ymlon every change. See
docs/devops/01-passport-ci-gate.md. - Native OpenAI tool-calling for the AI copilot (
src/ai/rig_tools.rs)
— rig-coreAgentBuilder/multi_turnwith real JSON-schema tool
definitions, replacing regex/JSON-scraped completion text for OpenAI.
xAI/Anthropic/Ollama still use the original text-instructed loop. - Cross-run AI memory (
src/ai/memory.rs) — a repeateddoctor --ai/
migrate-plan --airun against the same VM folds a summary of prior
findings into the query, capped at the last 20 runs.
GUESTKIT_AI_MEMORY_DIR/GUESTKIT_AI_MEMORY=0to relocate/disable. - MCP server for the AI copilot (
src/ai/mcp.rs,--features mcp) —
guestkit mcp-serve <disk> [--target <target>]exposes the same 6
read-only evidence tools over stdio to Claude Desktop / other MCP
hosts, independent of guestkit's own agent loop. guestkit fleet wave-plan(src/fleet/wave.rs) — orders a fleet's
disk images into dependency-aware migration waves (DB-role priority +
NFS storage-dependency edges, Kahn's-algorithm topological sort,
cycles reported rather than dropped or arbitrarily ordered).guestkit fleet watch(src/fleet/baseline.rs) — scheduled drift
monitoring: diffs each VM's current evidence against a stored golden
baseline (first run establishes it),--fail-on-driftfor pipeline
gating. Includes a KubernetesCronJobtemplate
(deploy/helm/zyvor/templates/fleet-drift-watch-cronjob.yaml) as the
reference scheduled-invocation path.- Helm chart CI —
ci.yml's newhelm-chartjob runshelm lintand
helm templateagainstdeploy/helm/zyvor, which previously had zero
CI coverage, across default values, every optional PVC-backed feature
enabled at once, hostPath-backed persistence, and all three real
deployment overlays (values-ci.yaml,values-k3s.yaml,
values-prod.yaml). - Manual-dispatch workflow for NBD-dependent tests
(self-hosted-nbd-tests.yml) — runs the full test suite with none of
ci.yml's NBD skips, for self-hosted runners with working loop/NBD
support.workflow_dispatchonly, deliberately never wired to
pull_request/push.
Fixed
- k3s E2E's
zyvor-apipod crash-looped on every run — the Helm chart's
defaultzyvorApi.agentMtls.enabled: truerequiresAGENT_BOOTSTRAP_TOKEN
(zyvor-api refuses to start otherwise: "AGENT_MTLS_BIND_ADDR is set but
AGENT_BOOTSTRAP_TOKEN is unset"), but the Deployment template only ever
wired that env var — and the Secret holding it — inside the
zyvorApi.auth.enabledblock.values-ci.yaml(mTLS on, full auth off)
hit exactly that gap;values-prod.yamlmasked it by having both auth and
a token on together, andvalues-k3s.yamlworked around it by disabling
mTLS outright (its own comment already described the bug). Decoupled the
zyvor-api-authSecret andAGENT_BOOTSTRAP_TOKENenv var from
auth.enabled— gated only on the token itself being set, matching what
zyvor-api's own config validation actually requires — and set a
CI-only placeholder token invalues-ci.yamlso the E2E job now exercises
the mTLS path instead of crash-looping.ci.yml's Helm Chart job now also
helm templates all three real overlays (values-ci.yaml,
values-k3s.yaml,values-prod.yaml) so a rendering break here is caught
without needing a live k3s cluster. - k3s E2E multi-round debugging — the job was failing on every run;
fixing it required peeling through several layers, each masking the
next:poll_job(deploy/scripts/e2e-smoke.sh) only recognized
"completed"as terminal, so a"failed"job status looked
identical to "still pending" for the full 5-minute poll budget, and
the worker's own error message (live_status.error) was never
printed. Now treatsfailed/cancelled/timeoutas terminal and
prints the error immediately.install-k3s-ubuntu.shnever got the loop/NBD device setup
ci.ymlneeded earlier this session (guestkit-worker's pod
bind-mounts the host's/dev, so the same root:disk-0660-node /
EACCES-looks-like-timeout issue applies here too). Added it —
confirmed live afterward:inspect/doctorboth complete in one
poll with a real bootability score. This specific k3s stack path
does not hit the deeper NBD-attach limitationci.yml's plain
cargo testjob still has to skip around.curl -sfswallows the response body on any non-2xx status, so the
next failure (provision) looked like an empty response
(Expecting value: line 1 column 1) instead of a real API error.
Added acurl_or_diehelper (splits HTTP status from body via
curl's-w, prints both on failure).- That revealed a real HTTP 500:
"No operating system found in disk image"fromprovision(POST /vms/{id}/provision, which mounts
the disk synchronously in zyvor-api's own process), on the same
imagedoctorhad just inspected successfully. Suspected (and
partially fixed) an unawaited asyncmigration-planjob racing
provision's own mount viaNbdDevice::find_available_device
(src/disk/nbd.rs) — that function does check device availability
and connect as two separate, unlocked steps with no cross-process
coordination, a real bug now flagged with a code comment — but
serializing migration-plan before provision did not fix it,
disproving the race as this failure's cause. provision_vm's.map_err(|e| ApiError::internal(e.to_string()))
(crates/zyvor-api/src/routes/vms.rs) only shows anyhow's outermost
.context()layer via plainDisplay. Changed toformat!("{e:#}")
(alternate Display, full chain) forprovision_vm's three
guestkit/export::kubevirt-derivedmap_errcalls — correct and
worth keeping, but the next run's error was still byte-identical to
before, because there was no chain to reveal:mount_all_ro
(src/cli/commands/mod.rs) returnsOption<String>, not
Result<String>, and.context("No operating system found in disk image")on aNone(anyhow'sContextimpl forOption) produces
an error with no wrapped source at all — the context message
genuinely is the entire error. Left the other ~90
.map_err(|e| ApiError::internal(e.to_string()))sites in the crate
alone; most wrap simple error types (serde_json,std::io) where
.to_string()isn't lossy.- The real swallowed information was one layer further down:
mount_all_rocallsg.inspect_os().unwrap_or_default()—
inspect_os()failing for any reason (guestfs launch issue, mount
error, permission problem) collapses to the identical empty-roots
Noneas "genuinely no OS found," discarding whateverinspect_os's
real error was before it could reach any context message. Changed to
log the real error (log::warn!) before discarding it. Didn't widen
mount_all_ro'sOption<String>return type toResult— it's used
across 9 files where callers only ever branch onSome/None, and
that ripple is out of scope for this investigation. Widened the
defaultEnvFilter(was "nothing enabled" withoutRUST_LOGset,
now falls back towarnglobally) somount_all_ro's new
log::warn!— and any otherlog::*!from guestkit's dependency
graph — actually reaches the pod's logs. First attempt at this also
added an explicittracing_log::LogTracer::init()call, reasoning
thatzyvor-apionly sets up atracingsubscriber and guestkit
logs through the plainlogfacade — wrong, and a real
regression:tracing-subscriber's "tracing-log" feature (on by
default) already bridgesloginto the subscriber as part of
.init(), so the explicit call double-registered the globallog
logger and panicked at startup withSetLoggerError, crash-looping
zyvor-apiagain. Confirmed viakubectl logsfrom the next E2E
run (once the "dump pod logs on failure" step below existed to
capture it) and reproduced in an isolated 10-line binary before
re-pushing — removed the explicitLogTracer::init()call and the
now-unneeded directtracing-logdependency; the isolated repro
confirmedlog::warn!still reaches the subscriber correctly
without it. - Also: nothing in
k3s-e2e.ymlever captured pod logs on failure —
every fix in this list up to this point was diagnosed purely from
HTTP response bodies, each requiring a full ~20-40min re-run just to
test. Added a failure-only step dumpingkubectl logs(all
containers, prefixed by pod) for every deployed component, plus
get pods -o wideanddescribe pods. - That finally showed it: no panic, no error, no
mount_all_ro
warning —inspect_os()genuinely returned an empty root list.
validate_root_partition/validate_initrd_boot_partition
(src/guestfs/inspect.rs) treat mount/extraction failures as
"not a valid root" by design, not as errors — a real mount
failure and "genuinely no OS" are indistinguishable at that layer
on purpose (LVM volumes on read-only NBD devices can legitimately
fail to mount for benign reasons).validate_initrd_boot_partition
is the cirros-cloud-image path — root filesystem lives inside the
initrd, not on a directly-mountable partition — and shells out to
zcat <initrd> | cpio -tto look inside it.
crates/zyvor-api/Dockerfilenever installedcpio(orgzip),
unlikecrates/guestkit-worker/Dockerfile's otherwise-ident...
v0.3.20
[0.3.20] - 2026-08-07
Added
- DevOps runbooks —
docs/devops/Passport CI gate, offline repair worker,
air-gap packages/VirtIO, fleet analyze, cutover weekend, failure triage,
cloud disk sources (S3/GCS/Azure), forensic IR, SBOM/inventory CI. - GitHub Wiki — operator cheat sheets (Passport, day-0, packages, env,
TUI, KubeVirt/GCF/agent) linked from README / docs INDEX. GUESTKIT_PACKAGE_MIRROR— HTTP fallback viacurl/wgetwhen host
dnf/apt-getis missing or fails (comma-separated bases; optional
{name}/{ext}templates). Helps macOS hosts stage PackageInstall.- Domain-leave first-boot RunOnce —
windows-domain-leavestages
GuestKitDomainLeaveRunOnce (Add-Computer -WorkGroupName) in addition
to Tcpip/Winlogon markers (DC computer-account delete still needs live AD). - Worker performance + migration profiles —
guestkit.profilejobs run
the same CLIInspectionProfileimplementations asguestkit profile. - Offline ServiceOperation / CommandExec staging — enable/disable via
systemd wants Symlink/FileDelete; start/restart and other commands stage
guestkit-firstboot-live.servicewhen chroot cannot run them. - UEFI-aware
fix-grub --force— detects ESP under the guest root and
runsgrub-install --target=x86_64-efi|arm64-efi --efi-directory=… --no-nvram --removable(BIOS path unchanged). - Windows AES/RC4 SAM NT-hash write —
rescue -o reset-password --password
reconstructs the SYSKEY bootkey from SYSTEM LSA class names, derives the
hashed bootkey from SAMF, and writes an AES-128-CBC (or legacy RC4)
encrypted NT hash into the userVblob. Falls back to SAM blank + RunOnce
net userif SYSTEM/bootkey/crypto fails. - PackageInstall host fetch — with
GUESTKIT_PACKAGE_FETCH=1, offline
plan applydownloads missing.rpm/.debon the host (dnf download/
yumdownloader/apt-get download) intoGUESTKIT_PACKAGE_CACHEor
~/.cache/guestkit/packages, then stages the first-boot oneshot as before. - Offline GRUB repair (
fix-grub) —rescue -o fix-grubbind-mounts
proc/sys/dev and runs chrootgrub2-mkconfig/grub-mkconfig/
update-grub;--forcealso attemptsgrub-installonto the NBD device
(BIOS) or EFI removable path when an ESP is present;
if chroot mkconfig fails, stagesguestkit-firstboot-grub.service.
--export-planwrites the first-boot FileWrite/Symlink ops.check-grub
remains diagnose-only. - System Reserved / ESP detection — offline Windows evidence probes
non-OS NTFS/FAT volumes forbootmgr+ BCD (legacy System Reserved) or
EFI Microsoft Boot (ESP). Surfaces onwindows.system_reserved, promotes
bcd_store_found/bootmgr_found, fixesesp_present(no longer aliased
to bootmgr). Boot check BOOT-014, migration MIG-W-011, Passport
flagssystem_reserved_layout+bcd_store_found. - Windows driver/hotfix migration diagnostics — offline HotFix registry +
$NtUninstall*/$hf_mig$/ CBS.log tail; VirtIO.syspresence on
WindowsDriverEntry.sys_present; BCD UTF-16 probe for testsigning /
nointegritychecks. Migration MIG-W-012 (hotfixes/servicing),
MIG-W-013 (VirtIO files); Passporthotfix_count/
hf_mig_present/driver_signature_enforcement. Hive paths resolve via
guestfs mount root. - Offline activation / ghost-NIC depth — SOFTWARE ProductId/EditionID/
DigitalProductId +oeminfo.ini→windows.activation(OEM/Retail/Volume);
SYSTEMEnum\PCIremnant/problem NICs →ghost_nics; Tcpip static
interfaces →static_nic_configs. Enriches MIG-W-006/007/008; Passport
activation_channel,ghost_nic_count,static_nic_count. - Offline BitLocker / VSS enrichment —
BitLockerStatus\BootStatus(On →
hard block), FVE/$BitLocker/fvevol artifacts (offline_uncertainwarning),
VSS+swprv services + System Volume Information inference. Fills
windows.bitlocker/windows.vssfor MIG-W-005/009; Passport
bitlocker_uncertain. - Day-0 plan/rescue depth —
windows-dhcp/windows-dns/linux-hostname
profiles; rescueenable-rdp/enable-winrm/set-timezone; Windows
set-hostnameapplies registry day-0 plan (was Linux/etc/hostname). - Cutover Passport signed-enterprise workflows —
passport keygen(Ed25519
seed + pubkey); emit--issuer/--expires-hours; verify--trust-keys
allowlist +--max-age-hoursfreshness gate (signing/verify needagent). - Production Helm —
values-prod.yaml: PVC-backed Postgres/Redis/MinIO
(eval stillemptyDir); Ingress TLS + cert-manager annotations; pinned
GHCRv0.3.19images; nightly image-vault backup CronJob + backup PVC. - Guest Control Fabric poll telemetry — airgap reconciler records per-method
latency + transport attempts; Redis fleet rollup;GET .../guest/poll-telemetry
(VM + fleet);guest/statusexposeslastPoll/telemetryMode. - Fleet analyze performance — parallel
--jobs/GUESTKIT_FLEET_JOBS
(default min(4, CPUs)); evidence-cache hit skips remount. - Cloud disk source depth — persistent
~/.cache/guestkit/cloudpulls;
S3GUESTKIT_S3_ENDPOINT/AWS_ENDPOINT_URL;azure://URIs; GCS
gcloud storagefallback; CI recipescripts/ci-cloud-disk-sources.sh. - Offline heuristic remediations + linux-grub —
systemctl enable/disable
→ Symlink/FileDelete; fail2ban/auditd/chrony/apparmor/sshd enable offline;
ufw default deny FileEdit; day-0linux-grub(--grub-timeout/
--grub-cmdline) for/etc/default/grub. - Offline PackageInstall staging — when
GUESTKIT_PACKAGE_CACHE(or
host_cache) holds matching.rpm/.deb, offlineplan applystages
packages + a first-boot systemd oneshot instead of skipping; optional
GUESTKIT_PACKAGE_FETCH=1downloads missing packages on the host first;
live install unchanged. - Windows offline password set —
rescue -o reset-password --password
prefers AES/RC4 SAM NT-hash write via SYSKEY; falls back to SAM blank +
HKLM RunOncenet userfor first boot; omit--passwordto blank only.
Changed
- Docs — Roadmap parked list cleared; CLI / quick-reference / feature guide /
fix-plans updated for AES SAM passwords,fix-grub, and
GUESTKIT_PACKAGE_FETCHoffline staging.
v0.3.19
[0.3.19] - 2026-08-06
Added
- Cutover Passport —
guestkit passport emit|verify: versioned CI-gateable
assurance artifact (evidence digest, boot/migration scores, FixPlan digest,
Windows BitLocker hard-block +windows_offline_ready, optional live
attestation via agent-proxy, optional Ed25519 sign). Suite handoff points to
HyperSDK (export) + hyper2kvm (convert/deploy). Web:POST /vms/:id/passport- dock download. Worker op
guestkit.passport.
- dock download. Worker op
plan generate -p windows-domain-leave— offline domain→workgroup
markers (--workgroup, defaultWORKGROUP).plan generate -p windows-timezone— offlineTimeZoneKeyName
(--timezone).plan generate -p windows-static-ip— offline static IPv4 on a known
interface GUID (--interface-guid --ip --mask [--gateway] [--dns]).
v0.3.18
[0.3.18] - 2026-08-06
Added
plan generate --profile windows-hostname— offline ComputerName + Tcpip
Hostname / NV Hostname (--hostnamerequired). Apply with--skip-backup.plan generate --profile windows-winrm— WinRM Automatic +
WINRM-HTTP-In-TCPfirewall rule. Apply with--skip-backup.Symlink/FileDeleteplan ops — offline guestfsln_sf/rm
(used by hardenedlinux-ssh).plan generate -p linux-ssh --user+--key/--key-file— inject
authorized_keysinto the enable plan.- Windows
rescue -o reset-password— offline SAM blank (chntpw-style)
viaregistry-write/ libhivex; clears password so interactive logon works. rescue --export-plan PLAN.yaml— emit a reviewable FixPlan for
enable-ssh / inject-ssh-key / set-hostname / reset-password / fix-fstab.- Offline
DriverInject— apply useshost_dir/GUESTKIT_VIRTIO_WINinject_windows_driver_dirwhen built withregistry-write,agent.
migrate-repair --virtio-win DIR— wires VirtIO host tree into
migration repairDriverInject(same as$GUESTKIT_VIRTIO_WIN).- Heuristic offline remediations — firewalld enable →
Symlink, ufw →
conf edit, more sshd FileEdits; preview tags live-only ops as offline-skip.
Fixed
linux-sshplan fidelity — wants enable viaSymlink(notCommandExec ln), removes/etc/ssh/sshd_not_to_be_run, matches rescue enable path.from_security_profilenaming — plan profile/tags follow the inspect
profile name (not hardcoded"security").rescue check-grub— diagnose-only rename (fix-grubkept as alias).
v0.3.17
[0.3.17] - 2026-08-06
Added
plan generate --profile linux-ssh— offline Linux SSH enablement
(systemdssh/sshdwants symlink +/etc/ssh/sshd_config.d/99-guestkit.conf
withPubkeyAuthentication yes). Apply with--skip-backup.planFileWriteoperation — create/overwrite a guest file offline.rescue inject-ssh-key—--user+--key/--key-fileappends to
authorized_keys.rescue set-hostname—--hostnamewrites/etc/hostnameand patches
/etc/hosts.
Fixed
rescue enable-ssh— actually creates the systemd wants symlink and
writes an sshd drop-in (previously only printed a manualsystemctlnote);
write drop-in before unit enable; prefer real wants dirs / relative
symlinks /ln -sfnwhen guestfsln_sfrejects unit paths.- Windows
guest-fsfreeze-freeze/thaw— route to VSS marker shadows
instead of the Linuxfsfreezebinary so KubeVirt quiesced snapshots work
on Windows guests.