AetherShell keeps your interactive shell alive across SSH disconnects. Because
it holds your live terminal — keystrokes, command output, and any secrets that
pass through it — its security properties matter. This document states what
AetherShell does and does not protect against, how to deploy it safely, and its
known limitations. A full adversarial review and its reproducible test harness
live under security-review/.
aetherdis a per-user daemon. Each user runs their own instance; there is no shared/root daemon.- It listens on a Unix-domain socket at
$XDG_RUNTIME_DIR/aethershell/sock(typically/run/user/<uid>/aethershell/sock), falling back to~/.aethershell/sock. - Sessions are PTY-backed
/bin/bashprocesses. The daemon holds the PTY master so the shell survives client disconnects. - The
aetherclient connects to the socket and streams terminal I/O.
The security boundary is the Unix-domain socket, protected by three layers:
- The runtime directory is mode
0700, owned by the user. - The socket file is mode
0600, owned by the user. - The daemon enforces
SO_PEERCRED: it rejects any connection whose peer uid is not the daemon's own uid, regardless of file permissions.
Layer 3 is defense in depth added after the security review: even if the socket
or directory mode is loosened (a shared XDG_RUNTIME_DIR, an odd home mount, a
misconfiguration, or a future regression), a different local user still cannot
list, attach to, inject into, or kill your sessions.
- Another local user reading, hijacking, injecting into, or destroying your sessions. Blocked by all three layers above.
- Terminal escape injection through session metadata. Session names, working
directories, command lines, and remote-host labels are derived from untrusted
sources (
/proc, utmp). They are sanitized of control bytes before being rendered byaether --listand the chooser, so a directory or file with a booby-trapped name cannot inject escape sequences into your terminal when you list or choose sessions. - Malformed / hostile clients: bad JSON and unknown requests are rejected; oversized frames are bounded; a connection that never completes the handshake is dropped after 15s; the number of concurrent connections is capped.
- PID reuse after a hot-upgrade: the daemon records each shell's process start-time and verifies it before treating a PID as the session's shell or signalling it, so it never kills an unrelated same-user process whose PID was recycled.
- Command injection via the connect wrapper: the
client_idpassed to the remote login command is shell-quoted safely.
- Root, or any code already running as your user. Anyone who can run code as
you can read your
/proc, your socket, and your terminals by definition. AetherShell adds no new privilege boundary against yourself; findings that require same-uid code execution are robustness/safety issues, not isolation breaks. - A compromised terminal emulator. AetherShell strips control bytes from the metadata it renders, but it cannot fix a terminal that mishandles sequences in the live PTY stream (which is, by design, passed through raw).
client_idconfidentiality.client_idis a routing hint, not a secret. It is passed on the SSH remote command line (visible in/proc/<pid>/cmdlineto other local users whenhidepid=0) and exported into the session environment. Anyone who can already connect to your socket and learns aclient_idcan reattach as that client. Authentication is theSO_PEERCREDuid check, notclient_id.- Network confidentiality/integrity. AetherShell does not transport anything over the network itself — it relies entirely on your SSH/Tailscale SSH transport for that. Use a properly configured SSH server.
- Persistence of secrets in memory. Up to 1 MiB of recent terminal output per session is held in daemon memory for scrollback replay (never written to disk). Treat a long-running daemon's memory as containing recent terminal contents.
- Prefer
XDG_RUNTIME_DIR. Run the daemon under a real user session so the socket lives in/run/user/<uid>(mode0700, managed by systemd-logind). The home-directory fallback works but depends on home-directory permissions. - Do not share
XDG_RUNTIME_DIRbetween users. TheSO_PEERCREDcheck will still reject cross-user connections, but a shared runtime dir is a smell. - Keep
/prochardened. Mounting/procwithhidepid=2reduces what other local users can learn about your processes (includingclient_idon the SSH command line). This is a host setting, not something AetherShell controls. - Understand the login hook's blast radius.
install.shinstalls/etc/profile.d/aether.sh, which routes every user's remote interactive SSH login throughaether, and runssystemctl --global enable. This is a box-wide change. Ifaether/aetherdmisbehave, interactive SSH logins could be affected. Recovery options (any of):- non-interactive access bypasses the hook:
ssh host bash -l - per-user disable:
touch ~/.aethershell/disabled - per-session disable:
AETHER_DISABLE=1 - remove the hook:
rm /etc/profile.d/aether.sh(or./install.sh --uninstall)
- non-interactive access bypasses the hook:
- Review before enabling for all users. For a multi-user box, consider
installing with
--connector-onlyon clients and enabling the hook selectively rather than globally.
- The PID-reuse guard verifies start-time only when it was recorded. A restore
state hand-crafted to omit it skips the check — but crafting that state already
requires running
aetherd --restoreas the user, i.e. same-uid code execution. kill_all,restart_all, andupgradeare available to any client that passes the uid check (i.e. you). There is no second confirmation at the protocol layer; the chooser confirms destructive actions in the UI.- AetherShell is Linux-only (it relies on
/proc, utmp, andSO_PEERCRED).
Please open a private report via the repository's security advisory feature, or
contact the maintainer directly. Include reproduction steps; the harness under
security-review/ is a good starting point.