Team Terminal Sharing, Sealed So the Service Can't Open It

Team Terminal Sharing, Sealed So the Service Can't Open It

The 0.11.1 release of shell.online, dated 2026-09-08 in the changelog, is the first version where you can share terminal sessions with your team through an account instead of pasting a URL and a password into chat. An organization, invite links, an owner and an assignee per session, handoffs, comments, and a browser that can start a coding agent on a colleague's linked machine. What the accounts service holds while all of that happens is the interesting part: never a session password it can read, and never terminal output.

Linking a machine publishes four things

Nothing here is required. The CLI works identically without an account, and a share started with a single shell command still prints its URL and eight-character password the way it always did.

shell login                 # opens a browser to approve this machine
shell login --no-browser    # prints the URL; open it on this same machine
shell whoami                # show the linked account
shell logout                # unlink and revoke this machine's token

Login is the OAuth 2.0 authorization code flow with PKCE and a loopback redirect. RFC 8252, the native-app profile of OAuth, requires public native clients to implement PKCE and describes redirecting to a listener on 127.0.0.1 so the code never crosses the network. RFC 7636 is why that matters: an intercepted authorization code is useless without the code verifier, and the verifier is never transmitted. The token the CLI receives is scoped to publishing sessions and can be revoked per machine.

You link a machine, not a terminal. From then on every share in any window publishes as it starts and is marked closed when the process exits. What gets published is the share URL, the command name, the host name, and the timing. The URL keeps its salt fragment so the session can be opened from the web app; the salt is not a secret, and no key derives from it without the password. A fragment carrying a raw key is stripped before anything leaves the machine, which you can read in the SafeShareURL function in the CLI's account client.

How to share terminal sessions with your team day to day

Signing up creates an organization and invite links add people to it. Everyone inside sees every member's sessions, which in v0.11 means a board or a table, with the choice remembered, a search box that matches a session's name or its command, and one menu to copy the link, the password, or the attach command.

Each session has an owner and an assignee. The owner started it. The assignee is responsible for it now, and the owner can hand a running session to someone else. The server route that does this refuses anyone who is neither the session's owner nor an organization admin, with the error text "only the session's owner can hand it off", and the target must be a member. Typing follows responsibility: the web terminal lets the owner and the assignee type, and everyone else in the organization watches.

Around the terminal, each session has a page with comments and mentions, and mentions and assignments collect in an inbox. The list shows the relay's own view of each session as online, starting, reconnecting, expired, or unknown. The walkthrough is in the shell.online documentation.

The hard part: a password the service relays but cannot open

Every shell.online session is end-to-end encrypted, and the browser password is the only thing a key derives from. A team feature that copied that password through a central service would undo the encryption for whoever runs the service. Two separate seals prevent that, and both are in the open-source repository.

The first seal covers sessions started from the browser. The daemon on a linked machine generates an ephemeral key pair each run, using Go's crypto/ecdh package on the P-256 curve, and publishes only the public half. When you start a session from the web app, the browser picks the password itself and seals it to that key, so the accounts service relays an envelope it cannot open and the machine unwraps it to start the process. The Go side is internal/account/sealed.go, which derives a 32-byte key from the ECDH shared secret with HKDF-SHA256 and an info string that binds the key to this one purpose, then encrypts with AES-GCM under a fresh 12-byte nonce. P-256 is the curve NIST specifies in section 3.2.1.3 of SP 800-186, and ephemeral elliptic-curve Diffie-Hellman is the agreement pattern SP 800-56A Rev. 3 covers. The key lives only as long as the daemon process. Stop the daemon and nothing sealed to it can be opened again.

The second seal is what makes it a team feature. Only the owner of a session holds its password, and colleagues need it without the service reading it. So every browser publishes its own ECDH P-256 public key with its membership, the private key stays in that browser's storage, and the owner's browser seals the password once per organization member. The sealForMembers function in the web app generates one ephemeral key pair per sharing round, derives a distinct AES-GCM key against each member's public key, and produces one envelope per person. The server stores those envelopes on the session record and rejects any share addressed to someone outside the organization.

Consequences to plan around: the accounts service cannot recover or reset a session password, because it never had one. A new member gets a sealed copy of an existing session only when the owner's browser is open to produce it. Signing in on a second browser creates a different key pair, so sharing is re-done rather than assumed. And a machine whose daemon has stopped cannot receive a browser-started session until any shell command starts the daemon again.

What the account does see, stated plainly

The metadata above, plus an audit log. When a signed-in member types into a session through the web app, the app assembles keystrokes into the line that was actually submitted, a command at a shell prompt or a prompt to an agent, and records that line with the member's email, along with interrupts and handoffs. The page describes itself as "everything entered in this team's sessions: commands in a terminal, prompts to an agent, and who entered them", and it exports as CSV. The sink watches only the input stream the browser sends. Terminal output is never transmitted to the accounts service, and neither is the password. If the service must not learn which commands your team ran, read that page before you invite anyone. The security model page is where the sealing guarantee sits alongside the other trust boundaries.

Starting a session on a colleague's machine is asked, not assumed

A signed-in browser can start Claude Code, GPT Codex, Hermes Agent, OpenClaw, or a plain terminal on a linked machine, but only after the person at that machine says yes. The login prompt spells out the consequence: anyone signed in to that account could start processes on the machine, as that user, without touching the terminal. Saying no leaves the machine publish-only, and the question is put again at the next sign-in. The flags do the same thing non-interactively.

shell login --allow-remote-start   # let your signed-in browser start sessions here
shell login --no-remote-start      # keep this machine publish-only
shell daemon status                # is my browser able to start sessions here?
shell daemon stop                  # stop until the next shell command
shell service install              # LaunchAgent on macOS, systemd user unit on Linux

Saying yes runs a small daemon for as long as the machine stays signed in. Any shell command restarts it after a reboot; a machine that sits idle and still has to be reachable installs it as a user service. Commands entered in the web app run through sh on Unix and PowerShell on Windows, so quoting and escaping behave as they do locally. Exactly one poller runs per machine, held by a lock the kernel releases even if the process dies, because two would each publish their own key. Logging out stops the daemon, unlinks the machine, and revokes its token.

The rules that did not change

The URL and password together are still a bearer credential. Anyone holding both can view a session and, unless it was started read-only, type with the permissions of the wrapped process. An organization does not change that; it changes how the password reaches the people who should have it. The relay still sees connection and lifecycle metadata and nothing inside the frames, as described in the introduction to shell.online on this blog. shell.online is developed by Pilot Protocol and released under the MIT license.

Share a terminal with your team, not with the service

Link a machine with one command, invite your organization, and hand off running sessions while the accounts service holds only envelopes it cannot open.

Try shell.online
About this article

Published by the Pilot Protocol team. Product claims are scoped to the availability labels and technical references linked in the article; deployment behavior can vary by version and environment.

How we publish · Suggest a correction · Technical references