Your Mac is not sleeping as much as you think it is.
darkwake.vercel.app · free · MIT · no permissions, no network
Close the lid and macOS keeps waking up — for Wi-Fi traffic, for maintenance
timers, for an app that never let go of a sleep assertion. It records every one
of these events, with the reason and the battery level at the time. Then it
buries them in ~85,000 lines of pmset -g log that nobody reads.
DarkWake reads them for you.
git clone https://github.com/nickzsche21/darkwake && cd darkwake && ./build.sh
open build/DarkWake.app # menu bar app
./build/darkwake --week # or just the CLIPut the CLI on your PATH if you want it everywhere:
sudo ln -sf "$PWD/build/DarkWake.app/Contents/MacOS/darkwake" /usr/local/bin/darkwakedarkwake |
last night: what woke it, and what it cost |
darkwake --week |
every night macOS still remembers |
darkwake --now |
what is holding your Mac awake this second |
darkwake --json |
the whole analysis, machine readable |
darkwake --render out.png |
draw the panel offscreen, light and dark |
darkwake --gif demo.gif |
record the report as an animated terminal GIF |
darkwake --stats |
parser sanity numbers, to check the work |
The menu bar icon is filled and orange while something is holding sleep open, and plain otherwise. Click it for last night; there is a 7-night view behind it.
Two commands, both of which need no privileges at all — no root, no Screen Recording, no Accessibility, no TCC prompt of any kind:
pmset -g log— sleep, wake and dark-wake events, plus every power assertion that was created and released, with the process that owned itpmset -g assertions— what is holding an assertion right now
There is no network code in this program. Nothing is uploaded, and there is nothing to sign in to.
It translates. A raw wake reason looks like
smc.sysState.Wake(0x70070000) wifibt SMC.OutboxNotEmpty wlan E_TKO_TCP_DATA ARPT/.
That is "Wi-Fi traffic woke it — that's Wake for network access". Turning
those tokens into English is most of the value here.
It does the interval maths properly. A released assertion reports how long
it was held, but that hold often starts before the window you are looking at,
and a process like coreaudiod holds one assertion per audio context. Summing
the durations gave "coreaudiod: 279h" inside an 8-hour night. DarkWake clips
each hold to the window and unions the overlapping ones per process, so the
answer can never exceed the length of the night.
- Gatekeeper.
build.shad-hoc signs the app. That is enough to run it on the machine that built it, and not enough for anyone who downloads a build: macOS will refuse to open it until it is signed with a Developer ID and notarised. Build from source, or right-click → Open and accept the warning. - macOS keeps roughly a week of power log, sometimes less. DarkWake cannot show you more history than the system kept.
- Battery numbers need battery. A night on the charger tells you nothing about drain, and DarkWake says so rather than inventing a figure.
- Assertions are not the whole story. An app can drain your battery while the Mac is awake without ever holding an assertion. This explains sleep, not everything.
runningboardd,clouddandassertiondhold assertions for other apps. DarkWake digs the real app out of the reason string where it can, but the attribution is only as good as what the daemon recorded.
Swift + AppKit, no dependencies, no Xcode project. One binary is both the CLI
and the menu bar app; LSUIElement keeps it out of the Dock.
./build.shMIT.

