diff --git a/public/blog/banners/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.svg b/public/blog/banners/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.svg new file mode 100644 index 00000000..ec741d9f --- /dev/null +++ b/public/blog/banners/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.svg @@ -0,0 +1,8 @@ + + + + GUIDE / SEP 11 2026 + A Read-Only Terminal Linkfor a Training Run You CheckFrom Your Phone + Share a training run with a read-only terminal... + pilotprotocol.network + diff --git a/public/blog/banners/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.svg b/public/blog/banners/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.svg new file mode 100644 index 00000000..deb9b9fa --- /dev/null +++ b/public/blog/banners/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.svg @@ -0,0 +1,8 @@ + + + + OPERATIONS / SEP 12 2026 + Self-Hosted Terminal Sharing onOne Docker Box: the shell.onlineRelay Without a CloudflareAccount + shell.online v0.12.2 adds self-hosted terminal... + pilotprotocol.network + diff --git a/public/blog/banners/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.svg b/public/blog/banners/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.svg new file mode 100644 index 00000000..c19fa886 --- /dev/null +++ b/public/blog/banners/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.svg @@ -0,0 +1,8 @@ + + + + GUIDE / SEP 10 2026 + VS Code 1.137 Runs Agent Sessionsin Their Own Process. TerminalSharing Without SSH or VPN DoesThat for a Failing Build + How to pair-debug a failing build with terminal... + pilotprotocol.network + diff --git a/src/data/blogPosts.json b/src/data/blogPosts.json index 71de0c30..863825c4 100644 --- a/src/data/blogPosts.json +++ b/src/data/blogPosts.json @@ -1467,5 +1467,35 @@ "tags": ["shell-online", "terminal-sharing", "end-to-end-encryption", "oauth", "team-collaboration"], "banner": "banners/team-terminal-sharing-sealed-so-the-service-cant-open-it.svg", "iso_date": "2026-09-09" + }, + { + "slug": "vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without", + "title": "VS Code 1.137 Runs Agent Sessions in Their Own Process. Terminal Sharing Without SSH or VPN Does That for a Failing Build", + "description": "How to pair-debug a failing build with terminal sharing without SSH or VPN, and how that compares to the Agent Host in VS Code 1.137.", + "date": "Sep 10", + "category": "Guide", + "tags": ["shell-online", "terminal-sharing", "vscode", "pair-debugging", "ci"], + "banner": "banners/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.svg", + "iso_date": "2026-09-10" + }, + { + "slug": "a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone", + "title": "A Read-Only Terminal Link for a Training Run You Check From Your Phone", + "description": "Share a training run with a read-only terminal link: one command, a phone-sized grid, relay-enforced no-input, E2EE, and what happens when the network drops.", + "date": "Sep 11", + "category": "Guide", + "tags": ["shell-online", "read-only-terminal", "training-runs", "remote-monitoring", "mobile-terminal"], + "banner": "banners/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.svg", + "iso_date": "2026-09-11" + }, + { + "slug": "self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a", + "title": "Self-Hosted Terminal Sharing on One Docker Box: the shell.online Relay Without a Cloudflare Account", + "description": "shell.online v0.12.2 adds self-hosted terminal sharing: a standalone relay on any Docker host, Caddy TLS, no Cloudflare account, same E2EE.", + "date": "Sep 12", + "category": "Operations", + "tags": ["shell-online", "self-hosting", "docker", "terminal-sharing", "end-to-end-encryption"], + "banner": "banners/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.svg", + "iso_date": "2026-09-12" } ] diff --git a/src/pages/blog/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.astro b/src/pages/blog/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.astro new file mode 100644 index 00000000..57023d4d --- /dev/null +++ b/src/pages/blog/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.astro @@ -0,0 +1,59 @@ +--- +import BlogLayout from '../../layouts/BlogLayout.astro'; + +const bodyContent = `

Hugging Face shipped TRL v1.13.0 on September 10 with a guide that trains one 1,048,576-token sequence per step on a single 8×H100 node. Qwen3-8B, 380 seconds a step, 56.2 GB per GPU, twelve steps. That is a run you start, walk away from, and want to glance at from the couch or the train. A read-only terminal link is the least-privilege way to do that: the process stays on the training box, nobody gets a shell, and the phone shows the same tqdm bar the launching terminal shows. This post walks through the exact command with shell.online, what the viewer sees, and what the link does and does not grant.

+ +
+

The command, and what it prints

+

The guide launches its example with accelerate. Prefix that launch with the CLI from shell.online and add the read-only flag before the command. The double dash ends option parsing so accelerate's own flags are never mistaken for ours:

+
shell --read-only -- accelerate launch \\
+    --config_file examples/sft_qwen3_8b_1m_context/context_parallel_8gpu.yaml \\
+    examples/sft_qwen3_8b_1m_context/sft_qwen3_8b_1m_context.py
+

The wrapped command runs in the background inside a local PTY. The URL prints only after the relay connection exists and the task has survived a short startup handshake, so a launch that dies on a bad config path prints its real exit status and no dead link. In an interactive terminal you also get a QR code whose fragment carries the salted URL and the password together, so a phone opens it with one scan. Piped and JSON output stay machine-clean.

+

The launching terminal is now free. Use shell list to see the process uptime and the relay's independent online, reconnecting, expired, or unknown state; unknown means the check was inconclusive, not that the link died. To take over locally, shell attach with the session ID puts you back in the PTY without disconnecting any browser, and Ctrl-X then D detaches again. The CLI reference lists every option and the exit codes.

+

The guide notes that the first step lands after ten minutes or so of loading and tokenizing. A viewer who opens the link during that window sees the accelerate startup output and then the progress bar updating in place, because the process is writing to a real PTY and tqdm behaves exactly as it does locally.

+
+ +
+

What a read-only terminal link shows on a phone

+

Viewers never own PTY sizing. A session with only desktop browsers attached runs on a 120×36 grid. The moment a phone or portrait viewer connects, the relay switches everyone to an 80-column grid and each browser fits that grid to its own screen; the last portrait viewer leaving restores 120×36. Read-only phones count for this too. The mobile behavior page describes the 80×24 compatibility grid; as of v0.11.2 the CLI advertises support for a taller 80×40 grid in its host handshake header, the relay picks 80×40 when the host supports it, and older CLIs still get 80×24. The negotiation lives in pull request #100, which also added two-finger pinch zoom on the terminal alone, from 50% to 150%, without disturbing anyone else's view.

+

For a training run, 80 columns matters. The progress line in the guide is short enough to survive the switch unwrapped:

+
{'loss': 4.311, 'grad_norm': 29.25, 'num_tokens': 1049000.0, 'epoch': 0.25}
+  8%|▊ | 1/12 [06:20<1:09:41, 380.10s/it]
+

Single-finger gestures scroll terminal history. If output outruns the network or the phone's rendering, stale display work is dropped and sends one authoritative screen snapshot from a bounded local ring buffer, so a returning viewer sees the current screen rather than a replay of every intermediate redraw. The reliability page covers the bounded queues and write timeouts that keep a high-output process from ever blocking on Cloudflare.

+
+ +
+

Read-only is enforced by the relay, not the browser

+

With the read-only flag, ordinary browser input and the confirmed Ctrl-D EOF are rejected by the Worker. Opening DevTools or hand-crafting WebSocket frames cannot turn the link into an interactive session, and the access mode cannot be weakened after creation. That is the property you want when the link is going to sit in a group chat for an hour: the worst a holder can do is watch.

+

Watching is still access. The URL and the eight-character password together are a bearer credential. Anyone holding both can read everything the terminal prints, which for a training run means dataset names, checkpoint paths, loss values, and any secret a careless script echoes. Treat the pair like a token, and rotate by starting a new share.

+

What the relay sees is the other half of the trust model. Terminal payloads are encrypted on the training box with AES-256-GCM before they enter Cloudflare and decrypted in the browser. The printed URL carries only a random salt in its fragment, which never reaches the server, and the CLI and browser derive the same key locally with PBKDF2-HMAC-SHA256 at 600,000 iterations, the key derivation function specified in RFC 8018. Cloudflare still observes connection IPs, timing, encrypted frame sizes, opcodes, access mode, the command label, and lifecycle events, and it does not persist terminal contents. The end-to-end encryption page is explicit that E2EE cannot hide traffic shape or stop the relay from dropping or delaying a valid frame.

+

Eight generated base64url characters give 48 bits of entropy, which is fine for a link that lives as long as one task. For a multi-day run, or one whose logs you would not want indexed, set SHELL_ONLINE_E2EE_PASSWORD to a longer unique value before launching and send it separately from the URL. Derivation still happens on the machine. The security model page lays out the full boundary, including the point that an interactive share types with the wrapped process's operating-system permissions, which is exactly why this workflow uses the read-only mode.

+
+ +
+

Network loss, deadlines, and the end of the run

+

The link can disappear. The process should not. The CLI and browser reconnect with bounded backoff, a temporary network failure never stops the local task, and the same ordinary share link remains recoverable for 12 hours after its host disconnects. A phone that loses signal in a tunnel comes back to the current screen, because the relay keeps the session and the CLI keeps the process.

+

Lifetime is bounded by the task. When accelerate exits, the share closes and its server-side state is deleted; there is nothing to clean up. If you want the link to expire before the run does, add an earlier deadline. Relative units and local times both work:

+
shell --read-only --auto-close 2h -- accelerate launch ...
+shell --read-only --auto-close "tomorrow 09:00" -- accelerate launch ...
+

An auto-close value can only shorten the lifetime, never extend it past task exit, and a missing or invalid value returns status 2 instead of becoming part of the command. Since v0.11.0 long deadlines are honored rather than silently clipped to the relay's rolling 12-hour lease, which matters for exactly this kind of overnight job.

+

Two more cases come up with training runs specifically. First, the guide's run reaches 63.6 GB on an 80 GB card and still fails without the expandable-segments allocator setting. When a run dies like that during the handshake, the CLI reports the status and prints no URL, so a monitoring link never points at a corpse. Second, when you resume from a checkpoint after a crash, the persistent mode keeps the same URL and password across relaunches:

+
shell --read-only --persistent ~/.shell-train-state -- accelerate launch ...
+

The state file stores an owner-only host identity, password, and E2EE material. Whoever already has the link opens the resumed run without a new credential exchange, and shell kill stops the current process without deleting the identity file. The source repository documents this under platforms and in the CLI help, alongside the Docker image that combines the same mechanism with an automatic restart policy.

+

None of this needs an account. Linking a machine with shell login is optional and publishes only the share URL, command name, host name, and timing to the web app, never terminal contents or the password. For a solo training run watched from a phone, the plain command above is the whole setup. The broader idea, a browser link to any terminal process, is covered in the introduction to shell.online.

+
+ +

Watch the run, not the shell

One command wraps a training launch in a read-only, end-to-end encrypted link that opens on any phone and closes itself when the job ends.

Try shell.online
`; +--- + + + diff --git a/src/pages/blog/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.astro b/src/pages/blog/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.astro new file mode 100644 index 00000000..c702ddc3 --- /dev/null +++ b/src/pages/blog/self-hosted-terminal-sharing-on-one-docker-box-the-shell-online-relay-without-a.astro @@ -0,0 +1,62 @@ +--- +import BlogLayout from '../../layouts/BlogLayout.astro'; + +const bodyContent = `

Cloudflare's status page logged an incident titled "Cloudflare Tunnel Availability Issues" that opened at 21:55 UTC on September 11 and was marked resolved at 08:57 UTC on September 12, roughly eleven hours later, with the impact rated minor and the affected component listed simply as Tunnel. Cloudflare Tunnel is a common way to reach a home lab or a dev box without opening an inbound port: the cloudflared daemon makes outbound-only connections to Cloudflare's network and traffic to your origin rides through that tunnel. It is a good design, and it has one obvious property. When the relay in the middle has a bad night, so does your access path. That is the honest framing for the feature shell.online shipped today in v0.12.2: self-hosted terminal sharing through a relay you run yourself, on an ordinary Docker host, with no Cloudflare account involved.

+ +

To be clear about what this post is not claiming: we have no evidence the hosted shell.online relay was affected by that incident, and Cloudflare's status page publishes no detail beyond the title, timing, and component. The point is narrower. A relay is a dependency. The other self-hosting path in the repo is our Cloudflare Workers build deployed under your own account, which still means Cloudflare. The standalone relay needs only Docker, a domain name, and two open ports.

+ +
+

What the relay does, and what it never sees

+

A quick recap for anyone new to the tool. You run one command, for example shell claude, and the CLI prints a URL, a password, and a QR code. Anyone holding both the URL and the password opens the same live terminal in a phone or desktop browser. The process and its pseudo-terminal stay on your machine. The relay's only job is to move frames between the CLI and the browser and to enforce access rules. By default the CLI encrypts every terminal frame before it leaves the machine and the browser decrypts it locally, so the relay carries ciphertext. Our security model page spells out what the hosted relay can still observe: frame type, encrypted size, timing, connection IP, access mode, the command label, and lifecycle events. Terminal contents are not on that list. The URL's #salt= fragment never reaches the relay either, because browsers do not send fragments, and the CLI and browser derive the same AES-256-GCM key locally from the salt and the password with PBKDF2-HMAC-SHA256 at 600,000 iterations.

+

The self-hosted relay does not change any of that. The docs put it plainly: terminal frames remain opaque to the relay when the CLI's default end-to-end encryption is used. What changes is who operates the box that sees the metadata, and whose uptime you depend on.

+
+ +
+

Self-hosted terminal sharing: bringing up the standalone relay

+

The standalone deployment lives in the standalone directory of the shell.online repository. It is a single Node.js service speaking WebSockets, keeping session metadata in a local state file, fronted by Caddy for TLS. The published image is ghcr.io/teoslayer/shell.online-relay, built for amd64 and arm64. Requirements from the self-hosting document: Docker Engine with Compose, a public domain, and ports 80 and 443. Point the domain's A or AAAA record at the host first, then:

+
git clone https://github.com/TeoSlayer/shell.online.git
+cd shell.online/standalone
+
+SHELL_ONLINE_PUBLIC_URL=https://relay.example.com \\
+SHELL_ONLINE_SITE=relay.example.com \\
+docker compose up -d --build
+

Two variables, two jobs. SHELL_ONLINE_PUBLIC_URL is the exact public origin the relay writes into share links and checks browser origins against. SHELL_ONLINE_SITE is the Caddy site address; giving it a hostname is what turns on automatic HTTPS. Caddy's documentation is specific about why the ports matter: the HTTP challenge needs port 80 reachable from outside, the TLS-ALPN challenge needs port 443, and Caddy enables both by default. Leave it at the default of http://localhost and Caddy serves plain HTTP, which is what the local smoke test in the docs relies on:

+
docker compose up -d --build
+curl http://localhost/api/health
+

The compose file is worth reading before you trust it, and it is short. The relay container runs with a read-only root filesystem, drops all capabilities, sets no-new-privileges, runs as a non-root user created in the Dockerfile, and mounts a small tmpfs at /tmp. Caddy publishes 80, 443, and 443/udp and proxies to the relay on port 8080 through a four-line Caddyfile. The health check hits /api/health every thirty seconds, and Caddy waits for the relay to report healthy before it starts.

+
+ +
+

Pointing the CLI at your relay

+

You do not reinstall or rebuild the CLI. The CLI reference lists SHELL_ONLINE_SERVER as the default relay URL and a per-invocation --server flag that overrides it:

+
SHELL_ONLINE_SERVER=https://relay.example.com shell claude
+shell --server https://relay.example.com claude
+

Everything else behaves as it does against the hosted service. The share URL now points at your domain, the browser page is served by your relay (the Docker build bundles the web client into the image), and read-only mode, --auto-close, --persistent, and the rest all work because the standalone server enforces the same protocol rules. The docs list them: authentication, same-origin browser policy, read-only mode, the input lease that stops two people typing over each other, viewer and frame and traffic limits, slow-client protection, the stable terminal grid, and task-bound expiry. One small tell that this was done properly rather than bolted on: the --no-e2ee output now refers to whatever relay you configured instead of assuming it is Cloudflare.

+

The access rule is unchanged too, and it needs saying every time. The URL plus the password is a bearer credential. Anyone who has both can watch the session, and unless you started it with --read-only, can type with the permissions of the wrapped process. Running your own relay does not soften that. What it does give you is a domain you control, so a leaked link at least points at infrastructure you can shut off, and shell password rotate followed by the session ID swaps the password and salt on a running session without killing the process.

+
+ +
+

What survives a relay restart, and what the state volume holds

+

The relay keeps a state file, by default at /var/lib/shell-online/relay.json inside the container, on a named volume called relay-state. According to the self-hosting docs it stores session metadata and host-token hashes so that a client started with --persistent can recover the same identity after the relay restarts. It does not store terminal contents, end-to-end encryption keys, or browser passwords. Back the volume up if you care about persistent links surviving a rebuild, and run exactly one relay replica per volume; this is a single-node deployment by design. Live sockets reconnect after a restart, which matches the behavior documented on our reliability page: the CLI and browser both reconnect with bounded backoff, and a temporary failure does not stop the wrapped process.

+

Two things the standalone relay does not include: the optional accounts app (organizations, session vault, browser-started sessions) and the hosted analytics dashboard. Accounts are a separate service that never participates in terminal transport, so a self-hosted relay works without them. If you want the org features against your own relay you deploy the app in the repo's app directory separately; it uses Firebase Authentication and PostgreSQL and is documented in its own README.

+
+ +
+

When this is the right call

+

Most people should keep using the hosted relay. It is already up, it needs no setup, and the encryption boundary is the same. Self-hosting makes sense in a few specific situations. Your compliance regime wants the relay's metadata (IPs, timing, command labels) inside your own perimeter. Your team already runs a Docker host with a domain and you would rather own one more small service than depend on someone else's status page. Or you were reminded this week that a tunnel provider can have an eleven-hour incident, and you want the remote path to a training box or an unattended coding agent to fail on your terms rather than theirs. In each of those cases the deployment is a clone, two environment variables, and a compose up, and the CLI you already have on every laptop and server keeps working with one variable changed.

+

For the background on how the browser link itself works, the earlier post on shell.online as a live terminal browser link covers the session model. We build shell.online at Pilot Protocol, it is MIT licensed, and the standalone server is one readable TypeScript file, so you can check every claim in this post against the source before you run it.

+
+ +

Run the relay where you want it

The hosted service works out of the box. When you need the relay on your own domain, the standalone Docker deployment is in the repo, and the CLI switches with one environment variable.

Try shell.online
`; +--- + + + diff --git a/src/pages/blog/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.astro b/src/pages/blog/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.astro new file mode 100644 index 00000000..132b0897 --- /dev/null +++ b/src/pages/blog/vs-code-1-137-runs-agent-sessions-in-their-own-process-terminal-sharing-without.astro @@ -0,0 +1,58 @@ +--- +import BlogLayout from '../../layouts/BlogLayout.astro'; + +const bodyContent = `

VS Code 1.137 shipped on September 9, 2026, and its release notes describe the Agent Host in one sentence: "The agent host lets you connect to the same agent session from multiple VS Code windows." Agent harnesses run in a dedicated process, and every window is a client of that process rather than its owner. The idea underneath is old and good. The thing doing the work should not die when the window that started it closes, and every screen looking at it should be a viewer, not the owner. shell.online applies that same split to any terminal process, and it is why terminal sharing without SSH or VPN works for a build that is failing on one machine while the person who can fix it sits at another.

+ +
+

What the Agent Host actually changes

+

Under the hood VS Code now talks to the harness over the Agent Host Protocol (AHP), which the concept documentation describes as state-first: "The host is the source of truth," and each client subscribes to channels for sessions, chats, terminals, and changesets. Two properties fall out of that. Multiple clients can observe and control the same session and stay in sync, and a session can keep running when no editor or other client is connected at all.

+

Reaching that host from another machine is the part worth reading closely. For remote sessions the Agent Host runs as a standalone process and exposes AHP as JSON-RPC over WebSocket. You start one with the code agent host command, it is protected with a connection token, and you reach it over SSH or a dev tunnel. The August architecture post frames the payoff as running the host next to your workspace on another machine "while you connect from the desktop or web to check progress, review changes, and manage sessions."

+

That is a good design for agent sessions. It also only covers agent sessions inside VS Code, and its remote path assumes the other person can be given SSH access or a tunnel. A failing build is often neither of those things. It is a plain process, and the colleague you want looking at it may have no account on the box and no reason to get one.

+
+ +
+

Terminal sharing without SSH or VPN, for one broken build

+

The shell.online CLI makes the same split, but smaller and more literal. It owns a pseudo-terminal and the process inside it on your machine. Browsers are viewers of that PTY and nothing more. One command wraps the build:

+
curl -fsSL https://shell.online/install | sh
+shell npm run build
+

The CLI prints a URL and an eight-character password only after the relay exists and the process has survived a short startup handshake. A build that dies in its first second reports the exit status and prints no link, so nobody is handed a dead URL. The session runs in the background by default and your own terminal comes back. Use shell --foreground when you want the output mirrored locally too, or shell attach with the session ID to take over from your own terminal later; Ctrl-X and then D detaches without stopping the build.

+

Send the URL and the password to the colleague. They open it on a laptop or a phone, see the terminal the build is drawing, and can type into it if you left the link interactive. The relay is Cloudflare, but the CLI encrypts each frame with AES-256-GCM before it leaves the machine and the browser decrypts on arrival. The URL carries only a random salt fragment, and both ends derive the key from the password with 600,000 rounds of PBKDF2-HMAC-SHA256, as the E2EE guide spells out. Cloudflare still sees connection IPs, timing, and encrypted frame sizes, and the security page lists that metadata rather than glossing over it. Terminal contents are not persisted server-side.

+

The access model is blunt and worth saying out loud: the URL and the password together are a bearer credential. Anyone holding both can view the terminal and, unless the link is read-only, type into it with the permissions of the wrapped process. That is the whole trust model. It is why an interactive build link goes to one named person, not to a channel.

+
+ +
+

Read-only when the other person should only look

+

Often the second person does not need the keyboard. They need to see the last two hundred lines of a linker error, or watch the test runner reach the case that hangs. For that:

+
shell --read-only npm run build
+

Read-only is enforced at the relay Worker, not by the page in the browser. Ordinary browser input and a confirmed EOF are rejected server-side, so opening DevTools or hand-crafting WebSocket frames does not turn the link into an interactive one. Access mode is fixed when the share is created and cannot be weakened afterwards. If you later decide the viewer should type, you start a new share.

+

Read-only also narrows what the bearer credential grants. With both values a viewer can see everything the build prints, and nothing else.

+
+ +
+

What happens when the network drops mid-build

+

This is where "the host is the source of truth" earns its keep. The reliability page states the rule directly: relay and browser failures are treated as recoverable display failures, never as permission to terminate local work. The CLI and the browser both reconnect with bounded backoff, and an ordinary share link stays recoverable for 12 hours after its host disconnects. The build keeps running through all of it, because it never depended on the viewer.

+

Build output is bursty, which creates a second problem: a viewer can fall behind. The CLI keeps a bounded local ring buffer so a viewer who arrives late or reconnects gets the screen restored, and if live output outruns the network or the browser's rendering, stale display work is dropped and one authoritative screen snapshot is sent instead. PTY reads never block waiting on Cloudflare. Queues are bounded and writes time out, so a process that writes faster than the relay can carry keeps running at full speed locally. The 0.11.0 entry in the changelog of the shell.online repository records a fix in exactly this area: authenticated E2EE recovery snapshots are now preserved during relay backpressure, so large output can no longer eject viewers to the password screen. Builds are the workload that finds that kind of bug.

+

Two more lifecycle details matter for a pairing session. When the build exits, the share closes on its own; the link does not outlive the process. If you want a harder deadline, shell --auto-close 2h npm run build closes the share after two hours even if the process is somehow still going. And if the colleague opens the link from a phone, the session grid switches from 120 by 36 to 80 by 24 for everyone until the last phone leaves, because viewers never own PTY sizing. Every viewer sees the same line wrapping in the compiler output.

+
+ +
+

Where the two designs differ, honestly

+

The Agent Host carries structured state: chats, changesets, tool approvals, terminals as addressable resources. shell.online carries one PTY and nothing else. It does not know what a build is, cannot show a diff, and has no notion of approving a tool call. What it gives up in structure it gets back in reach. Any process on any machine that can run the static binary qualifies, including the OpenWrt routers and Raspberry Pi boards in the platform matrix, and any viewer with a modern browser, with no account, SSH key, tunnel, or VPN on either side.

+

The remote path is the sharpest contrast. A remote Agent Host is reached over SSH or a dev tunnel with a connection token. A shell.online link is reached over HTTPS with a URL and a password, and the relay only ever handles ciphertext. Both are bearer credentials in practice. The difference is what the holder can reach: an SSH session is the whole machine, while a share link is one process, at that process's permissions, and optionally with no keyboard at all.

+

Rule of thumb for a failing build: an interactive link to the one person who will type the fix, a read-only link to everyone who just wants to watch it go green, and let the share close itself when the build exits.

+

The full flag list, including the auto-close grammar and exit codes, is in the CLI reference. The broader case for a live terminal link is in the post that introduced shell.online on this blog.

+
+ +

Share the build, not the box

One command, one link, one password. The process stays on your machine and the relay never sees plaintext.

Try shell.online
`; +--- + + +