Security modelVersion 1

How SSH Guru is built

A browser based SSH client with an AI assistant and an optional hardware bridge for private networks. This page describes how it is built and what it does and does not protect against.

September 2026 · plain text version

01Summary

The SSH client runs in the browser. Credentials are encrypted in the browser with a key derived from the user's passphrase. The service stores ciphertext it cannot open. Every command proposed by the assistant needs a click from the user before it runs.

02Architecture

browser ──WebSocket (TLS)──▶ relay ──TCP──▶ server            (public hosts)

browser ──WebSocket (TLS)──▶ relay ──tunnel──▶ ESP32 bridge ──TCP──▶ server
                                               (on the LAN)         (private hosts)

The SSH client is Go compiled to WebAssembly and runs in the page. Key exchange, authentication and encryption happen there. The relay forwards bytes it cannot read. The bridge does the same on the far side.

There is no server side SSH client anywhere in the product.

03Trust boundaries

ComponentHoldsCan read the SSH session
Browservault key, SSH credentials, bridge pairing secretyes, it is the endpoint
Backend APIciphertext, connection metadata, model provider credentials (readable, see limitations)no
Relayone HMAC key for its own tokensno
Bridge (ESP32)Wi-Fi credentials, its device token, its pairing secret, its rulesno

The backend and the relay are not trusted with credentials and cannot open a connection on their own.

04Vault

Private keys, passphrases and passwords are encrypted in the browser before they are sent anywhere.

If the passphrase is lost, the stored secrets are lost. There is no recovery.

05Command approval

The assistant proposes commands. It cannot run them. Approval happens in the browser.

Each proposed command gets a risk tier from a fixed rule set, not from the model:

TierMeaningTo run
0read onlyone click, or autopilot if the user enables it
1changes stateone click
2destructive or irreversibletype the hostname to confirm

Unrecognised commands are tier 1. If the user edits a command before running it, the tier is recomputed.

Command output goes through a deterministic secret scanner before any model sees it. Tokens, keys, passwords and connection strings are replaced with typed placeholders. IP addresses, hostnames and email addresses can be scrubbed as well.

The assistant is instructed to treat command output as data, not instructions. That is a mitigation. The approval step in the browser is what actually stops a command from running.

06Host keys

The server host key is pinned on first use and a change produces a warning. The check runs inside the WASM client, so the relay cannot suppress or forge it. This applies to bridged connections the same way as direct ones.

07Bridges

A bridge is an ESP32-S3 flashed from the browser over USB and plugged into power on the private network.

It only dials out.

The board opens one outbound TLS WebSocket to the relay and keeps it open. Nothing is forwarded on the router and nothing listens on the LAN. Unplugging the board removes the path.

Rules live on the board.

During setup the user writes an allow list of subnets or hosts with ports, and an optional deny list. Default is deny. The rules are stored on the device and enforced there, after DNS resolution. They can only be changed over USB. The app keeps a copy for display.

Only the user's browser can ask it to dial.

A pairing secret is generated in the browser during setup, written to the board over USB, and kept in the user's vault. Every connection request is signed in the browser with that secret over the bridge id, destination host, port, timestamp and a nonce. The board checks the signature, the timestamp and the nonce before it connects anywhere. The server forwards that signature but cannot create one.

Wi-Fi credentials go from the browser over USB to the board. They are not sent to the service.

08Platform

09Limitations

10Review

I am most interested in findings on:

Detailed internal review notes are available privately on request. If you find a live issue, please report it to me before publishing it.