Local-first SSH workspace for people who live in terminals.
Connect to a remote machine once, then work with its shell, files, ports, transfers, lightweight editor, and BYOK AI from one place.
Native Tauri app · Pure Rust SSH · No account required for core SSH workflows
Zero Electron. Zero OpenSSL. Zero Telemetry. Zero Subscription. BYOK-first. Pure Rust SSH.
🌐 oxideterm.app — Documentation & website
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Português | Tiếng Việt
- Manage SSH terminals, SFTP, port forwards, in-terminal transfers, and local shells side by side
- Keep working through network hiccups with Grace Period reconnect
- Use your own AI provider to inspect live sessions and perform approved workspace actions
| If you care about... | OxideTerm gives you... |
|---|---|
| One remote node, many tools | Terminal, SFTP, port forwarding, trzsz, mini IDE, monitoring, and AI context stay attached to the same SSH workspace |
| Local-first SSH workflows | SSH, SFTP, forwarding, local shell, and config work without signup; cloud sync is opt-in via official plugin |
| BYOK AI instead of platform credits | OxideSens uses your OpenAI/Ollama/DeepSeek/OpenAI-compatible endpoint with MCP and RAG support |
| Reconnect stability | Grace Period probes the old connection for 30s before replacing it, so vim/htop/yazi can survive short network drops |
| Pure Rust, native app | Tauri 2.0 native app, russh 0.59 compiled against ring, no Electron, no OpenSSL/libssh2 dependency |
| Credential safety | Passwords and API keys stay in OS keychain, saved connection metadata is sealed locally, and .oxide files use ChaCha20-Poly1305 + Argon2id encryption |
OxideTerm focuses on local-first SSH workflows. It is built for users who want terminal, files, ports, transfers, lightweight editing, and AI context to stay centered around their own machines and remote nodes.
It is not trying to be a hosted cloud agent platform or a benchmark-only terminal renderer. The product direction is narrower: make remote work feel like one local workspace, without requiring an OxideTerm account.
SSH Terminal + OxideSens AI![]() |
SFTP File Manager![]() |
Built-in IDE (CodeMirror 6)![]() |
Smart Port Forwarding![]() |
Download the latest release from GitHub Releases.
| Category | Features |
|---|---|
| Terminal | Local PTY (zsh/bash/fish/pwsh/WSL2), SSH remote, local serial terminals, split panes, broadcast input, session recording/playback (asciicast v2), WebGL rendering, 30+ themes + custom editor, command palette (⌘K), zen mode, trzsz in-band file transfer |
| SSH & Auth | Connection pooling & multiplexing, ProxyJump (unlimited hops) with topology graph, auto-reconnect with Grace Period, Agent Forwarding. Auth: password, SSH key (RSA/Ed25519/ECDSA), SSH Agent, certificates, keyboard-interactive 2FA, Known Hosts TOFU |
| SFTP | Dual-pane browser, drag-and-drop, smart preview (images/video/audio/code/PDF/hex/fonts), transfer queue with progress & ETA, bookmarks, archive extraction |
| IDE Mode | CodeMirror 6 with 24 languages, file tree + Git status, multi-tab, conflict resolution, integrated terminal. Optional remote agent for Linux; unsupported architectures can self-build and upload |
| Port Forwarding | Local (-L), Remote (-R), Dynamic SOCKS5 (-D), lock-free message-passing I/O, auto-restore on reconnect, death reporting, idle timeout |
| AI (OxideSens) | Target-first assistant for saved connections, live SSH sessions, terminal buffers, SFTP paths, settings, and knowledge base entries; can diagnose remote output, run approved commands, inspect files, and explain failures without an OxideTerm account |
| Plugins | Runtime ESM loading, 18 API namespaces, 24 UI Kit components, frozen API + Proxy ACL, circuit breaker, auto-disable on errors |
| CLI | oxt companion: JSON-RPC 2.0 over Unix Socket / Named Pipe, status/health/list/session inspect/forward/config/connect/focus/attach/SFTP/import/AI, human + JSON output |
| Security | .oxide encrypted export (ChaCha20-Poly1305 + Argon2id 256 MB), encrypted local config at rest, OS keychain, Touch ID (macOS), portable encrypted keystore, host key TOFU, zeroize memory clearing |
| i18n | 11 languages: EN, 简体中文, 繁體中文, 日本語, 한국어, FR, DE, ES, IT, PT-BR, VI |
OxideTerm keeps the product surface local-first, but the internals are built for SSH-heavy work. The full implementation notes are preserved below for readers who want the engineering details.
Architecture, SSH internals, reconnect, AI, forwarding, plugins, and more
OxideTerm separates terminal data from control commands into two independent planes:
┌─────────────────────────────────────┐
│ Frontend (React 19) │
│ xterm.js 6 (WebGL) + 19 stores │
└──────────┬──────────────┬───────────┘
│ Tauri IPC │ WebSocket (binary)
│ (JSON) │ per-session port
┌──────────▼──────────────▼───────────┐
│ Backend (Rust) │
│ NodeRouter → SshConnectionRegistry │
│ Wire Protocol v1 │
│ [Type:1][Length:4][Payload:n] │
└─────────────────────────────────────┘
- Data plane (WebSocket): each SSH session gets its own WebSocket port. Terminal bytes flow as binary frames with a Type-Length-Payload header — no JSON serialization, no Base64 encoding, zero overhead in the hot path.
- Control plane (Tauri IPC): connection management, SFTP ops, forwarding, config — structured JSON, but off the critical path.
- Node-first addressing: the frontend never touches
sessionIdorconnectionId. Everything is addressed bynodeId, resolved atomically server-side by theNodeRouter. SSH reconnect changes the underlyingconnectionId— but SFTP, IDE, and forwards are completely unaffected.
The entire SSH stack is russh 0.59 compiled against the ring crypto backend:
- Zero C/OpenSSL dependencies — the full crypto stack is Rust. No more "which OpenSSL version?" debugging.
- Full SSH2 protocol: key exchange, channels, SFTP subsystem, port forwarding
- ChaCha20-Poly1305 and AES-GCM cipher suites, Ed25519/RSA/ECDSA keys
- Custom
AgentSigner: wraps system SSH Agent and satisfies russh'sSignertrait, solving RPITITSendbound issues by cloning&AgentIdentityto an owned value before crossing.await
pub struct AgentSigner { /* wraps system SSH Agent */ }
impl Signer for AgentSigner { /* challenge-response via Agent IPC */ }- Platform support: Unix (
SSH_AUTH_SOCK), Windows (\\.\pipe\openssh-ssh-agent) - Proxy chains: each hop independently uses Agent auth
- Reconnect:
AuthMethod::Agentreplayed automatically
Most SSH clients kill everything on disconnect and start fresh. OxideTerm's reconnect orchestrator takes a fundamentally different approach:
- Detect WebSocket heartbeat timeout (300s, tuned for macOS App Nap and JS timer throttling)
- Snapshot full state: terminal panes, in-flight SFTP transfers, active port forwards, open IDE files
- Intelligent probing:
visibilitychange+onlineevents trigger proactive SSH keepalive (~2s detection vs 15-30s passive timeout) - Grace Period (30s): probe the old SSH connection via keepalive — if it recovers (e.g., WiFi AP switch), your TUI apps (vim, htop, yazi) survive completely untouched
- If recovery fails → new SSH connection → auto-restore forwards → resume SFTP transfers → reopen IDE files
Pipeline: queued → snapshot → grace-period → ssh-connect → await-terminal → restore-forwards → resume-transfers → restore-ide → verify → done
All logic runs through a dedicated ReconnectOrchestratorStore — zero reconnect code scattered in hooks or components.
Reference-counted SshConnectionRegistry backed by DashMap for lock-free concurrent access:
- One connection, many consumers: terminal, SFTP, port forwards, and IDE share a single physical SSH connection — no redundant TCP handshakes
- State machine per connection:
connecting → active → idle → link_down → reconnecting - Lifecycle management: configurable idle timeout (5m / 15m / 30m / 1h / never), 15s keepalive interval, heartbeat failure detection
- WsBridge heartbeat: 30s interval, 5 min timeout — tolerates macOS App Nap and browser JS throttling
- Cascade propagation: jump host failure → all downstream nodes automatically marked
link_downwith status sync - Idle disconnect: emits
connection_status_changedto frontend (not just internalnode:state), preventing UI desync
Privacy-first AI assistant with dual interaction modes:
- Inline panel (
⌘I): quick terminal commands, output injected via bracketed paste - Sidebar chat: persistent conversations with full history
- Target-first workspace context: sees saved connections, live SSH sessions, terminal buffers, SFTP paths, settings, and knowledge base entries as workspace targets
- Approved actions: can diagnose remote output, run approved commands, inspect files, and explain failures without requiring an OxideTerm account
- MCP support: connect external Model Context Protocol servers (stdio & SSE) for third-party tool integration
- RAG Knowledge Base (v0.20): import Markdown/TXT documents into scoped collections (global or per-connection). Hybrid search fuses BM25 keyword index + vector cosine similarity via Reciprocal Rank Fusion. Markdown-aware chunking preserves heading hierarchy. CJK bigram tokenizer for Chinese/Japanese/Korean.
- Providers: OpenAI, Ollama, DeepSeek, OneAPI, or any
/v1/chat/completionsendpoint - Security: API keys stored in OS keychain; on macOS, key reads gated behind Touch ID via
LAContext— no entitlements or code-signing required, cached after first auth per session
Full local (-L), remote (-R), and dynamic SOCKS5 (-D) forwarding:
- Message-passing architecture: SSH Channel owned by a single
ssh_iotask — noArc<Mutex<Channel>>, eliminating mutex contention entirely - Death reporting: forward tasks actively report exit reason (SSH disconnect, remote port close, timeout) for clear diagnostics
- Auto-restore:
Suspendedforwards automatically resume on reconnect without user intervention - Idle timeout:
FORWARD_IDLE_TIMEOUT(300s) prevents zombie connections from accumulating
Upload and download files directly through the SSH terminal session — no SFTP connection required:
- In-band protocol: files travel as base64-encoded frames inside the existing terminal stream — works transparently through ProxyJump chains and tmux without extra ports or agents
- Bidirectional: server runs
tsz <file>to send files to the client;trztriggers client upload; drag-and-drop supported - Directory support: recursive transfers via
trz -d/tsz -d - Transfer limits: configurable per-session limits for chunk size, file count, and total bytes
- Native Tauri I/O: file reads and writes use Tauri native file dialogs and Rust I/O — no browser memory constraints
- Live notifications: toast notifications for start, completion, cancellation, and errors — including a hint when trzsz is detected but the feature is disabled
- Enable in Settings → Terminal → In-Band Transfer
Dynamic ESM loading with a security-hardened, frozen API surface:
- PluginContext API: 8 namespaces — terminal, ui, commands, settings, lifecycle, events, storage, system
- 24 UI Kit components: pre-built React components (buttons, inputs, dialogs, tables…) injected into plugin sandboxes via
window.__OXIDE__ - Security membrane:
Object.freezeon all context objects, Proxy-based ACL, IPC whitelist, circuit breaker with auto-disable after repeated errors - Shared modules: React, ReactDOM, zustand, lucide-react exposed for plugin use without bundling duplicates
Three-tier render scheduler that replaces fixed requestAnimationFrame batching:
| Tier | Trigger | Rate | Benefit |
|---|---|---|---|
| Boost | Frame data ≥ 4 KB | 120 Hz+ (ProMotion native) | Eliminates scroll lag on cat largefile.log |
| Normal | Standard typing | 60 Hz (RAF) | Smooth baseline |
| Idle | 3s no I/O / tab hidden | 1–15 Hz (exponential backoff) | Near-zero GPU load, battery savings |
Transitions are fully automatic — driven by data volume, user input, and Page Visibility API. Background tabs continue flushing data via idle timer without waking RAF.
Portable, tamper-proof connection backup:
- ChaCha20-Poly1305 AEAD authenticated encryption
- Argon2id KDF: 256 MB memory cost, 4 iterations — GPU brute-force resistant
- SHA-256 integrity checksum
- Optional key embedding: private keys base64-encoded into the encrypted payload
- Pre-flight analysis: auth type breakdown, missing key detection before export
- Unlimited chain depth:
Client → Jump A → Jump B → … → Target - Auto-parse
~/.ssh/config, build topology graph, Dijkstra pathfinding for optimal route - Jump nodes reusable as independent sessions
- Cascade failure propagation: jump host down → all downstream nodes auto-marked
link_down
Cross-platform local shell via portable-pty 0.8, feature-gated behind local-terminal:
MasterPtywrapped instd::sync::Mutex— dedicated I/O threads keep blocking PTY reads off the Tokio event loop- Shell auto-detection:
zsh,bash,fish,pwsh, Git Bash, WSL2 cargo build --no-default-featuresstrips PTY for mobile/lightweight builds
- Native ConPTY: directly invokes Windows Pseudo Console API — full TrueColor and ANSI support, no legacy WinPTY
- Shell scanner: auto-detects PowerShell 7, Git Bash, WSL2, CMD via Registry and PATH
- IDE Mode: CodeMirror 6 over SFTP, 24 languages, file tree with Git status, multi-tab, conflict resolution — optional remote agent (~1 MB) for enhanced features on Linux
- Resource profiler: live CPU/memory/network via persistent SSH channel reading
/proc/stat, delta-based calculation, auto-degrades to RTT-only on non-Linux - Custom theme engine: 31 built-in themes, visual editor with live preview, 20 xterm.js fields + 24 UI color variables, auto-derive UI colors from terminal palette
- Session recording: asciicast v2 format, full record and playback
- Broadcast input: type once, send to all split panes — batch server operations
- Background gallery: per-tab background images, 16 tab types, opacity/blur/fit control
- CLI companion (
oxt): ~1 MB binary, JSON-RPC 2.0 over Unix Socket / Named Pipe, status/health/list/session inspect/forward/config/connect/focus/attach/SFTP/import/AI with human or--jsonoutput - WSL Graphics
⚠️ experimental: built-in VNC viewer — 9 desktop environments + single-app mode, WSLg detection, Xtigervnc + noVNC
| Plugin | Description | Repository |
|---|---|---|
| Cloud Sync | Encrypted self-hosted sync — upload and import .oxide snapshots via WebDAV, HTTP JSON, Dropbox, Git, or S3 |
oxideterm.cloud-sync |
| Telnet Client | Native Telnet client for routers, switches, and legacy devices — no external binary needed | oxideterm.telnet |
📸 11 languages in action
![]() English |
![]() 简体中文 |
![]() 繁體中文 |
![]() 日本語 |
![]() 한국어 |
![]() Français |
![]() Deutsch |
![]() Español |
![]() Italiano |
![]() Português |
![]() Tiếng Việt |
OxideTerm uses the native WebView runtime provided by the operating system. Most users already have it installed; install these manually only if the app fails to launch or your environment is air-gapped.
| Platform | Runtime Dependency |
|---|---|
| Windows | WebView2 Runtime — pre-installed on Windows 10 (1803+) and Windows 11. For air-gapped / intranet environments, use the Evergreen Standalone Installer (offline, ~170 MB) or deploy the Fixed Version runtime via group policy. |
| macOS | None (uses native WebKit) |
| Linux | libwebkit2gtk-4.1 (usually pre-installed on modern desktops) |
OxideTerm supports a fully self-contained portable mode — all data (connections, secrets, settings) is stored beside the application binary, making it suitable for USB drives or air-gapped environments.
Option A — Marker file (simplest): create an empty file named portable (no extension) next to the app.
| Platform | Where to place the portable file |
|---|---|
| macOS | Next to OxideTerm.app (its sibling directory) |
| Windows | Next to OxideTerm.exe |
| Linux (AppImage) | Next to the .AppImage file |
/my-usb/
├── OxideTerm.app (or .exe / .AppImage)
├── portable ← empty file you create
└── data/ ← created automatically on first launch
Option B — portable.json (custom data directory): place a portable.json in the same location:
{
"enabled": true,
"dataDir": "my-data"
}enableddefaults totrueif omitteddataDirmust be a relative path (no..); defaults todataif omitted
- First launch — a bootstrap screen prompts you to create a portable password. This password encrypts a local keystore (ChaCha20-Poly1305 + Argon2id) that protects all saved secrets.
- Subsequent launches — enter the password to unlock. On macOS with Touch ID, you can optionally bind biometric unlock in Settings → General → Portable Runtime.
- Instance lock — only one OxideTerm instance can use a portable data directory at a time (
data/.portable.lock). - Management — change the portable password or toggle biometric unlock in Settings → General → Portable Runtime.
- Portability — copy the entire folder (app +
portablemarker +data/) to another machine. The password travels with the keystore.
Tip
Automatic updates are disabled in portable mode. To update, replace the application binary while keeping the data/ directory.
- Rust 1.85+
- Node.js 18+ (pnpm recommended)
- Platform tools:
- macOS: Xcode Command Line Tools
- Windows: Visual Studio C++ Build Tools
- Linux:
build-essential,libwebkit2gtk-4.1-dev,libssl-dev
git clone https://github.com/AnalyseDeCircuit/oxideterm.git
cd oxideterm && pnpm install
# Build CLI companion (required for CLI features)
pnpm cli:build
# Full app (frontend + Rust backend with hot reload)
pnpm run tauri dev
# Frontend only (Vite on port 1420)
pnpm dev
# Production build
pnpm run tauri build| Layer | Technology | Details |
|---|---|---|
| Framework | Tauri 2.0 | Native binary, 25–40 MB |
| Runtime | Tokio + DashMap 6 | Full async, lock-free concurrent maps |
| SSH | russh 0.59 (ring) |
Pure Rust, zero C deps, SSH Agent |
| Local PTY | portable-pty 0.8 | Feature-gated, ConPTY on Windows |
| Frontend | React 19.1 + TypeScript 5.8 | Vite 7, Tailwind CSS 4 |
| State | Zustand 5 | 19 specialized stores |
| Terminal | xterm.js 6 + WebGL | GPU-accelerated, 60fps+ |
| Editor | CodeMirror 6 | 24 language modes |
| Encryption | ChaCha20-Poly1305 + Argon2id | AEAD + memory-hard KDF (256 MB) |
| Storage | redb 2.1 | Embedded KV store |
| i18n | i18next 25 | 11 languages × 22 namespaces |
| Plugins | ESM Runtime | Frozen PluginContext + 24 UI Kit |
| CLI | JSON-RPC 2.0 | Unix Socket / Named Pipe |
Measured with tokei, excluding dependencies and build artifacts.
| Metric | Current Size |
|---|---|
| Total code | 286K+ |
| TypeScript / TSX | 130K+ |
| Rust | 100K+ |
| Frontend test code | 24K+ |
| Frontend test files | 128 |
Source files (src + src-tauri/src) |
664 |
| Concern | Implementation |
|---|---|
| Passwords | OS keychain (macOS Keychain / Windows Credential Manager / libsecret) |
| Portable Keystore | ChaCha20-Poly1305 encrypted vault beside the app, optional biometric binding via OS keychain |
| AI API Keys | OS keychain + Touch ID biometric gate on macOS |
| Export | .oxide: ChaCha20-Poly1305 + Argon2id (256 MB memory, 4 iterations) |
| Memory | Rust memory safety + zeroize for sensitive data clearing |
| Host keys | TOFU with ~/.ssh/known_hosts, rejects changes (MITM prevention) |
| Plugins | Object.freeze + Proxy ACL, circuit breaker, IPC whitelist |
| WebSocket | Single-use tokens with time limits |
- SSH Agent forwarding
- Full ProxyCommand support
- Audit logging
- Agent enhancements
- Session search & quick-switch
- Rust-native migration via GPUI (Zed's GPU-accelerated framework) — [in progress]
OxideTerm is maintained on a best-effort basis by a solo developer. Bug reports and reproducible regressions are prioritized; feature requests are welcome, but may not always be implemented.
If OxideTerm helps your workflow, a GitHub star, issue reproduction, translation fix, plugin, or pull request all make the project easier to keep moving.
GPL-3.0 — this software is free software licensed under the GNU General Public License v3.0.
You are free to use, modify, and distribute this software under the terms of the GPL-3.0. Any derivative work must also be distributed under the same license.
OxideTerm changed from PolyForm Noncommercial 1.0.0 to GPL-3.0 starting with v1.0.0. We made this switch deliberately: no "open source" cosplay with noncommercial traps or no-competition riders, just clear copyleft freedom for users, forks, redistributors, and commercial operators.
Public code is not automatically open source. If a project advertises a familiar license while adding riders like "no redistribution", "no repackaging", "no competing products", or "no unauthorized distribution platforms", that is source-available branding, not the freedom users expect from open source. OxideTerm does not add no-compete or anti-redistribution riders: the GPL-3.0 terms are the terms.
Full text: GNU General Public License v3.0
russh · portable-pty · Tauri · xterm.js · CodeMirror · Radix UI
286,000+ lines of code — built with ⚡ and ☕














