Skip to content

Releases: IngmarStein/havm

havm 1.0.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 20:45
47b23d3

The first stable release of havm — a zero-config CLI for running Home Assistant OS on Apple Silicon using Apple's native Virtualization framework.

The 0.x series was the public preview; 1.0.0 marks the CLI, config schema, and data layout as stable. havm run is still one command from download to boot, and this release does not touch your existing disk image, NVRAM, or machine identifier.

Highlights

  • Zero-config first boot — downloads the HA OS image, decompresses it (statically linked liblzma; no xz binary needed), copies it to a 32 GiB sparse APFS disk, and boots over UEFI. No kernel extraction, no initrd, no hand-tuned command line.
  • Persistent and headless — the EFI variable store and a stable machine identifier survive reboots, so your MAC address doesn't move. Built to run under brew services and start at login.
  • Bridge networking by default — the VM gets its own LAN address so Home Assistant discovery works; falls back to NAT at runtime if the binary lacks the com.apple.vm.networking entitlement.
  • Graceful shutdown — on SIGTERM/Ctrl+C: Supervisor API → debug SSH on 22222 → ha host shutdown, with force-stop only as a last resort.
  • USB accessory passthrough (macOS 27+) — attach coordinators and other USB devices from a menu bar item; they hot-attach to the running VM.
  • SSH access — an optional virtual CONFIG disk carries your authorized_keys, imported on boot for root SSH on port 22222.
  • Prometheus metrics — a built-in HTTP endpoint for VM and USB state.
  • havm import-utm — bring an existing UTM Home Assistant VM over.
  • --console — attach to the guest's virtio console (hvc0) for debugging.

Requirements

  • macOS 15 or later on Apple Silicon
  • USB accessory passthrough requires macOS 27 or later
  • Bridge networking requires the com.apple.vm.networking entitlement (Apple approval); prebuilt releases are signed with it, self-compiled ad-hoc builds fall back to NAT

Install

brew install ingmarstein/havm/havm
havm run

Or build from source (Xcode 27+, Swift 6.4) with ./scripts/build.sh release.

