diff --git a/public/blog/banners/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.svg b/public/blog/banners/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.svg new file mode 100644 index 00000000..09b45201 --- /dev/null +++ b/public/blog/banners/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.svg @@ -0,0 +1,8 @@ + diff --git a/src/data/blogPosts.json b/src/data/blogPosts.json index d6a3e8d3..2396d4b5 100644 --- a/src/data/blogPosts.json +++ b/src/data/blogPosts.json @@ -1818,5 +1818,15 @@ "tags": ["shell-online", "routers", "ssh", "network-security", "embedded-linux"], "banner": "banners/a-remote-router-shell-without-port-forwarding-or-an-exposed-ssh-port-after-mikro.svg", "iso_date": "2026-10-07" + }, + { + "slug": "two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de", + "title": "Two People, One Collaborative Terminal Session in the Browser: How shell.online Decides Who Types During a GhostAction Cleanup", + "description": "How a collaborative terminal session in the browser decides who types: shell.online's 1.8-second input lease, read-only links and rotation.", + "date": "Oct 8", + "category": "Security", + "tags": ["shell-online", "incident-response", "github-actions", "terminal-sharing"], + "banner": "banners/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.svg", + "iso_date": "2026-10-08" } ] diff --git a/src/pages/blog/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.astro b/src/pages/blog/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.astro new file mode 100644 index 00000000..eaaa5b90 --- /dev/null +++ b/src/pages/blog/two-people-one-collaborative-terminal-session-in-the-browser-how-shell-online-de.astro @@ -0,0 +1,82 @@ +--- +import BlogLayout from '../../layouts/BlogLayout.astro'; + +const bodyContent = `
GitGuardian published its write-up of the returning GhostAction campaign on 7 October 2026, and the cleanup it describes is not a one-person job. Gaetan Ferry and Guillaume Valadon counted 772 public repositories hit between 31 August and 30 September, with injected workflows that hardcoded secret names copied from the victims' own workflows and posted the values to a bare IP address over plain HTTP. Their remediation advice has two halves: rotate every secret the workflow could reach, and find and revoke the GitHub credential that pushed the workflow in the first place. That is a long session of gh commands, provider consoles and second opinions, which is exactly when teams reach for a collaborative terminal session in the browser instead of pasting output into a chat thread.
+ +This post is about one narrow piece of that: what happens in shell.online when two or three people have the same terminal open and more than one of them wants to type. The answer is in the relay and host source, so every number below comes from the code on main in the shell.online repository, not from a feature page.
+ +Truffle Security's scan of a 224-million-repository snapshot, reported by BleepingComputer on 30 September, found 543,699 unique credentials that still authenticated, with a median exposure of 784 days. Only one of 101,886 committed npm tokens still worked. The difference is who revokes: npm acts on leaked tokens itself, while a database connection string has no central issuer to do that. For every credential like that, a person has to do the rotation.
+GitHub's own secure use reference for Actions says to rotate secrets periodically and keep the GITHUB_TOKEN to the minimum permissions. In practice the rotation after an incident looks like one terminal running commands such as these, with a colleague reading over the output:
+gh run list --repo acme/api --limit 50
+gh secret set DEPLOY_SSH_KEY --repo acme/api < ./new_deploy_key
+gh secret set AZURE_CREDENTIALS --repo acme/api
+The last line, with no --body, opens an interactive prompt for the value. If a second person's keystrokes land in the middle of that prompt, the stored secret is wrong and nobody notices until a deploy fails. Interleaved input in a shared terminal is a nuisance in a dev server. In a rotation it produces a broken credential.
+The person doing the rotation starts a named shell on their own machine:
+shell --name "GhostAction cleanup" bash
+It prints a URL, a password and a QR code. A teammate opens the link in a browser, enters the password, and sees the same live terminal. There is no SSH account to create and no VPN. The process and its PTY stay on the machine that started it, and the browser is a view onto it. The command reference lists the rest of the local controls: shell list to find the session ID, shell attach <ID> to rejoin it from another local terminal, Ctrl-X then D to detach without stopping it.
+Up to 16 browser viewers can join one session; the relay closes the seventeenth with "session is full". Each one appears as a numbered guest avatar, and controller agents connected through an MCP grant show up separately as an "Agent: <label>" chip, so everyone can see who is in the room.
+Every input frame from a browser goes through the relay, and the relay decides whether to forward it to the host. The rule is a short lease, 1,800 milliseconds long:
+On the viewer side, the page shows "Local owner is typing…" or "Guest 2 is typing…" and disables input while someone else holds it. Since v0.13.0 the browser also clears any keystrokes it had queued behind encryption or backpressure when another collaborator takes the lock, so a delayed burst cannot arrive in the middle of someone else's turn.
+On the host, all input (foreground stdin, a local attach, browser frames, MCP sends) goes through one arbiter before it reaches the PTY. The relay does not have to read keystrokes to arbitrate. It decides from which socket sent an input frame and when, and with end-to-end encryption on (the default) the frame contents stay sealed between the browser and the host.
+It is a lease on bursts of typing, not a floor you hold. After 1.8 seconds of silence anyone else with an interactive link can type. If you stop halfway through a command to read something, and a colleague starts typing, their characters land on your half-written command line in the shell. The lock prevents two streams of keys from mixing at the same instant. It does not give you a private turn.
+So for a rotation session the practical arrangement is one driver and several watchers, and the cleanest way to get that is to give the watchers a link that cannot type at all:
+shell --read-only --name "GhostAction cleanup (watch)" bash
+The security model page states that read-only input is rejected by both the relay and the CLI, so a modified browser cannot turn a watch link into an interactive one. In the relay source, a typing event from a read-only session is ignored before it can claim the lease. The tradeoff is that read-only is chosen when the session is created. A watcher who later needs to type needs a new interactive share.
+The share URL and password together are a bearer credential. Anyone holding both can view, and on an interactive link type, with the permissions of the wrapped process. During a secret rotation that process is a shell holding a GitHub token with admin rights on your repositories.
That makes the shared session another secret in the incident. Keep the URL and password out of the ticket and out of any channel with integrations that log message bodies. The QR code carries the password too.
+Three controls help here, all documented in the CLI reference:
+Rotation does not undo anything. Commands a viewer already typed have already run, and anything they already saw or copied stays with them. The security page says this directly, and it applies to the session password the same way it applies to the GitHub secrets you are rotating.
+By default the CLI encrypts terminal frames before they reach the relay and the browser decrypts them, so the relay forwards ciphertext. It sees timing, frame sizes, which viewers are connected and who held the input lease; it does not see the gh output or the secret you pasted. The encryption details page covers the key derivation. Running with --no-e2ee drops that and leaves only HTTPS and WSS transport encryption, which would put rotation output in reach of the relay. Do not use it for this.
+Two other limits matter on a long cleanup. The host has to stay awake and online, because nothing runs in the cloud. And a session that drops off the network reconnects without killing the local process, but a reboot of the host ends the shell, whatever link you saved.
+shell.online is developed by the team behind Pilot Protocol, and the same team maintains it in the open. If you want the basics of the tool before using it on an incident, the introduction to the live terminal browser link walks through the first session. The arbitration code discussed here lives in worker/index.ts (claimInputLease) and cmd/shell/session_unix.go in the MIT-licensed source on GitHub, so the 1.8-second figure is something you can read and argue with.
+Install the CLI, run shell --read-only bash, and send the link and password to the one person who needs to watch. The process stays on your machine.
Try shell.online