DEV Community

Artemii Amelin
Artemii Amelin

Posted on

shell.online 0.10 Adds Accounts and Organizations. The Accounts Service Still Cannot Read a Terminal.

We tagged shell.online v0.10.0 and v0.10.1 this morning. Until today there was no account anywhere in the tool: prefix a command with shell, get a URL and an eight-character password, open the live terminal from any browser. v0.10.0 is the first tagged release carrying accounts, organizations, and a web app that lists sessions from every linked machine and opens them as tabs you can type into. It also adds a one-scan QR in the terminal.

The constraint for all of it was that the new service must not become a place where terminal contents can be read, by us or by anyone who gets into our infrastructure. Here is what that turned into in code.

What the service is allowed to hold

When a linked machine starts a session, the CLI publishes one record. The struct has an id, the share URL, the command, an optional name and origin, three flags (read-only, encrypted, persistent), the host name, and a start time. There is no field for terminal output and no field for the browser password.

The share URL needs one more filter. E2EE produces two fragment forms. A #salt= fragment carries the PBKDF2 salt, which is not a secret: without the password no key can be derived from it. A #key= fragment carries a raw AES key. The publish path runs the URL through an allowlist that keeps #salt= and drops everything else, so a new fragment form has to be reviewed before it can be published. That function is SafeShareURL in the account package of the shell.online source.

Two more things follow the same rule. CLI access and refresh tokens are stored as SHA-256 hashes only. And prerelease builds had copied committed terminal input into the audit table, so migration 004 deletes those rows at deploy time and the service no longer accepts input events at all. What remains is collaboration metadata: who opened a session and who handed it off.

Starting a session from the browser

The web app can start a process on a linked machine. That is a real capability, so shell login asks for it instead of assuming it:

  Start sessions from the browser?
  Anyone signed in to you@example.com could start processes on this
  machine, as you, without touching this terminal.
  You can say no and still publish sessions with 'shell <command>'.

  Allow it? [y/N]
Enter fullscreen mode Exit fullscreen mode

Only a yes is remembered. Say no and the machine stays publish-only, and the question comes back next sign-in.

Say yes and a small daemon polls for queued work. The hard part is the password. The browser that starts the session picks the browser password, the machine that runs the process needs it, and the only path between them is the accounts service. Sending it in the clear would hand the service the one thing E2EE exists to keep from it.

So the daemon generates an ephemeral P-256 ECDH key pair on each run and publishes the public half. The browser derives a shared secret against that key with WebCrypto, runs HKDF-SHA256 with a fixed info string, and seals the password under AES-256-GCM with a 12-byte nonce. The service relays an envelope it cannot open. The private key lives only as long as the daemon process, so stopping the daemon ends the ability to read anything sealed to it. The Go side is sealed.go in the repo, with a test helper that seals exactly as the web app does.

Organizations reuse the envelope. Everyone in an organization sees every member's sessions in the list. For a member to open one, the password is sealed once per member to that member's browser key, and the API returns only the caller's own share. The store holds all of them and can open none.

The QR carries both halves of the credential

After creating a share in an interactive terminal, the CLI now renders a QR code. The printed link stays password-free so the two pieces can still be sent over separate channels. The QR is the one artifact that carries both, and it does so in the URL fragment as #salt=…&password=…. Browsers do not send fragments in HTTP or WebSocket requests, so a phone that scans it derives the key locally and Cloudflare never receives the password. The README says it plainly: anyone with the text credentials or the QR can see the terminal and, unless the share is read-only, type with the permissions of the wrapped process.

The v0.10.1 follow-up was about rendering. The first cut emitted a colour change per module and slower terminals repainted the code one cell at a time. The renderer now writes one foreground and background pair per row and lets the half-block glyphs carry both vertical modules, and the test asserts exactly two ANSI sequences per rendered row. The pull request that landed it measured a normal QR dropping from roughly 1,150 ANSI transitions to 48.

Where Falcon Guardian's answer differs

CrowdStrike announced Falcon Guardian at Fal.Con last week. The press release describes a sensor that discovers known and shadow AI agents on Windows and macOS, keeps a live inventory of running and dormant agents, and connects agent behaviour to endpoint telemetry so a causal chain runs from user prompt, identity, tool call, and skill use to every downstream system action. Agent Access Controls decide which agents may run at all.

That is the right shape for a corporate endpoint fleet, and it depends on the sensor seeing everything. What we shipped today is the other trade. The inventory service holds command name, host, and timing, and the causal chain lives with whoever holds the password, in a browser tab that decrypts locally. A managed laptop can run both. They answer "what is this agent doing" from opposite ends of the trust question.

The default-no consent prompt is the same rule Pilot Protocol applies between agents. Nodes on Pilot Protocol are private by default, and a peer becomes reachable only after a signed mutual handshake, with the tunnel keyed by Ed25519-signed X25519 and AES-256-GCM, as the Pilot repo README lists under Security. A human driving a machine from a browser gets the same treatment: nothing until an explicit yes, and the yes is scoped to one account and one machine.

Two smaller details belong in the record. The web app offers to launch a coding-agent harness, and each machine reports which it can run: claude-code, codex, hermes, and openclaw, detected by presence on PATH and never executed to read a version string. And the changelog for this release admits that the first build of shell login pointed at an accounts address that had never resolved. It was fixed before the tag, which is what the tag is for.

Top comments (0)