Re-Connectable secure remote shell
See the codeEternal Terminal is a remote shell that automatically reconnects without interrupting the session.
Website: https://mistertea.github.io/EternalTerminal/.
The easiest way to install etis by using Homebrew:
brew install et
If the install fails on including csignal, see https://github.com/MisterTea/EternalTerminal/issues/662#issuecomment-2408889829
Then if you want a daemon to launch etserver on every boot:
On m1 (Apple Silicon) Macs:
sudo sed 's:/usr/local/bin/etserver:/opt/homebrew/bin/etserver:g' ../init/launchd/homebrew.mxcl.et.plist | sudo tee /Library/LaunchDaemons/homebrew.mxcl.et.plist
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
On x86 Macs:
sudo cp ../init/launchd/homebrew.mxcl.et.plist /Library/LaunchDaemons/homebrew.mxcl.et.plist
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
Alternatively, a package is available in MacPorts:
sudo port install et
For Ubuntu, use our PPA:
sudo add-apt-repository ppa:jgmath2000/et
sudo apt-get update
sudo apt-get install et
Or see "Debian/Ubuntu" below to install and build from source (e.g., for ARM).
For Debian, use our deb repo:
echo "deb [signed-by=/etc/apt/keyrings/et.gpg] https://mistertea.github.io/debian-et/debian-source/ $(grep VERSION_CODENAME /etc/os-release | cut -d= -f2) main" | sudo tee -a /etc/apt/sources.list.d/et.list
sudo mkdir -m 0755 -p /etc/apt/keyrings # only if you're using Debian 11 or older
curl -sSL https://github.com/MisterTea/debian-et/raw/master/et.gpg | sudo tee /etc/apt/keyrings/et.gpg >/dev/null
sudo apt update
sudo apt install et
Up to the present day the only way to install is to build from source.
sudo dnf install epel-release
sudo dnf install et
On FreeBSD, use:
pkg install eternalterminal
sudo dnf install et
zypper ar -f obs://network
zypper ref
zypper in EternalTerminal
Install dependencies:
Fedora (tested on 25):
sudo dnf install boost-devel libsodium-devel protobuf-devel \
protobuf-compiler cmake gflags-devel libcurl-devel
Gentoo:
sudo emerge dev-libs/boost dev-libs/libsodium \
dev-libs/protobuf dev-util/cmake dev-cpp/gflags
Download and install from source:
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
cmake ../
make
sudo make install
If you use flakes, you can import the bundled NixOS module and let it manage
the package, /etc/et.cfg, and the etserver service:
{
inputs.et.url = "github:MisterTea/EternalTerminal";
outputs = { nixpkgs, et, ... }: {
nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
et.nixosModules.default
({ ... }: {
services.eternalTerminal = {
enable = true;
openFirewall = true;
port = 2022;
settings.Networking.bind_ip = "0.0.0.0";
};
})
];
};
};
}
Eternal Terminal works under WSL (Windows Subsystem for Linux). Follow the ubuntu instructions.
See docker/README.md
Verify that the client is installed correctly by looking for the et executable: which et.
Verify that the server is installed correctly by checking the service status: systemctl status et. On some operating systems, you may need to enable and start the service manually: sudo systemctl enable --now et.
You are ready to start using ET!
An Eternal Terminal installation may provide these executables:
et is the command-line client. It uses SSH to authenticate and start the
remote session, then maintains the reconnectable ET connection.etserver is the system service that accepts ET client connections and
routes them to the correct user's session. Its default TCP port is 2022.etterminal is an internal server-side helper launched through SSH. Normal
users do not invoke it directly.htm is the foreground client for HTM, the bundled terminal multiplexer.
It speaks tmux control mode so compatible terminal emulators can show native
windows, tabs, and splits.htmd is the per-user HTM daemon. It owns persistent multiplexer sessions
and is started automatically by htm when needed.For the protocol-level relationship between et, etserver, and
etterminal, see the protocol documentation. For HTM, see
the HTM design documentation.
If you'd like to modify the server settings (e.g. to change the listening
port), edit /etc/et.cfg. On NixOS, set services.eternalTerminal.settings
instead so the generated /etc/et.cfg stays in sync with your system
configuration.
ET uses ssh for handshaking and encryption, so you must be able to ssh into the machine from the client. Make sure that you can ssh user@hostname.
ET uses TCP, so you need an open port on your server. By default, it uses 2022.
Once you have an open port, the syntax is similar to ssh. Username is default to the current username starting the et process, use -u or user@ to specify a different one if necessary.
et hostname (etserver running on default port 2022, username is the same as current)
et user@hostname:8000 (etserver running on port 8000, different user)
On macOS and Linux, et saves each direct session's reattachment credentials
in ~/.et/sessions (owner-only, plaintext), so after a client crash or reboot
you can return to a remote shell that is still running. Sessions get a
generated YYYYMMDD-xxxx name unless you pick one; --attach and --kill
accept a name or a unique substring of the name or terminal title. Port
forwards, agent forwarding, and jumphosts are not restored on attach.
et --name work hostname # start (or reattach to) a named session
et --list # list saved sessions without contacting servers
et --attach work # reattach after the client restarted
et --kill work # end the remote session and remove its record
et --no-persist hostname # do not write credentials to disk
You can specify a jumphost and the port et is running on jumphost using --jumphost and --jport. If no --jport is given, et will try to connect to default port 2022.
et hostname -jumphost jump_hostname (etserver running on port 2022 on both hostname and jumphost)
et hostname:8888 --jumphost jump_hostname --jport 9999
Additional arguments that et accepts are port forwarding pairs with --tunnel "18000:8000, 18001-18003:8001-8003" (or OpenSSH-style -L), and a command to run immediately after the connection is set up through --command or as a positional command after the host. Short flags match OpenSSH: -p is the sshd port, --port is the etserver port, and -t requests a pty.
Starting from the latest release, et supports parsing both user-specific and system-wide SSH config files. The config file is required when your sshd on server/jumphost is listening on a port which is not 22. Here is an example SSH config file showing how to setup when
Host dev
HostName 192.168.1.1
User fred
Port 5555
ProxyJump user@jumphost.example.org:22
With the ssh config file set as above, you can simply call et with
et dev (etserver running on port 2022 on both hostname and jumphost)
et dev:8000 -jport 9000 (etserver running on port 9000 on jumphost)
To isolate ET from ambient SSH configuration, pass an absolute path with
--ssh-config. ET reads only that file for its own destination and jumphost
lookup and passes the same file to every SSH process it starts, including the
implicit ProxyJump connection. The file must be readable, regular, and not a
symbolic link. The path must be absolute (/path on Unix; C:\path,
C:/path, or a UNC path on Windows) and may contain only ASCII letters, digits,
/, ., _, and - (plus : and \ on Windows). Spaces are not allowed:
OpenSSH does not quote this path when constructing its implicit ProxyJump
command. Pass --ssh-config none or the equivalent --no-ssh-config to
disable both user and system SSH configuration entirely. The two options are
mutually exclusive.
ET can stand in for ssh as the transport for VS Code's and Cursor's
Remote-SSH extensions, so a remote window rides out laptop sleep and network
changes instead of prompting you to reload. Both ends need a recent ET: the
client must include
#874, and the server's
etserver and etterminal must support et -T (the 7.0.0 packages do not).
ssh hostname must already work without a password prompt (use a key).
Point the editor at et1, which is installed next to et. It runs
et --close-on-hangup --disconnect-timeout=10080 "$@": the session is kept on
the server through a week-long disconnect (the value is in minutes) and ended
when the editor closes the connection. Remote-SSH only accepts a path to a
single executable, so if you need more et options (for example --port for
an etserver not on the default port), copy et1 and add them there.
For VS Code, add to settings.json (use the output of command -v et1 as the
path):
{
"remote.SSH.path": "/usr/local/bin/et1",
"remote.SSH.useLocalServer": false,
"remote.SSH.reconnectionGraceTime": 604800
}
remote.SSH.useLocalServer: false is required. In the default local-server
mode, VS Code kills the transport process a few seconds after it stops
responding, which throws away the ET client that would have reconnected.remote.SSH.reconnectionGraceTime (seconds) is how long the VS Code server
on the remote keeps your window's state while disconnected. The default is
three hours. It only takes effect when that server starts, so run
"Remote-SSH: Kill VS Code Server on Host..." after changing it.For Cursor, set remote.SSH.path to et1 as well. Cursor's Remote-SSH
extension has no local-server mode or grace-time setting; its closest
equivalent is remote.SSH.serverShutdownTimeout (seconds, default 300), which
controls how long the Cursor server stays up after the last client disconnects.
Normally et drives a terminal for a human. et --ctl instead backgrounds a
session with no local terminal and serves it on a per-user unix socket, and the
companion etctl command drives that session by name from a script or program.
You get a command's clean output and real exit code, instead of scraping a
rendered screen.
et --ctl --name main user@hostname # background a named session (idempotent via: etctl open main user@hostname)
etctl run main 'uname -a' # run a command; captured output + the real exit code
etctl read main # retained output (non-destructive; --cursor N to resume)
etctl expect main 'Password:' # wait for a prompt
etctl writeln main --secret # answer it without echoing the password
etctl key main eof # send Ctrl-D to end the session cleanly
etctl open is an idempotent wrapper over et --ctl. HOST comes first, and
any further et arguments follow it, so
etctl open main user@hostname --command '<cmd>' runs a startup command on
connect (for example, to drop into a bare, prompt-free shell for cleaner
capture). The session's remote shell is stamped with ETCTL_SESSION=<name>, so a
process can tell which named control session it is running under. The socket
lives at ~/.et/control/<name>.sock (0700 dir, 0600 socket, owning-uid
only), beside the session's saved record in ~/.et/sessions/<name>.
run frames a commandrun has to know where a command's output starts and ends and what it exited
with, and it has to inject the command safely (a multi-line body must run as one
command, and quotes/braces/!/parse errors must not desync the frame or hang on
a continuation prompt). It picks the cleanest of three framings for the far-side
prompt, detected once per session and cached (~/.et/control/<name>.framing):
C/D marks.eval "$(cat <<'BODY' ... )" -- so it is handed
to the shell as data, immune to its own syntax -- and boundaries come from OSC
133. No echo markers, but the wrapper is visible.echo <mark> ... <mark>:$?.run also trims the prompt-prep sequences a shell splices around the output (a
zsh/fish end-of-line mark, iTerm2's OSC 1337 context report, a title,
bracketed-paste toggles) so the captured output is the command's alone, and it
disables history expansion on the control session (a script-driven session has no
use for interactive !). Force a framing with --framing=osc133 (bracketed
paste) or --framing=mark (echo markers); the default is --framing=auto.
Run etctl with no arguments for the full verb list, etctl <verb> --help for
any verb's options, and etctl --version for the version. --ctl is not
available on Windows.
To build Eternal Terminal on Mac, install its dependencies with Homebrew:
brew install autoconf automake libtool
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
cmake ../
make -j$(nproc) && sudo make install
To run an et server for testing, run ./etserver. To run an et
server daemon persistently across reboots:
sudo cp ../init/launchd/homebrew.mxcl.et.plist /Library/LaunchDaemons
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
Grab the deps and then follow this process.
Debian/Ubuntu Dependencies:
sudo apt install libsodium-dev autoconf libtool \
libprotobuf-dev protobuf-compiler libutempter-dev libcurl4-openssl-dev \
build-essential ninja-build cmake git zip pkg-config
Fetch source, build and install:
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
# For ARM (including OS/X with apple silicon):
if [[ $(uname -a | grep 'arm\|aarch64') ]]; then export VCPKG_FORCE_SYSTEM_BINARIES=1; fi
cmake -DCPACK_GENERATOR=DEB ../
make -j$(nproc) package
sudo dpkg --install *.deb
Once built, the binary only requires libprotobuf-dev.
Disable et server by sudo systemctl disable --now et
Install dependencies:
sudo yum install epel-release
sudo yum install cmake3 boost-devel libsodium-devel protobuf-devel \
protobuf-compiler gflags-devel protobuf-lite-devel libcurl-devel \
perl-IPC-Cmd perl-Data-Dumper libunwind-devel libutempter-devel
Install scl dependencies
sudo yum install centos-release-scl
sudo yum install devtoolset-11 devtoolset-11-libatomic-devel rh-git227
Download and install from source (see #238 for details):
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
scl enable devtoolset-11 rh-git227 'cmake3 ../'
scl enable devtoolset-11 'make && sudo make install'
sudo cp ../systemctl/et.service /etc/systemd/system/
sudo cp ../etc/et.cfg /etc/
Find the actual location of et:
which etserver
Correct the service file (see #180 for details).
sudo sed -ie "s|ExecStart=[^[:space:]]*[[:space:]]|ExecStart=$(which etserver) |" /etc/systemd/system/et.service
Alternatively, open the file /etc/systemd/system/et.service in an editor and correct the ExectStart=... line to point to the correct path of the etserver binary.
ExecStart=/usr/local/bin/etserver --cfgfile=/etc/et.cfg --logtostdout
Reload systemd configs:
sudo systemctl daemon-reload
Start the et service:
sudo systemctl enable --now et.service
Builder Dockerfiles are located at deployment/. Supported OSes: CentOS 8, openSUSE and Ubuntu.
If you have any problems with installation or usage, please file an issue on GitHub.
Eternal Terminal uses three binaries:
et (client): Runs on the user's machine (client). Connects to a remote server over SSH to launch the terminal session, then connects to etserver on port 2022 for the persistent session.etterminal (server-side user process): Runs on the server as the user (launched by et via SSH). Hosts the terminal session and connects to etserver via a FIFO to register the session.etserver (server daemon): Runs permanently on the server (usually as root/system service). Listens on TCP port 2022 (default) and manages connections between et clients and etterminal sessions.Which machines need each part:
et.etserver (service/demon) and etterminal (installed for users; launched by et over SSH).Port: By default etserver listens on TCP 2022 (configured in /etc/et.cfg via the Networking.port setting).
Service setup: After installation, start/enable etserver via systemd (systemctl enable --now et) or launchd (macOS), or run ./etserver for testing.
htm to htmd IPC protocol(top 24 of 103)
9,803 followers · starred Aug 2023
1,019 followers · starred Aug 2025
333 followers · starred May 2025
0 followers · starred Feb 2024
Re-Connectable secure remote shell
See the codeEternal Terminal is a remote shell that automatically reconnects without interrupting the session.
Website: https://mistertea.github.io/EternalTerminal/.
The easiest way to install etis by using Homebrew:
brew install et
If the install fails on including csignal, see https://github.com/MisterTea/EternalTerminal/issues/662#issuecomment-2408889829
Then if you want a daemon to launch etserver on every boot:
On m1 (Apple Silicon) Macs:
sudo sed 's:/usr/local/bin/etserver:/opt/homebrew/bin/etserver:g' ../init/launchd/homebrew.mxcl.et.plist | sudo tee /Library/LaunchDaemons/homebrew.mxcl.et.plist
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
On x86 Macs:
sudo cp ../init/launchd/homebrew.mxcl.et.plist /Library/LaunchDaemons/homebrew.mxcl.et.plist
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
Alternatively, a package is available in MacPorts:
sudo port install et
For Ubuntu, use our PPA:
sudo add-apt-repository ppa:jgmath2000/et
sudo apt-get update
sudo apt-get install et
Or see "Debian/Ubuntu" below to install and build from source (e.g., for ARM).
For Debian, use our deb repo:
echo "deb [signed-by=/etc/apt/keyrings/et.gpg] https://mistertea.github.io/debian-et/debian-source/ $(grep VERSION_CODENAME /etc/os-release | cut -d= -f2) main" | sudo tee -a /etc/apt/sources.list.d/et.list
sudo mkdir -m 0755 -p /etc/apt/keyrings # only if you're using Debian 11 or older
curl -sSL https://github.com/MisterTea/debian-et/raw/master/et.gpg | sudo tee /etc/apt/keyrings/et.gpg >/dev/null
sudo apt update
sudo apt install et
Up to the present day the only way to install is to build from source.
sudo dnf install epel-release
sudo dnf install et
On FreeBSD, use:
pkg install eternalterminal
sudo dnf install et
zypper ar -f obs://network
zypper ref
zypper in EternalTerminal
Install dependencies:
Fedora (tested on 25):
sudo dnf install boost-devel libsodium-devel protobuf-devel \
protobuf-compiler cmake gflags-devel libcurl-devel
Gentoo:
sudo emerge dev-libs/boost dev-libs/libsodium \
dev-libs/protobuf dev-util/cmake dev-cpp/gflags
Download and install from source:
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
cmake ../
make
sudo make install
If you use flakes, you can import the bundled NixOS module and let it manage
the package, /etc/et.cfg, and the etserver service:
{
inputs.et.url = "github:MisterTea/EternalTerminal";
outputs = { nixpkgs, et, ... }: {
nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
et.nixosModules.default
({ ... }: {
services.eternalTerminal = {
enable = true;
openFirewall = true;
port = 2022;
settings.Networking.bind_ip = "0.0.0.0";
};
})
];
};
};
}
Eternal Terminal works under WSL (Windows Subsystem for Linux). Follow the ubuntu instructions.
See docker/README.md
Verify that the client is installed correctly by looking for the et executable: which et.
Verify that the server is installed correctly by checking the service status: systemctl status et. On some operating systems, you may need to enable and start the service manually: sudo systemctl enable --now et.
You are ready to start using ET!
An Eternal Terminal installation may provide these executables:
et is the command-line client. It uses SSH to authenticate and start the
remote session, then maintains the reconnectable ET connection.etserver is the system service that accepts ET client connections and
routes them to the correct user's session. Its default TCP port is 2022.etterminal is an internal server-side helper launched through SSH. Normal
users do not invoke it directly.htm is the foreground client for HTM, the bundled terminal multiplexer.
It speaks tmux control mode so compatible terminal emulators can show native
windows, tabs, and splits.htmd is the per-user HTM daemon. It owns persistent multiplexer sessions
and is started automatically by htm when needed.For the protocol-level relationship between et, etserver, and
etterminal, see the protocol documentation. For HTM, see
the HTM design documentation.
If you'd like to modify the server settings (e.g. to change the listening
port), edit /etc/et.cfg. On NixOS, set services.eternalTerminal.settings
instead so the generated /etc/et.cfg stays in sync with your system
configuration.
ET uses ssh for handshaking and encryption, so you must be able to ssh into the machine from the client. Make sure that you can ssh user@hostname.
ET uses TCP, so you need an open port on your server. By default, it uses 2022.
Once you have an open port, the syntax is similar to ssh. Username is default to the current username starting the et process, use -u or user@ to specify a different one if necessary.
et hostname (etserver running on default port 2022, username is the same as current)
et user@hostname:8000 (etserver running on port 8000, different user)
On macOS and Linux, et saves each direct session's reattachment credentials
in ~/.et/sessions (owner-only, plaintext), so after a client crash or reboot
you can return to a remote shell that is still running. Sessions get a
generated YYYYMMDD-xxxx name unless you pick one; --attach and --kill
accept a name or a unique substring of the name or terminal title. Port
forwards, agent forwarding, and jumphosts are not restored on attach.
et --name work hostname # start (or reattach to) a named session
et --list # list saved sessions without contacting servers
et --attach work # reattach after the client restarted
et --kill work # end the remote session and remove its record
et --no-persist hostname # do not write credentials to disk
You can specify a jumphost and the port et is running on jumphost using --jumphost and --jport. If no --jport is given, et will try to connect to default port 2022.
et hostname -jumphost jump_hostname (etserver running on port 2022 on both hostname and jumphost)
et hostname:8888 --jumphost jump_hostname --jport 9999
Additional arguments that et accepts are port forwarding pairs with --tunnel "18000:8000, 18001-18003:8001-8003" (or OpenSSH-style -L), and a command to run immediately after the connection is set up through --command or as a positional command after the host. Short flags match OpenSSH: -p is the sshd port, --port is the etserver port, and -t requests a pty.
Starting from the latest release, et supports parsing both user-specific and system-wide SSH config files. The config file is required when your sshd on server/jumphost is listening on a port which is not 22. Here is an example SSH config file showing how to setup when
Host dev
HostName 192.168.1.1
User fred
Port 5555
ProxyJump user@jumphost.example.org:22
With the ssh config file set as above, you can simply call et with
et dev (etserver running on port 2022 on both hostname and jumphost)
et dev:8000 -jport 9000 (etserver running on port 9000 on jumphost)
To isolate ET from ambient SSH configuration, pass an absolute path with
--ssh-config. ET reads only that file for its own destination and jumphost
lookup and passes the same file to every SSH process it starts, including the
implicit ProxyJump connection. The file must be readable, regular, and not a
symbolic link. The path must be absolute (/path on Unix; C:\path,
C:/path, or a UNC path on Windows) and may contain only ASCII letters, digits,
/, ., _, and - (plus : and \ on Windows). Spaces are not allowed:
OpenSSH does not quote this path when constructing its implicit ProxyJump
command. Pass --ssh-config none or the equivalent --no-ssh-config to
disable both user and system SSH configuration entirely. The two options are
mutually exclusive.
ET can stand in for ssh as the transport for VS Code's and Cursor's
Remote-SSH extensions, so a remote window rides out laptop sleep and network
changes instead of prompting you to reload. Both ends need a recent ET: the
client must include
#874, and the server's
etserver and etterminal must support et -T (the 7.0.0 packages do not).
ssh hostname must already work without a password prompt (use a key).
Point the editor at et1, which is installed next to et. It runs
et --close-on-hangup --disconnect-timeout=10080 "$@": the session is kept on
the server through a week-long disconnect (the value is in minutes) and ended
when the editor closes the connection. Remote-SSH only accepts a path to a
single executable, so if you need more et options (for example --port for
an etserver not on the default port), copy et1 and add them there.
For VS Code, add to settings.json (use the output of command -v et1 as the
path):
{
"remote.SSH.path": "/usr/local/bin/et1",
"remote.SSH.useLocalServer": false,
"remote.SSH.reconnectionGraceTime": 604800
}
remote.SSH.useLocalServer: false is required. In the default local-server
mode, VS Code kills the transport process a few seconds after it stops
responding, which throws away the ET client that would have reconnected.remote.SSH.reconnectionGraceTime (seconds) is how long the VS Code server
on the remote keeps your window's state while disconnected. The default is
three hours. It only takes effect when that server starts, so run
"Remote-SSH: Kill VS Code Server on Host..." after changing it.For Cursor, set remote.SSH.path to et1 as well. Cursor's Remote-SSH
extension has no local-server mode or grace-time setting; its closest
equivalent is remote.SSH.serverShutdownTimeout (seconds, default 300), which
controls how long the Cursor server stays up after the last client disconnects.
Normally et drives a terminal for a human. et --ctl instead backgrounds a
session with no local terminal and serves it on a per-user unix socket, and the
companion etctl command drives that session by name from a script or program.
You get a command's clean output and real exit code, instead of scraping a
rendered screen.
et --ctl --name main user@hostname # background a named session (idempotent via: etctl open main user@hostname)
etctl run main 'uname -a' # run a command; captured output + the real exit code
etctl read main # retained output (non-destructive; --cursor N to resume)
etctl expect main 'Password:' # wait for a prompt
etctl writeln main --secret # answer it without echoing the password
etctl key main eof # send Ctrl-D to end the session cleanly
etctl open is an idempotent wrapper over et --ctl. HOST comes first, and
any further et arguments follow it, so
etctl open main user@hostname --command '<cmd>' runs a startup command on
connect (for example, to drop into a bare, prompt-free shell for cleaner
capture). The session's remote shell is stamped with ETCTL_SESSION=<name>, so a
process can tell which named control session it is running under. The socket
lives at ~/.et/control/<name>.sock (0700 dir, 0600 socket, owning-uid
only), beside the session's saved record in ~/.et/sessions/<name>.
run frames a commandrun has to know where a command's output starts and ends and what it exited
with, and it has to inject the command safely (a multi-line body must run as one
command, and quotes/braces/!/parse errors must not desync the frame or hang on
a continuation prompt). It picks the cleanest of three framings for the far-side
prompt, detected once per session and cached (~/.et/control/<name>.framing):
C/D marks.eval "$(cat <<'BODY' ... )" -- so it is handed
to the shell as data, immune to its own syntax -- and boundaries come from OSC
133. No echo markers, but the wrapper is visible.echo <mark> ... <mark>:$?.run also trims the prompt-prep sequences a shell splices around the output (a
zsh/fish end-of-line mark, iTerm2's OSC 1337 context report, a title,
bracketed-paste toggles) so the captured output is the command's alone, and it
disables history expansion on the control session (a script-driven session has no
use for interactive !). Force a framing with --framing=osc133 (bracketed
paste) or --framing=mark (echo markers); the default is --framing=auto.
Run etctl with no arguments for the full verb list, etctl <verb> --help for
any verb's options, and etctl --version for the version. --ctl is not
available on Windows.
To build Eternal Terminal on Mac, install its dependencies with Homebrew:
brew install autoconf automake libtool
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
cmake ../
make -j$(nproc) && sudo make install
To run an et server for testing, run ./etserver. To run an et
server daemon persistently across reboots:
sudo cp ../init/launchd/homebrew.mxcl.et.plist /Library/LaunchDaemons
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.et.plist
Grab the deps and then follow this process.
Debian/Ubuntu Dependencies:
sudo apt install libsodium-dev autoconf libtool \
libprotobuf-dev protobuf-compiler libutempter-dev libcurl4-openssl-dev \
build-essential ninja-build cmake git zip pkg-config
Fetch source, build and install:
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
# For ARM (including OS/X with apple silicon):
if [[ $(uname -a | grep 'arm\|aarch64') ]]; then export VCPKG_FORCE_SYSTEM_BINARIES=1; fi
cmake -DCPACK_GENERATOR=DEB ../
make -j$(nproc) package
sudo dpkg --install *.deb
Once built, the binary only requires libprotobuf-dev.
Disable et server by sudo systemctl disable --now et
Install dependencies:
sudo yum install epel-release
sudo yum install cmake3 boost-devel libsodium-devel protobuf-devel \
protobuf-compiler gflags-devel protobuf-lite-devel libcurl-devel \
perl-IPC-Cmd perl-Data-Dumper libunwind-devel libutempter-devel
Install scl dependencies
sudo yum install centos-release-scl
sudo yum install devtoolset-11 devtoolset-11-libatomic-devel rh-git227
Download and install from source (see #238 for details):
git clone --recurse-submodules --depth 1 https://github.com/MisterTea/EternalTerminal.git
cd EternalTerminal
mkdir build
cd build
scl enable devtoolset-11 rh-git227 'cmake3 ../'
scl enable devtoolset-11 'make && sudo make install'
sudo cp ../systemctl/et.service /etc/systemd/system/
sudo cp ../etc/et.cfg /etc/
Find the actual location of et:
which etserver
Correct the service file (see #180 for details).
sudo sed -ie "s|ExecStart=[^[:space:]]*[[:space:]]|ExecStart=$(which etserver) |" /etc/systemd/system/et.service
Alternatively, open the file /etc/systemd/system/et.service in an editor and correct the ExectStart=... line to point to the correct path of the etserver binary.
ExecStart=/usr/local/bin/etserver --cfgfile=/etc/et.cfg --logtostdout
Reload systemd configs:
sudo systemctl daemon-reload
Start the et service:
sudo systemctl enable --now et.service
Builder Dockerfiles are located at deployment/. Supported OSes: CentOS 8, openSUSE and Ubuntu.
If you have any problems with installation or usage, please file an issue on GitHub.
Eternal Terminal uses three binaries:
et (client): Runs on the user's machine (client). Connects to a remote server over SSH to launch the terminal session, then connects to etserver on port 2022 for the persistent session.etterminal (server-side user process): Runs on the server as the user (launched by et via SSH). Hosts the terminal session and connects to etserver via a FIFO to register the session.etserver (server daemon): Runs permanently on the server (usually as root/system service). Listens on TCP port 2022 (default) and manages connections between et clients and etterminal sessions.Which machines need each part:
et.etserver (service/demon) and etterminal (installed for users; launched by et over SSH).Port: By default etserver listens on TCP 2022 (configured in /etc/et.cfg via the Networking.port setting).
Service setup: After installation, start/enable etserver via systemd (systemctl enable --now et) or launchd (macOS), or run ./etserver for testing.
htm to htmd IPC protocol(top 24 of 103)
9,803 followers · starred Aug 2023
1,019 followers · starred Aug 2025
333 followers · starred May 2025
0 followers · starred Feb 2024