A terminal session daemon that keeps your shell alive when you disconnect. Like tmux or screen, but without the built-in window manager — panes and tabs are left to your terminal emulator.
Run rif dev to create a session named “dev”. Detach with Ctrl+\. Reattach later with rif dev — terminal state is restored exactly where you left off.
Why
Terminal multiplexers bundle two unrelated concerns: session persistence and window management. If you already use a capable terminal emulator (WezTerm, Kitty, Alacritty), you don’t need tmux’s panes and tabs — you need a daemon that keeps your shell running.
rif is that daemon. It does one thing: persistent sessions. Your terminal emulator handles the rest.
Usage
rif <session> Attach to (or create) a session
rif attach <session> Same as above
rif attach -d <session> Create session without attaching
rif new <session> Same as attach -d
rif list [-s] List sessions (-s for short/scriptable format)
rif run <session> <cmd...> Run a command in a session (-d for detached, --fish)
rif send <session> <text> Send keystrokes to a session
rif print <session> <text> Inject text into session display
rif write <session> <path> Write stdin to a file via the session
rif tail <name>... Follow session output in real-time
rif history <session> Print session output (--vt, --html)
rif detach [<session>] Detach all clients
rif kill <name>... [-f] Kill sessions (-f for SIGKILL)
rif wait <name>... Wait for sessions to complete
rif completions <shell> Print completions (bash, zsh, fish)
All subcommands have short aliases: a, n, r, s, p, wr, t, hi, d, k, w, l/ls, c, v, h.
Workflows
Basic session persistence:
1rif dev # create or reattach to "dev"
2# ... work ... Ctrl+\ to detach
3rif dev # pick up where you left off
Background tasks:
1rif run -d build make -j8 # run in the background
2rif tail build # follow output
3rif wait build # block until it finishes
4echo $status # exit code is forwarded
Multi-pane projects — use your terminal emulator for splits, rif for persistence:
1# WezTerm
2wezterm cli split-pane -- rif attach project.2
3rif attach project.1
4
5# Kitty
6kitty @ launch --type=window --cwd=current rif attach project.2
7rif attach project.1
SSH:
1ssh -t server rif attach dev # attach to a remote session
Glob matching — kill, tail, and wait support prefix globs:
1rif kill 'dev*' # kill all sessions starting with "dev"
2rif tail 'build*' # follow output from multiple sessions
Environment
| Variable | Description |
|---|---|
RIF_SESSION | Set inside sessions to the current session name |
RIF_SESSION_PREFIX | Prefix applied to session names (for grouping) |
RIF_SHELL | Override the shell to spawn (default: $SHELL) |
RIF_DIR | Override the socket directory |
Architecture
A background daemon owns a PTY and a shell. Clients connect via Unix domain sockets. The daemon runs a single poll() loop over: signal pipe, server socket, PTY master, and all client fds. Terminal state is tracked with a vt100 parser and replayed on reattach.
Sessions communicate over a binary protocol — 5-byte header (1 tag + 4 LE length) followed by payload. Daemonization uses a single fork with setsid() and /dev/null redirection. Signals (SIGCHLD, SIGTERM, SIGWINCH) are handled via the self-pipe trick to stay async-signal-safe.
The detach key is Ctrl+\ (0x1c). VQUIT is disabled in raw mode so the byte reaches the client instead of generating SIGQUIT.