Skip to main content

muxplex

Web-based tmux session dashboard — access, monitor, and manage all your tmux sessions from any browser on any device.

muxplex dashboard


Features

Dashboard

  • Live session grid — preview tiles with ANSI-colored terminal snapshots, auto-refreshed
  • Two view modes — Auto (scrollable grid) and Fit (all sessions fill the viewport)
  • Hover preview — full-size overlay of session content on tile hover
  • Activity indicators — bell notification badges on tiles; amber favicon dot + (N) count in browser tab title when sessions have unseen activity
  • Session creation+ button with device selector dropdown when multi-device is enabled; custom command template support
  • Session deletion× button with custom command template support
  • Mobile-friendly — responsive layout, PWA-capable for home-screen install

Terminal

  • Full interactive terminal — powered by xterm.js + ttyd
  • Native clipboard — Ctrl+Shift+C to copy, Cmd+V (macOS) / Ctrl+Shift+V (Linux) to paste
  • Mouse select auto-copy — selecting text copies to system clipboard on release
  • OSC 52 tmux clipboard bridge — tmux copy mode selections go to system clipboard
  • Search — Ctrl+F opens a search bar to find text in terminal scrollback (xterm-addon-search)
  • Clickable URLs — Ctrl+Click (Cmd+Click on macOS) opens URLs in terminal output in a new tab (xterm-addon-web-links)
  • Inline image rendering — Sixel and iTerm2 graphic protocols for tools like yazi file manager (xterm-addon-image)
  • Sidebar session switcher — quick-switch between sessions with live previews

Settings

  • In-browser settings panel — gear icon or , shortcut
  • Display — font size, grid columns, hover delay, view mode, device badges, activity indicator
  • Sessions — default session, sort order, hidden sessions, auto-open, bell sound, notifications
  • Commands — custom create/delete session templates
  • Multi-Device — remote instance federation
  • CLImuxplex config list/get/set/reset

Multi-Device

  • Remote session aggregation — federate multiple muxplex instances into a unified dashboard view
  • Device selector in new session+ button shows a device dropdown when multi-device is enabled; create sessions on any connected instance directly from the dashboard
  • Remote bell-clear — opening a session on a remote device automatically clears its activity notification via federation API (POST /api/bell/clear)
  • Unique session keys — sessions identified by remoteId:name across devices, preventing bell-state collisions for identically-named sessions on different machines

Service Management

  • muxplex service install/start/stop/restart/status/logs/uninstall
  • Platform-aware — systemd user service on Linux/WSL, launchd agent on macOS
  • Config-driven — service reads all options from ~/.config/muxplex/settings.json (no flags in the service file)

Authentication

  • PAM authentication — Linux/macOS system credentials
  • Password mode — auto-generated or set via MUXPLEX_PASSWORD env var
  • Localhost bypass — no auth needed on 127.0.0.1
  • Secure session cookies — signed with configurable TTL

Developer Tools

  • muxplex doctor — dependency + config diagnostics with update check
  • muxplex upgrade — smart version check + auto-update + service restart
  • muxplex config — CLI settings management

Agents & Automation

  • Public HTTP API — the contract is discoverable at /openapi.json and /docs; headless clients authenticate with a Bearer federation key
  • Terminal input over the APIPOST /api/sessions/{name}/input lets an agent type into a live session (RCE by design, default-CLOSED, fenced by input_enabled + input_allowed_sessions)
  • Vendor-neutral guide — point any agent (or a curl script) at Driving muxplex from an agent

HTTPS / TLS

  • muxplex setup-tls — auto-detect and set up TLS certificates
  • Tailscale — real Let's Encrypt certs via tailscale cert (recommended when every client has Tailscale)
  • mkcert — locally-trusted certs, zero browser warnings (when mkcert is installed on each client)
  • Local CA — persistent root CA + signed leaf for browser-trusted HTTPS on plain LAN names (spark-1, 192.168.1.5) without Tailscale or a public domain; install the CA once per client → see Trusting the local CA
  • Self-signed — fallback for immediate HTTPS (browser shows warning)
  • Required for browser clipboard API on non-localhost, and for stable PWA install (browsers refuse to keep installed PWAs in standalone mode against an untrusted origin)

Prerequisites

  • Python 3.11+ — installed via uv or system Python
  • tmux — terminal multiplexer
    • macOS: brew install tmux
    • Ubuntu/WSL: sudo apt install tmux
  • ttyd — terminal sharing over HTTP (required for interactive terminal access)

