Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions src/data/blogPosts.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
import BlogLayout from '../../layouts/BlogLayout.astro';

const bodyContent = `<p>Hugging Face shipped <a href="https://github.com/huggingface/trl/releases/tag/v1.13.0">TRL v1.13.0</a> 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 <span class="brand-shell">shell.online</span>, what the viewer sees, and what the link does and does not grant.</p>

<section>
<h2>The command, and what it prints</h2>
<p>The guide launches its example with accelerate. Prefix that launch with the CLI from <a href="https://shell.online">shell.online</a> 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:</p>
<pre><code>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</code></pre>
<p>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.</p>
<p>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 <a href="https://shell.online/cli/">CLI reference</a> lists every option and the exit codes.</p>
<p>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.</p>
</section>

<section>
<h2>What a read-only terminal link shows on a phone</h2>
<p>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 <a href="https://shell.online/mobile/">mobile behavior page</a> 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 <a href="https://github.com/TeoSlayer/shell.online/pull/100">pull request #100</a>, which also added two-finger pinch zoom on the terminal alone, from 50% to 150%, without disturbing anyone else's view.</p>
<p>For a training run, 80 columns matters. The progress line in the guide is short enough to survive the switch unwrapped:</p>
<pre><code>{'loss': 4.311, 'grad_norm': 29.25, 'num_tokens': 1049000.0, 'epoch': 0.25}
8%|▊ | 1/12 [06:20&lt;1:09:41, 380.10s/it]</code></pre>
<p>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 <a href="https://shell.online/reliability/">reliability page</a> covers the bounded queues and write timeouts that keep a high-output process from ever blocking on Cloudflare.</p>
</section>

<section>
<h2>Read-only is enforced by the relay, not the browser</h2>
<p>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.</p>
<div class="callout"><p>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.</p></div>
<p>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 <a href="https://www.rfc-editor.org/rfc/rfc8018">RFC 8018</a>. 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 <a href="https://shell.online/e2ee/">end-to-end encryption page</a> is explicit that E2EE cannot hide traffic shape or stop the relay from dropping or delaying a valid frame.</p>
<p>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 <a href="https://shell.online/security/">security model page</a> 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.</p>
</section>

<section>
<h2>Network loss, deadlines, and the end of the run</h2>
<p>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.</p>
<p>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:</p>
<pre><code>shell --read-only --auto-close 2h -- accelerate launch ...
shell --read-only --auto-close "tomorrow 09:00" -- accelerate launch ...</code></pre>
<p>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.</p>
<p>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:</p>
<pre><code>shell --read-only --persistent ~/.shell-train-state -- accelerate launch ...</code></pre>
<p>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 <a href="https://github.com/TeoSlayer/shell.online">source repository</a> documents this under platforms and in the CLI help, alongside the Docker image that combines the same mechanism with an automatic restart policy.</p>
<p>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 <a href="https://pilotprotocol.network/blog/shell-online-live-terminal-browser-link">the introduction to shell.online</a>.</p>
</section>

<div class="cta"><h3>Watch the run, not the shell</h3><p>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.</p><a href="https://shell.online/">Try shell.online</a></div>`;
---
<BlogLayout
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="September 11, 2026"
tags={["shell-online", "read-only-terminal", "training-runs", "remote-monitoring", "mobile-terminal"]}
canonicalPath="/blog/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone"
author={{ name: 'Artemii Amelin', type: 'Person' }}
bannerImage="/blog/banners/a-read-only-terminal-link-for-a-training-run-you-check-from-your-phone.svg"
>
<Fragment set:html={bodyContent} />
</BlogLayout>
Loading
Loading