12 KiB
macOS 13 KVM/Cryptex compatibility diagnostic
This separate manual candidate probes Recovery readiness on the existing Ubuntu Docker daemon with KVM, the real Intel host CPU and macOS 13. It does not install macOS, erase a disk, install .NET or Apple CLT, or run Meeting Assistant. Passing proves only a fresh macOS 13+ x86_64 Recovery guest with root identity, a working launchd system domain, DiskArbitration and exactly one writable 64-GiB guest disk.
Baseline: bootstrap commit 4606de069678e8f95dfe3c7dad1bf5ce5384d30c; separate branch codex/macos-ci-kvm-compatibility. KVM, CPU passthrough, Recovery major version and guest Cryptex staging change together. This is a compatibility experiment, not a causal single-variable A/B test. The TCG/bootstrap experiment remains separate.
Reasons and remaining gaps
The existing daemon's Intel Celeron 1037U lacks AVX/AVX2; a separate diagnostic proved KVM enabled/paused state and clean exit. CPU_MODEL=host preserves actual instruction availability rather than advertising AVX2 through emulated Skylake. This candidate refuses a TCG or CPU-model fallback.
All four Swift helpers target x86_64-apple-macos13.0; the macOS 14 EventKit call has an existing macOS 13 fallback. Inspected native Mach-O files in pinned .NET SDK 10.0.401 x64 declare minos 12.0. These source/binary minima are not runtime qualification or vendor support: macOS 13 is outside Microsoft's current .NET 10 supported-OS policy. This probe does not install that SDK, compile helpers or test calendar/audio permissions.
Official CryptexFixup 1.0.5 activates without AVX2 and registers for normal, installer/Recovery and safe-mode boots. It redirects installer/updater ramrod to Apple Silicon's Rosetta Cryptex and bypasses APFS root-hash authentication on Ventura and newer. It does not emulate missing instructions. This kernel patch affects only the owned guest, never a host module.
Recovery cache gap: CryptexFixup does not replace an already running Recovery BaseSystem shared cache. Its installer/update selector targets the installed Cryptex, but this readiness-only run invokes no installer. Staging or loading it therefore proves no Recovery userland compatibility. Actual CPU/kernel behavior, guest injection, all native gates and any later installed-Cryptex/build/test behavior remain unqualified until observed.
Apple Recovery uses the pinned public InternetRecovery protocol with board ID and session/asset tokens, without Apple ID or workstation credentials. The macOS 13 selection, downloaded hash and actual guest version are retained; the hook downloads no full installer or SDK.
Entry point and dependencies
Orchestration/validation remain the .NET 10 file-based app tools/ci/MacOsNativeDiagnostic.cs. Bash/Python stay only in the existing pinned Linux/macOS boot integration.
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --help
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --validate
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --validate --source /path/to/clean/pinned/dockur-clone --cryptex-archive /path/to/CryptexFixup-1.0.5-RELEASE.zip --output /path/to/fresh/validation
--validate checks result/container contracts without Docker. With --source it verifies the actual Cryptex ZIP/bundle, source seams, generated OpenCore configuration and staging/checksum contracts, checks Bash syntax, then exercises four raw/zlib Recovery fixtures and twelve rejection cases with independent C# CRC32 readback. It also checks preservation of a successful resource snapshot after a later failed capture, leaving the supplied source untouched. It does not download/extract the LongQT ISO, verify a complete Apple Recovery image or execute the active-Lilu runtime checks. The ISO checksum is enforced during the later Docker build; active Lilu and EFI-copy checks execute only during container boot. The optional local Cryptex ZIP must match the release size/hash; omitting it downloads only the public 69,703-byte release. Use a fresh output directory. Dependencies are .NET 10, Git, Bash and Python 3 with its standard library; manual execution also requires the existing Linux/x64 Docker daemon and its existing KVM device.
The manual-only workflow keeps these owned run/cleanup entry points; validation invokes neither:
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --output artifacts/native-macos
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --cleanup --output artifacts/native-macos
Exact bootasset contract
Dockur stays pinned to 16a5b470cdd601bae8b05b02d748d7edfb36c12e. Original Recovery patcher/staging, Dockerfile, OpenCore script and active config hashes are verified before edits. Both existing QEMU image digests remain pinned; other existing upstream downloads are observed through image identity. source-hashes.json includes the generated Recovery patcher, both original/replacement daemon variants and udif_checksums.py, staged from tools/ci/macos-native-udif-checksums.py. This small Python module belongs to the existing Linux UDIF runtime; C# supplies orchestration, validation fixtures and an independent CRC32 implementation.
Run 4173 at 45d7bde71f9f5a1f7121585fe3ee9fc81f7c585f failed before QEMU started: the full macOS 14 plist pattern was absent from the macOS 13 download. The original image's hash was not retained. An independently downloaded comparison for the same board Mac-4B682C642B45593E is macOS 13.6/22G120, Apple product 042-23155, 710,918,897 bytes, SHA256 c19bd12f5cb1651b87b74d04f02a636da762ea46b81c7ebc9f205fa2a976d599. Its Apple chunklist signature and chunks verified before any changes. It is comparison evidence, not the missing run-4173 image identity.
The HFS+ catalog identifies /System/Library/LaunchDaemons/com.apple.recoveryosd.plist as file ID 57231, logical size 465 bytes and one 4,096-byte allocated block. Its exact XML SHA256 is af9d7f6c1948079bd4384d27b6882678d6fb4e338fcf6a8be8f84fceef174ad6. This variant has ProcessType=Interactive; the previous macOS 14 variant has App. The patch accepts only these two exact layouts with exactly one daemon label and original ProgramArguments=[/usr/libexec/recoveryosd]. It preserves each variant's fields and process type, removes only the XML doctype to fit the wrapper arguments, and pads to the original file size. Unknown, duplicate, malformed or wrong-argument layouts fail before image writes. The early rc.cdrom hook remains mount-only; the unchanged wrapper runs the Apple daemon.
The checksum binding validates the original flattened UDIF boundaries and CRC32 values, stages every recompressed chunk before writing, then updates only the changed mish CRC32 and koly data-fork/master CRC32. libdmg-hfsplus provides the checksum semantics; an independent C# reader matched all eight mish checksums on the unchanged comparison. Raw and inflated zlib bytes enter logical CRCs in run order; observed IGNORE runs are omitted. Unobserved ZERO runs, other compression/checksum types, overlaps and invalid boundaries are rejected. Base64 characters are replaced within the same metadata region, preserving its whitespace, length, partition tables and trailer offsets; the entire modified image is read again to verify CRCs. Apple chunklist authentication applies exclusively to the unchanged input, not the deliberately modified guest image. CRC integrity proves no Apple authenticity or native runtime gate.
The original LongQT v0.7 template, 15,884,288 bytes, is now Docker-ADD-checksummed to SHA256 287328995d4198f1b05166f087d85bf7ef66bedafe150d17ad112ac8de60051d. Runtime copies actual EFI_RELEASE/EFI/OC/Kexts, including Lilu 1.7.1, even with official OpenCore DEBUG executables. Active Lilu: executable 526,984 bytes, SHA256 0c016d93cfe40c7fa3965813175c1b991a76f3d295efd5be66ae712b4a3ffb52; Info.plist SHA256 fc885f3319f326e3af60e7965a5216b671772d39d40993ec695758bb43d6ea3a. Staging checks both hashes and bundle version. Cryptex declares Lilu 1.4.7; Lilu history includes Ventura/Sonoma installer/Recovery support before 1.7.1. Existing Lilu is kept.
CryptexFixup-1.0.5-RELEASE.zip, 69,703 bytes, SHA256 25041d94a0fe9a0261caf0ba89b36dfcb21682bf3c697a34bcaddc839576ab30, is checked in C#. Only expected Info.plist/executable files are accepted; identity/version/dependency and individual hashes are recorded. Runtime checks files before/after copying into fresh guest EFI.
Active /assets/config.plist receives exactly one enabled Cryptex immediately after enabled Lilu, preserving every other kext's order. Entry: Arch=x86_64, BundlePath=CryptexFixup.kext, ExecutablePath=Contents/MacOS/CryptexFixup, PlistPath=Contents/Info.plist, MinKernel=22.0.0, empty MaxKernel. OpenCore Kernel.Add requires dependencies first; bounds are Darwin versions. Runtime rechecks order/enabled/paths/architecture/bounds and rejects unverified /custom.plist.
No new force/beta argument is needed for actual no-AVX2 CPUs. Baseline arguments remain. Validation rejects disabling arguments, -crypt_allow_hash_validation (disables the APFS patch) and unexpected Cryptex force/beta overrides. Manifest/profile enter the boot signature; this candidate always rebuilds boot.img and accepts no old cache as evidence.
Gates, privileges and cleanup
The Apple wrapper is byte-identical to baseline: background /Volumes/installstate/readiness.sh then exec /usr/libexec/recoveryosd under the same launchd job/PID. Source evidence does not prove Apple's executable ran.
Readiness changes only minimum macOS 14 to 13. Validation normalizes that gate to 14 and requires baseline SHA256 4d428f594dac14eff64ed87b172c81ecf85ac91da8c5460cd6ec4b1d310800c3. Architecture, UID, services, disk size/writability/uniqueness, retries, proof bounds, timers and native-wait/cleanup/flush metrics remain identical. Limits stay 45 seconds per native command, 180 seconds for UID, ten minutes disk readiness, 40 minutes host and 45 minutes workflow.
Container profile: KVM=Y, CPU_MODEL=host, VERSION=13, 4-GiB guest, two guest/host CPUs, 6-GiB memory/swap and 512-MiB shared memory. Fresh anonymous /storage holds the 64-GiB disk; evidence reads /storage/13/setup.dmg. Existing resource budget checks remain.
Only device mapping: exactly /dev/kvm:/dev/kvm:rw. Inspection rejects other devices/permissions, added capabilities, device requests/rules, binds, tmpfs overrides, published ports, host networking, privileged mode, wrong limits, unexpected persistent mounts or changed CPU/OS profile. No host modules, infrastructure, secrets, SSH or app lifecycle actions are involved. Guest slirp networking remains.
Evidence retains run/profile identity, source/assets, EFI staging, container/resources, macOS 13 Recovery hash, native proof/result/outcome and cleanup. [recovery-original] logs the exact download's size/SHA256 before modifying it, including when patch failure later deletes the source. guest-container-resources.last-success.stdout.log and its timestamp/hash receipt preserve the last successful resource snapshot independently of a later failed stopped-container docker exec. Optional final Unix HMP capture includes info kvm, info status and a bounded PPM exported from /tmp; capture success passes no native gate.
Both cleanup paths keep exact token/label/ID checks. docker rm --force --volumes removes only the owned container and anonymous volume, then its exact image; no unrelated objects or pruning. Evidence stays seven days. Full native CI still needs a subsequent actual installed remote guest to build/sign helpers and pass the full suite, including five native tests without skips.