XShell is layered: transport, peer-to-peer channel, application, and authentication each protect you independently. Breaking one does not break the others. This page is the long version — what each layer does, what it doesn't, and what we considered when designing it.
Four independent layers protect every session. Each is meaningful on its own; together they remove single points of failure.
Every WebSocket connection — host to relay, browser to relay — runs over TLS. The relay's certificate is verified by the browser and the agent. This protects against passive network observers and standard man-in-the-middle attacks at the network layer.
When a direct peer-to-peer connection is established between your browser and your device, it runs over a DTLS-secured WebRTC data channel. The DTLS fingerprints in the signaling messages are integrity-protected by the authenticated end-to-end channel, so the relay cannot swap them to insert itself.
Terminal I/O, file transfer chunks, and editor traffic are encrypted end-to-end with AES-256-SIV — independent of the transport. Keys are derived via HKDF-SHA256 from the SRP session key, mixed with fresh nonces from both sides and the device identifier. The relay never sees the derived keys.
Secure Remote Password is a zero-knowledge proof: your password is never transmitted in any form — not in plaintext, not encrypted, not hashed in transit. Both sides prove knowledge of the password to each other without exposing it. Ed25519 public-key authentication is also supported for passwordless connections.
The properties below are structural — they come from how the system is built, not from a policy the operator promises to keep.
Our relay forwards bytes between your device and your browser. It does not hold the keys to decrypt them. Even with full access to the relay's memory and disk, the operator cannot read your sessions.
The agent makes an outbound WebSocket connection. It does not listen on any port and does not require firewall changes. Attackers cannot scan a service that isn't bound to a public interface.
Every encrypted packet carries a monotonic continuity counter and an authenticated tag. Replayed or tampered packets are rejected on receipt. The counter resets per session, per direction.
Device enrollment is the trust boundary; access tokens are short-lived and refresh tokens are subordinate to enrollment state. Revoke an enrollment and every token derived from it stops working immediately.
The pairing code shown in your workspace authorizes one enrollment, then dies. Refresh and access tokens are stored hashed — never in plaintext. Raw tokens are never logged.
The agent — the code that runs on your machine — is licensed under GPLv3 and built from source you can read. You can verify exactly what's running, what it's connecting to, and what it's sending.
The end-to-end key is not stored, not exchanged, and not recoverable from observed traffic.
The browser and the agent each prove they know the password without sending it. In XShell, your agent plays the role of the SRP "server" — the verifier never leaves the machine you are connecting to. The relay sees neither the verifier nor any data that could be used to mount an offline dictionary attack against it.
Both sides independently compute the same SRP session key K from the proof. K is never transmitted.
The encryption keys are derived from K using HKDF-SHA256, with both the host nonce and the client nonce mixed in as salt, and the device identifier as info. Fresh nonces from both sides mean neither party alone can predict or replay the derived keys.
Each session uses its own derived key. SIV mode is misuse-resistant — even if internal counter state were to repeat, the cipher does not catastrophically fail.
Every encrypted packet carries a monotonic counter, starting at 1 per session and direction. Replays and reordering are rejected by the receiver before the payload is processed.
The scenarios we designed against, and how the system holds up.
| Threat | Mitigation |
|---|---|
| Relay operator goes rogue | Session data is end-to-end encrypted with AES-256-SIV under keys the relay never holds. The worst case is denial of service — the operator can refuse to route, but cannot read or forge sessions. |
| Network observer / ISP / state actor | All transport is TLS. The peer-to-peer path is DTLS over WebRTC. Application traffic is independently end-to-end encrypted. Observed traffic looks like opaque encrypted frames. |
| Stolen credentials database | There is no credentials database to steal. XShell never stores credentials in the database. The salt and verifier derived from your password stay on your local disk and never leave it. |
| Stolen refresh token | Refresh tokens are bound to a specific enrollment. Revoke the enrollment from your workspace and the token stops working immediately. They do not participate in session encryption or device authorization, so your data and device remain protected. |
| Replayed or modified packets in flight | Every encrypted packet includes a monotonic continuity counter and a full-packet authentication tag. Out-of-order, replayed, or tampered packets are rejected. |
| WebRTC signaling injection | WebRTC offer / answer / ICE candidates are exchanged over the authenticated end-to-end channel after SRP completes. DTLS fingerprints inside SDP are integrity-protected, blocking relay-level peer impersonation. |
Concrete boundaries, so you don't have to guess.
The agent is open source. Read the code, build it yourself, and verify the security properties on this page.