Skip to main content

tmuxd

PyPI Python License

tmux + ttyd as a Python library: terminals that outlive the connection, that a program can type into and a person can open in a browser.

English · 简体中文 · Changelog


Everyone has written ttyd tmux new -A -s work at some point. tmux keeps the session alive, ttyd makes it visible in a browser. It works — but the result has no handle on it. Who opened that session, in which directory, is it still alive? Want to feed it a command from the outside? SSH in and type tmux send-keys.

tmuxd is that command turned into something you import.

from tmuxd import Tmuxd

t = Tmuxd(port=12345, token="changeme")   # ttyd is up; tmux is not yet
s = t.session(id="id5", cwd="~/proj", cmd="claude")
s.send("run the tests", enter=True)
print(s.url)                              # http://127.0.0.1:12345/?arg=id5

Send that URL to anyone and their browser is in that terminal — watching, and able to take over the keyboard. A program hands out the work; a person watches it run.

Quick start

Needs tmux (≥ 3.0) and ttyd on the machine — see Requirements.

As a library — no server needed

pip install tmuxd          # zero runtime dependencies
from tmuxd import Tmuxd

with Tmuxd(port=12345, token="changeme") as t:
    s = t.session(id="deploy", cwd="/srv/app", cmd="./deploy.sh")
    print("watch it here:", s.url)
# ttyd goes with your process; the deploy is still running

Your process holds the instance, so there is nothing else to run.

From the command line — needs a server

pip install "tmuxd[server]"     # + fastapi + uvicorn
tmuxd start                     # ttyd on :7681, control API on :7682
tmuxd new  -s work -c ~/proj
tmuxd send -s work "npm test" --enter
tmuxd url  -s work -o           # open it in a browser
tmuxd ls
tmuxd stop                      # stops the server; sessions keep running

A CLI command lives for milliseconds and can hold neither ttyd nor session state, so it asks a server that can. That is why the CLI and the server install together — why.

What makes it different

It is designed by subtraction. What was removed says more than what is left.

  • One session is one terminal. No windows, no panes — the multiplexing half of tmux is not used. Want more terminals? Open more sessions.
  • Write only, no reading. No capture, no run, no output stream, no recording, no event stream. Reading a terminal belongs to a person (open the URL — ttyd already does that better than any API could) or to ssh (clean stdout, a real exit code, binary safety). What stays is the one write action neither of them can do.
  • The facade is short-lived, the house is not. ttyd is a child of your process; the tmux server is nobody's child. kill -9 your program and the sessions carry on, with their working directory and command remembered.
  • It never touches your own tmux. Only the binary is probed, and the pool always opens on a dedicated tmux -L tmuxd. Your tmux ls is unchanged.
  • A person and a program type into the same terminal. Not a feature we built — tmux gives it away, which is why the whole design is arranged around it.
  • No permission tiers. Everything is read-write. Holding the token means holding a shell on that machine, so a read-only switch here would be a boundary that is not really there. Lock upstairs, where identity exists.

Two ways in

Library CLI
Holds the instance your process tmuxd serve
Needs a server no yes
Install pip install tmuxd pip install "tmuxd[server]"
Ports ttyd only ttyd + control API
Exposing it mount tmuxd.server.router() in the app you already run control API on :7682

Two ports, two audiences. :7681 is ttyd and it is for peoples.url goes straight to a colleague. :7682 is the control API and it is for programs — JSON in, JSON out, seven endpoints. Driving another machine is ssh box tmuxd …, not a port on the internet.

Requirements

tmux ≥ 3.0 apt install tmux · brew install tmux · dnf install tmux
ttyd ≥ 1.6 bundled in the Linux wheels · macOS: brew install ttyd
Python ≥ 3.9
OS Linux, macOS

On Linux, pip install is enough. The wheels carry an upstream ttyd build for their architecture (x86_64, aarch64, armv7l — glibc and musl alike, since upstream links statically). A ttyd already on PATH still wins: that one can be fixed by apt upgrade and ours can only be fixed by a release of tmuxd.

On macOS you install ttyd yourselfbrew install ttyd. Upstream has never shipped a Darwin build (checked back to 1.7.3: ten musl ELFs and one win32.exe, every time), and Homebrew's is dynamically linked against five of its own packages, so re-shipping it would do badly what brew does well. macOS gets the py3-none-any wheel, which installs everywhere and simply expects ttyd on PATH.

Windows is not supported — tmux has no Windows build, and tmuxd imports fcntl.

If your machine is not ready — an architecture no wheel covers, no tmux, or you want a newer ttyd than the one we vendored — there is an optional tmuxd install. It fetches a checksum-verified ttyd from upstream (falling back to the bundled one when the network is down) and tells you the exact command for tmux on this machine, then records both paths in ~/.tmuxd.json so the library and the CLI find them next time. A ready machine never runs it, and Tmuxd() does not check whether you have.

