shelltogo
v1.3.1
Published
TUI agent for ShellToGo - access your terminal from anywhere
Downloads
13
Readme
tmux-relay Agent
Production-ready Node.js/TypeScript CLI agent for the tmux-relay project. Enables mobile access to tmux sessions from anywhere by connecting your workspace to the relay server.
Overview
The agent is a lightweight CLI tool that:
- Runs in your development workspace (devpod, cloud VM, homelab server)
- Authenticates via GitHub (using
ghCLI or device OAuth flow) - Generates persistent 3-word BIP39 session codes
- Connects outbound to the relay server via secure WebSocket (WSS)
- Discovers and manages tmux sessions and panes
- Relays terminal I/O between tmux and mobile clients
- Implements rate limiting and security features
Installation
Via npx (recommended)
npx tmux-relayVia npm global install
npm install -g @shelltogo/agent
tmux-relayFrom source
cd packages/agent
npm install
npm run build
npm startUsage
First Run
$ npx tmux-relay
╔═══════════════════════════════════════╗
║ ║
║ shelltogo Agent v1.0.0 ║
║ ║
║ Mobile access to tmux sessions ║
║ ║
╚═══════════════════════════════════════╝
🚀 shelltogo Agent Setup
Step 1: Creating directory structure...
✓ Directories created
Step 2: Checking tmux installation...
✓ tmux is installed (version: 3.3a)
Step 3: Checking git installation...
✓ git is installed
Step 4: Creating tmux configuration...
✓ tmux configuration created
Step 5: Installing TPM (Tmux Plugin Manager)...
✓ TPM installed
Step 6: Installing tmux plugins...
✓ Plugins installed
Step 7: Checking for tmux sessions...
No tmux sessions found.
You can create one with:
tmux new -s relay-alpha
✅ Setup complete!With Existing Sessions
$ npx tmux-relay
shelltogo Agent v1.0.0
Checking GitHub authentication... ✓ alice
Connecting to relay... ✓
════════════════════════════════════════════════════
Workspace: apple-brave-crystal
URL: https://relay.shelltogo.dev/r/apple-brave-crystal
════════════════════════════════════════════════════
Found tmux sessions:
● relay-alpha (3 panes)
● relay-bravo (1 pane)
Waiting for connections...
─────────────────────────────────────────────────────
10:32:15 Connected: alice (mobile)
10:32:18 Attached: relay-alpha:1Architecture
Components
packages/agent/
├── src/
│ ├── cli.ts # CLI entry point, banner, session display
│ ├── agent.ts # Main Agent class
│ ├── auth.ts # GitHub authentication
│ ├── tmux.ts # tmux integration
│ ├── config.ts # Configuration management
│ ├── setup/ # Setup utilities
│ │ ├── index.ts # Setup orchestration
│ │ ├── directory.ts # Directory creation
│ │ ├── tmux-check.ts # tmux detection
│ │ ├── tmux-install.ts # tmux installation
│ │ └── tmux-config.ts # tmux configuration
│ └── index.ts # Package exports
├── dist/ # Compiled JavaScript
├── package.json
└── tsconfig.jsonKey Features
1. GitHub Authentication (FR-1.2, FR-1.3)
- gh CLI Integration: Checks for existing
gh auth status - Device Flow Fallback: Browser-based OAuth if gh CLI not available
- Token Validation: Verifies tokens via GitHub API
// Try gh CLI first
const ghAuth = await checkGhAuth();
// Fall back to device flow
if (!ghAuth) {
const auth = await deviceFlowAuth(relayUrl);
}2. Session Code Generation (FR-2.1)
Uses full BIP39 wordlist (256 words) from @shelltogo/shared:
- 3-word codes:
apple-brave-crystal - 256³ = 16,777,216 possible combinations
- Persistent across restarts in
.shelltogo/session
import { generateSessionCode } from '@shelltogo/shared';
const code = generateSessionCode();
// => "apple-brave-crystal"3. tmux Discovery (FR-2.7, FR-2.8)
# List sessions
tmux list-sessions -F "#{session_name}|#{session_windows}|..."
# List panes
tmux list-panes -t "session" -a -F "#{pane_index}|#{pane_title}|..."const sessions = await discoverSessions();
// [
// { name: "relay-alpha", panes: [...] },
// { name: "relay-bravo", panes: [...] }
// ]4. Terminal I/O Relay (FR-4.1)
Uses pipe-pane for reliable output capture:
// Create FIFO for output
await execAsync(`mkfifo "${fifoPath}"`);
// Attach pipe-pane
await execAsync(`tmux pipe-pane -t "${target}" -o "cat > ${fifoPath}"`);
// Read output
fifoStream.on('data', (chunk) => {
outputHandlers.forEach(handler => handler(chunk.toString()));
});
// Send input
exec(`tmux send-keys -t "${target}" -l -- ${JSON.stringify(data)}`);5. Scrollback Management (FR-4.2, FR-4.3)
// Initial viewport (3x screen = ~150 lines)
const viewport = await getViewport(sessionName, pane, 150);
// Additional scrollback on demand
const history = await getScrollback(sessionName, pane, 500);6. Security & Rate Limiting (FR-6.1, FR-6.2)
// Track failed auth attempts
interface SecurityState {
failedAuths: number;
failedAuthsWindow: number[]; // timestamps
rotationCount: number;
}
// Rotate session code after threshold
if (recentFailures >= 5 || totalFailures >= 20) {
await rotateSessionCode();
}7. Reconnection Logic (FR-5.1, FR-5.2, FR-5.3)
// Load persisted session code
const savedCode = await loadWorkspaceSession(workspaceRoot);
this.sessionCode = savedCode || generateSessionCode();
// Attempt to reclaim on reconnect
ws.on('close', () => {
scheduleReconnect(); // Reconnect after 5 seconds
});Message Protocol
Agent → Relay
{ type: 'register', code: 'apple-brave-crystal', name: 'laptop', owner: 'alice' }
{ type: 'unregister' }
{ type: 'sessions_update', sessions: TmuxSessionSummary[] }
{ type: 'rotate', newCode: 'orange-delta-falcon' }
{ type: 'to_client', clientId: 'xxx', payload: ... }
{ type: 'broadcast', payload: ... }
{ type: 'client_rejected', clientId: 'xxx', reason: 'unauthorized' }Relay → Agent
{ type: 'registered', code: 'apple-brave-crystal' }
{ type: 'register_failed', reason: 'code_taken' }
{ type: 'client_connected', clientId: 'xxx', username: 'alice' }
{ type: 'client_disconnected', clientId: 'xxx' }
{ type: 'client_message', clientId: 'xxx', payload: ... }Terminal Protocol (Mobile ↔ Agent via Relay)
// Session management
{ type: 'sessions', sessions: TmuxSession[] }
{ type: 'create_session', name?: string }
{ type: 'session_created', session: TmuxSession }
// Pane attachment
{ type: 'attach', session: 'relay-alpha', pane: 1 }
{ type: 'attached', session: 'relay-alpha', pane: 1 }
{ type: 'detach' }
// Terminal I/O
{ type: 'buffer', lines: string[], hasMore: boolean, totalLines: number }
{ type: 'output', data: string }
{ type: 'input', data: string }
{ type: 'resize', cols: number, rows: number }
// Scrollback
{ type: 'fetch_scrollback', before: number, count: number }
{ type: 'scrollback', lines: string[], startLine: number }
// Keepalive
{ type: 'ping' }
{ type: 'pong' }Configuration
User Config (~/.shelltogo/config.json)
{
"relayUrl": "wss://relay.shelltogo.dev",
"githubToken": "ghp_...",
"githubUser": "alice"
}Workspace Config (.shelltogo/session)
apple-brave-crystaltmux Config (.tmux.conf)
Auto-generated with:
- Mouse mode enabled
- tmux-resurrect plugin (session persistence)
- tmux-continuum plugin (auto-save every 15 min)
- Session storage in
.tmux/resurrect/
Command Line Options
# Normal startup
npx tmux-relay
# Force re-run setup
npx tmux-relay setup
# Environment variables
SHELLTOGO_RELAY_URL=wss://custom.relay.url npx tmux-relayDevelopment
Build
npm run buildDevelopment Mode (Hot Reload)
npm run devTesting
npm test # Run all tests
npm run test:watch # Watch modeDependencies
Production
ws- WebSocket client for relay connectionchalk- Terminal colors and formattinginquirer- Interactive CLI promptsora- Loading spinners@shelltogo/shared- Shared types and utilities
Development
typescript- TypeScript compilertsx- TypeScript executor for dev modevitest- Fast unit test runner@types/*- Type definitions
Troubleshooting
tmux not found
# macOS
brew install tmux
# Ubuntu/Debian
sudo apt-get install tmux
# RHEL/CentOS
sudo yum install tmuxGitHub authentication failed
# Use gh CLI
gh auth login
# Then restart agent
npx tmux-relayConnection to relay failed
Check relay URL configuration:
# Default
wss://relay.shelltogo.dev
# Custom
export SHELLTOGO_RELAY_URL=wss://your-relay.example.com
npx tmux-relaySession code already taken
The agent will automatically generate a new code if the previous one is taken by another workspace.
Security Considerations
- WebSocket over TLS: All connections use WSS (WebSocket Secure)
- GitHub Authentication: Both agent and mobile clients must authenticate
- User Validation: Agent validates mobile connections are from same GitHub user
- Rate Limiting: Tracks and blocks excessive failed auth attempts
- Session Code Rotation: Automatically rotates codes on suspicious activity
- No Credential Storage on Relay: Relay never sees or stores credentials
Performance
- Connection Latency: < 500ms initial connection
- Input Latency: < 100ms keypress to echo
- Scrollback Fetch: < 200ms for 500 lines
- Memory Usage: < 100MB for 10 panes with 10k scrollback each
License
MIT
Support
For issues, questions, or contributions:
- GitHub Issues: https://github.com/yourusername/shelltogo/issues
- Documentation: https://shelltogo.dev/docs
