Agents need to run commands on real machines. The usual answer is to paste a key into a prompt, which puts a live secret in a context window, a log and a chat history all at once. Browser SSH is built so that never has to happen.
28 July 2026 · 5 min read · Browser SSH
An AI agent that can read your code but cannot touch your servers is only half useful. The moment you want it to restart a service, tail a log or check why a deploy failed, it needs to reach a real machine. Every way of arranging that seems to end with a credential somewhere it should not be.
The obvious version is the worst one: paste a private key or a password into the prompt. That secret is now in the model's context, in whatever transcript the tool keeps, in the provider's logs, and in your shell history. You cannot rotate your way out of a leak you cannot see, and you usually cannot tell afterwards which of those copies still exists.
The second version is barely better. Give the agent a shell and let it read your SSH config. Now the credential is on disk next to a process you are deliberately allowing to run arbitrary commands.
Browser SSH starts from a different premise: the agent should never be able to see a credential, even in principle, and that property should hold no matter how the agent misbehaves.
Every saved credential is envelope encrypted with AES-256-GCM under a per credential AWS KMS data key. The encryption context on that key is bound to the user and the machine, so a ciphertext lifted out of the database cannot be decrypted against a different user or a different host even by someone holding the KMS permissions.
Only ciphertext is stored. Decryption happens exclusively in memory at connect time and the plaintext is never logged. The agent API is not merely reluctant to return a stored secret, it has no code path that does. Revealing a raw credential at all requires an explicitly MFA gated export from the dashboard, and that MFA accepts TOTP or a WebAuthn passkey.
The interesting design point is not the cipher, it is the direction of the guarantee. The system is not asking the agent to behave. It is arranged so that an agent asking for a credential gets nothing back, because nothing in the surface it can reach knows how to produce one.
Even with the vault sealed, there is still a bootstrapping problem. At some point you have to give the agent something, and if that something is a long lived API key then you have moved the secret rather than removed it. Pasting a live key into a prompt has all the same problems as pasting an SSH key.
So the handoff is a one time claim instead. The dashboard issues a claim secret that lives for five minutes, can be redeemed exactly once, and is scoped to a single machine and a single mode. The agent posts it once and receives a scoped API key in return. What you paste into the prompt is therefore already spent by the time anyone could read it back out of a transcript.
The scope is not decorative. A key is minted in one of three modes, and the mode is fixed at issue:
Scope answers what an agent may touch. Policy answers what it may run. Each API key carries its own command policy, so two agents working the same machine can have genuinely different authority rather than nominally different labels.
Agents are told to call whoami first, and that call returns the key's mode, scope, policy, plan and remaining quota. This is a small thing that matters more than it looks: an agent that knows its own limits fails cleanly and explains itself, instead of blundering into a refusal it cannot interpret and retrying in a loop.
Around that sit the bounds that assume something will go wrong anyway. Host keys are pinned on the first verified connect and enforced from then on, so a swapped host fails rather than silently succeeding. A brute force lockout freezes a machine after repeated failed connects. An SSRF guard stops a saved host from pointing at cloud metadata or a private control plane, which is the classic way a benign looking hostname turns into credential theft. Every exec has a per plan timeout and output cap. Secret redaction scrubs credential shaped strings out of output before it is returned, because the fastest way to leak a secret through a system that never stores one is to cat a file that contains it.
The audit log records every action from every surface, and its events never contain credentials or command output. An audit trail that quotes the thing it is auditing is a second copy of the secret.
The same backend serves a dashboard REST API authenticated with a Supabase JWT, an agent API at /v1 authenticated with hashed scoped keys, and an MCP server. Agents that speak the OpenAI tool calling format get 15 tools, including ssh_exec, ssh_batch_exec, session and SFTP file operations, and machine verification, with SSE streaming, Idempotency-Key support, and Retry-After plus X-RateLimit headers on a 429.
Keeping one backend behind three doors is what makes the guarantee hold. If the agent API were a separate service with its own copy of the credential logic, the interesting question would stop being how the vault works and start being whether both implementations agree. They cannot disagree if there is only one of them.
Honesty about the tradeoffs: the browser terminal is a real xterm PTY, and getting one requires brokering a handshake token and starting an on demand Fargate proxy task, which costs roughly 15 to 40 seconds of cold start. It scales back to zero when idle, which is the reason the free tier can exist at all, and the reason the first connect after a quiet period is slower than a local ssh.
That is the trade. A persistent always warm proxy would be faster and would mean either a much higher floor cost or a much smaller free tier. Given the choice between a fast first connect and a product people can try without a card, the wait wins.