Repository navigation
Add Eternal Terminal (et/etctl) as a cmux ssh terminal transport #25932
Description
Problem or motivation
I live in Eternal Terminal (ET) for remote work. ET keeps the session on the server and reconnects the client over TCP, so a shell survives Wi-Fi changes, sleep/wake, VPN flaps and even a client restart. It carries the raw terminal stream, so scrollback and OSC sequences (OSC 52, 9;4 progress, notifications) arrive untouched, and it has its own SSH agent forwarding and port tunnels.
ET main also has a control plane for scripts and agents now: et --ctl and etctl (MisterTea/EternalTerminal#819, #820). etctl open NAME host starts a named session as a local daemon with a unix socket, and etctl run NAME 'cmd' runs a command in that shell and returns its output and real exit code. etctl attach NAME shows the same shell to a human. So an agent and I can share one remote shell, and it outlives the terminal pane that shows it.
cmux already recognizes et command lines (#4321, parseEternalTerminalCommandLine), but only for file drop. A hand-run et host in a workspace gets none of the remote-workspace features (daemon bootstrap, browser proxy, uploads, saved transport), the same gap #8126 described for a hand-run mosh. cmux ssh gets those features, but its interactive terminal dies with the network or with cmux itself.
Proposed solution
Add et beside ssh and mosh on WorkspaceRemoteTerminalTransport:
cmux ssh user@host --transport etThe same shape as the mosh transport (and the tssh proposal in #14726):
- The interactive terminal goes through the local
etclient. Daemon bootstrap, relay, browser proxy, uploads and reconnect stay on the existing SSH management lane. - Remember the transport in workspace metadata and session snapshots, and reattach on restore.
- Probe for a local
etand a reachableetserver(default port 2022) before opening. If either is missing, say why and continue over SSH. - Pass through the ET options people already need:
--port,--terminal-path(whenetterminalis off the remote's non-interactivePATH, the same failure class as a missingmosh-server),--forward-ssh-agent, and--tunnel/--reversetunnel.
When the local et supports --ctl, it would be great to go one step further and run the interactive terminal as a named control session (et --ctl NAME host plus etctl attach NAME):
- The session survives the pane and cmux restarts, so restore is a plain reattach to the same shell, with nothing re-run.
- Agents in other panes can drive it with
etctl run NAME ...and the human sees each command in the pane as it runs. - The workspace can show the session name so
etctlcallers know what to target.
Alternatives considered
- Run
et hostinside a normal workspace. Roaming works, but cmux doesn't treat it as remote (no daemon bootstrap, browser proxy, uploads or saved transport). - My current stopgap is a 40-line wrapper:
etctl open NAME host, thencmux new-workspace --command 'etctl attach NAME'andcmux surface resume setso the pane reattaches when cmux restores. Sessions survive cmux upgrades and agents can drive them, but the workspace still isn't a remote workspace. --transport moshroams, but drops agent forwarding and OSC on the interactive terminal (Notifications don't work over Mosh connections to remote hosts #1871), and its server state dies with the client.
Additional context
- ET: https://github.com/MisterTea/EternalTerminal ;
etctlusage is in the README's "Programmatic control (etctl)" section. - Related: Recognize Eternal Terminal (et) as a remote connection for file drop/SCP #4321, Feature request: first-class Mosh transport for remote workspaces #8126, Add tssh as a roaming SSH terminal transport #14726, Notifications don't work over Mosh connections to remote hosts #1871.
- I wrote the
et --ctl/etctlpatches, so I'm happy to answer questions on the ET side and to test a macOS client against macOS and Linuxetserverhosts.
Activity
github-actions commentedon Oct 27, 2026
Triaged by rule:
area: cli,area: remote, from words in the title.Wrong? Change the labels and they will stay changed: this only labels an issue that has no triage label yet. The rules are in docs/triage.md.