Files
meeting-assistant/docs/macos-native-diagnostic.md
T
dh 45d7bde71f
PR and Push Build/Test / build-and-test (push) Canceled after 0s
PR and Push Build/Test / portable-build-and-test (push) Canceled after 0s
ci: probe macOS Ventura on existing KVM hardware
2026-10-03 20:58:12 +02:00

8.8 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, then checks Bash syntax, leaving the supplied source untouched. It does not download/extract the LongQT ISO 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 and Bash; 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.

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. 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.