Unified control layer over tmux — sessions, persistence, plugins, CLI, and a web UI.
Project description
dmux
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
Why dmux?
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.
Highlights
- 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.
Requirements
- Python 3.10+
tmux3.2+ onPATH- Linux or macOS terminal (Windows users: WSL)
Install
pip install dmux
That's it — TPM is vendored in the wheel, so the plugin manager works out of the box:
dmux --version
dmux --help
Prefer 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.
Quick start
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 browser
State and SQLite live under ~/.local/share/dmux/ (or $XDG_DATA_HOME/dmux/).
The web UI
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 socket
What 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.
Tmux plugins (TPM)
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 source
Or 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.
CLI reference
| 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.
JSON API
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 |
Architecture
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.
Configuration
| 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) |
Development
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 UI
FAQ
Is 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.
Roadmap
- 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.
Contributing
- 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.
License
MIT — see LICENSE.
TPM is bundled under its own MIT license (see src/dmux/vendor/tpm/LICENSE.md).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dmux-0.2.0.tar.gz.
File metadata
- Download URL: dmux-0.2.0.tar.gz
- Upload date:
- Size: 149.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38a21a91b465cf5da4bbc9399e11d2213cc5c2b5e3cacff6164f1ce5774533df
|
|
| MD5 |
913f3e1855ef9b4c7f3289261078681d
|
|
| BLAKE2b-256 |
c1c76c9721fd464fbd3340370cb031cb203dde4f914a9fa6406d93d24a358416
|
Provenance
The following attestation bundles were made for dmux-0.2.0.tar.gz:
Publisher:
release.yml on davidix/dmux
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmux-0.2.0.tar.gz -
Subject digest:
38a21a91b465cf5da4bbc9399e11d2213cc5c2b5e3cacff6164f1ce5774533df - Sigstore transparency entry: 1340495448
- Sigstore integration time:
-
Permalink:
davidix/dmux@e0769bc1f99fbc9c82826f4d57793c6f2d205594 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/davidix
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e0769bc1f99fbc9c82826f4d57793c6f2d205594 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dmux-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dmux-0.2.0-py3-none-any.whl
- Upload date:
- Size: 153.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbb1b46a149ba0818cbb5f76e915e443c0ff57cb503e123b737f65374ebfd837
|
|
| MD5 |
6774a958ac1d02f32682d4094dbae81e
|
|
| BLAKE2b-256 |
675dcf2930cd8705ce876e984daa8eeb8fc34e876e285005668e6f966195609f
|
Provenance
The following attestation bundles were made for dmux-0.2.0-py3-none-any.whl:
Publisher:
release.yml on davidix/dmux
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmux-0.2.0-py3-none-any.whl -
Subject digest:
bbb1b46a149ba0818cbb5f76e915e443c0ff57cb503e123b737f65374ebfd837 - Sigstore transparency entry: 1340495450
- Sigstore integration time:
-
Permalink:
davidix/dmux@e0769bc1f99fbc9c82826f4d57793c6f2d205594 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/davidix
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e0769bc1f99fbc9c82826f4d57793c6f2d205594 -
Trigger Event:
push
-
Statement type: