Guaranate is a small, native macOS command-line utility for managing
sleep-prevention sessions with a friendlier, more scriptable interface than
caffeinate.
It talks to the native macOS power-management APIs (IOKit IOPMAssertion*)
directly. It does not wrap /usr/bin/caffeinate.
The name is a play on guaraná, the Brazilian stimulant, and Apple's
caffeinate command.
Keep your Mac awake when work needs to finish. Give your Mac some guaraná.
v0.1 — native CLI foundation. Timed sessions and process-lifetime sessions
work end-to-end. The broader surface (until, status, why, external leases)
is planned and tracked in PLAN.md. Rows marked unreleased are on
main and ship in the next release.
| Feature | State |
|---|---|
guaranate <duration> timed session |
✅ shipped |
guaranate (no duration) runs until interrupted |
✅ shipped |
Native IOPMAssertion (no caffeinate) |
✅ shipped |
| Elapsed / remaining / end-time display | ✅ shipped |
| Ctrl+C / SIGTERM cleanup, no stale assertion | ✅ shipped |
| Non-TTY-friendly output | ✅ shipped |
guaranate while <cmd> for a command's lifetime |
✅ unreleased |
guaranate --watch <pid> for a running process |
✅ unreleased |
guaranate until <HH:MM> |
🔜 v0.2 |
status / why / --json |
🔜 v0.3 |
acquire / renew / release leases |
🔜 v0.4 |
This README is the quickstart. The full user guide — install, session options, assertion modes, terminal output, and a CLI reference generated from the binary's own help output — is published at guaranate.dev.
Its source lives in docs-website/, a self-contained
Astro Starlight site deployed with
Zephyr Cloud; every build also gets its own
immutable preview URL.
cd docs-website && npm install && npm run devbrew tap ryok90/guaranate
brew install guaranateDownload the universal (arm64 + x86_64) build from the
latest release, verify
its checksum, and put it on your PATH:
shasum -a 256 -c guaranate-*-macos-universal.tar.gz.sha256
tar -xzf guaranate-*-macos-universal.tar.gz
cp guaranate-*-macos-universal/guaranate /usr/local/bin/ # or anywhere on your PATHRequires Swift 6 (Xcode 16+) on macOS 14 or later.
git clone https://github.com/ryok90/guaranate.git
cd guaranate
swift build -c release
cp .build/release/guaranate /usr/local/bin/ # or anywhere on your PATHKeep the Mac awake for a human-readable duration:
guaranate 30m
guaranate 2h
guaranate 1h30m
guaranate 90s
guaranate 3600 # a bare integer is interpreted as seconds
guaranate # no duration: stay awake until interrupted (Ctrl+C)While active, Guaranate acquires a power assertion and shows a live frame. On a color terminal it renders a gradient progress bar, a metrics table, and a centered header, sized to your terminal width:
🌿 Guaranate
▏██████████████░░░░░░░░░░░░░░▕ 50%
Elapsed · · · · · · · 00:42:17
Remaining · · · · · · · 01:17:43
Ends · · · · · · · 23:43:07
Assertion · · · · · System sleep
Display · · · · · · May sleep
Press Ctrl+C or q to stop
The bar is drawn with a green→berry-red gradient (truecolor or 256-color,
degrading to solid green) and sizes itself to the terminal. Assertion state is
color-coded (amber when the display is kept awake), the cursor is hidden while
the frame is live, and a summary card is shown on completion. Running with no
duration stays awake indefinitely; that frame swaps the progress bar for a
spinner (⠋ Awake — until interrupted). Color and Unicode degrade
independently: NO_COLOR drops color, and a dumb or non-UTF-8 terminal falls
back to a plain ASCII bar ([####----]) with no escape codes.
Press q or Ctrl+C to end a live session; it is also released
automatically when the duration elapses or on SIGTERM — never leaving a stale
sleep inhibitor behind.
Hold the assertion for exactly as long as a command runs:
guaranate while npm test
guaranate while ./build.sh --release
guaranate while --display -- ./deploy.sh # flags go before the commandA mistyped flag before the command is reported as an unknown option rather than
run as a program; -- is required for a program whose own name starts with -.
The command gets its own process group and the controlling terminal — its output
and input pass straight through, so there is no live frame, just a start line and
a completion summary — and Guaranate exits with the command's own exit code
(128 + signal if it is killed by one, 127 if the command is not found, 126
if it is not executable). Ctrl+C and Ctrl+Z behave exactly as they would without
Guaranate in front: an interrupt arrives once, and Ctrl+Z stops the whole job for
fg to resume, assertion held while it is paused. Signals sent to Guaranate are
relayed to the command's whole process group, so its children are signalled with
it rather than left behind, and the assertion is released only once the command
has exited — a stopped session relays when it is continued, which fg, bg, your
shell's kill %job and a closing terminal all do for you; a raw signal to a paused
session waits for that continue, exactly as it would for any stopped process.
Resuming re-decides the terminal, so a job put in the background with bg leaves
your shell's keyboard alone. A signal the calling shell deliberately ignores stays
ignored in the command, too. Guaranate's own start and
completion lines go to stderr, so stdout carries the command's output alone and
while is safe in a pipeline: a reader that goes away reaches the command exactly
as it would unwrapped, and never the session.
Hold the assertion until an already-running process exits:
guaranate --watch 4821
guaranate -w 4821Watching only observes: the process is never started, signaled, or killed, and
Ctrl+C detaches and leaves it running. It works for processes owned by other
users, is bound to the process's (pid, start time) pair so a recycled pid can
never inherit the assertion, and attributes the assertion to the watched
process — pmset -g assertions reports Created for PID: 4821. An unused pid
is rejected before anything is acquired, and --watch cannot be combined with
a duration.
The default prevents user-idle system sleep while still letting the display sleep — the right mode for builds, downloads, and unattended compute.
guaranate 2h # prevent idle system sleep (display may sleep)
guaranate 2h --display # also keep the display awake
guaranate 2h --system # prevent all system sleep| Option | Description |
|---|---|
-d, --display |
Also keep the display awake. |
-s, --system |
Prevent all system sleep. |
-r, --reason <text> |
Reason recorded on the power assertion. |
-w, --watch <pid> |
Hold the assertion until that process exits. |
-v, --version |
Print the version. |
-h, --help |
Show help. |
When stdout is not a TTY (pipes, files, CI), the live frame degrades to a single start line and a single completion line — no per-second churn.
Guaranate uses IOKit power assertions directly:
IOPMAssertionCreateWithNameto acquire an assertion of the requested type (PreventUserIdleSystemSleep,PreventUserIdleDisplaySleep, orPreventSystemSleep);IOPMAssertionReleaseon every exit path.
Apple's open-source caffeinate.c is treated only as an engineering reference
for assertion semantics — no Apple implementation code is copied, and
/usr/bin/caffeinate is never invoked.
You can confirm a live assertion with:
pmset -g assertionsThe important boundary is CLI → GuaranateCore → macOS power APIs. A future
menu-bar companion would depend on GuaranateCore, not the CLI executable.
Sources/
├── GuaranateCLI/ # commands, terminal rendering, process runtime
│ ├── Guaranate.swift # @main root command, subcommand container
│ ├── TimedSession.swift # timed / --watch session: render loop, release
│ ├── ProcessSession.swift # while: spawn, forward signals, propagate exit
│ ├── Commands/ # AssertionOptions, RunCommand, WhileCommand
│ └── Output/
│ └── TerminalRenderer.swift
└── GuaranateCore/ # pure, IOKit-free logic (unit-tested)
├── Power/ # PowerAsserting protocol + IOKit PowerManager
├── Process/ # ChildProcess, CommandInvocation, ExitStatus,
│ # ProcessIdentity
├── Time/ # DurationParser, Deadline, TimeFormatting
└── Terminal/ # ProgressBar (pure bar math for the live frame)
Native IOKit interaction sits behind the PowerAsserting protocol, so time,
duration, and (later) lease logic are fully testable without changing the host
machine's sleep state.
swift build # build
swift test # run the unit tests
swift run guaranate 10mSee CHANGELOG.md for notable changes and
AGENTS.md for repository conventions. Every pull request with
user-facing impact adds an entry under ## [Unreleased] in CHANGELOG.md; CI
enforces this.
MIT. Guaranate's implementation is original.