Tip: Run muxplex doctor to check all dependencies and system status.


Quick Start (uvx — no install)

Run muxplex directly without installing anything permanently:

uvx muxplex

Then open http://localhost:8088 in your browser.

Note: uvx is part of uv. Install uv with curl -LsSf https://astral.sh/uv/install.sh | sh.


Install Permanently

uv tool install muxplex
muxplex doctor  # verify dependencies

Upgrade later with either:

uv tool upgrade muxplex   # standard uv workflow
muxplex upgrade           # also restarts the service if installed

Installing from git instead? uv tool install git+https://github.com/bkrabach/muxplex tracks the default branch and gets unreleased commits. Do not pin a tag (...@v1.2.3) unless you mean it: uv tool upgrade resolves strictly within the recorded requirement, so a pinned rev reports "Nothing to upgrade" forever. Released versions on PyPI are the recommended path.

Then run it any time with:

muxplex

Install as a Service

muxplex service install
# → prompts to set host to 0.0.0.0 for network access

The service starts automatically on login (macOS) or at boot (Linux) and restarts on failure.

# Open in browser
open http://localhost:8088

To stop and remove:

muxplex service uninstall

CLI Reference

muxplex                              Start server (default)
muxplex serve [flags]                Start with CLI flag overrides
muxplex service install              Install + enable + start as OS service
muxplex service uninstall            Stop + disable + remove
muxplex service start|stop|restart   Manage running service
muxplex service status               Show service status
muxplex service logs                 Tail service logs
muxplex config                       Show all settings
muxplex config get <key>             Show one setting
muxplex config set <key> <value>     Set a setting
muxplex config reset [key]           Reset one or all to defaults
muxplex upgrade [--force]            Smart update with version check
muxplex doctor                       Check dependencies + config
muxplex show-password                Show current auth password
muxplex reset-secret                 Regenerate signing secret
muxplex setup-tls [--method auto]   Set up TLS certs (Tailscale/mkcert/self-signed)
muxplex setup-tls --status          Show current TLS configuration
muxplex env                          Print `eval`-able TMUX_TMPDIR export

Service management

muxplex service install     # Write service file + enable + start
muxplex service uninstall   # Stop + disable + remove service file
muxplex service start       # Start the service
muxplex service stop        # Stop the service
muxplex service restart     # Stop + start
muxplex service status      # Show running/stopped + PID
muxplex service logs        # Tail service logs

The service runs muxplex serve with no flags — it reads all options from ~/.config/muxplex/settings.json. To change host/port, edit the config (or use the Settings UI in the browser) and restart:

muxplex config set host 0.0.0.0
muxplex service restart

Examples

# Start with defaults from settings.json
muxplex

# Override port for this run only
muxplex --port 9000

# Override host for this run only
muxplex serve --host 0.0.0.0

HTTPS / TLS setup

# Auto-detect the best TLS method and set up certificates
muxplex setup-tls

# Use a specific TLS method
muxplex setup-tls --method tailscale
muxplex setup-tls --method mkcert
muxplex setup-tls --method selfsigned
muxplex setup-tls --method ca           # persistent local CA + signed leaf

# Show current TLS status and configuration
muxplex setup-tls --status

# Override TLS cert/key for a single run (without saving to config)
muxplex serve --tls-cert /path/cert.pem --tls-key /path/key.pem

# Check TLS configuration and dependencies
muxplex doctor

Auto-detection priority: Tailscale (if tailscale is installed and a cert is available) → mkcert (if mkcert is installed) → self-signed (always available as a fallback). Use --method to override.

Note: Tailscale certs have a 90-day expiry. Run muxplex setup-tls --method tailscale to renew when needed.

When to use --method ca

