Aether lets you share your terminal instantly across any network. By using direct peer-to-peer connections, it completely bypasses the headache of SSH keys, firewalls, and port forwarding so you can focus on collaborating
Aether is a frictionless, peer-to-peer terminal sharing tool. Instantly broadcast a live, interactive terminal session across any network using a simple 6-digit code. Designed for immediate collaboration, Aether entirely eliminates the need for user accounts, SSH key management, or complex firewall configurations.
$ aether shareA E T H E R
Secure terminal sharing
> Session code: 4 5 2 2 3 7
> Share URL: https://useaether.vercel.app/term/452237
> Give this code to anyone you want to share your terminal with.
Connecting to signaling server...
✓ Connected to signaling server
✓ Waiting for viewer...
The viewer opens the link, enters the code, and gets a live interactive terminal - streamed directly from your machine over an encrypted peer-to-peer connection. The signaling server never touches your terminal data.
Aether uses WebRTC DataChannels to establish direct, peer-to-peer (P2P) connections for terminal streaming. This architecture ensures low latency and high security, as your terminal data never passes through a central server.
┌───────────────────────────────────────┐ ┌───────────────────────────────────────┐
│ HOST MACHINE │ │ REMOTE BROWSER │
│ │ 1. WebSockets │ │
│ ┌───────────────────────────────────┐ │ (Offer/Answer/ICE Relay) │ ┌───────────────────────────────────┐ │
│ │ AETHER CLI AGENT │ │ ┌───────────┐ │ │ WEB VIEWER │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Signaling │ │ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Session Mgr │ │ ws client │◄├─┼──────────►│ Server │◄────────────┼─┤ │ ws client │ │ React 19 UI │ │ │
│ │ └─────────────┘ └─────────────┘ │ │ │(Express 5)│ │ │ └─────────────┘ └─────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │ └───────────┘ │ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ @roamhq/wrtc│ │ Env Sanitiz.│ │ │ (Never sees term data) │ │ │ Native WebRTC │ │ xterm.js 6 │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ └──────┬──────┘ └──────┬──────┘ │ │
│ └────────┼─────────────────┼────────┘ │ │ └────────┼─────────────────┼────────┘ │
│ │ │ │ 2. WebRTC (P2P) │ │ │ │
│ │ ▼ │ Channel: "terminal" (E2EE) │ │ │ │
│ │ ┌─────────────┐ │◄═══════════════════════════════════►│ │ │ │
│ └──────────┤ Local Shell │ │ │ └─────────────────┘ │
│ PTY I/O Binding │ (node-pty) │ │ Raw PTY Output ───────► │ Terminal Render & Input │
│ └─────────────┘ │ ◄────── Keystrokes & Resizes │ │
└───────────────────────────────────────┘ └───────────────────────────────────────┘
Connection flow:
- Agent spawns a pseudo-terminal (PTY) and registers a 6-digit session code on the signaling server via WebSocket.
- Viewer enters the code in the web app; the server confirms the session exists.
- Signaling server relays the WebRTC offer → answer → ICE candidates between both peers.
- DataChannel opens - terminal I/O flows directly peer-to-peer, end-to-end encrypted.
- Server steps aside - it no longer participates in the session.
Aether is a monorepo structured as three npm workspaces:
| Workspace | Role | Key Technologies |
|---|---|---|
agent/ |
CLI tool that shares your terminal | Node.js,node-pty, @roamhq/wrtc |
server/ |
WebSocket signaling server | Express 5,ws, MongoDB (optional) |
web/ |
Browser-based terminal viewer | React 19, xterm.js 6, Tailwind CSS 4 |
Once the P2P connection is established, all communication uses a single reliable ordered DataChannel named "terminal":
| Direction | Payload |
|---|---|
| Agent → Viewer | Raw PTY output (text + ANSI escape sequences) |
| Viewer → Agent | Raw keystrokes,or { type: "resize", cols, rows } for terminal resize events |
- Node.js ≥ 22 (includes npm ≥ 10)
- Python & C++ build tools (Required by
node-ptynative bindings`) - MongoDB (Optional -server runs without it)
Setting up build tools:
# Windows
npm install --global windows-build-tools
# macOS
xcode-select --install
# Linux
sudo apt install build-essential python3# 1. Clone
git clone https://github.com/indrasuthar07/Aether.git
cd Aether
# 2. Install
npm install
# 3. Configure
cp .env.example .envstart each workspace individually:
npm run dev:server # Signaling server
npm run dev:web # Web app
npm run dev:agent # Agent CLIRun the following on the machine you want to share:
aether shareAether will:
- Spawn your default shell (PowerShell on Windows,
bash/zshon macOS/Linux) - Register a unique 6-digit session code
- Print a shareable link
- Wait for a viewer to connect via WebRTC
- Open project URL
- Click "Open Web Terminal"
- Enter the 6-digit session code
- Done - you're in a live, interactive session
Usage: aether [command]
Commands:
share Share your terminal (default command)
help, -h Show help information
version, -v Print the current version
Aether/
├── agent/ # CLI terminal-sharing agent
│ ├── src/
│ │ ├── index.ts # Entry point & CLI parser
│ │ ├── session.ts # Session orchestrator (core logic)
│ │ ├── signaling-client.ts # WebSocket signaling client
│ │ ├── webrtc.ts # WebRTC peer connection wrapper
│ │ ├── pty.ts # PTY spawn + environment sanitization
│ │ ├── config.ts # Configuration with env overrides
│ │ ├── code.ts # 6-digit session code generator
│ │ └── logger.ts # Colored CLI output
│ └── package.json
│
├── server/ # WebSocket signaling server
│ ├── src/
│ │ ├── index.ts # Express + WebSocket server setup
│ │ ├── signaling.ts # Message handling & relay logic
│ │ ├── room.ts # Room lifecycle & management
│ │ ├── connection-manager.ts # Per-IP & global connection limits
│ │ ├── rate-limiter.ts # Sliding-window rate limiting
│ │ ├── validation.ts # Payload whitelisting & size gates
│ │ ├── config.ts # Server configuration
│ │ ├── logger.ts # Structured JSON logging
│ │ ├── db.ts # MongoDB connection (optional)
│ │ ├── models/Session.ts # Session schema (scaffolded)
│ │ └── routes/health.ts # GET /health endpoint
│ └── package.json
│
├── web/ # Browser-based terminal viewer
│ ├── src/
│ │ ├── App.tsx # Router & layout
│ │ ├── main.tsx # React entry point
│ │ ├── types.ts # Shared types & constants
│ │ ├── index.css # Tailwind theme + xterm overrides
│ │ ├── pages/
│ │ │ ├── Home.tsx # Landing page
│ │ │ ├── ConnectTerminal.tsx # Session code entry
│ │ │ ├── TerminalPage.tsx # Live terminal session
│ │ │ └── NotFound.tsx # 404 page
│ │ ├── components/
│ │ │ ├── Nav.tsx # Floating navigation bar
│ │ │ ├── Footer.tsx # Page footer
│ │ │ ├── CodeInput.tsx # 6-digit OTP-style input
│ │ │ ├── StatusBar.tsx # Terminal session status bar
│ │ │ ├── TerminalMockup.tsx # Decorative terminal (landing)
│ │ │ └── TerminalView.tsx # xterm.js wrapper component
│ │ └── hooks/
│ │ ├── useSignaling.tsx # WebSocket signaling hook
│ │ ├── useWebRTC.tsx # WebRTC peer connection hook
│ │ └── useTerminal.tsx # xterm.js lifecycle hook
│ └── package.json
│
├── .env.example # Environment variable template
├── package.json # Root workspace config
└── tsconfig.base.json # Shared TypeScript configuration
Security is a first-class concern in Aether, not an afterthought.
All terminal data is transmitted over WebRTC DataChannels, which mandate DTLS encryption - the same protocol underpinning HTTPS. The signaling server is architecturally incapable of reading terminal content; it only relays connection metadata during the handshake.
The agent scrubs 50+ patterns of sensitive environment variables before spawning the PTY shell. Cleared patterns include:
AWS_*·AZURE_*·GCP_*·GITHUB_*·STRIPE_*·OPENAI_*·*_API_KEY·*_TOKEN·*_SECRET·*_PASSWORD·*_DATABASE_URL· and more
Aether's own configuration variables are also excluded from the PTY environment.
| Control | Details |
|---|---|
| Origin validation | Browser clients must matchALLOWED_ORIGINS; CLI agents are exempt (no Origin header) |
| Rate limiting | Sliding-window per-IP limits - connections: 20/min, registrations: 5/min, joins: 15/min |
| Connection caps | Per-IP: 10 · Global: 500 |
| Payload validation | Whitelist-based key filtering with per-type size gates (offer/answer: 32 KB · ICE: 4 KB) |
| Message size limit | DataChannel messages capped at 4,096 bytes |
| Room TTL | Rooms auto-expire after 5 minutes of inactivity |
- Each session has a unique 6-digit code (guessing is rate-limited)
- Only one viewer may connect per session at a time
- Agent disconnect immediately tears down the room and evicts any active viewer
- Agent:Node.js 22+, node-pty (terminal spawning), @roamhq/wrtc (WebRTC), ws (WebSockets), nanoid.
- Server:Express 5, ws (WebSockets), Mongoose .
- Web:React 19, Vite 8, xterm.js 6 (browser terminal), Tailwind CSS 4, React Router 7, Lucide React.
- Shared:TypeScript 5, WebRTC (P2P transport).
Contributions are welcome. Here's how to get involved:
- Fork the repository
- Create a feature branch :
git checkout -b feature/my-feature - Commit your changes :
git commit -m "feat: describe what you did" - Push and open a Pull Request
- The monorepo uses npm workspaces - always run
npm installfrom the root npm run devstarts the server and web app together viaconcurrently- The agent must be started separately with
npm run dev:agent - TypeScript strict mode is enabled across all three workspaces
- Commits follow Conventional Commits -
feat:,fix:,chore:,docs:, etc.