tmuxd never adopts the tmux you use yourself: it runs its own pool on a dedicated socket, so tmux ls shows exactly what it showed before.

Documentation

docs/v1/sdk Python SDKTmuxd, sessions, exceptions
docs/v1/cli Command line — 14 commands, the server, exit codes, config
docs/v1/works Design notes — why it was cut down to this
CHANGELOG.md 2.0 is a breaking release — what changed, and how to migrate

Development

pip install -e ".[dev]"
pytest                              # ~198 tests, ~50s
pytest tests/exact_targeting -v     # a single scenario

Tests run against real tmux, real ttyd and a real uvicorn — this project's whole value lives at the seam with those programs, and mocking them would test nothing. Each test gets its own tmux socket, so running the suite never disturbs a tmux you have open. They are organised by scenario, not by module.

License

Apache-2.0 — see LICENSE.

tmuxd drives ttyd (MIT) and tmux (ISC) as external programs; it neither vendors nor modifies them.

Download files

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

Source Distribution

tmuxd-2.0.0.tar.gz (163.5 kB view details)

Uploaded Source

Built Distributions

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

tmuxd-2.0.0-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl (801.4 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64musllinux: musl 1.2+ x86-64

tmuxd-2.0.0-py3-none-manylinux_2_17_armv7l.musllinux_1_2_armv7l.whl (769.7 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARMv7lmusllinux: musl 1.2+ ARMv7l

tmuxd-2.0.0-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl (807.1 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64musllinux: musl 1.2+ ARM64

tmuxd-2.0.0-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file tmuxd-2.0.0.tar.gz.

File metadata

  • Download URL: tmuxd-2.0.0.tar.gz
  • Upload date:
  • Size: 163.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tmuxd-2.0.0.tar.gz
Algorithm Hash digest
SHA256 592283610c37d86e8742e0301b2f68a156a4b170ba49da99df0780f86ad68417
MD5 5b4df058f185bb4b304283a3cc814e33
BLAKE2b-256 c297490f002f51c88533da89548b3292adb5c3b26eb91aa0cf28b5e10ec8ca5e

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmuxd-2.0.0.tar.gz:

Publisher: release.yml on memory-co/tmuxd

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

File details

Details for the file tmuxd-2.0.0-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for tmuxd-2.0.0-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 52faa76afd083ced1b3a0568007631888174ef3eb495a158a8b0b5fcd442e295
MD5 72b00a3d77c075cf530701e057f9b601
BLAKE2b-256 100d6b9b921959138c216a3af81ba6544c4ee44c28a0eb18a39f5ebb1a5ceecf

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmuxd-2.0.0-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl:

Publisher: release.yml on memory-co/tmuxd

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

File details

Details for the file tmuxd-2.0.0-py3-none-manylinux_2_17_armv7l.musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for tmuxd-2.0.0-py3-none-manylinux_2_17_armv7l.musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 7cfa107af450dd3668be3deb0573ae88b3a980c6138f15c245c49f740ba5e993
MD5 6ae5972646b903882d45d0783644803c
BLAKE2b-256 e8b85d2b911e1a366dae6c35ae3ba243dc8f834b29d7d673fa976e1489fc4b9d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmuxd-2.0.0-py3-none-manylinux_2_17_armv7l.musllinux_1_2_armv7l.whl:

Publisher: release.yml on memory-co/tmuxd

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

File details

Details for the file tmuxd-2.0.0-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for tmuxd-2.0.0-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 4f710e2f60a9ace2beb59f323d61f51df399528559bab52efbe71e8db2ea4259
MD5 158ef8a151ccbd8f6832750f5f685ccf
BLAKE2b-256 2848dcb5641be9310e6bb1f465f81dbf31702f4aaa6333d008cc8ec44ce6280a

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmuxd-2.0.0-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl:

Publisher: release.yml on memory-co/tmuxd

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

File details

Details for the file tmuxd-2.0.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for tmuxd-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d6a0c14c76adacc1aa31f99a72c54860598febcf79686c6e481b6be2f1fe1978
MD5 01515f806c0cde31c732efd9b9d30a4e
BLAKE2b-256 a243e6251e1f244ed2ca012831ada50571642c36f309b39ec39c050196d0b242

See more details on using hashes here.

Provenance

The following attestation bundles were made for tmuxd-2.0.0-py3-none-any.whl:

Publisher: release.yml on memory-co/tmuxd

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

Release history Release notifications | RSS feed

2.1.0

5 files

This release

2.0.0 This release

5 files

1.0.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