DEV Community

Andrew
Andrew

Posted on • Originally published at andrew.ooo

Rmux Review: Rust Terminal Multiplexer Built for AI Agents

Originally published on andrew.ooo — visit the original for any updates, code snippets that aged out, or follow-up posts.

TL;DR

Rmux is a from-scratch Rust rewrite of the terminal multiplexer idea, built specifically for the agentic era. It hit Show HN on May 21, 2026, posted by author shideneyu with the tagline "A programmable terminal multiplexer with a Playwright-style SDK" — and the framing landed. The pitch in one sentence: drop-in tmux compatibility (90 commands, your keybindings work), plus a typed async Rust SDK on the same daemon so agents and scripts can drive any CLI or TUI app the way Playwright drives a browser.

Key facts:

  • v0.2.0 released May 18, 2026 (3 days before Show HN), MIT/Apache-2.0 dual-licensed
  • 90 tmux-compatible CLI commands implemented — new-session, split-window, send-keys, attach-session, etc.
  • Typed Rust SDK (rmux-sdk on crates.io) with stable pane IDs, structured snapshots, locator-style waits
  • Native on Linux, macOS, and Windows — uses Unix PTYs on *nix, real ConPTY on Windows (no WSL required)
  • ratatui-rmux widget for embedding live panes inside Ratatui TUI apps
  • #![forbid(unsafe_code)] in upper-level crates, OS boundary code isolated in runtime crates
  • Single daemon behind both surfaces — anything the CLI can do, the SDK can do, and vice versa
  • Limitations: fresh public preview, "bugs are expected" per the README, scriptable plugin/scripting system not yet broken out, no Lua/scripting parity with tmux configs

If you've ever written a flaky tmux send-keys + tmux capture-pane | grep loop to babysit a long-running tool from a script — or had an SSH session die and lose a Claude Code or Codex run — Rmux is the most principled fix shipped in 2026 so far.

What Rmux Actually Is

The author, shideneyu on GitHub and HN, explained the origin in the Show HN thread:

"RMUX started from a frustration: I've used tmux for years and got tired of scraping output with grep and sleeps to automate anything. So I rebuilt the multiplexer from scratch in Rust, with a programmable layer on top."

That's the core insight. Tmux is wonderful for humans and brittle for machines. The moment you try to automate — wait for a build to finish, send input only after a prompt appears, scrape a value out of top — you end up writing sleep 3 && capture-pane -p | tail -n 20 | grep ... and praying.

Rmux's answer is two parallel surfaces on top of one daemon:

1. The CLI surface (rmux binary): looks and acts like tmux. All 90 of the most-used tmux commands are implemented, your muscle memory works, your .tmux.conf-style keybindings carry over. You can drop it in.

2. The SDK surface (rmux-sdk crate): a typed async Rust API that talks to the same daemon over a local socket. Sessions and panes are first-class objects with stable IDs. You get wait_for_text("ready") instead of sleep && grep. Structured PaneSnapshot objects (rows, cols, attributes) instead of raw escape-sequence soup. This is the "Playwright for terminals" part.

Both surfaces talk to a hidden daemon that owns the PTYs, sessions, layouts, hooks, and buffers. Detach your human terminal — your SDK script keeps driving. Kill your SDK script — your human session is still attachable.

Install

For the impatient, on macOS or Linux:

curl -fsSL https://rmux.io/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Windows PowerShell:

irm https://rmux.io/install.ps1 | iex
Enter fullscreen mode Exit fullscreen mode

From crates.io:

cargo install rmux --locked
Enter fullscreen mode Exit fullscreen mode

Verify with rmux --version — should report 0.2.0. SHA256 checksums for the prebuilt binaries are published with the v0.2.0 GitHub Release, which is the right thing to do but still a rarity for a v0.2 launch.

A first session feels exactly like tmux:

