Start a Terminal Session From the Browser on a Mac That Sat Idle for Days: the launchd Priority Fix in shell.online v0.23.3
Two of the most used coding agents now assume a machine that stays up and answers when you come back to it. OpenAI's Codex 0.157.0, released today, enables "automatic background-server startup for eligible interactive sessions". Anthropic's Remote Control documentation says Claude "keeps running locally the entire time", and that when a laptop sleeps or the network drops it "reconnects automatically when your machine comes back online". shell.online takes the same position for any terminal program: link a machine once, and you can start a terminal session from the browser on it later, from wherever you are. Today's v0.23.3 tag fixes a problem that only shows up once a Mac has been doing exactly that for a few days.
How you start a terminal session from the browser on a linked machine
Account linking is optional and the CLI works identically without it. When you do link, the machine is what gets linked, never a terminal. Remote start is a separate, explicit choice on top of that:
curl -fsSL https://shell.online/install | sh
shell auth --allow-remote-start
shell service install
The app guide describes the second line as "Allow your signed-in browser to start sessions on this machine" and adds a warning worth repeating: "The process runs on that machine, not on an app server. Enabling this grants remote execution authority; choose it deliberately." Withdraw it at any time with the same command and --no-remote-start, pause the listener with shell daemon stop, and check it with shell daemon status.
The third line is what makes a machine useful days later. Without it, the small listener that accepts browser-start requests runs only after you have typed a shell command, and stops at the next reboot. With it, the CLI writes a user launch agent under ~/Library/LaunchAgents on macOS, which is where Apple's launchd documentation says per-user agents belong, and launchd keeps the listener alive across restarts. The install command refuses to run if remote start is off, because a service for a machine that does not accept browser starts would keep a process alive to do nothing.
From then on, a signed-in browser at app.shell.online can start Claude Code, Codex, or a plain shell on that Mac. The password for a browser-started session is picked in the browser and sealed to that one machine with an ephemeral P-256 key the machine published, so the accounts service relays an envelope it cannot open. That detail matters for the fix below.
Five seconds per keystroke, and the network was fine
The report that led to v0.23.3 is written up in pull request #268. Some sessions hosted on a Mac took around five seconds on average to echo a keystroke, and at times over thirty. The TCP connect time to the relay was about ten milliseconds, so the relay and the link were not the problem. The host was.
The launch agent that shell service install wrote declared the job with ProcessType set to Background. The launchd.plist manual is direct about what that means: "The resource limits applied to Background jobs are intended to prevent them from disrupting the user experience." Apple's quality-of-service guide describes that class as work that "focuses on energy efficiency" and "takes significant time, such as minutes or hours". Not a terminal waiting for a keypress.
Every session the daemon starts from the browser is a child of that launchd job, and it inherits the clamp. The pull request measured it: the lowest CPU priority, 4 where a normal process runs at 31, plus throttled disk I/O. The I/O part is what turned a scheduling nuisance into a thirty-second stall. When the machine ran short of memory, macOS swapped the idle sessions out. On the affected Mac, Claude Code sessions two to three days old had 90 to 96 percent of their memory swapped according to vmmap. Each keystroke then had to page the heap back in at background I/O priority, queued behind Spotlight and whatever build was running. The one session someone had started by hand from a terminal ran at normal priority and had only 37 percent swapped.
The clamp cannot be lifted after the fact. The pull request notes that taskpolicy -B does not raise a running process out of the background class; only launchd can, at launch. So the fix has to be in the plist, and existing sessions keep the priority they were born with until they are restarted.
What v0.23.3 changes, and what it deliberately does not
The plist now says Interactive. The manual describes that class as jobs that "run with the same resource limitations as apps, that is to say, none", and tells you to use it only "if an app's ability to be responsive depends on it, and cannot be made Adaptive". Adaptive moves a job between classes "based on activity over XPC connections", which is not how a Go process relaying pseudo-terminal frames over WebSockets talks to anything, so Interactive is the honest choice. The comment next to the key in service_darwin.go records the swap behaviour in the file itself.
The part that took a decision rather than a one-line edit is upgrading. Installing v0.23.3 does not rewrite an existing launch agent. Reloading the agent restarts the daemon, and a restart re-keys it, which drops the keys browsers have sealed passwords to. Instead, shell service status now checks the installed plist for the old Background value and prints a plain instruction. From the source:
Installed at <path>
It runs sessions at background priority, which makes idle ones slow to respond.
Run 'shell service install' to refresh it. Running sessions keep the old priority until restarted.
So the upgrade path on an existing Mac is one command, run when you choose: shell service install. Sessions started after that run at normal priority. The changelog entry for 0.23.3 records the same thing. Linux is untouched, because the systemd unit never lowered the daemon's priority in the first place.
What a browser-started session does and does not expose
Since the fix lives in the daemon layer, here is exactly what that layer sees. Linking a machine publishes the share URL, the command name, the host name, and timing to the account. It never publishes terminal contents and never publishes the browser password. Terminal traffic between the CLI and a viewer is end-to-end encrypted by default; the encryption notes cover the frame cipher and what an explicit --no-e2ee gives up.
The credential model is the same as for any share. The URL and the eight-character password together are a bearer credential: anyone holding both can view and, unless the session was started read-only, type with the permissions of the wrapped process. A browser-started session runs on that machine as your user, through sh on Unix. That is the "remote execution authority" the docs warn about, and it is why remote start is asked for rather than assumed. If you only want to watch a long job, start it with shell --read-only and give out that link instead.
Edge cases behave as they do for a session started at the keyboard. The reliability page puts it this way: "The process runs on your computer. A temporary browser or network failure does not intentionally stop it." The CLI and browser retry on their own, the session ends when its process exits or earlier with --auto-close, and the README states the one rule no plist can change: a sleeping or offline host cannot be controlled. The launch agent keeps the listener alive across restarts. It does not keep a closed laptop awake.
Why this class of bug will keep appearing
Codex starting a background server on its own today, and Claude Code reconnecting after sleep, both point the same direction: a developer's machine is becoming a host that is expected to be responsive to a request that arrives days after the last keypress. Operating systems make reasonable assumptions about background jobs, and those assumptions are wrong for a terminal a person is about to type into from a phone. The shell.online daemon is one example; any always-on agent host that installs itself as a service will have to pick a process class, and the default is the slow one.
If you want the plain version of what a share link is before setting up a linked machine, the introduction to live terminal links covers the one-command case. The source is MIT licensed, and the service installer, the status check, and the tests that assert the plist never says Background are all in cmd/shell for anyone who wants to read the change rather than take this post's word for it.
Link a machine, then open it from anywhere
One install, one auth command, and a session you can start from a phone. The process stays on your hardware, the password stays sealed to it, and after v0.23.3 the keystroke comes back at the speed you expect.
Try shell.online