Known limitations

  • Shutdown is SSH/API-driven. HA OS on aarch64 uses PSCI, so the ACPI power button is ignored. With neither ha.api_token nor ssh.authorized_keys configured, havm can only force-stop the VM.
  • ha host shutdown needs the SSH add-on on port 22; the debug SSH on 22222 runs shutdown -h now directly and is the more reliable path.
  • USB passthrough needs macOS 27 — on macOS 15–26 discovery is skipped with a log message.
  • UTM import is partial — QEMU-based VMs (UTM's default for x86 guests) can't be imported, and SSH keys configured in UTM don't carry over.

Full documentation: https://ingmarstein.github.io/havm/

havm 0.3.1

Choose a tag to compare

@github-actions github-actions released this 28 Aug 18:49
a307f61

What's new in v0.3.1

Fixed: guest IP resolution broke the "ready" detection

havm run could report Guest reachable at fe80:: and then time out
waiting for the Home Assistant web UI, because the hostname resolver
result was copied into a too-small buffer — IPv6 addresses came back
truncated (first 8 bytes plus stack garbage). macOS 15's mDNS resolver
returns the IPv6 link-local record for homeassistant.local first,
which surfaced the bug; it could in principle hit any macOS version.

The resolver now copies the address correctly, walks the full result
list, and prefers IPv4 — the address type the guest reliably gets
via DHCP. Link-local IPv6 (fe80::/10) results are skipped (they need
a scope ID to be usable); a global IPv6 address is used as a fallback
on IPv6-only setups, and the banner URL is bracketed accordingly
(http://[...]:8123).

If you were affected, the guest itself was always fine — only the
reported address/URL was wrong. As an interim workaround,
network.guest_hostname: <IPv4> in ~/.config/havm/config.yml skips
mDNS entirely.

Full Changelog: v0.3.0...v0.3.1

havm 0.3.0

Choose a tag to compare

@github-actions github-actions released this 28 Aug 14:58
401eee2

What's new in v0.3.0

Runs on macOS 15 or later

The minimum deployment target drops from macOS 27 (Golden Gate) to
macOS 15 (Sequoia). All core functionality — EFI boot, bridge/NAT
networking, the interactive serial console, the SSH-key CONFIG disk,
graceful shutdown, and Prometheus metrics — works unchanged on
macOS 15 and 26.

USB accessory passthrough (the menu bar hot-attach of Zigbee
coordinators etc.) remains a macOS 27 feature: it depends on the
AccessoryAccess framework and VZUSBPassthroughDevice. On macOS 15–26,
havm run logs a message and continues without it. The CONFIG disk is
unaffected — SSH key import still works on every supported release.

New --log-level option

havm run --log-level {debug,info,warning,error} overrides
logging.level from the config file — handy for debugging a Homebrew
service without touching its config.

Reliability & diagnostics

  • logging.level: error now maps to .error (previously mapped to
    .critical, which suppressed all output).
  • An unexpected VM stop now exits with status 1, so launchd
    (brew services) reliably restarts the process.
  • A warning is logged when the Home Assistant web UI doesn't respond
    within 5 minutes of the guest becoming reachable.
  • Config hot-reload parse failures, PID file write/remove errors, MAC
    address regeneration, and console cmdline patch failures are now
    logged instead of failing silently.
  • MemorySize parsing rejects values at the exact 2^64 boundary.

Dependencies

  • swift-log 1.15.0

Full Changelog: v0.2.4...v0.3.0

havm 0.2.4

Choose a tag to compare

@github-actions github-actions released this 11 Aug 18:34
8619638

What's new in v0.2.4

Config file overrides now actually apply

Keys in config.yml like vm.cpu_count, vm.memory_size,
vm.disk_size, and haos.release_channel were silently ignored: the
YAML parser matches keys literally, and these fields had no mapping for
their documented snake_case names. Settings such as a custom memory size
therefore had no effect even when the config file was in the right
place. They're now honored — and configs written by havm import-utm
(always snake_case) work too.

havm run reports which config file it uses

On startup havm run now logs the exact config path it looked at,
including (not found — using defaults) when there's no file — so a
config placed in the wrong location (e.g. ~/.config/havm/config.yml
when running as a Homebrew service) is immediately obvious.

More robust shutdown & networking

  • A timeout now bounds the SSH-based shutdown step, so a stalled guest
    kernel can no longer hang the shutdown defer block indefinitely.
  • DHCP lease results are cached, avoiding repeated reads of
    /var/db/dhcpd_leases while the guest is still booting.
  • Clearer message when bridge networking falls back to NAT (an empty
    interface list definitively means the com.apple.vm.networking
    entitlement is missing).

Documentation

  • The docs site supports deep links to sections (e.g. #data-layout)
    with copy-link buttons on every heading.
  • havm import-utm now documents that only Apple Virtualization-based
    UTM VMs can be imported; QEMU-based VMs are not supported.
  • Clarified that the config location differs when running as a Homebrew
    service.
  • Documented that the USB accessory menu bar app name is set by macOS
    (the launching terminal, or "unknown app"), not by havm.

What's Changed

  • chore(deps): bump actions/cache from 4 to 6 by @dependabot[bot] in #4

New Contributors

Full Changelog: v0.2.2...v0.2.4

v0.2.3

Choose a tag to compare

@github-actions github-actions released this 21 Jul 22:01
bf4a990

What's new in v0.2.3

Console boot messages (--console)

The --console flag now shows the full Linux kernel boot log on your
terminal — no more blank screen during startup. havm automatically
patches the kernel command line to enable the virtio serial console, so
you can follow the boot process from the first systemd message onwards.
Fresh installs also boot directly to multi-user mode instead of dropping
to a rescue shell prompt.

Graceful console exit

Typing poweroff in the guest now exits havm cleanly. Previously the
process would hang after the VM stopped.

Human-readable disk sizes

Resize messages now show "919 MiB" instead of "963661824 B". Fractional
values use one decimal place; whole numbers stay clean ("4 GiB", not
"4.0 GiB").

Faster disk image setup

Fresh installs now use APFS copy-on-write cloning (clonefile(2))
instead of a full copy, making setup nearly instant and preserving the
sparse-file space savings.

Metrics improvements

Dual-stack Prometheus endpoint on both 127.0.0.1:9210 and [::1]:9210
by default. Local endpoint reuse allows multiple listeners to share the
same port. IPv6 addresses in log output are now correctly bracketed.

Performance & modernisation

DispatchSourceTimer replaces asyncAfter for the boot-phase poll loop,
DispatchSemaphore replaced with async continuation, YAML config parsed
only once, JSON log double-allocation eliminated, signalShutdown data
race fixed. NSString bridging replaced with modern URL APIs, XCTest
migrated to Swift Testing, and NSLock replaced with Mutex.

Other fixes

  • USB VID/PID extraction from accessory descriptors
  • Health poll timeout prevents infinite wait on unreachable guests
  • Observer /ping (port 4357) polled for earlier readiness signal
  • SIGHUP triggers graceful restart for config hot-reload
  • cleanup --all removes everything including downloads and cache

Full Changelog: v0.2.2...v0.2.3

havm 0.2.2

Choose a tag to compare

@github-actions github-actions released this 09 Jul 18:45
a756a5c

What's new in 0.2.2

Boot experience

  • The ASCII art boot banner now only appears in interactive terminals — no more splash screen in launchd or Homebrew service logs.
  • The banner is written to stderr, keeping stdout clean for JSON log output and --console mode.

Reliability

  • Force-stop errors from the virtualization framework are no longer logged as errors when the stop is intentional (e.g., on Ctrl+C after SSH shutdown).
  • The NVRAM EFI variable store is now properly flushed to disk on exit, preventing GRUB boot state loss after unclean shutdowns.

Documentation

Internal

  • Replaced the swift-prometheus dependency with a minimal ~20-line serializer, dropping swift-atomics and one whole Swift target.
  • Merged HavmRuntime into HavmCore — one less module boundary.
  • IPv6 address literals and DNS resolution are now supported for SSH connections and API calls.

Full Changelog: v0.2.1...v0.2.2

havm 0.2.1

Choose a tag to compare

@github-actions github-actions released this 02 Jul 17:58
28f94af

What's new

  • --console debug flag — connects your terminal directly to the VM's serial console (/dev/hvc0) via VZVirtioConsoleDeviceSerialPortConfiguration. Get a root shell without SSH or network — useful for debugging networking issues. Terminal is set to raw mode with ONLCR for proper CR-LF translation. Type poweroff to shut down, or send SIGTERM from another terminal. Forces text log format to keep stdout clean.

Full Changelog: v0.2.0...v0.2.1

havm 0.2.0

Choose a tag to compare

@github-actions github-actions released this 02 Jul 15:03
0f1e34c

v0.2.0

Breaking changes

  • Bridge networking is now the default. The VM gets a LAN-reachable IP
    for mDNS discovery (homeassistant.local) and local integrations. Self‑compiled
    binaries without the com.apple.vm.networking entitlement automatically fall
    back to NAT with a warning. Explicit type: nat in config.yml overrides as
    before.

Added

  • havm_disk_usage_bytes Prometheus gauge. Tracks the main disk image's
    logical (configured) and allocated (actual APFS) size. Computed on scrape,
    no timer overhead.

Changed

  • Event-driven idle — zero polling, zero run-loop drain. After guest boot
    completes, no GCD timers, no CFRunLoopRun, no run(mode:before:). Signals
    and VZ delegate callbacks arrive through GCD dispatch sources. VM stop is
    detected via VZVirtualMachineDelegate instead of state polling.

  • GitHub API requests use ETag conditional caching. Persists the ETag
    between runs; subsequent requests return 304 Not Modified which costs zero
    rate-limit tokens. Only counts against the rate limit when a new HA OS
    release is actually published.

  • Rate-limited errors show the reset time. The x-ratelimit-reset Unix
    timestamp is parsed and translated into a human‑readable duration
    ("try again in 6m 24s").

Fixed

  • Setup errors use the structured logger instead of raw fputs to stderr,
    matching the log format configured for the session.

havm 0.1.5

Choose a tag to compare

@github-actions github-actions released this 29 Jun 22:35
0bc93bb

Changes

  • Restored entropy device and memory balloon — accidentally removed in an earlier CONFIG disk fix. Entropy provides random numbers to the guest kernel for crypto/ASLR; the balloon lets macOS reclaim idle guest memory under pressure.

Shutdown improvements

  • Skip REST API shutdown when api_token is empty — no more 401 warnings when a token isn't configured
  • Graceful shutdown hints — prints a tip listing all three methods when none succeed and no token is set

Documentation site

  • New GitHub Pages site at ingmarstein.github.io/havm — dark-themed landing page with real terminal output, feature cards, and multi-page docs
  • Documented Homebrew service paths/opt/homebrew/etc/havm/config.yml and /opt/homebrew/var/lib/havm/

Full Changelog: v0.1.4...v0.1.5

havm 0.1.4

Choose a tag to compare

@github-actions github-actions released this 29 Jun 07:32
e170aba

What's new

  • Prometheus metrics — Built-in HTTP endpoint (GET /metrics) exposing
    VM state and USB accessory count. Enable with metrics.enabled: true in
    your config. Scrape with Prometheus and feed into Grafana dashboards or
    alerting rules. Includes a /health endpoint for liveness checks.
  • Shell completions — bash, zsh, and fish completions via Homebrew's
    generate_completions_from_executable. Works out of the box with no
    config changes.
  • Smaller binary — Release binary is ~2.2 MB (was ~4.5 MB). Symbol
    tables are stripped before codesigning.

Internal

  • Bumped swift-metrics and swift-prometheus to latest versions
  • Removed redundant example config file (README is the canonical reference)