A browser-based SSH client running entirely on Cloudflare's edge. Connect to any SSH server from your browser — no plugins, no extensions, no local software required.
EdgeSSH compiles a full SSH protocol implementation (forked russh) to WebAssembly, then runs it inside a Cloudflare Durable Object. The Durable Object opens a raw TCP socket to your SSH server via cloudflare:sockets and bridges the SSH session to your browser over WebSocket.
Browser ↔ WebSocket ↔ Cloudflare DO (WASM SSH) ↔ TCP ↔ SSH Server
Sessions are persistent — you can close your browser tab and reattach later. Terminal output is buffered in the DO so you won't miss anything.
- Full SSH in WASM — Password and private key authentication (ed25519, RSA, ECDSA). Encrypted key passphrases supported.
- Persistent sessions — SSH sessions live in Durable Objects. Detach and reattach from any browser.
- Host key verification — Trust On First Use (TOFU). Keys stored per-user, with mismatch warnings.
- Passkey auth — Passwordless login via WebAuthn. Single-owner self-deploy or multi-user demo mode.
- Private IP blocking — Connections to RFC 1918, loopback, and link-local addresses are rejected.
- Terminal — xterm.js with WebGL rendering, window resize, and fit-to-container.
| Component | Stack | Location |
|---|---|---|
crates/russh/ |
Rust | Forked russh SSH protocol library |
crates/zerossh-do/ |
Rust → WASM | SSH bindings for Durable Object runtime |
crates/zerossh-wasm/ |
Rust → WASM | SSH bindings for browser runtime (legacy) |
worker/ |
TypeScript (Hono) | Cloudflare Worker: API, auth, Durable Object |
web/ |
TypeScript (Vite) | Frontend: auth UI, server dashboard, terminal |
- Rust stable toolchain with
wasm32-unknown-unknowntarget - wasm-pack
- Node.js
- Wrangler CLI
- mise (optional, for task orchestration)
-
Create Cloudflare resources
Create a KV namespace and update the ID in
worker/wrangler.toml:npx wrangler kv namespace create ZEROSSH_KV
-
Set secrets
cd worker # Required echo "your-jwt-secret" | npx wrangler secret put JWT_SECRET echo "your-32-byte-hex-key" | npx wrangler secret put ENCRYPTION_KEY # Optional: enable demo mode echo "true" | npx wrangler secret put DEMO_MODE # Optional: Turnstile bot protection for demo mode echo "your-site-key" | npx wrangler secret put TURNSTILE_SITE_KEY echo "your-secret-key" | npx wrangler secret put TURNSTILE_SECRET
-
Install dependencies
cd web && npm install cd ../worker && npm install
-
Build and deploy
mise run deploy
Or manually:
wasm-pack build crates/zerossh-do --target web --release wasm-pack build crates/zerossh-wasm --target web --release cd web && npm run build cd ../worker && npx wrangler deploy
# Build WASM packages first
mise run wasm
# Start frontend dev server
cd web && npm run dev
# Start worker dev server (separate terminal)
cd worker && npm run dev- Self-deploy (default): First visitor registers a passkey and becomes the sole owner. No other users can register.
- Demo (
DEMO_MODE=true): Anyone can log in. Optionally gated by Cloudflare Turnstile. User data expires after 24 hours.
MIT