{"slug": "show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease", "title": "Show HN: Rucksack alpha CLI that pins a Mac to a hotspot and holds a sleep lease", "summary": "Noah Nawara released rucksack v0.1.0-alpha.1, a macOS CLI tool that pins a Mac to a phone hotspot and holds a sleep lease so local coding agents continue running with the lid closed. The source-only alpha passes CI and short hotspot desk tests, but has no signed package and closed-lid behavior in a bag is not yet verified. The tool requires macOS, Xcode Command Line Tools, and Rust 1.86 or newer.", "body_md": "Close your laptop and keep your agents running.\n\n`rucksack`\n\nis a macOS command-line tool for leaving your desk while a local coding agent is\nstill working. It moves your Mac onto your phone hotspot, verifies the handoff, and holds a\nbounded lease that keeps the Mac awake with the lid closed. You keep steering the same\nCodex, Claude Code, or Cursor session from your phone. Switching to the hotspot is one\nstep, where moving the work to a cloud agent means standing up a fresh environment.\n\nSource-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.\n\n```\ncargo install --locked --git https://github.com/noahnawara/rucksack --tag v0.1.0-alpha.1 rucksack-cli rucksack-helper\nrucksack helper install\n```\n\nBoth crate names are required. `rucksack`\n\nfinds its helper as a file beside itself, so installing\nonly `rucksack-cli`\n\nreports success and can never pack. Installing both puts them in one directory.\n\nRun `helper install`\n\nyourself, in a terminal. `pack`\n\nwould do it on first use, but it needs `sudo`\n\n,\nand `sudo`\n\nreads your password from a terminal that an agent session does not have. Needs macOS,\nXcode Command Line Tools, and Rust 1.86 or newer; release packaging targets macOS 14 and newer.\nRead the [installation guide](/noahnawara/rucksack/blob/main/INSTALL.md) for the clone-and-build route and removal.\n\nOr paste this to Codex, Claude Code, or Cursor and let it do the first part:\n\n```\nInstall rucksack on this Mac by running exactly this, both crate names included:\n\ncargo install --locked --git https://github.com/noahnawara/rucksack --tag v0.1.0-alpha.1 rucksack-cli rucksack-helper\n\nWithout `rucksack-helper` the install reports success and `rucksack` can never pack, so don't drop\nit. It compiles for a couple of minutes and needs nothing added to my PATH. Don't run `rucksack\npack` to test it. Then tell me to run `rucksack helper install` myself in a terminal window — it\nasks for my password once, and getting it out of the way now means no later `rucksack pack` ever\nstops for it. Don't try to run it for me: you have nowhere to type a password.\n```\n\nThere is no setup command. The first `pack`\n\ninstalls the power helper and remembers the\nnetwork it ended up on.\n\nRun that first one yourself, in a terminal — `rucksack helper install`\n\ndoes the same job on its\nown. Installing the helper needs your password, `sudo`\n\nasks for it on a terminal, and the agent\nsession that will run every later `pack`\n\nhas no terminal to be asked on. Doing it once, in\nperson, is what keeps every `pack`\n\nafter it silent.\n\n``` bash\n$ rucksack pack\n\nConnecting to Noah…\nJoined.\nAwake for 24 hours, or until the battery hits 15%. Ends 09:14 tomorrow.\nPacked. Close the lid and go.\n```\n\nIf macOS cannot join the hotspot itself — the normal case for Apple Instant Hotspot — rucksack opens Wi-Fi settings, says what to pick, and waits:\n\n```\nChoose “Noah” in Wi-Fi — keep the lid open until this says Packed. Waiting…\n```\n\nIt then carries on by itself. There is nothing to confirm and nothing to re-run. The lid clause\nis the one thing worth reading twice: until `pack`\n\nsays `Packed`\n\n, nothing is holding this Mac\nawake yet, so closing it during the wait sleeps the machine.\n\nThe lease belongs to the **Mac**, not to one conversation. Closing the lid affects every\nprocess, so every running task benefits, and a task finishing does not end the lease.\n\nrucksack does not change how your agents behave. It writes no instructions, prompts, or\npolicy into them, and installs no hooks; your permission, approval, and sandbox settings\nstay exactly as you configured them. It installs one thing: a `rucksack`\n\nskill for Codex,\nClaude Code, and Cursor, so \"I'm leaving\" works as a sentence in a conversation. Whichever of\nthem you use carries the remote conversation. The CLI and helper have no backend.\n\nWhen you are back at a desk, `rucksack unpack`\n\nrestores normal sleep, and says what the trip\nwas. It is also the recovery path, and works from any state.\n\n``` bash\n$ rucksack unpack\n\nPacked for 3h 12m · battery 79% → 61% · 240 MB\nStill on your phone. Pick a Wi-Fi network when you can.\nUnpacked. This Mac sleeps normally.\n```\n\nEach 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.\n\n```\nrucksack pack     switch to your hotspot and hold this Mac awake\nrucksack status   is it still packed?\nrucksack unpack   let this Mac sleep again\nrucksack pair     print a Codex Remote Control pairing code\nrucksack star     star this project on GitHub\nrucksack helper   install or remove the power helper\n```\n\n`pack`\n\ntakes `--for 90m`\n\nto shorten the session, `--hotspot \"Name\"`\n\nto name the network,\n`--usb`\n\nfor iPhone USB tethering, and `--here`\n\nwhen you are already on the network you want.\n\n`caffeinate`\n\nkeeps a Mac awake for as long as it runs. `caffeinate -d`\n\nalso holds the\ndisplay on, and `caffeinate -i make`\n\nkeeps the machine up until that command finishes. With\nthe lid open, on power or on battery, it does what you want and rucksack has nothing to add.\n\nClose the lid and it stops helping. A `caffeinate`\n\nassertion prevents *idle* sleep, and\nclosing the lid on battery is a *forced* sleep condition evaluated by closed-display policy,\nso the assertion does not cover it. That is the whole gap: `caffeinate`\n\nis useful\ndefense-in-depth while the lid is open, and it is not the closed-lid primitive.\n\nThe setting that does cover it is `SleepDisabled`\n\n, and it is global, persistent, and\nroot-only. Nothing releases it when a process exits, so leaving it on is how a Mac ends up\npermanently unable to sleep. rucksack takes that stronger setting and gives it the thing\n`caffeinate`\n\nhad for free: an owner, and an end.\n\nrucksack does not run, wrap, or extend `caffeinate`\n\n. It refuses to start when `SleepDisabled`\n\nis already on, because that value is the baseline it has to hand back, and two owners make\ncleanup ambiguous. rucksack never stops or modifies whatever holds it.\n\nTwo 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.\n\nSo `pack`\n\ntreats the route as a precondition, not an afterthought — and \"the internet works\"\nis not the test, because the office network you are walking away from also works. rucksack\naccepts a network only once it can show the Mac has actually moved onto the commute one: by\nits name, by an iPhone hotspot gateway, by the default route visibly leaving where it\nstarted, or because you said so with `--here`\n\n. Then it checks that the route really reaches\nthe internet, requiring Apple's own captive-network success page so a hotel portal cannot\npass.\n\nLosing that network later does **not** end the session. A tunnel is exactly when your work\nmost needs to stay alive, so an outage is reported by `status`\n\nand nothing else.\n\nThe Rust workspace has a CLI, shared core, and a small root-owned helper. The helper runs\none fixed command, `/usr/bin/pmset -a disablesleep 0|1`\n\n, and cannot run anything else. It\nrefuses to acquire unless normal sleep is the verified baseline, saves that baseline before\nchanging it, and verifies the result afterward.\n\nThe 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.\n\nRunning `sudo pmset -a disablesleep 1`\n\nby hand sets the same value, with no saved baseline,\nno expiry, and no release when the battery or the temperature says stop. It survives a\ncrash and a reboot as a machine that no longer sleeps.\n\nKeep-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.\n\nCloud 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.\n\nProvider 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.\n\nSee [prior art](/noahnawara/rucksack/blob/main/docs/PRIOR_ART.md) for related projects.\n\nA closed Mac running builds or other heavy work can get hot. Test your workload on a ventilated desk before carrying it.\n\nThe lease ends for host-level reasons only:\n\n- you run\n`unpack`\n\n- the time limit arrives\n- the battery reaches its floor (15% by default)\n- macOS reports serious thermal pressure or actual throttling\n- the battery gauge cannot be read three times in a row while on battery\n- the helper stops answering, and its own TTL restores sleep\n\nIn each case, `rucksack`\n\nrestores normal sleep. An agent finishing its task does not end the\nsession, and neither does losing the network.\n\nIf `pack`\n\ncannot finish, it rolls back and lets the Mac sleep. That is the safe failure mode.\n\n[Installation](/noahnawara/rucksack/blob/main/INSTALL.md)[Architecture](/noahnawara/rucksack/blob/main/docs/ARCHITECTURE.md)[Agent support](/noahnawara/rucksack/blob/main/docs/ADAPTERS.md)[Security policy](/noahnawara/rucksack/blob/main/SECURITY.md)and[threat model](/noahnawara/rucksack/blob/main/docs/THREAT_MODEL.md)[Documentation index](/noahnawara/rucksack/blob/main/docs/README.md)[Contributing](/noahnawara/rucksack/blob/main/CONTRIBUTING.md)\n\nMIT. See [LICENSE](/noahnawara/rucksack/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease", "canonical_source": "https://github.com/noahnawara/rucksack", "published_at": "2026-07-26 18:34:14+00:00", "updated_at": "2026-07-26 18:52:18.247427+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Noah Nawara", "rucksack", "Codex", "Claude Code", "Cursor", "macOS", "Xcode Command Line Tools", "Rust"], "alternates": {"html": "https://wpnews.pro/news/show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease", "markdown": "https://wpnews.pro/news/show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease.md", "text": "https://wpnews.pro/news/show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease.txt", "jsonld": "https://wpnews.pro/news/show-hn-rucksack-alpha-cli-that-pins-a-mac-to-a-hotspot-and-holds-a-sleep-lease.jsonld"}}