One control layer over tmux — sessions, persistence, plugins, and a beautiful web UI.
A modern command center for tmux. Manage sessions and panes from a typed CLI, persist them to SQLite, configure TPM plugins (and the Freed-Wu status bar) from a wizard in your browser — all without leaving your terminal flow.
Quick start · Web UI · Plugins (TPM) · Why dmux? · Site
You already love tmux. dmux makes it easier to live in.
| Pain | dmux fix |
|---|---|
Sessions vanish on reboot or accidental kill-server. |
dmux save / dmux restore — full snapshots in SQLite, one command. |
| Per-project layouts re-built from muscle memory every time. | .dmux/layout.json per project root, restored on demand. |
TPM plugins are great, but ~/.tmux.conf is a wall of set -g @plugin. |
Plugin manager UI + CLI: add, install, update, clean, source — without editing. |
| Freed-Wu status bar is powerful but its template syntax is dense. | A real wizard in the browser builds the JIT #{status-left:…} for you. |
| Want a quick visual of who's running what? | Live web UI: pane mosaic, fuzzy filter, dark mode, glassmorphic topbar — under 1 MB. |
| Need scripts to drive tmux from CI / hooks? | Typed JSON API (/api/v1/...) you can curl from anywhere. |
Built on libtmux, Typer, and Flask. Vendors TPM so plugin installs are zero-setup.
- Session manager — list, create, attach, rename, kill from one CLI, with smart attach (
switch-clientinside tmux,attachoutside). - Persistence —
dmux savesnapshots every session/window/pane (working dir, command, layout) into SQLite under~/.local/share/dmux/.dmux restorerebuilds them, even after a reboot. - Rich snapshots with
tmux-resurrect— opt in to capture per-pane processes, full pane scrollback, and vim/neovim sessions alongside dmux's structural save. The web UI lists every on-disk resurrect file (including ones written byprefix + Ctrl-sortmux-continuum) with session/window/pane counts and ahistorybadge so you can see at a glance whether scrollback will be replayed. - Project workspaces —
.dmux/layout.jsonper repo root. Walk into any project,dmux restore, get the same layout you left. - Fuzzy navigation —
dmux pickfor sessions/windows/panes via stdlibdifflib. No fzf required. - Web UI — Bootstrap 5, dark/light theme with no-flash init, live pane mosaic, plugin manager, status-bar wizard, snapshot restore modal with conflict detection. Served by a tiny Flask app on
127.0.0.1. - TPM, batteries-included — TPM is vendored at
src/dmux/vendor/tpm.dmux plugins bootstrapwrites~/.config/dmux/plugins.tmuxand hooks~/.tmux.conffor you. - Freed-Wu status-bar wizard — segment table editor that builds JIT or AOT templates and writes them back to
plugins.tmux. Source in tmux does a real reload (sources yourtmux.confandrefresh-client -S), so changes show up live. - JSON API — every UI action is a documented endpoint (
/api/v1/sessions,/api/v1/plugins/source, etc.). Build your own dashboards or shell scripts. - Type-checked —
py.typedpackage, mypy-clean public surface. - Tested — 59-test smoke suite covering CLI, API, plugin manager, snapshots, resurrect integration.
- Python 3.10+
tmux3.2+ onPATH- Linux or macOS terminal (Windows users: WSL)
pip install dmuxThat's it — TPM is vendored in the wheel, so the plugin manager works out of the box:
dmux --version
dmux --helpPrefer a hacking checkout?
git clone https://github.com/davidix/dmux.git
cd dmux
pip install -e ".[dev]"Note:
dmuxships with TPM as vendored code undersrc/dmux/vendor/tpm. The PyPI wheel includes it. From a fresh git checkout you can pull it as a submodule:git submodule update --init src/dmux/vendor/tpm.
dmux list # show every session / window / pane
dmux new mysession # create a session
dmux attach mysession # smart attach (switch-client inside tmux)
dmux save # snapshot every session into SQLite
dmux restore # rebuild them after a reboot
dmux layout grid # apply a preset to the focused window
dmux pick # fuzzy pick session/window/pane
dmux ui --open # launch the web UI in your browserState and SQLite live under ~/.local/share/dmux/ (or $XDG_DATA_HOME/dmux/).
dmux ui --open # http://127.0.0.1:8756
dmux ui --port 8757 # if 8756 is busy
dmux ui -S /tmp/my.sock # talk to a non-default tmux socketWhat you get in the browser:
- Sidebar — every session with attach/kill controls, fuzzy filter, live counts.
- Topbar — sticky, glassmorphic, animated "live" pulse, light/dark toggle (persisted, no flash).
- Pane mosaic — every pane in the focused window as a tile, with hover lift and a corner status dot.
- Plugins (TPM) — list, add, remove, install, update, clean, Source in tmux. Inline editor for
plugins.tmux(CodeMirror) with awesome-list autocomplete. - Status-bar wizard — segment table editor for
Freed-Wu/tmux-status-bar. Builds the JIT#{status-left:…}template and writes it back toplugins.tmux. Source reloads it live. - Snapshot restore — one modal listing both dmux's SQLite snapshots and on-disk
tmux-resurrectfiles (including ones written byprefix + Ctrl-sortmux-continuum). Each resurrect entry shows session/window/pane counts, captured foreground commands (vim, ssh, …), and ahistory/no historybadge that tells you whether pane scrollback will be replayed. "Kill conflicting sessions" actually kills the named sessions before invokingrestore.sh, so the restore can no longer silently skip them.
The UI is not a background service. Close the terminal → port stops responding. That's intentional: dmux is a tool you start when you need it, not a daemon.
Want a tour without installing? See the GitHub Pages site.
The classic TPM workflow, as a typed CLI and a web UI.
# 1. Bootstrap once: writes ~/.config/dmux/plugins.tmux and hooks ~/.tmux.conf
dmux plugins bootstrap
# 2. Add plugins
dmux plugins add tmux-plugins/tmux-sensible
dmux plugins add Freed-Wu/tmux-status-bar
# 3. Install
dmux plugins install
# 4. Reload in the running tmux server (does a full re-source + refresh)
dmux plugins sourceOr skip the CLI and use the Plugins (TPM) view in the web UI — same flows, with autocomplete from the tmux-plugins awesome list.
dmux plugins source doesn't just source-file plugins.tmux — it also re-sources your main tmux.conf (mirroring TPM's prefix+I reload) and runs refresh-client -S, so plugins that cache state at TPM init time (Freed-Wu/tmux-status-bar, dracula, …) actually pick up your edits without a kill-server.
| Command | Purpose |
|---|---|
dmux list |
List sessions, windows, panes |
dmux new <name> |
Create a new session |
dmux attach <name> |
Smart attach (switch-client inside tmux, attach outside) |
dmux kill <name> |
Kill a session |
dmux rename <old> <new> |
Rename a session |
dmux save |
Snapshot every session to SQLite |
dmux restore |
Rebuild sessions from the latest snapshot |
dmux layout <preset> |
Apply a preset (even-h, even-v, main-h, main-v, tiled) |
dmux pick |
Fuzzy pick a session / window / pane |
dmux copy |
Copy current tmux buffer to system clipboard |
dmux ui [--open] [--port] [-S] |
Launch the web UI |
dmux plugins {bootstrap,add,…} |
TPM management (status, add, remove, install, update, clean, source) |
Run any subcommand with --help for full options.
Every UI action is a curl-able endpoint under /api/v1/:
curl http://127.0.0.1:8756/api/v1/sessions | jq
curl -XPOST http://127.0.0.1:8756/api/v1/sessions -d '{"name":"work"}' \
-H 'Content-Type: application/json'
curl -XPOST http://127.0.0.1:8756/api/v1/plugins/source -d '{}' \
-H 'Content-Type: application/json'Useful endpoints:
| Method | Path | Use |
|---|---|---|
GET |
/api/health |
Liveness probe |
GET |
/api/v1/sessions |
List every session/window/pane |
POST |
/api/v1/sessions |
Create a session |
DELETE |
/api/v1/sessions/<name> |
Kill |
POST |
/api/v1/sessions/<name>/layout |
Apply layout preset |
POST |
/api/v1/snapshots/save / GET /api/v1/snapshots |
Save / list snapshots |
POST |
/api/v1/snapshots/restore |
Restore a snapshot (SQLite or a resurrect_file); kill_existing clears conflicts first |
GET |
/api/v1/snapshots/resurrect |
tmux-resurrect status (configured / installed / save dir / count) |
GET/DELETE |
/api/v1/snapshots/resurrect/files |
List / delete on-disk tmux_resurrect_*.txt files (with per-snapshot pane-content archives) |
GET |
/api/v1/plugins |
TPM status + configured plugins |
GET/PUT |
/api/v1/plugins/fragment |
Read / write plugins.tmux |
POST |
/api/v1/plugins/{install,update,clean,source} |
TPM operations |
POST |
/api/v1/plugins/plugin-lines |
Replace one plugin's option block |
src/dmux/
├── api/ Flask app + JSON routes
├── cli.py Typer CLI (dmux, dmux plugins …)
├── modules/ clipboard + autosave daemon
├── navigation/ stdlib-only fuzzy targets
├── persistence/ SQLite StateManager + JSON snapshots
├── services/ libtmux wrapper, TPM integration
├── workspaces/ .dmux/layout.json per project root
├── web/ Bootstrap 5 UI (templates/ + static/)
├── vendor/tpm/ Vendored tmux-plugins/tpm
└── data/ Awesome-list catalog + plugin defaults
No background processes. No daemons. The web UI runs only while dmux ui is in the foreground.
| Environment variable | Effect |
|---|---|
XDG_DATA_HOME |
Override ~/.local/share/dmux/ for SQLite + snapshots |
XDG_CONFIG_HOME |
Override ~/.config/dmux/ for plugins.tmux |
DMUX_TMUX_CONF |
Use a different tmux.conf (e.g. ~/.config/tmux/tmux.conf) |
git clone https://github.com/davidix/dmux.git
cd dmux
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest # 59-test smoke suite
ruff check src tests # lint
mypy src # type-check
dmux ui --open # try the UIIs this a tmux replacement? No. dmux is a control layer on top of tmux. The tmux server, plugins, and ~/.tmux.conf you already have keep working.
Does it work without TPM? Yes — every session/persistence/UI feature works without ever touching plugins. TPM integration is opt-in via dmux plugins bootstrap.
Is the web UI safe to expose? It's bound to 127.0.0.1 by design. There is no auth. Don't put it behind a public reverse proxy without adding one.
Why "dmux"? The d is for "dashboard" — and it tab-completes faster than tmuxp.
Can I drive it from CI / cron? Yes — every action has a stable CLI command and a JSON endpoint.
Does dmux plugins source actually reload everything? It sources plugins.tmux, then your tmux.conf (TPM's own prefix+I strategy), then refresh-client -S. Plugins that cache state at init pick up changes without a server restart.
- Public PyPI release
- Per-pane shell history capture into snapshots (via
tmux-resurrectintegration) -
dmux watch— live snapshot daemon (opt-in) - Theme presets in the web UI
-
dmux export --format=tmuxpinterop
PRs welcome.
- Fork → feature branch → PR.
- Keep
pytestgreen andruff checkclean. - Don't touch
src/dmux/vendor/tpm/(vendored upstream). - Add a smoke test for any new endpoint or CLI command.
If dmux saved you 10 minutes today, please star the repo — it's the cheapest way to fund more.
MIT — see LICENSE.
TPM is bundled under its own MIT license (see src/dmux/vendor/tpm/LICENSE.md).