rmux new-session -d -s work
rmux split-window -h -t work
rmux send-keys -t work 'echo "hello from rmux"' Enter
rmux attach-session -t work
Enter fullscreen mode Exit fullscreen mode

If you've never used tmux: that creates a detached session called work, splits it horizontally, sends a command into the right pane, then attaches your terminal so you can see both panes side by side. Press Ctrl+B D (the default prefix, like tmux) to detach again — the session keeps running, the process keeps running, you can re-attach from another terminal or another SSH connection later.

The SDK Is the Whole Point

The CLI is table stakes. The SDK is why you'd switch.

Add it to a Cargo project:

[dependencies]
rmux-sdk = "0.2"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
Enter fullscreen mode Exit fullscreen mode

Here's the canonical "wait for output, then capture state" pattern, adapted from the README:

use std::time::Duration;
use rmux_sdk::{
    EnsureSession, EnsureSessionPolicy, Rmux, SessionName, TerminalSizeSpec,
};

#[tokio::main]
async fn main() -> rmux_sdk::Result<()> {
    let rmux = Rmux::builder()
        .default_timeout(Duration::from_secs(5))
        .connect_or_start()
        .await?;

    let session_name = SessionName::new("work").expect("valid session name");
    let session = rmux
        .ensure_session(
            EnsureSession::named(session_name)
                .policy(EnsureSessionPolicy::CreateOrReuse)
                .detached(true)
                .size(TerminalSizeSpec::new(120, 32)),
        )
        .await?;

    let pane = session.pane(0, 0);
    pane.send_text("printf 'ready\\n' && sleep 1\n").await?;

    pane.wait_for_text("ready").await?;
    let snapshot = pane.snapshot().await?;
    println!("{}x{}", snapshot.cols, snapshot.rows);

    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

Read that carefully. The interesting lines are:

  • connect_or_start() — connects to a running daemon if one exists, starts one if not. No more "did I tmux start-server yet?" race conditions.
  • EnsureSessionPolicy::CreateOrReuse — idempotent session bootstrap. Re-run the script, get the same session.
  • pane.wait_for_text("ready").await? — this is the killer feature. No sleeps. No polling loops. A proper async wait that resolves the moment the text appears in the pane buffer (with the default_timeout as the bound).
  • pane.snapshot().await? — typed PaneSnapshot with cols, rows, and structured cell data. You don't parse strings; you read fields.

The mental model maps almost 1:1 to Playwright's: a typed handle to a long-lived environment, locator-style waits, structured introspection of state.

Ratatui Integration: Embed Live Panes in TUI Apps

This is the bit that made me sit up. The ratatui-rmux companion crate exposes a PaneWidget that renders a live pane snapshot directly inside a Ratatui TUI app:

use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use ratatui_rmux::{PaneState, PaneWidget};
use rmux_sdk::PaneSnapshot;

fn render(snapshot: PaneSnapshot, area: Rect, buffer: &mut Buffer) {
    let state = PaneState::from_snapshot(snapshot);
    PaneWidget::new(&state).render(area, buffer);
}
Enter fullscreen mode Exit fullscreen mode

What you can build with that: a TUI dashboard that owns its own panes (a build log, a server log, an agent's terminal session) and renders them as widgets next to your own controls, without subprocess-management or PTY-allocation code in your app. The dashboard is the multiplexer's other face. For anyone building agent observability tools or live operator UIs, this collapses a giant amount of boilerplate.

Why "For the Agentic Era" Actually Means Something Here

A lot of 2026 tooling slaps "for AI agents" on the README and changes nothing about the design. Rmux earns the label because of three concrete properties:

1. Detachable execution + reconnect. When a Claude Code, Codex, or aider run is going for 45 minutes and your SSH connection dies, a regular shell loses the process. With rmux (or tmux), the daemon owns the PTY — the agent keeps running, the new SSH attaches and resumes the human view. For long-lived coding-agent sessions over flaky connections, this isn't a nice-to-have, it's the difference between "shipped" and "had to restart from scratch."

2. Structured snapshots, not byte streams. When an agent supervisor needs to know "did my child process print the success line yet?", a byte-stream pipe forces it to write a parser. A typed PaneSnapshot lets it pattern-match on rows and cells. The supervisor agent can be small and dumb because the data is shaped right.

3. Real ConPTY on Windows, no WSL. This is the one most projects skip. WezTerm, Alacritty, even some "cross-platform" Rust shells punt to "use WSL" on Windows. Rmux uses real ConPTY (the Windows pseudo-console API) and a per-user Named Pipe for IPC. If you're building agent tooling that has to run on a developer's actual Windows machine — or in a Windows Server VM in CI — that matters more than benchmarks.

Community Reception (First 24 Hours)

The Show HN thread is still young (39 points, 29 comments at time of writing — early but front-paging), and the author is actively replying. Representative threads:

  • "Why not just script-wrap tmux?" — tmux's automation surface is the same byte-stream-and-regex pattern that motivated rmux. Wrapping it doesn't fix the mismatch.
  • "Daemon protocol stability?"rmux-proto (the IPC DTO crate) is published publicly so SDKs in other languages can be built against it. Python and Node bindings aren't shipped, but the wire format is documented.
  • "What about screen / zellij?"screen predates structured automation as a concern. Zellij has a WASM-plugin-centric philosophy; rmux bets on out-of-process SDK clients instead. Different shape, not strictly competitive.

The r/rust thread skews implementation-curious: ConPTY integration, the workspace split, and the #![forbid(unsafe_code)] posture in upper crates. Build hygiene is unusually thorough for v0.2: cargo clippy --all-targets --locked -D warnings, a scripts/no-network-in-runtime.sh assertion, and a platform-neutrality checker.

Honest Limitations

This is a fresh preview. Be realistic.

  • "Bugs are expected." The README says it. Don't put rmux on critical CI paths on day 3 — run it for personal use and prototypes for a release cycle or two first.
  • No Lua/scripting parity with tmux configs. Keybindings and core options carry over; a 400-line .tmux.conf with custom hooks and plugins does not.
  • No official Python / Node SDK yet. Either shell out to the CLI or hand-roll against the public rmux-proto wire format. Community ports are likely soon.
  • Daemon protocol is local-only by design. Unix socket or Named Pipe. To drive a remote rmux, you SSH in and run the SDK there — which is usually what you want anyway.
  • Daemon memory footprint at idle isn't documented. Irrelevant on a dev machine; you'd want to measure if you're spawning hundreds per CI build.

How It Compares

Tool Daemon Typed SDK Windows-native Snapshot API License
rmux 0.2 yes yes (Rust async) yes (ConPTY) yes (typed) MIT/Apache-2.0
tmux yes no no (WSL) capture-pane (text) ISC
Zellij yes WASM plugins partial partial MIT
WezTerm mux yes Lua yes partial MIT
screen yes no no no GPLv3
expect / pexpect no partial (Python) partial byte stream various

The closest competitor by design philosophy is honestly Playwright for terminals, which doesn't really exist as a category yet. Rmux is making it one.

Setup Recipes

Babysit a Coding Agent Over SSH

ssh build-server
rmux new-session -d -s coder
rmux send-keys -t coder 'cd ~/project && claude code' Enter
rmux attach-session -t coder
# do prompt work, then Ctrl+B D to detach
exit  # SSH disconnects, agent keeps running
Enter fullscreen mode Exit fullscreen mode

Reconnect from anywhere with ssh build-server && rmux attach-session -t coder. The agent's terminal is exactly as you left it, scrollback included.

Drive a TUI Installer from CI

pane.send_text("./installer.sh\n").await?;
pane.wait_for_text("License [y/N]").await?;
pane.send_text("y\n").await?;
pane.wait_for_text("Install directory:").await?;
pane.send_text("/opt/myapp\n").await?;
pane.wait_for_text("Installation complete").await?;
Enter fullscreen mode Exit fullscreen mode

This is the genuinely new capability. You could do it before with expect, but expect is a string-soup language from 1990. Rmux makes it a typed Rust program with proper error handling.

FAQ

Is rmux a drop-in replacement for tmux?

For day-to-day use: largely yes. The 90 most-used commands work, your prefix-key reflexes work. For exotic configs (heavy Lua scripting, custom hooks, plugin ecosystems like TPM): not yet. Treat it as "tmux that also happens to be programmable" rather than "tmux but better at everything tmux does."

Can I use rmux from Python or Node, not Rust?

Not with an official SDK yet. Your options today are: (1) shell out to the rmux CLI from your script, which works but loses the typed-SDK ergonomics, or (2) write against the public rmux-proto wire format. A community Python binding is the kind of thing that often appears within a month of an HN launch like this — keep an eye on the issues.

Does the Windows version really work without WSL?

Yes. It uses real ConPTY (the Windows pseudo-console API introduced in Windows 10 1809) for PTY allocation and per-user Named Pipes for IPC. The project's scripts/check-platform-neutrality.sh enforces that platform-specific code stays inside the boundary crates. If you've been waiting for a terminal multiplexer that doesn't make Windows a second-class citizen, this is the first credible one.

Is it stable enough to use today?

For personal automation, prototypes, and exploratory agent work: yes, with the README's caveat that bugs are expected at v0.2.0. For production CI on critical paths: wait one or two release cycles, watch the issue tracker, and pin the version. The build discipline (clippy, fmt, locked deps, forbid(unsafe_code)) is reassuring; the version number is honest.

How does this fit alongside coding agents like Claude Code or Codex?

Two complementary roles. First, as a host: run the agent's CLI inside an rmux session so it survives SSH disconnects and you can attach/detach (huge for long runs over flaky connections). Second, as a target: an agent supervisor (a Rust or Python orchestrator) uses the rmux SDK to drive other terminal tools — installers, REPLs, TUI debuggers — that the inner agent needs to interact with. The supervisor pattern is the more interesting one and the one rmux is uniquely good at versus tmux.

How does it compare to Forge?

Different layer. Forge is a reliability layer for the agent's reasoning and tool-calling loop. Rmux is a reliability layer for the agent's environment — the terminal sessions the agent (or the human supervising it) lives inside. Both target the same underlying frustration (small models / fragile sessions need scaffolding to be useful), but they don't overlap.

Verdict

Rmux is the cleanest version of "rebuild a Unix workhorse for the agentic era" I've seen in 2026. The decision to ship a tmux-compatible CLI and a typed SDK on the same daemon — instead of forcing users to pick — is the kind of constraint that makes a tool actually adoptable.

The Windows-native story (real ConPTY, no WSL) is what would tip me from "interesting" to "I'm trying this" for any project that needs cross-platform agent tooling. The Ratatui widget integration is what would tip a TUI builder. The wait_for_text locator API is what would tip anyone who has ever written a sleep 3 && grep loop and known it was wrong.

Caveats are real and the author owns them: v0.2.0, bugs expected, no Python/Node SDK yet, no scripting parity with mature tmux configs. But the bones are correct, the build hygiene is unusually disciplined for a v0.2 launch, and the design rationale survives contact with the obvious alternatives.

If you run long-lived processes over SSH, supervise CLI tools from code, or build TUI dashboards that wrap other terminal apps — install it today, port one annoying automation script, and report back in 30 days. That's the right size of bet on a fresh tool that ships its protocol publicly and forbids unsafe code in its upper crates.


Repo: github.com/helvesec/rmux · Docs: rmux.io/docs · Show HN: news.ycombinator.com/item?id=48219918 · License: MIT / Apache-2.0

Top comments (0)