A web-based tmux workspace manager with live terminal panes. View and manage your tmux sessions across multiple hosts from any browser on your network.
Session Deck gives you a browser-based dashboard for your tmux sessions. Each pane is a live terminal connected to a real tmux session — you can type, run commands, and see output in real time. Organize panes into workspaces with drag-and-drop layouts.
Key features:
- 🖥️ Live terminal panes via xterm.js connected to tmux sessions over WebSocket
- 📐 Split-tree workspace layouts with drag-to-resize borders
- 🔀 Drag-and-drop pane rearrangement (swap or directional split)
- 🗂️ Multiple workspaces with 6 built-in presets (or build your own)
- 🌐 Multi-host support — manage tmux on any SSH-accessible machine
- 🔔 Activity notifications — pulsing badge when background workspaces have new output
- 🔍 SSH connectivity testing with tmux detection and setup guidance
- ⌨️ Keyboard shortcuts for everything
- 📋 Clean copy/paste from terminal panes
- 💾 Persistent workspaces and host configuration (SQLite)
- 📱 PWA — install as a standalone app, pin to taskbar
- 🚀 First-run setup wizard for new installations
- Node.js 20+ on the host machine
- tmux installed on at least one host
- SSH access to any remote hosts you want to manage (key-based auth recommended)
git clone https://github.com/jesseprojects/session-deck.git
cd session-deck
npm install
npm run build# Start the server (default: port 7890)
npm start
# Or with custom port
SESSION_DECK_PORT=3000 npm startOpen http://<your-host>:7890 in a browser. On first launch, the setup wizard walks you through:
- Import hosts from your
~/.ssh/config(or add them manually) - Test connectivity — verify SSH access and tmux availability
- Create your first workspace with a layout preset
# Start backend with auto-reload + Vite dev server with HMR
npm run devBackend runs on :7890, frontend dev server on :5173 with API proxy.
| Variable | Default | Description |
|---|---|---|
SESSION_DECK_PORT |
7890 |
Server port |
SESSION_DECK_HOST |
0.0.0.0 |
Bind address |
SESSION_DECK_LOG_LEVEL |
info |
Log level (fatal/error/warn/info/debug/trace) |
SESSION_DECK_DB_PATH |
./data/session-deck.db |
SQLite database path |
SESSION_DECK_AUTH |
none |
Auth method: none, basic, or oidc |
SESSION_DECK_AUTH_USER |
— | Username (basic auth) |
SESSION_DECK_AUTH_PASS |
— | Password (basic auth) |
SESSION_DECK_OIDC_ISSUER |
— | OIDC issuer URL (e.g. Entra, Authentik) |
SESSION_DECK_OIDC_CLIENT_ID |
— | OIDC client ID |
SESSION_DECK_OIDC_CLIENT_SECRET |
— | OIDC client secret |
SESSION_DECK_OIDC_REDIRECT_URI |
— | OIDC callback URL |
SESSION_DECK_OIDC_SCOPES |
openid profile email |
OIDC scopes |
SESSION_DECK_TRUSTED_NETWORKS |
— | CIDRs to bypass auth (e.g. 192.168.0.0/16) |
SESSION_DECK_SESSION_SECRET |
— | Session cookie secret (set in production!) |
SESSION_DECK_SESSION_MAX_AGE |
86400 |
Session cookie max age in seconds |
Session Deck supports three auth modes:
SESSION_DECK_AUTH=none # or just don't set itSESSION_DECK_AUTH=basic
SESSION_DECK_AUTH_USER=admin
SESSION_DECK_AUTH_PASS=your-secure-password
SESSION_DECK_SESSION_SECRET=random-32-char-stringMicrosoft Entra ID example:
- In Azure Portal → App registrations → New registration
- Set redirect URI to
https://your-host/auth/callback(Web platform) - Create a client secret under Certificates & Secrets
- Note your tenant ID from the Overview page
SESSION_DECK_AUTH=oidc
SESSION_DECK_OIDC_ISSUER=https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0
SESSION_DECK_OIDC_CLIENT_ID=your-client-id
SESSION_DECK_OIDC_CLIENT_SECRET=your-client-secret
SESSION_DECK_OIDC_REDIRECT_URI=https://deck.hha.sh/auth/callback
SESSION_DECK_SESSION_SECRET=random-32-char-stringAuthentik / Keycloak example:
SESSION_DECK_AUTH=oidc
SESSION_DECK_OIDC_ISSUER=https://auth.example.com/application/o/session-deck/
SESSION_DECK_OIDC_CLIENT_ID=your-client-id
SESSION_DECK_OIDC_CLIENT_SECRET=your-client-secret
SESSION_DECK_OIDC_REDIRECT_URI=https://deck.example.com/auth/callback
SESSION_DECK_SESSION_SECRET=random-32-char-stringAllow unauthenticated access from specific networks:
SESSION_DECK_TRUSTED_NETWORKS=192.168.0.0/16,10.0.0.0/8This is useful when running behind a VPN or on a trusted LAN — users from those networks skip the login page entirely.
Create /etc/systemd/system/session-deck.service:
[Unit]
Description=Session Deck — Web-Based tmux Workspace Manager
After=network.target
[Service]
Type=simple
User=your-user
Group=your-user
WorkingDirectory=/path/to/session-deck
ExecStart=/usr/bin/node src/index.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=session-deck
Environment=NODE_ENV=production
Environment=SESSION_DECK_PORT=7890
Environment=SESSION_DECK_LOG_LEVEL=info
Environment=LANG=C.UTF-8
Environment=LC_ALL=C.UTF-8
# Security — must NOT use PrivateTmp (tmux socket lives in /tmp)
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now session-deck
⚠️ Do not enablePrivateTmp=true— it isolates/tmp, making the tmux socket invisible to the service.ProtectSystemandProtectHomealso interfere with SSH access to remote hosts.
Session Deck connects to remote hosts via SSH to manage tmux sessions. For each remote host:
-
Generate an SSH key (if you don't have one):
ssh-keygen -t ed25519 -C "session-deck" -
Copy the key to the remote host:
ssh-copy-id user@remote-host
-
Verify passwordless access:
ssh user@remote-host "echo ok" -
Add to Session Deck via Settings → Servers → Add Host (or import from SSH config).
Session Deck reads ~/.ssh/config for the user running the service. Hosts defined there can be imported with one click.
When you test a host's connectivity, Session Deck detects whether tmux is installed. If it's missing, it shows the correct install command for the host's OS:
| OS | Command |
|---|---|
| Ubuntu/Debian | sudo apt install -y tmux |
| Fedora | sudo dnf install -y tmux |
| CentOS/RHEL | sudo yum install -y tmux |
| Alpine | sudo apk add tmux |
| Arch | sudo pacman -S tmux |
| macOS | brew install tmux |
| Key | Action |
|---|---|
1–9 |
Switch workspace |
N |
New workspace |
I |
Toggle properties panel |
Shift+1–9 |
Focus pane by index |
Ctrl+Shift+F |
Zoom / unzoom pane |
Esc |
Unzoom / close menu |
- Click a pane to focus it
- Right-click a pane header for context menu (change session, split, close, zoom)
- Right-click a workspace tab for context menu (rename, duplicate, delete)
- Drag a pane header to another pane to swap or split
- Drag the border between panes to resize
Select text with the mouse in any terminal pane, then:
Ctrl+C— copy selection (when text is selected and no process captures it)Ctrl+V— paste from clipboard- Right-click also provides copy/paste options
When a tmux session produces output in a workspace you're not currently viewing, that workspace tab shows a pulsing orange dot. Switch to the workspace to clear the badge.
Session Deck polls tmux session_activity timestamps every 10 seconds using a lightweight API endpoint (/api/activity) that skips type and context detection — typically completes in under 50ms for local sessions.
Session Deck is a PWA — you can install it as a standalone app from Edge or Chrome:
- Open Session Deck in your browser
- Click the install icon in the address bar (or ⋮ → Install Session Deck)
- Right-click the taskbar icon → Pin to taskbar
The app runs in its own window with no browser chrome, has its own Alt+Tab entry, and shows the Session Deck icon in the taskbar.
Click the SESSION DECK logo in the top-left to access:
- Servers — Add, edit, remove, and test SSH hosts

- Sessions — Create, rename, and kill tmux sessions (with per-host filtering)

- Appearance — Accent color picker and session type colors

- Help — Keyboard shortcuts and documentation
Press Ctrl+K to open the command palette — fuzzy search across all actions:
Browser (any device on LAN)
↓ HTTP + WebSocket
Session Deck Server (Node.js)
├── Fastify — REST API + static file serving
├── WebSocket — terminal I/O via node-pty
├── tmux CLI — local session management
├── SSH → remote tmux CLI → sessions on other hosts
├── SSH config parser → host discovery
└── SQLite — workspaces, hosts, layout presets
Backend: Node.js, Fastify, node-pty, better-sqlite3 Frontend: Svelte 5, xterm.js, Vite Terminal: xterm.js with fit, web-links, and clipboard addons
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Health check |
| GET | /api/hosts |
List configured hosts |
| GET | /api/sessions/:host |
List tmux sessions on a host |
| POST | /api/sessions/:host |
Create a tmux session |
| PUT | /api/sessions/:host/:name |
Rename a session |
| DELETE | /api/sessions/:host/:name |
Kill a session |
| GET | /api/workspaces |
List workspaces |
| POST | /api/workspaces |
Create workspace |
| PUT | /api/workspaces/:id |
Update workspace |
| DELETE | /api/workspaces/:id |
Delete workspace |
| GET | /api/managed-hosts |
List managed hosts |
| POST | /api/managed-hosts |
Add a host |
| PUT | /api/managed-hosts/:id |
Update a host |
| DELETE | /api/managed-hosts/:id |
Delete a host |
| POST | /api/managed-hosts/import-ssh-config |
Import hosts from SSH config |
| POST | /api/managed-hosts/:id/test |
Test host connectivity |
| POST | /api/managed-hosts/test-all |
Test all hosts |
| GET | /api/activity |
Session activity timestamps (lightweight polling) |
| GET | /api/settings |
App settings |
| GET | /api/session-types |
Session type color mappings |
| WS | /ws/terminal |
Terminal WebSocket |
MIT






