-
Notifications
You must be signed in to change notification settings - Fork 658
Config Legacy to Emulate NVRAM
Replacement for the classic Clover RC scripts. Keeps your Startup Disk choice alive across reboots, shutdowns, and bootloader overrides.
➡️ View Page CloverLogoutHook
Legacy BIOS · APFS + HFS+ · Multi-disk
- Overview
- Architecture
- The Sticky-Anchor Pattern
- ESP Mount Strategy
- Boot-Volume Resolution
- Self-Healing Pass
- Boot-Args Control
- Shutdown Flush
- Command Surface
- Usage
- Support & Troubleshooting
- Filesystem Footprint
- macOS Compatibility
- Zero Dependencies
- Performance & System Impact
- Credits
CloverLogoutHook is a script + system-level LaunchDaemon used to emulate non-volatile random-access memory (NVRAM) on systems where native NVRAM is broken, non-functional, or loses its data upon shutdown. It keeps your Startup Disk choice alive across reboots, shutdowns, and bootloader overrides on Hackintosh systems running Clover Legacy Boot.
- Cold boot — shutdown ❄️ → power on
- Warm restart — menu → Restart 🔄
- GUI override — picking a different volume in Clover's boot picker (one-shot, sticky choice resumes)
It replaces the deprecated Clover RC scripts (broken since macOS 13 Ventura) with zero external dependencies.
🆕 v1.4 highlights: HFS+ boot support (the daemon now runs on Mavericks-era systems), multi-disk internal discovery, full
uninstallcleanup, and its own state folder/Library/Logs/CloverHook.
-
Check
EmuVariableUefi.efiexists in →/EFI/CLOVER/drivers/BIOS/— Legacy boots never loaddrivers/UEFI/ -
For Big Sur 11 and above you will need in
/EFI/CLOVER/config.plistBoot/Arguments →amfi=0x80 -
Check EmuVariableUefi.efi, OpenRuntime.efi exists in → /EFI/CLOVER/drivers/BIOS/ — Legacy boots never load drivers/UEFI/
-
Again for Big Sur 11 and above you will need in
/EFI/CLOVER/config.plistRtVariables/CsrActiveConfig →0x803and RtVariables/BooterConfig →0x28
┌─────────────────────────────────────────────────────────────────────┐
│ macOS KERNEL SPACE │
│ │
│ NVRAM (emulated by EmuVariableUefi.efi — lives in RAM) │
│ │
│ Key example: efi-boot-device = <GUID path string> │
│ Written by: Startup Disk prefpane / bless │
│ Consumed by: macOS after each boot (keys vanish from live RAM) │
└────────────────────────────┬────────────────────────────────────────┘
│
│ every 2 seconds
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CloverLogoutHook daemon │
│ (root LaunchDaemon — user space) │
│ │
│ 1. DUMP : nvram -x -p → raw XML plist (tmp file) │
│ 2. COMPARE : cmp vs last.xml baseline → no change? skip. │
│ 3. MERGE : LIVE-WINS against sticky.xml anchor: │
│ • copy anchor → final │
│ • PlistBuddy Delete every key present in live │
│ • PlistBuddy Merge live → final │
│ Result: fresh values WIN; anchor re-adds only │
│ keys that macOS consumed (efi-boot-device*, …). │
│ 4. WRITE : mount Clover volume (hidden, transient) │
│ → install → unmount │
│ 5. ANCHOR : if final contains heal-keys → refresh sticky.xml │
└────────────────────────────┬────────────────────────────────────────┘
│
│ at boot time
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CLOVER (firmware) │
│ │
│ EmuVariableUefi.efi reads /nvram.plist on the volume │
│ → imports every key into emulated NVRAM RAM │
│ → boot-manager honors efi-boot-device → auto-selects volume │
└─────────────────────────────────────────────────────────────────────┘
The core innovation. A naive NVRAM-to-file dump has a fatal flaw on Hackintosh:
⚠️ macOS consumesefi-boot-device*keys after reading them at boot — they vanish from live NVRAM within seconds of the OS starting. A pure mirror dump would progressively strip the file of exactly the keys needed for boot selection.
| File | Role | Update rule |
|---|---|---|
last.xml |
Raw live NVRAM snapshot | Refreshed every cycle (baseline for cmp) |
sticky.xml |
Anchor — superset containing heal keys | Refreshed only when final output contains any heal key |
nvram.plist (on Clover volume)
|
What Clover imports | Result of live-wins merge against anchor |
# PlistBuddy Merge does NOT overwrite existing keys.
# So: strip from anchor every key live provides, then merge live in.
cp anchor → final
for key in $(keys_in_live); do
PlistBuddy -c "Delete :${key}" final # 2>/dev/null || true
done
PlistBuddy -c "Merge live.xml" final
✅ Property:
finalis always a superset — fresh values from live overwrite old ones, and the anchor re-adds anything live has lost. Selecting A→B→A→… works indefinitely because the fresh live value always lands in the final file.
The daemon never keeps a persistent mount and never interferes with user tools. The volume appears nowhere on the desktop, no Finder sidebar entry, no residual state.
| Situation | Behavior |
|---|---|
| NVRAM unchanged (typical cycle) | No mount at all — zero disk activity |
| Change detected, volume unmounted |
diskutil mount -nobrowse → write → unmount (~1–2 s) |
| Change detected, user already has the volume mounted | Reuse user's mount — never steal or eject |
Mount point used for transient operations: /private/var/.CloverEFI (dot-prefixed → invisible to Finder).
Two worlds, one resolver:
-
APFS boots —
/is a synthesized snapshot inside a synthesized container, backed by a physical store on a real disk. The script walks the whole chain to find the physical host disk. -
HFS+ boots (v1.4) — no APFS layer exists; the boot disk is the root disk. Handled automatically.
root slice : disk3s5s1 ← mount / apfs container : disk3 ← regex ^(disk[0-9]+) physical store : disk1s2 ← awk '/Physical Store/' in diskutil list disk3 host whole disk : disk1 ← regex ^(disk[0-9]+) esp identifier : disk1s1 ← awk '/EFI/' in diskutil list disk1
v1.3+ multi-disk: if the boot disk has no EFI-typed partition — or the mounted one lacks EFI/CLOVER — all internal disks are scanned (boot disk first) for an HFS+ volume hosting EFI/CLOVER (MBR disks, boot1h installs, old rigs). External/USB drives are never touched. On HFS+ Clover volumes, nvram.plist is written to the volume root — exactly where Legacy Clover reads it.
Only macOS built-ins: mount, diskutil, awk. No external interpreters.
Two safety nets run against file corruption or external amputation:
-
At daemon start (after every boot): a forced sync pass (
force=1) rebuildsnvram.plistfromlive ⊕ anchor, bypassing the no-change fast path. -
Every 60 s (
VERIFY_EVERY=30cycles × 2 s): if the on-volume file lacks any key present in the anchor, a forced rebuild is triggered automatically.
boot-args can be managed from the terminal, without touching config.plist — using the setargs command. The value is stored in the sticky anchor and the Clover file as native EFI binary form (<data>).
🧪 Experiment-proven: Clover merges config.plist arguments with the NVRAM-file arguments at boot (config without
-v+ file with-v= verbose boot). The file injects flags the config does not have.
An example below ⬇
sudo ./CloverLogoutHook.command setargs "-lilubetaall alcid=11 keepsyms=1 amfi=0x80 revpatch=sbvmm -v"
- Stored as
<data>(round-trip verified before writing) - Live NVRAM mirror attempted (allowed on older macOS, refused on modern — expected)
- Applied at next boot — config.plist no longer needs to carry these flags
./CloverLogoutHook.command diagnose
# shows, with its storage type:
sticky boot-args : -lilubetaall alcid=11 ... -v [data/Clover-compatible]
| Method | How | Best for |
|---|---|---|
| config.plist | Edit Boot → Arguments in Clover Configurator |
The classic way — args always applied |
| setargs command 🆕 | sudo ... setargs "..." |
Terminal control, without touching config |
| Both | They merge at boot | config = base args · file = extra/experimental flags |
⚠️ ClearNVRAM (F11) semantics: if you F11 while the daemon anchor exists, the sticky file will restore the args on the next cycle (phoenix behavior). For a clean slate, uninstall the daemon first, then F11, then reinstall — exactly like the OC LogoutHook semantics.
Note: on modern macOS the live NVRAM write is refused by the kernel (expected) — persistence travels through the nvram.plist file, which Clover imports at every boot.
launchd sends SIGTERM at shutdown/restart; Ctrl+C sends SIGINT. The trap:
on_shutdown() {
sync_once 1 merge # final flush; one retry if disks are being torn down
heal_live_vars 1
exit 0
}
trap on_shutdown TERM INT
This captures the very last NVRAM state — e.g. a Startup Disk click made one second before shutdown — before the OS releases the disks.
| Command | sudo? | Purpose |
|---|---|---|
dump (default)
|
✓ | One-shot sync + heal |
watch |
✓ | Continuous daemon loop (used by LaunchDaemon) |
install |
✓ | Copy binary + install LaunchDaemon |
uninstall |
✓ | Full cleanup: daemon + binary + CloverHook folder |
status |
- | Daemon state + file overview |
setargs |
✓ | boot-args can be managed |
diagnose |
– | Full resolver trace + anchor health |
clean |
✓ | Wipe tmp files, snapshots, sticky anchor (re-seed required after) |
prune |
✓ | Rebuild file strictly from live (drops consumed keys) |
version |
– | Print version banner |
- Install the Clover Package with this specifics options
- All the drivers necessary for a legacy configuration are present. BIOS.zip
- Reboot your Hackintosh before using the command for installing CloverLogoutHook
cd /Library/Application\ Support/CloverLogoutHook
chmod +x ./CloverLogoutHook.command
sudo ./CloverLogoutHook.command install
cd /Library/Application\ Support/CloverLogoutHook
./CloverLogoutHook.command diagnose
cd /Library/Application\ Support/CloverLogoutHook
./CloverLogoutHook.command status
./CloverLogoutHook.command diagnose
tail -30 /Library/Logs/CloverHook/nvramhook.log
sudo ./CloverLogoutHook.command diagnose
sudo ./CloverLogoutHook.command setargs "-lilubetaall alcid=11 keepsyms=1 amfi=0x80 revpatch=sbvmm -v"
cd /Library/Application\ Support/CloverLogoutHook
sudo ./CloverLogoutHook.command uninstall
Open System Preferences → Startup Disk, select your volume, close the window. One click is all it takes — the daemon captures it within 2 seconds.
./CloverLogoutHook.command diagnose
# expected:
esp identifier : disk1s1
anchor has : efi-boot-device ✓
anchor has : efi-boot-device-data ✓
The daemon now runs silently at every boot. No action is ever required again: pick any volume in Startup Disk — from any of your macOS installations — and the choice survives reboots, shutdowns, and Clover GUI overrides.
Install the daemon on every modern macOS you boot from (v1.4 supports HFS+ boots — Mavericks and later each capture their own Startup Disk clicks). Older systems without the daemon (Snow Leopard…) are covered for reading: their boot choice is restored from the file written by the other installations.
./CloverLogoutHook.command status # quick state overview
./CloverLogoutHook.command diagnose # resolver trace + anchor health
sudo ./CloverLogoutHook.command dump # force a sync now
sudo ./CloverLogoutHook.command clean # reset logs + anchor (re-seed after)
sudo ./CloverLogoutHook.command uninstall # full removal
./CloverLogoutHook.command status
./CloverLogoutHook.command diagnose
tail -30 /Library/Logs/CloverHook/nvramhook.log
- Check
EmuVariableUefi.efiexists in/EFI/CLOVER/drivers/BIOS/— Legacy boots never loaddrivers/UEFI/. -
diagnose— the anchor keys must show ✓. - Mount your ESP/Clover volume and verify
nvram.plistcontainsefi-boot-device. - Some Clover builds read the file at the volume root, others inside
EFI/CLOVER/— this tool writes to the volume root.
The daemon can only capture choices made while it is running. If the restart was triggered from a system without the daemon, nothing wrote the choice. Fix: install the daemon on that system too (v1.4 runs on Mavericks+ HFS boots), or perform one Startup Disk click from an OS that has it.
The daemon reuses an existing mount of the target volume — wherever it is mounted (/Volumes/EFI, /private/tmp/ESP, …) — and only mounts transiently (~1–2 s) when nothing exists. On a rare collision, simply remount: your tool always has priority.
v1.3+ scans all internal disks (boot disk first) for a volume hosting EFI/CLOVER. The first match wins; external and USB drives are excluded by design. Note: with several Clover volumes present, the daemon writes to the first discovered one — keep your Clover installations at the same version to avoid drift.
sudo ./CloverLogoutHook.command clean
# then: ONE Startup Disk click to re-seed the anchor
⚠️ Aftercleanoruninstall, the sticky anchor is gone — make ONE Startup Disk click to re-seed it before relying on persistence again.
Always include the four musketeers:
- Full output of
./CloverLogoutHook.command diagnose - Output of
./CloverLogoutHook.command status - Last 30 lines of
/Library/Logs/CloverHook/nvramhook.log - Your macOS version(s),
diskutil list, and Clover version
| Path | Purpose |
|---|---|
/usr/local/bin/clover-logout-hook |
Installed script (copy of the .command) |
/Library/LaunchDaemons/com.clover.nvramhook.daemon.plist |
LaunchDaemon descriptor |
<Clover-volume-root>/nvram.plist |
What Clover imports every boot |
/Library/Logs/CloverHook/nvramhook.log |
Event log (auto-rotated at 400 KB) |
/Library/Logs/CloverHook/last.xml |
Live NVRAM baseline |
/Library/Logs/CloverHook/sticky.xml |
Persistent anchor |
/Library/Logs/CloverHook/last-esp.txt |
Cached Clover-volume identifier |
/private/var/.CloverEFI/ |
Transient hidden mount point (only during write) |
-
APFS or HFS+ boot volume — the resolver handles both worlds (v1.4): APFS synthesized chains and plain HFS+ disks. On APFS rigs it walks the
Physical Storemarkers; on HFS it takes the boot disk directly. -
/usr/libexec/PlistBuddy— available since 10.5 Leopard, stable API. -
launchctl bootstrap system— modern LaunchDaemon API introduced in 10.11 El Capitan. Falls back tolaunchctl load -wif bootstrap fails. -
perl -MMIME::Base64— bundled with every macOS, unchanged for years.
| macOS | Version | Status |
|---|---|---|
| Snow Leopard | 10.6 | ✅ Boots via this tool — daemon untested there (reading covered) |
| Lion | 10.7 | |
| Mountain Lion | 10.8 | |
| Mavericks | 10.9 | ✅ Validated — v1.4 HFS boot support |
| Yosemite | 10.10 | ✅ Validated |
| El Capitan | 10.11 | ✅ Validated |
| Sierra | 10.12 | ✅ Validated |
| High Sierra | 10.13 | ✅ Validated |
| Mojave | 10.14 | ✅ Validated |
| Catalina | 10.15 | ✅ Validated |
| Big Sur | 11 | ✅ Validated |
| Monterey | 12 | ✅ Validated |
| Ventura | 13 | ✅ Primary target — tested & validated |
| Sonoma | 14 | ✅ Validated |
| Sequoia | 15 | ✅ Validated |
| Tahoe | 26 | ✅ Validated |
🍀 Multi-OS rigs: the daemon only needs to run on the modern macOS installations you boot from. Older systems (Snow Leopard, Lion…) boot through the same Clover +
nvram.plistchain and are fully covered by the persisted keys — no daemon required on them. Validated in the field on the same machine. 🐅
bash ✓ built-in since macOS 1.0
awk, sed ✓ built-in since macOS 1.0
perl ✓ bundled since macOS 10.3 (MIME::Base64 included)
PlistBuddy ✓ /usr/libexec since macOS 10.5
plutil ✓ built-in since macOS 10.5
diskutil ✓ built-in since macOS 10.0
nvram ✓ built-in since macOS 10.0
launchctl ✓ built-in since macOS 10.4
✅ No Homebrew. No Xcode CLT. No Python. No Node. No git. The script can be copied to any Hackintosh with Clover Legacy and works immediately after
install.
0.1% on average Measured on a machine (Legacy BIOS) running the v1.6 daemon — real-world numbers from Activity Monitor and ps aux. This was validated on macOS.
| Component | Footprint |
|---|---|
| Sleeping bash process | ~2–4 MB RSS |
| Sub-processes (nvram, awk, grep — spawn then released) | ~30 ms per cycle, then freed |
| Total stable | ~3–5 MB constant |
For comparison: a single Safari tab = 200–800 MB. The daemon is invisible in Activity Monitor.
The live-wins + fast-path design keeps the CPU near zero:
| Phase | Frequency | CPU Cost |
|---|---|---|
Sleep loop (sleep 2) |
Continuous | ~0% |
| Cheap check (nvram dump + cmp) | Every 2 s | ~15–30 ms |
| Full sync (mount + merge + write) | Only on real change | ~1–2 s one-shot |
| Self-heal verify | Every 60 s | Same as cheap check |
| State | CPU |
|---|---|
| 99% of the time (idle) | 0.0% — daemon sleeps |
| Startup Disk click | One spike at 1–3% for ~200 ms |
| Daily average | < 0.1% |
| Situation | ESP Write |
|---|---|
| NVRAM unchanged (99% of the time) | ZERO — not even mounted |
| Real change detected | 1 mount + 1 write (~2 KB) + unmount |
Energy impact : NEGLIGIBLE
- Daemon sleeps 99.9% of the time (sleep = zero CPU)
- No network, no GPU, no high-precision timers
- Estimated average draw: < 0.01 W
| Production daemon criteria | CloverLogoutHook |
|---|---|
| Minimal RAM | ✅ 3–5 MB |
| Invisible CPU | ✅ fast-path + sleep loop |
| Zero unnecessary disk I/O | ✅ change-detection only |
| No external dependencies | ✅ macOS built-ins only |
| Clean, rotating logging | ✅ 400 KB cap |
| Clean teardown | ✅ transient mount |
ps aux | grep clover-logout-hook | grep -v grep
# → %CPU and %MEM columns
# A/B proof:
sudo launchctl bootout system/com.clover.nvramhook.daemon
ps aux | grep clover-logout-hook | grep -v grep
sudo launchctl bootstrap system /Library/LaunchDaemons/com.clover.nvramhook.daemon.plist
Built by chris1111 and AI GLM-5 during a live debugging session (August 2026).
CloverLogoutHook
Made with ♥ for the Hackintosh Legacy community.
License: GPL-3.0 Copyright © 2026 CloverHackyColor, All Rights Reserved.
- Slice, with help of Kabyl, usr-sse2, jadran, Blackosx, dmazar, STLVNUB, pcj, apianti, JrCs, pene, FrodoKenny, skoczy, ycr.ru, Oscar09, xsmile, SoThOr, rehabman, Download-Fritz, nms42, Sherlocks, Zenit432, cecekpawon, stinga11, TheRacerMaster, solstice, Micky1979, Needy, joevt, ErmaC, vit9696, ath, savvas, syscl, goodwin_c, clovy, jief_machak, chris1111, vector_sigma, LAbyOne, Florin9doi, YBronst, Hnanoto.
Intel, Apple, Oracle, Chameleon, rEFIt and Xom, nanosvg.
Chameleon team, crazybirdy, JrCs, chris1111.
- Clover, rEFIt, XNU, VirtualBox. The main is EDK2 latest revision
- Recent developments and changes in details at Clover Change Explanations
- Support forum discussion AppleLife (Russian) Insanelymac (English) macos86.it (Italian)