ssh-obi
Developed by Human-life Information Platforms Institute (Menhera.org).
ssh-obi keeps SSH shells alive when the connection drops, when a laptop
sleeps, or when the client moves between networks.
It is designed to feel like plain SSH:
- Use the same destination names, keys, jump hosts, and SSH config you already use.
- Keep using your local terminal scrollback, search, selection, and copy/paste.
- Reconnect to the same remote shell after a network break.
- Keep several independent sessions on the same remote account.
- Install per user, without a root service or a custom network port.
ssh-obi is intentionally not a terminal window manager. It does not implement
panes, tabs, or in-band escape commands. If you want window management, run
tmux or another multiplexer inside the remote shell.
Status
ssh-obi v0.1.3 is the current release. It is available on crates.io,
tagged as v0.1.3 on GitHub, and distributed as release tarballs from
https://obi.menhera.org/.
The v0.1.3 release adds MOTD printing before new session shells start and
caps automatic reconnect retries with a small exponential backoff. The 0.1
wire protocol baseline remains unchanged.
The documentation on this site is the user-facing source for the published bootstrap scripts, release tarballs, install flow, and supported platforms.
Quick Examples
Connect to a host:
ssh-obi user@example.com
Create a new session even if free sessions already exist:
ssh-obi --new user@example.com
List existing sessions without attaching:
ssh-obi --list user@example.com
List sessions from inside the remote account:
ssh-obi-server --list
Detach the currently attached client for a known session from your local machine:
ssh-obi --detach --session abc123 user@example.com
Or detach from inside the remote shell without killing it:
ssh-obi-server --detach
What To Expect
- A network disconnect does not kill the remote shell.
- Remote output continues to be collected while you are detached.
- Recent output is replayed on reconnect.
- Some recently displayed output may appear twice after reconnect.
- New sessions show the remote host MOTD before the shell prompt, unless
~/.hushloginsuppresses it. - Windows is a client-only platform. Remote servers are Unix-like systems.
What To Read Next
- Getting Started for the shortest usable flow.
- Installation for Unix and Windows bootstrap commands.
- Connecting for commands and session selection.
- Sessions for detach, reconnect, replay, and exit behavior.
- Platforms and Downloads for supported systems and published tarball names.
- Changelog for release notes.
Getting Started
This page assumes you already have a local ssh-obi client. If you do not, see
Installation.
First Connect
Run:
ssh-obi user@example.com
The client starts the system ssh binary and prepares the remote side over the
same SSH connection. If a compatible server component is already installed, the
session starts immediately.
If a compatible server is already installed at ~/.ssh-obi/bin, at
~/.cargo/bin, or on the remote PATH, ssh-obi uses it. If the server
component is missing or incompatible and a prebuilt tarball exists for the
remote platform, ssh-obi asks before installing it into ~/.ssh-obi/bin on
the remote account. No root access is needed for the built-in install path.
After installation, ssh-obi attaches to a new or existing session.
When a new session is created, the remote host MOTD is printed before the shell
starts, unless the remote account has ~/.hushlogin.
Session Selection
When you connect, ssh-obi looks for sessions owned by the same remote user.
If no free session exists, a new session is created.
If exactly one free session exists, the client attaches to it automatically.
If multiple free sessions exist, the client prompts locally:
Select a session to attach:
# INIT DETACH WHAT
1 2026-05-01 09:14 2026-05-02 11:02 bash
2 2026-05-01 14:30 2026-05-02 09:55 vim notes.md
3 2026-05-02 10:11 2026-05-02 10:48 cargo watch
n (new session)
>
Busy sessions are shown by --list, but they are not selectable in the
interactive picker.
Detach Without Killing The Shell
From inside the remote shell:
ssh-obi-server --detach
This detaches the client. The shell keeps running. The local client exits with status 0 and does not reconnect.
Closing the laptop, losing Wi-Fi, or killing the local SSH connection is different: the client treats that as ambiguous and attempts to reconnect.
Reconnect Behavior
After the first successful attach, the client knows the session id. If the SSH connection disappears without a graceful detach or shell exit report, the client reconnects and asks for the same session.
If the old broker is still attached when reconnect starts, the reconnecting client asks that stale client to detach and then retries the attach.
Reconnect retries use short exponential backoff delays: 125ms, 250ms, 500ms, 1s, then 2s for later attempts. The client gives up after 10 reconnect attempts.
On reattach, recent output is replayed first, then live forwarding resumes. The replay buffer is bounded, so old history belongs in your local terminal scrollback.
Installation
ssh-obi publishes release tarballs at https://obi.menhera.org/. The
bootstrap scripts download those tarballs and install binaries into
~/.ssh-obi/bin on Unix-like systems or %USERPROFILE%\.ssh-obi\bin on
Windows.
The crate is also published on crates.io as ssh-obi.
Signature verification is not implemented for the MVP. The bootstrap trusts HTTPS.
Local Client Install From crates.io
If you already have a Rust toolchain, install the local client with:
cargo install ssh-obi
This installs the client binary, ssh-obi. It does not install the remote
server binary by default. For normal use, let the client bootstrap or update
the remote server component when you connect.
For remote platforms where no prebuilt server tarball is published, install a server-capable build in the remote account with:
cargo install --features server-bin ssh-obi
The attach bootstrap checks ~/.cargo/bin/ssh-obi-server directly, so this
works even when that directory is not on PATH.
OpenBSD does not have prebuilt ssh-obi release tarballs. On OpenBSD remote
hosts, install a Rust toolchain first, then run:
cargo install --features server-bin ssh-obi
After that, connect normally; the bootstrap will find the Cargo-installed
ssh-obi-server.
Unix Install
To preinstall or update a Unix-like account without starting a session:
wget -O - https://obi.menhera.org/bootstrap.sh | sh -s -- --install
The sh -s -- --install form is deliberate and portable:
-stells a POSIX shell to read the script from standard input.- The first
--ends shell option parsing. - The second
--installis passed to the bootstrap script.
Do not use sh - -- --install. Portable /bin/sh implementations treat the
first -- as a script filename, not as “read from stdin”.
The Unix installer:
- Detects the OS and CPU.
- Downloads
release-<target>.tar.gz. - Extracts
ssh-obi-serverand, when present,ssh-obi. - Installs binaries into
~/.ssh-obi/bin. - Adds that directory to shell startup files used by non-interactive SSH:
~/.bashrc,~/.zshenv, and fishconf.d. - Prints
OBI-INSTALL-COMPLETEin install-only mode.
The bootstrap does not edit ~/.bash_profile, because ssh-obi needs paths
available to non-interactive remote commands.
You can run the installer repeatedly to update the installed binaries. It will
not add duplicate ssh-obi PATH entries to shell startup files.
Install During Connect
Most users do not need to run the Unix bootstrap manually. The local client handles the remote check during normal connection:
ssh-obi user@example.com
If the remote server component is missing or incompatible, the local client asks for confirmation and installs it into that remote account.
During normal attach, the bootstrap probes for an existing compatible server in this order:
~/.ssh-obi/bin/ssh-obi-server~/.cargo/bin/ssh-obi-serverssh-obi-serverfound onPATH
The PATH probe supports distro-packaged installs. The direct Cargo path
supports remote platforms where cargo install --features server-bin ssh-obi
is the practical server install path.
Windows Client Install
Windows can run the client only. It cannot run the remote server.
PowerShell is preferred:
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://obi.menhera.org/bootstrap.ps1 | iex"
cmd.exe cannot execute a batch file directly from an HTTPS URL. Download the
batch file, then run it:
curl.exe -fsSL -o "%TEMP%\ssh-obi-bootstrap.bat" https://obi.menhera.org/bootstrap.bat && "%TEMP%\ssh-obi-bootstrap.bat" --install
Both Windows bootstraps:
- Support x86_64 Windows.
- Download
release-x86_64-pc-windows-msvc.tar.gz. - Install
ssh-obi.exeinto%USERPROFILE%\.ssh-obi\bin. - Add that directory to the user’s PATH.
- Print
OBI-INSTALL-COMPLETE. - Never start a server.
Restart the terminal if ssh-obi.exe is not found immediately after PATH
updates.
Windows Terminal is recommended for interactive use. ssh-obi.exe enables
Windows virtual terminal input while attached, so arrow keys and other
line-editing keys are forwarded to the remote shell correctly in terminals that
support that console mode.
You can run either Windows bootstrap repeatedly to update ssh-obi.exe. The
installer only adds the install directory to the user PATH when it is not
already present.
Manual Install From A Tarball
Manual install is also possible:
mkdir -p "$HOME/.ssh-obi/bin"
gzip -dc release-x86_64-unknown-linux-musl.tar.gz | tar -xf - -C "$HOME/.ssh-obi/bin"
For Unix server-capable targets, the tarball contains:
LICENSE-APACHELICENSE-MPLssh-obissh-obi-server
For Windows client-only targets, the tarball contains:
LICENSE-APACHELICENSE-MPLssh-obi.exe
Uninstall
Remove the install directory:
rm -rf "$HOME/.ssh-obi"
Then remove the ssh-obi PATH lines from any shell startup files that the
installer updated.
On Windows, delete %USERPROFILE%\.ssh-obi and remove that directory from the
user PATH.
Connecting
Basic Commands
Attach to an existing free session or create one if none exists:
ssh-obi user@example.com
Always create a new session:
ssh-obi --new user@example.com
Attach to a specific session:
ssh-obi --session abc123 user@example.com
List sessions and exit:
ssh-obi --list user@example.com
List sessions directly on the server host:
ssh-obi-server --list
Detach the attached client for a specific session and exit:
ssh-obi --detach --session abc123 user@example.com
Detach from inside the remote shell:
ssh-obi-server --detach
Using SSH Options
ssh-obi invokes the system ssh binary and forwards common OpenSSH options
before the destination.
Examples:
ssh-obi -p 2222 user@example.com
ssh-obi -i ~/.ssh/work_ed25519 user@example.com
ssh-obi -J bastion.example.com user@app.example.com
ssh-obi -F ~/.ssh/config work-host
ssh-obi -o StrictHostKeyChecking=accept-new user@example.com
ssh-obi -vv user@example.com
Common passthrough options include:
- Standalone flags such as
-4,-6,-A,-a,-C,-q,-T,-t,-X,-x,-Y, and-v/-vv/-vvv. - Options with values such as
-p,-i,-J,-F,-o,-l,-L,-R,-D, and-W.
ssh-obi manages the remote command itself. Remote command arguments are not
supported:
ssh-obi user@example.com uptime
Use plain ssh for one-shot remote commands.
Picking A Session
Free sessions are selectable. Busy sessions may be displayed for awareness but are not assigned picker numbers.
Columns:
INIT: when the session was created.DETACH: when a client last detached, or-if it has never detached.WHAT: best-effort foreground command detected for the session.
The WHAT column is only a hint.
New sessions print the remote host MOTD before the shell starts. The MOTD
sources are /run/motd.dynamic, /etc/motd, and readable non-empty files in
/etc/motd.d/. Create ~/.hushlogin on the remote account to suppress this
MOTD output.
ssh-obi-server --list is the local server-host equivalent. It lists all alive
sessions for the current remote user, free or busy, and includes session IDs.
When run from inside an ssh-obi session, the current session is marked with
* in the CUR column. When run outside an ssh-obi session, no row is
marked current.
When The Remote Shell Exits
If the remote shell exits while attached, the local client mirrors the exit status where possible.
If the connection is lost before the client receives a shell-exit report, the client reconnects first. If the session is gone or no exit status can be recovered, the client reports failure rather than guessing success.
When an automatic reconnect targets a known session and that session still
reports an attached client, ssh-obi sends a detach request for that same
session and retries the attach. Normal first-time attach attempts do not detach
busy sessions automatically.
Reconnect retries use 125ms, 250ms, 500ms, 1s, and then capped 2s delays. The client gives up after 10 attempts.
A deliberate detach through ssh-obi-server --detach is graceful. The client
exits with status 0 and does not reconnect.
Sessions
An ssh-obi session is one long-lived remote shell. The session can outlive
many SSH connections.
Creating Sessions
Use ssh-obi user@host to attach to a free session or create one when none is
available.
Use ssh-obi --new user@host to always create a new session.
Use ssh-obi --session ID user@host to attach to a specific session.
New sessions start the remote user’s shell as a login shell, using the usual
leading-dash argv[0] convention such as -bash or -zsh. This lets shell
startup behavior match interactive SSH more closely.
Before the shell starts, new sessions print the remote host MOTD. ssh-obi
prints readable non-empty /run/motd.dynamic and /etc/motd files, followed
by readable non-empty files in /etc/motd.d/ in filename order. A
~/.hushlogin file in the remote user’s home directory suppresses this MOTD
output.
New sessions also start in the remote user’s home directory. TERM is
forwarded from the local client when it is useful; if it is missing or dumb,
ssh-obi uses xterm-256color.
When both sides support initial-window-size.v1, the client sends the current
terminal size before creating or attaching to a session. New remote PTYs start
with that size, and reattaches apply the size before replaying buffered output.
Busy Sessions
A session can have only one attached client. If another client is already attached, the session is busy.
Busy sessions are still visible in --list. You can also ask a known busy
session to detach its current client:
ssh-obi --detach --session ID user@host
On the server host itself, ssh-obi-server --list lists all alive sessions for
the current Unix user. If it is run inside an ssh-obi session, that session is
marked in the CUR column. If it is run outside an ssh-obi session, no
session is marked current.
During automatic reconnect, ssh-obi already knows the session it is trying to
recover. If that session is still marked busy because the previous broker has
not fully gone away, the reconnecting client asks the stale attached client to
detach and then retries the attach. Reconnect retries use capped exponential
backoff: 125ms, 250ms, 500ms, 1s, then 2s for later attempts, with a maximum of
10 attempts.
Detach
Detach means “drop the client, keep the shell”.
When the network drops, the remote shell keeps running and waits for another client.
When the user runs:
ssh-obi-server --detach
the local client exits cleanly and does not reconnect.
The shell is not sent SIGHUP.
Output While Detached
Remote output continues to be collected while detached.
This prevents commands that print output from getting stuck just because no client is attached.
Detached output is kept in a bounded replay buffer. When the buffer fills, old bytes are evicted.
Reconnect And Replay
After the first attach, the client knows the session id. On an ambiguous disconnect, it starts a fresh SSH connection and requests that same session.
If that reconnect attempt finds the session busy, the client sends a detach control request for the same session and retries. Manual first-time attaches still report a busy session rather than detaching another client automatically. Reconnect retries stop after 10 attempts.
The session sends recent output, then resumes live forwarding. This can duplicate bytes the local terminal already displayed before the disconnect. That is acceptable and expected.
Anything older than the replay buffer belongs in the local terminal scrollback.
Shell Exit
When the shell exits, the session ends. A later ssh-obi user@host invocation
will not list that session.
Platforms and Downloads
Local Client Platforms
Supported local client platforms:
- Linux
- macOS
- FreeBSD, NetBSD, and illumos where release artifacts are published
- Windows x86_64
The client requires a working system ssh binary.
On Windows, use Windows Terminal or another console that supports Windows virtual terminal input. This lets special keys such as arrows, Home/End, and other line-editing keys reach the remote shell as normal terminal escape sequences.
Remote Server Platforms
Supported remote server platforms:
- Linux
- macOS
- FreeBSD
- OpenBSD when installed from Cargo or a distro package
- NetBSD
- illumos
- Other Unix-like systems where
ssh-obi-servercan be built from source and installed with Cargo or a distro package.
The server component is not supported on Windows.
systemd-based Linux distributions are supported, but systemd is not required. No system-wide service install is required. On Linux systems with systemd and a user bus, new PTY children are placed into a transient user scope on a best-effort basis; non-systemd Linux systems continue without that step.
Downloads
Release files are served from https://obi.menhera.org/.
The tarball list below is the prebuilt artifact set. Some Unix-like platforms,
such as OpenBSD, are intentionally supported through a locally installed
ssh-obi-server rather than a prebuilt tarball. Install with Cargo or a distro
package, then connect normally; the bootstrap will use a compatible server from
~/.cargo/bin/ssh-obi-server or from PATH.
OpenBSD has no prebuilt binaries. On OpenBSD, install a Rust toolchain and run:
cargo install --features server-bin ssh-obi
Server-capable tarballs:
release-x86_64-unknown-linux-musl.tar.gzrelease-aarch64-unknown-linux-musl.tar.gzrelease-riscv64gc-unknown-linux-musl.tar.gzrelease-powerpc64le-unknown-linux-musl.tar.gzrelease-s390x-unknown-linux-musl.tar.gzrelease-x86_64-apple-darwin.tar.gzrelease-aarch64-apple-darwin.tar.gzrelease-x86_64-unknown-freebsd.tar.gzrelease-x86_64-unknown-netbsd.tar.gzrelease-x86_64-unknown-illumos.tar.gz
Client-only tarballs:
release-x86_64-pc-windows-msvc.tar.gz
Server-capable tarballs contain ssh-obi and ssh-obi-server. Windows
client-only tarballs contain ssh-obi.exe.
Out Of Scope
- Android.
- Windows remote servers.
- Terminal pane/window management.
- UDP transport.
- Screen-state replication.
Troubleshooting
ssh-obi cannot find ssh
Install OpenSSH client tools and make sure ssh is on PATH. ssh-obi uses the
system ssh binary and does not include its own SSH implementation.
Remote install reports unsupported target
The Unix bootstrap chooses a tarball from uname -s and uname -m. If it
prints an unsupported target error, no release tarball is currently published
for that OS/CPU pair.
Use Platform Support to check the published target list.
If the platform can build Rust code, install the server on the remote account instead:
cargo install --features server-bin ssh-obi
The bootstrap probes ~/.cargo/bin/ssh-obi-server directly and also accepts a
compatible ssh-obi-server on PATH, so Cargo-installed and distro-packaged
servers can be used without a prebuilt tarball.
Windows install succeeds but ssh-obi.exe is not found
The Windows bootstrap updates the user’s PATH. Existing terminals may not see that change.
Open a new terminal and try:
ssh-obi.exe --help
Arrow keys do not work from Windows
Use Windows Terminal or another console with Windows virtual terminal input
support. While attached, ssh-obi.exe enables that mode so special keys are
sent to the remote PTY as escape sequences.
To check whether key bytes are reaching the remote shell, run:
cat -v
Then press Up. A working Windows client should print something like ^[[A.
Press Ctrl-C to leave cat.
The picker shows (unknown) in WHAT
The WHAT column is best-effort. It depends on the remote OS and process table
details. Failure to detect the foreground command does not affect session
correctness.
ssh-obi deliberately avoids wrapping or instrumenting your shell to improve
this field.
Output repeats after reconnect
This is expected. Reattach replays the session’s current bounded buffer before
live forwarding resumes. ssh-obi does not maintain a per-client replay
cursor.
Old output is missing after reconnect
The session replay buffer is bounded. Old output belongs in the local terminal scrollback.
Typing feels softer than plain SSH
ssh-obi is designed to stay close to plain SSH, but the interactive path is
not byte-for-byte identical to an OpenSSH client attached directly to a remote
PTY.
Input travels through the local ssh-obi client, the system ssh process, the
remote broker, a Unix-domain socket, and the per-session daemon before it reaches
the remote PTY. Output returns through the same layers in reverse. Each layer
flushes promptly, and there is no intentional long sleep in the hot path.
On Unix clients, ssh-obi does intentionally coalesce immediately pending
stdin bytes for a very short window before framing and sending them. This keeps
paste and bursty input from becoming one protocol frame per byte, but it can add
up to about 2 ms before an isolated keystroke is sent. That small batching
window, plus normal SSH, network, scheduler, and terminal latency, can feel a
little softer than direct SSH on very low-latency links.
This behavior is a latency/throughput tradeoff, not a correctness issue. It does not change the bytes the remote shell receives, introduce in-band escape commands, or change reconnect/replay semantics.
Reconnect eventually gives up
Automatic reconnect uses capped exponential backoff: 125ms, 250ms, 500ms, 1s,
then 2s for later attempts. After 10 failed reconnect attempts, ssh-obi
reports failure instead of retrying forever.
A session is busy
A session can have only one attached client. If another client is already
attached, a second attach attempt gets SessionBusy.
During automatic reconnect, ssh-obi treats SessionBusy specially only for
the exact session it is trying to recover: it asks the stale attached client to
detach and then retries. Manual attaches still leave the busy session alone.
Use:
ssh-obi --list user@example.com
to inspect sessions, or:
ssh-obi --detach --session ID user@example.com
to ask the session to detach the attached client for a known session.
MOTD is shown when a session starts
New sessions print the remote host MOTD before the shell starts. This includes
readable non-empty /run/motd.dynamic and /etc/motd files, plus readable
non-empty files in /etc/motd.d/.
To suppress this output for the remote account, create:
touch ~/.hushlogin
A deliberate detach reconnects unexpectedly
Use the in-session helper:
ssh-obi-server --detach
This asks the remote session to detach and causes the client to exit gracefully. Simply closing the terminal, killing SSH, or losing the network is ambiguous, so the client reconnects.
PowerShell bootstrap cannot run
Use a command that bypasses the current process policy:
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://obi.menhera.org/bootstrap.ps1 | iex"
If that is blocked by local policy, download the script and inspect it before running it in an allowed shell.
Changelog
v0.1.3
Released for the 0.1.x protocol line.
Added
- New sessions print the remote host MOTD before the shell starts, using
/run/motd.dynamic,/etc/motd, and readable non-empty files under/etc/motd.d/. A user~/.hushloginsuppresses this MOTD output.
Changed
- Automatic reconnect retries now use a small capped exponential backoff: 125ms, 250ms, 500ms, 1s, then 2s, with a maximum of 10 attempts.
Notes
- The protocol baseline remains
0.1.
v0.1.2
Released for the 0.1.x protocol line.
Added
- On Linux systems booted with systemd and a user bus, newly spawned PTY
children are moved into a transient user scope before exec. This follows the
same systemd
StartTransientUnitshape used by tmux >= 3.2 for cgroup detaching, and falls back cleanly when systemd is unavailable. - During automatic reconnect, if the target session still reports
SessionBusy, the client asks the stale attached client to detach and then retries the reconnect. - The Unix bootstrap reports an explicit OpenBSD message when no compatible
server is already installed. OpenBSD has no prebuilt release tarballs; install
a Rust toolchain and run
cargo install --features server-bin ssh-obi. ssh-obi-server --listlists currently alive sessions for the remote user on the server host, including busy sessions and a marker for the current session when run inside anssh-obisession.
Fixed
- OpenBSD install-only bootstrap runs now complete successfully when a
compatible
ssh-obi-serveralready exists at~/.cargo/bin/ssh-obi-serveror onPATH.
Notes
- The protocol baseline remains
0.1. - Non-systemd Linux systems and non-Linux Unix systems do not require any systemd tooling.
v0.1.1
Released for the 0.1.x protocol line.
Added
- The client sends an initial terminal window size before attach and new-session
requests when the remote server supports
initial-window-size.v1. - New sessions create the remote PTY with the local terminal size when that size is available.
- Reattaches apply the local terminal size before replaying buffered output.
- The Unix bootstrap detects compatible
ssh-obi-serverbinaries in three places before trying a tarball install:~/.ssh-obi/bin/ssh-obi-server,~/.cargo/bin/ssh-obi-server, andssh-obi-serverfound onPATH.
Notes
- The protocol baseline remains
0.1;initial-window-size.v1is negotiated as a capability. - Platforms without a published release tarball can still be used as remote
servers when a compatible
ssh-obi-serveris installed by Cargo or a distro package.
v0.1.0
Initial public release.
Added
- Local
ssh-obiclient and Unix remotessh-obi-server. - Per-user long-lived remote sessions.
- Attach, new-session, list, and detach commands.
- Reconnect to a known session after ambiguous disconnects.
- Bounded replay of recent output on reattach.
- Shell exit status forwarding.
- Unix bootstrap install flow and Windows client-only bootstrap flow.
Developer Docs
This chapter is for contributors and release maintainers. The rest of this book is intentionally user-facing.
Repository Commands
Build the client:
cargo build --release
Build both client and server:
cargo build --release --features server-bin --bins
Run tests:
cargo test --features server-bin
Run clippy:
cargo clippy --all-targets --features server-bin -- -D warnings
Format:
cargo fmt --all
Documentation Site
The site is generated from docs-src/ with mdBook and published from docs/.
Use:
./build-docs.sh
Do not run mdbook build directly for published updates. mdBook overwrites
docs/, so build-docs.sh copies every non-mdBook artifact needed by GitHub
Pages after each build:
bootstrap.shbootstrap.batbootstrap.ps1.nojekyll- release tarballs found in known release output locations
Release Builds
The current release is v0.1.3. It is published to crates.io and tagged on
GitHub as v0.1.3. Release tarballs for the bootstrap installers are served
from https://obi.menhera.org/.
Use:
./build-release.sh
Default build routing:
cross-rs: x86_64/aarch64 Linux musl, FreeBSD, illumos, NetBSD.cargo-zigbuildpluszig: Darwin, riscv64 Linux musl, powerpc64le Linux musl.cargo +nightly zigbuild -Z build-std=std,panic_abort: s390x Linux musl.cargo-xwin: Windows x86_64 MSVC client.
Each target gets its own Cargo target directory under target/release-build/.
This prevents host-side Cargo build-script executables from being reused across
build environments with different libc baselines.
NetBSD uses a tiny target-only libexecinfo fallback because the cross-rs
NetBSD image currently lacks that target library.
Windows uses the MSVC target because it produces a smaller and more compatible client binary than the GNU target in this release flow.
Useful environment variables:
CROSS: cross-rs command, defaultcross.CARGO_ZIGBUILD: cargo-zigbuild command, defaultcargo-zigbuild.CARGO_XWIN: cargo-xwin command, defaultcargo-xwin.OUT_DIR: output directory for tarballs, default current directory.RELEASE_TARGET_ROOT: Cargo target root, defaulttarget/release-build.CROSS_SERVER_TARGETS: override cross-rs server target list.ZIGBUILD_SERVER_TARGETS: override zigbuild server target list.CLIENT_ONLY_TARGETS: override client-only target list.ZIGBUILD_BUILD_STD_TOOLCHAIN: nightly toolchain for build-std targets, defaultnightly.LLVM_LIB: explicitllvm-libpath for MSVC C build steps.
High-Level Architecture
There are three remote process roles:
- Daemon: long-lived process that owns one PTY and one shell.
- Broker: short-lived process started by SSH to connect the client to a daemon.
- Detach helper: short-lived process run inside the remote shell by
ssh-obi-server --detach.
The local client starts the system ssh binary, sends the bootstrap as the
remote command, waits for OBI-SERVER-READY, negotiates capabilities, selects
or creates a session, and then forwards terminal bytes.
The daemon must keep reading from the PTY while detached. Stopping reads can fill the kernel PTY buffer and block remote commands that print output.
Protocol Notes
Frames use:
msg_type: u8
flags: u8
length: u32, big endian
payload: length bytes of CBOR
Maximum payload length is 1 MiB. Unknown message types within the size limit are skipped silently.
Feature negotiation is capability-based. Existing capability message formats are frozen once released; breaking changes require new capability names.
Current capability names include:
pty.v1replay.v1detach.v1session-list.v1exit-code.v1initial-window-size.v1
initial-window-size.v1 lets the client send terminal dimensions before an
attach or new-session request. The broker applies the size before replay on
reattach, and new daemons create their PTY with that size when possible. The
protocol baseline remains 0.1.
MOTD output is local to daemon startup and does not require a wire capability.
The daemon collects readable non-empty /run/motd.dynamic, /etc/motd, and
sorted /etc/motd.d/* files unless ~/.hushlogin exists, then writes those
bytes to the PTY child before shell exec.