Security model
What Nightlid’s helper can and can’t do
Keeping a closed MacBook awake needs one system setting that only an administrator process can change. So Nightlid comes with a small helper that runs as root. You should know exactly what it does — here it is, including the limits.
Why a root helper at all
With the lid closed, on battery and without an external display, macOS sleeps a MacBook no matter which power assertions apps hold (that’s why caffeinate doesn’t help). The only switch that changes this is the system power setting SleepDisabled — the same one sudo pmset -a disablesleep 1 sets. Changing it requires root.
The helper is a launch daemon that macOS registers through its own SMAppService API. You approve it once in System Settings → General → Login Items, and you can switch it off there at any time. Nightlid never asks you to edit sudoers and never stores your password.
What the helper can do
| Call | What it does |
|---|---|
acquireLease / renewLease / releaseLease | Hold, extend or give back a time-limited lease. Sleep is disabled if and only if at least one unexpired lease exists. |
sleepNow | Put the Mac to sleep right away. |
setEmergencyFloor | Set the emergency battery floor (3–20 %) that the helper enforces on its own. |
status, protocolVersion | Report its state: leases, the sleep flag, last action. |
That is the entire interface. Internally the helper calls exactly two system functions that change anything: set the SleepDisabled power setting, and request system sleep.
What it can’t do
- It can’t run commands or other programs — it doesn’t spawn processes at all.
- It doesn’t read or write your files, and has no network code.
- It can’t keep the Mac awake on its own: without a renewed lease it restores normal sleep.
- It doesn’t accept connections from other apps — see the next section.
Who may talk to it
The app and the nightlid command-line tool talk to the helper over XPC (the macOS inter-process channel) on the privileged Mach service app.nightlid.helper. Both sides check each other’s code signature on every connection:
- The helper accepts a client only if it is signed with Apple’s Developer ID by Nightlid’s developer team and has the identifier of the Nightlid app or the Nightlid CLI. Any other process is rejected before it can make a call.
- The app and CLI accept a helper only with the helper’s identifier and the same team signature.
- Each connection is version-checked; unknown protocol versions are refused.
Leases: crash-safe by design
- Short TTL. A lease lives 90 seconds and the app renews it every 30. If the app crashes, is force-quit or hangs, the lease expires and normal sleep returns within about 90 seconds. TTLs are clamped to 5 seconds – 10 minutes.
- Owned by a process. A lease belongs to the client ID and the process that took it. Another process can’t renew or release it.
- Hard cap. No lease lives longer than 24 hours after it was first acquired, however often it is renewed.
- Limited. At most 16 live leases; acquire/renew/release are rate-limited per connection.
- Monotonic time. Expiry uses a clock that can’t be moved by changing the system time.
- Memory only. Leases are never written to disk. When the helper starts, it discards everything and makes sure sleep is allowed. If it finds sleep disabled with the lid closed after its own crash, it clears the flag and sleeps the Mac 15 seconds later unless the app re-acquires.
Ending a session actually sleeps the Mac
Turning SleepDisabled off does not put a Mac to sleep whose lid is already closed — macOS only re-checks the lid when it opens or closes. A naïve tool can therefore leave a closed Mac running until the battery is empty. When the last lease ends with the lid closed, Nightlid’s helper clears the flag, verifies it was cleared, re-checks that the lid is still closed and then requests sleep. If the Mac is still awake, it retries every 60 seconds (up to 3 times within 5 minutes).
Exceptions: with an external display and power connected, macOS keeps a closed Mac awake itself (clamshell mode), so the helper doesn’t force sleep after a normal release — but it does when the app crashed and can’t confirm the display. During logout, restart and shutdown the helper clears the flag but never forces sleep, so it can’t get in the way.
Emergency limits in the helper
These apply even when the app isn’t running:
- Battery: at the emergency floor (default 8 %) while the battery is draining, all leases are dropped, the Mac is put to sleep and new leases are refused.
- Heat: at macOS’s “critical” thermal state, the same.
Everything else — your battery floor (default 20 %), the thermal cut-off you choose, timers, the maximum length, “only when connected to power”, Low Power Mode — is enforced by the app, which ends its lease when a rule triggers.
The app side
- Agent hooks edit
~/.claude/settings.jsonand~/.codex/hooks.jsononly when you install them. The installer keeps every other entry, writes a backup (*.nightlid-bak, plus the very first original as*.nightlid-orig), preserves file permissions and refuses files it can’t parse. - The hook command exits immediately, never blocks your agent and never writes to its output.
nightlid://links opened from a web browser or an unidentified app need your confirmation before they start anything. Every start from outside the app is announced in a notification.
Limits — what we can’t promise
- Root is root. The helper is small and single-purpose, but a bug in it runs with administrator rights. That’s why it does as little as possible and is covered by tests.
- Local processes running as you can ask the Nightlid app to start a session or report agent activity (the same way the
nightlidCLI does). They can’t bypass the safety rules, inputs are validated and rate-limited, and outside starts are announced — but they could keep your Mac awake within those rules. A process running as you can do far worse anyway. - Private system interfaces. The
SleepDisabledsetting isn’t a documented public API (it’s whatpmsetuses). A future macOS update could change its behaviour; we check every new macOS release before recommending it. - Best-effort signals. Logout/restart detection relies on undocumented system notifications. If the lid state can’t be read, the helper doesn’t force sleep (except in an emergency).
- Heat and battery. Nightlid reacts to macOS’s thermal pressure and battery readings; it can’t make a Mac in a closed bag stay cool. Use common sense for heavy jobs.
Reporting a vulnerability
Please email support@nightlid.app with “security” in the subject. We’ll acknowledge within a few days and credit you if you like. Security fixes to the helper are free for every license, including after the update period.