Close your laptop and keep your agents running.
rucksack is a macOS command-line tool for leaving your desk while a local coding agent is
still working. It moves your Mac onto your phone hotspot, verifies the handoff, and holds a
bounded lease that keeps the Mac awake with the lid closed. You keep steering the same
Codex, Claude Code, or Cursor session from your phone. Switching to the hotspot is one
step, where moving the work to a cloud agent means standing up a fresh environment.
Source-only alpha. CI passes, and short hotspot desk tests have passed. There is no signed package yet, and closed-lid behaviour in a bag is not yet verified.
cargo install --locked --force --git https://github.com/noahnawara/rucksack --tag v0.1.0-alpha.7 rucksack-cli rucksack-helper
rucksack helper installBoth crate names are required. rucksack finds its helper as a file beside itself, so installing
only rucksack-cli reports success and can never pack. Installing both puts them in one directory.
Run helper install yourself, in a terminal. pack would do it on first use, but it needs sudo,
and sudo reads your password from a terminal that an agent session does not have. Needs macOS,
Xcode Command Line Tools, and Rust 1.86 or newer; release packaging targets macOS 14 and newer.
Read the installation guide for the clone-and-build route and removal.
Or paste this to Claude Code, Codex, or Cursor and let it do the first part. This is the same text the website hands out:
Paste this to your coding agent (Claude Code, Codex, or Cursor). Not into a terminal.
Install rucksack on this Mac. Run exactly this, with both crate names:
cargo install --locked --force --git https://github.com/noahnawara/rucksack --tag v0.1.0-alpha.7 rucksack-cli rucksack-helper
Both names matter. Drop rucksack-helper and the install still reports success, but rucksack can
never pack. It takes a couple of minutes and nothing needs adding to my PATH. Don't run `rucksack
pack` to check it worked: that takes a real lease and moves me onto my hotspot. `rucksack --version`
is the safe check.
Then tell me to run `rucksack helper install` myself, in a terminal window. It asks for my password
once. Don't try to run it for me, you have nowhere to type a password.
From then on, when I say I'm leaving, heading out, or have to go: run `rucksack pack` straight away,
don't ask me first, and tell me the last line it prints. If it's waiting for a network, tell me
that, and that the lid stays open until it says Packed. It finishes by itself, so never run it
twice. When I'm back, run `rucksack unpack` and read me the trip line with the numbers exactly as
printed.
The first time I finish a trip, unpack says so. When it does, ask me whether I want to star the
project on GitHub, and ask it properly: use your question tool, the one that gives me buttons to
click, not a line of chat I will scroll past. Run `rucksack star` if I say yes, drop it for good if
I say no, and only ever ask once.
Then put the two paragraphs above into the file you load every session, ~/.claude/CLAUDE.md for
Claude Code or ~/.codex/AGENTS.md for Codex. Pasting it here only lasts this conversation, and "I
have to go" is not something I want to explain twice.
There is no setup command. The first pack installs the power helper and remembers the
network it ended up on.
Run that first one yourself, in a terminal — rucksack helper install does the same job on its
own. Installing the helper needs your password, sudo asks for it on a terminal, and the agent
session that will run every later pack has no terminal to be asked on. Doing it once, in
person, is what keeps every pack after it silent.
$ rucksack pack
Connecting to Noah…
Joined.
Battery 92%. About 7h 8m before it reaches 10%, when this Mac sleeps and running work stops.
Agents get 10 minutes' notice to write down where they got to.
Packed. Close the lid and go.
The hours are the battery's, not the lease's. A session is capped at 24 hours, but almost no Mac
has 24 hours of charge in a bag, so pack reports whichever runs out first — and on a commute
that is the battery. Ten minutes before the end, rucksack status starts telling any agent that
asks to stop starting new work and write down where it got to.
If macOS cannot join the hotspot itself — the normal case for Apple Instant Hotspot — rucksack opens Wi-Fi settings, says what to pick, and waits:
Choose “Noah” in Wi-Fi — keep the lid open until this says Packed. Waiting…
It then carries on by itself. There is nothing to confirm and nothing to re-run. The lid clause
is the one thing worth reading twice: until pack says Packed, nothing is holding this Mac
awake yet, so closing it during the wait sleeps the machine.
The lease belongs to the Mac, not to one conversation. Closing the lid affects every process, so every running task benefits, and a task finishing does not end the lease.
rucksack does not change how your agents behave. It writes no instructions, prompts, or
policy into them, and installs no hooks; your permission, approval, and sandbox settings
stay exactly as you configured them. It installs one thing: a rucksack skill for Codex,
Claude Code, and Cursor, so "I'm leaving" works as a sentence in a conversation. Whichever of
them you use carries the remote conversation. The CLI and helper have no backend.
When you are back at a desk, rucksack unpack restores normal sleep, and says what the trip
was. It is also the recovery path, and works from any state.
$ rucksack unpack
Packed for 3h 12m · battery 79% → 61% · 240 MB
Still on your phone. Pick a Wi-Fi network when you can.
Unpacked. This Mac sleeps normally.
Each of those numbers is measured or absent. A battery gauge that says nothing, a byte counter that reset, or an interface that changed mid-trip all shorten that line rather than fill it in with something plausible. The byte figure spans the watcher's first heartbeat to its last, so it reads slightly low rather than slightly high.
rucksack pack switch to your hotspot and hold this Mac awake
rucksack status is it still packed?
rucksack unpack let this Mac sleep again
rucksack pair print a Codex Remote Control pairing code
rucksack star star this project on GitHub
rucksack helper install or remove the power helper
pack takes --for 90m to shorten the session, --hotspot "Name" to name the network,
--usb for iPhone USB tethering, and --here when you are already on the network you want.
caffeinate keeps a Mac awake for as long as it runs. caffeinate -d also holds the
display on, and caffeinate -i make keeps the machine up until that command finishes. With
the lid open, on power or on battery, it does what you want and rucksack has nothing to add.
Close the lid and it stops helping. A caffeinate assertion prevents idle sleep, and
closing the lid on battery is a forced sleep condition evaluated by closed-display policy,
so the assertion does not cover it. That is the whole gap: caffeinate is useful
defense-in-depth while the lid is open, and it is not the closed-lid primitive.
The setting that does cover it is SleepDisabled, and it is global, persistent, and
root-only. Nothing releases it when a process exits, so leaving it on is how a Mac ends up
permanently unable to sleep. rucksack takes that stronger setting and gives it the thing
caffeinate had for free: an owner, and an end.
rucksack does not run, wrap, or extend caffeinate. It refuses to start when SleepDisabled
is already on, because that value is the baseline it has to hand back, and two owners make
cleanup ambiguous. rucksack never stops or modifies whatever holds it.
Two things end a session when you walk away, not one. The Mac sleeps, and the default route changes. Keeping the Mac awake solves the first and leaves the second, which is why a session can survive the lid and still die at the door.
So pack treats the route as a precondition, not an afterthought — and "the internet works"
is not the test, because the office network you are walking away from also works. rucksack
accepts a network only once it can show the Mac has actually moved onto the commute one: by
its name, by an iPhone hotspot gateway, by the default route visibly leaving where it
started, or because you said so with --here. Then it checks that the route really reaches
the internet, requiring Apple's own captive-network success page so a hotel portal cannot
pass.
Losing that network later does not end the session. A tunnel is exactly when your work
most needs to stay alive, so an outage is reported by status and nothing else.
The Rust workspace has a CLI, shared core, and a small root-owned helper. The helper runs
one fixed command, /usr/bin/pmset -a disablesleep 0|1, and cannot run anything else. It
refuses to acquire unless normal sleep is the verified baseline, saves that baseline before
changing it, and verifies the result afterward.
The helper accepts lease operations over a Unix socket. An unprivileged watcher renews the lease while it checks battery level, thermal pressure, and the session deadline. The watcher cannot change the sleep setting itself. If it dies, the lease expires on its own and the helper restores normal sleep.
Running sudo pmset -a disablesleep 1 by hand sets the same value, with no saved baseline,
no expiry, and no release when the battery or the temperature says stop. It survives a
crash and a reboot as a machine that no longer sleeps.
Keep-awake apps such as Amphetamine and KeepingYouAwake hold the setting for as long as you leave them on. They are the right tool when you are staying. rucksack is the one you run when you are leaving: it is a command, it is bounded, and it checks the network as well as the power state.
Cloud agents and remote dev environments solve a different problem. A cloud run starts from a clean checkout, while rucksack keeps the session that already has your working tree, your uncommitted changes, your local environment, and your permission and sandbox settings.
Provider remotes carry the phone-side conversation: Claude Code's remote control and the Codex and Cursor equivalents. rucksack exists to keep them reachable, since they have nothing to talk to once the Mac is asleep or off the network.
See prior art for related projects.
A closed Mac running builds or other heavy work can get hot. Test your workload on a ventilated desk before carrying it.
The lease ends for host-level reasons only:
- you run
unpack - the time limit arrives
- the battery reaches its floor (10% by default)
- macOS reports serious thermal pressure or actual throttling
- the battery gauge cannot be read three times in a row while on battery
- the helper stops answering, and its own TTL restores sleep
In each case, rucksack restores normal sleep. An agent finishing its task does not end the
session, and neither does losing the network.
If pack cannot finish, it rolls back and lets the Mac sleep. That is the safe failure mode.
- Installation
- Architecture
- Agent support
- Security policy and threat model
- Documentation index
- Contributing
MIT. See LICENSE.