The ca method is for the case where you want browser-trusted HTTPS on plain LAN names (e.g. https://my-host:8088, https://192.168.1.5:8088) but can't use Tailscale (no client install, blocked by IT policy, or the URL must be the bare LAN name) and don't want to buy a public domain.

It generates a persistent root CA in ~/.config/muxplex/ca/ and signs a 13-month leaf with it. The leaf's SAN automatically includes the hostname, <hostname>.local, localhost, the primary LAN IPv4 address, and the Tailscale MagicDNS name (if Tailscale is connected). Install the CA (not the leaf) once on each client; subsequent leaf rotations don't require re-trusting.

Not part of the auto cascade — must be opted into explicitly.

→ See docs/TRUSTING_THE_LOCAL_CA.md for per-platform install instructions (Windows, macOS, Linux, iOS, Android, Firefox).

Fetching the CA over the network: GET /api/ca

Installing the CA on each client previously required scp-ing it off the server, which needs SSH access the client may not have — and it's easy to grab the wrong file (muxplex.crt, the leaf the server presents on the wire) instead of the CA, producing "unable to get local issuer certificate". GET /api/ca serves the CA's public certificate directly over HTTP(S) — no SSH, no auth (a CA public cert isn't a secret; it's the trust anchor clients are meant to install), and no ambiguity about which file it is:

curl -k https://my-host:8088/api/ca -o muxplex-ca.crt

-k is acceptable only for this one bootstrap fetch of a public trust anchor (there's nothing sensitive to expose by skipping verification here). For high-trust setups, confirm the fingerprint out-of-band before trusting it:

openssl x509 -in muxplex-ca.crt -noout -fingerprint -sha256

Returns 404 if this server isn't using setup-tls --method ca (e.g. it's on Tailscale, mkcert, or self-signed instead).

tmux socket (the "invisible session" hazard)

muxplex looks for tmux sessions under a specific socket directory (the tmux_socket_dir setting, mapped to tmux's TMUX_TMPDIR environment variable). Any other tool or script that creates a tmux session without setting the same TMUX_TMPDIR lands on a different tmux server and is silently invisible to muxplex — tmux list-sessions from your interactive shell will show it, but muxplex's dashboard, API, and Stream Deck sidecar never will, because they're watching a different socket. This bites hardest when tmux_socket_dir is left at its default (""): a systemd/launchd service process doesn't inherit your login shell's TMUX_TMPDIR, so the service quietly falls back to tmux's compiled-in default (/tmp/tmux-$UID) even if your shell rc sets something else.

The one-line fix — run this before creating a session you want muxplex to see:

eval "$(muxplex env)"
tmux new-session -d -s my-session   # now lands where muxplex can see it

muxplex env prints a single export TMUX_TMPDIR=... line (nothing else, so eval is always safe) resolved from the configured tmux_socket_dir — or, if that's unset, your shell's own TMUX_TMPDIR — or, failing both, tmux's own default. GET /api/instance-info also exposes tmux_socket_dir (the exact value the running server resolves, since that endpoint runs inside the server process itself) so remote tools/agents can discover it without SSH access or tribal knowledge.


Configuration

All settings are stored in ~/.config/muxplex/settings.json.

Key Default Description
host 127.0.0.1 Bind address (set to 0.0.0.0 for network access)
port 8088 Server port
auth pam Authentication mode: pam or password
session_ttl 604800 Session cookie TTL in seconds (7 days; 0 = browser session)
default_session null Session to auto-open on load
sort_order manual Session ordering: manual, alphabetical, recent
hidden_sessions [] Sessions hidden from the dashboard
views [] Named session views for grouping and filtering sessions
stale_key_grace_hours 24.0 Hours before a session key absent from all known live sessions is pruned from views/hidden_sessions (syncable; per-device bookkeeping is local-only). Federation-aware: a remote device's keys are only ever evaluated for pruning while that device is currently reachable (see "Stale-key pruning" below) -- an offline device's view membership is never touched.
window_size_largest false Auto-set tmux window-size largest on connect
auto_open_created true Auto-open newly created sessions
new_session_template tmux new-session -d -s {name} Command template for creating sessions
delete_session_template tmux kill-session -t {name} Command template for deleting sessions
input_enabled false Global opt-in for POST /api/sessions/{name}/input (typing into sessions over the API). RCE by designfalse makes the endpoint a hard 403. Local-file-only: can ONLY be set by editing settings.json on disk — deliberately not settable via PATCH /api/settings (a Bearer-key holder must not be able to self-authorize input) and not federation-syncable.
input_allowed_sessions [] Glob patterns (matched case-INsensitively — both name and pattern are .casefold()-ed before fnmatch.fnmatchcase, so behavior is deterministic across platforms) naming sessions that may receive API terminal input, e.g. ["*"] for all sessions, ["amplifier-*"] for a prefix family, or an exact name (matches only itself). A session matching none of the patterns is a 403 even when input_enabled is true — this is how your own working panes stay un-typeable. Empty list = deny everything. Local-file-only: can ONLY be set by editing settings.json on disk — deliberately not settable via PATCH /api/settings and not federation-syncable.
tmux_socket_dir "" Override tmux's socket directory (maps to TMUX_TMPDIR). Set this if your tmux sessions live somewhere other than /tmp/tmux-$UID (e.g. a custom TMUX_TMPDIR in your shell rc) -- a systemd/launchd service does not inherit your login shell's environment, so without this the service can't see sessions created with a custom socket directory.
device_name "" (hostname) Display name for this device
federation_key "" Server-to-server authentication key for federation
remote_instances [] Remote muxplex instances to aggregate
multi_device_enabled false Enable multi-instance federation
tls_cert "" Path to TLS certificate file (empty = HTTP)
tls_key "" Path to TLS private key file (empty = HTTP)
fontSize 14 Terminal and tile preview font size (px)
hoverPreviewDelay 1500 Hover preview popup delay (ms)
gridColumns "auto" Number of grid columns ("auto" or integer)
bellSound false Play audio sound on terminal bell
viewMode "auto" Grid tile sizing: auto or fit
showDeviceBadges true Show device name labels on tiles
showHoverPreview true Show hover preview popover on tile hover
activityIndicator "both" Activity style: none, glow, dot, both
gridViewMode "flat" Multi-device grid layout: flat, grouped, filtered
sidebarOpen null Sidebar state: true, false, or null (auto-detect from screen width)
settings_updated_at 0.0 Unix timestamp of last settings write (used for federation sync)
views_updated_at 0.0 Unix timestamp of last change to views/hidden_sessions specifically. Metadata like settings_updated_at, used to arbitrate views-specific federation sync conflicts independently of unrelated field changes (e.g. a fontSize edit no longer bumps this). Not itself a syncable setting -- see AGENTS.md's federation section.

Priority: CLI flags > settings.json > defaults.

→ Writing something that drives muxplex? The rows above define input_enabled / input_allowed_sessions as configuration. For the operational side — auth, the read endpoints, session lifecycle, the terminal-input contract and its threat model, and copy-pasteable curl examples — see docs/AGENT_GUIDE.md. It's vendor-neutral: point any agent or script at it.


Keyboard Shortcuts

Shortcut Action
Ctrl+Shift+C Copy terminal selection to system clipboard
Cmd+V / Ctrl+Shift+V Paste from system clipboard (native browser paste)
Ctrl+F Open terminal search bar
Enter / Shift+Enter Next / previous search match
Ctrl+Click (Cmd+Click) Open URL in new tab
, (comma) Open settings
Escape Close settings / return to dashboard

Mouse select in the terminal auto-copies to the system clipboard on release.


Platform Support

Platform Service Auth
Linux (Ubuntu/Debian) systemd user service PAM
macOS launchd agent PAM
WSL systemd user service PAM

Project Structure

muxplex/
├── muxplex/
│   ├── __init__.py
│   ├── __main__.py          # python -m muxplex entry
│   ├── cli.py               # CLI entry point and subcommand dispatch
│   ├── main.py              # FastAPI app, routes, WebSocket proxy
│   ├── auth.py              # PAM/password auth middleware
│   ├── sessions.py          # tmux session enumeration + snapshots
│   ├── bells.py             # Bell flag detection + clear rules
│   ├── state.py             # Persistent state (JSON)
│   ├── settings.py          # User settings management
│   ├── service.py           # Service install/start/stop (systemd + launchd)
│   ├── ttyd.py              # ttyd process lifecycle
│   ├── frontend/
│   │   ├── index.html        # Main SPA
│   │   ├── login.html        # Login page
│   │   ├── app.js            # Dashboard, sidebar, settings, previews
│   │   ├── terminal.js       # xterm.js terminal + clipboard
│   │   ├── style.css         # All styles (dark theme)
│   │   ├── manifest.json     # PWA manifest
│   │   ├── wordmark-on-dark.svg
│   │   └── tests/            # JavaScript unit tests
│   └── tests/                # Python tests (pytest)
├── assets/branding/          # Logos, icons, design system
├── docs/plans/               # Historical design + implementation plans
├── scripts/                  # Utility scripts (asset generation)
├── pyproject.toml
└── README.md

Development

Setup

git clone https://github.com/bkrabach/muxplex
cd muxplex

# Install with dev dependencies
uv pip install -e ".[dev]"

Run the server

muxplex
# or directly:
python -m muxplex

Run tests

# Python tests (pytest)
python -m pytest muxplex/tests/ --ignore=muxplex/tests/test_integration.py

# JavaScript tests (node:test)
node --test muxplex/frontend/tests/test_terminal.mjs
node --test muxplex/frontend/tests/test_app.mjs

Brand Assets

Design language, color tokens, and brand assets live in assets/branding/. See assets/branding/DESIGN-SYSTEM.md for the full design reference.

To regenerate PNG/favicon assets from SVG sources:

python3 scripts/render-brand-assets.py

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

muxplex-0.21.0.tar.gz (1.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

muxplex-0.21.0-py3-none-any.whl (380.9 kB view details)

Uploaded Python 3

File details

Details for the file muxplex-0.21.0.tar.gz.

File metadata

  • Download URL: muxplex-0.21.0.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for muxplex-0.21.0.tar.gz
Algorithm Hash digest
SHA256 d35e496267ad5a41f97580f13adba862bc4c5ec154f796769a2d89f813869c3f
MD5 0288fad9fe62a660cb5c2ee330dc06db
BLAKE2b-256 ef75ad15ee3929f4daeda57067cfefee1fa93fb82dfa1b4e89b457938d7f4a8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for muxplex-0.21.0.tar.gz:

Publisher: publish.yml on bkrabach/muxplex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file muxplex-0.21.0-py3-none-any.whl.

File metadata

  • Download URL: muxplex-0.21.0-py3-none-any.whl
  • Upload date:
  • Size: 380.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for muxplex-0.21.0-py3-none-any.whl
Algorithm Hash digest
SHA256 34ff5a4be16dcf4557ff9c7ab95c77418db57fb07161b47683c9dafaee41a4a7
MD5 1cc54682eecec163092301b5f4aa2a0e
BLAKE2b-256 07da7fde2c058c71cacd629bc29c398ece0e3c045e848671df52c286a04f70e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for muxplex-0.21.0-py3-none-any.whl:

Publisher: publish.yml on bkrabach/muxplex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.58.2

2 files

0.58.1

2 files

0.58.0

2 files

0.57.3

2 files

0.57.2

2 files

0.57.1

2 files

0.57.0

2 files

0.56.2

2 files

0.56.1

2 files

0.56.0

2 files

0.55.2

2 files

0.55.1

2 files

0.55.0

2 files

0.54.0

2 files

0.53.0

2 files

0.52.0

2 files

0.50.0

2 files

0.49.1

2 files

0.49.0

2 files

0.48.3

2 files

0.48.2

2 files

0.48.1

2 files

0.48.0

2 files

0.47.12

2 files

0.47.11

2 files

0.47.10

2 files

0.47.9

2 files

0.47.8

2 files

0.47.7

2 files

0.47.6

2 files

0.47.5

2 files

0.47.4

2 files

0.47.3

2 files

0.47.2

2 files

0.47.1

2 files

0.47.0

2 files

0.46.1

2 files

0.46.0

2 files

0.45.1

2 files

0.45.0

2 files

0.44.0

2 files

0.43.0

2 files

0.42.0

2 files

0.41.0

2 files

0.40.0

2 files

0.39.0

2 files

0.38.1

2 files

0.38.0

2 files

0.37.0

2 files

0.36.1

2 files

0.36.0

2 files

0.35.0

2 files

0.34.0

2 files

0.33.0

2 files

0.32.0

2 files

0.31.6

2 files

0.31.5

2 files

0.31.4

2 files

0.31.3

2 files

0.31.2

2 files

0.31.1

2 files

0.31.0

2 files

0.30.1

2 files

0.30.0

2 files

0.29.0

2 files

0.28.0

2 files

0.27.0

2 files

0.26.1

2 files

0.26.0

2 files

0.25.0

2 files

0.24.0

2 files

0.23.0

2 files

0.22.0

2 files

This release

0.21.0 This release

2 files

0.20.1

2 files

0.20.0

2 files

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.1

2 files

0.16.0

2 files

0.15.1

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.1

2 files

0.7.0

2 files

0.6.10

2 files

0.6.9

2 files

0.6.8

2 files

0.6.7

2 files

0.6.6

2 files

0.6.5

2 files

0.6.4

2 files

0.6.3

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.0

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page