moshpit
An SSH and Mosh inspired tool written in Rust.
Overview
moshpit is a suite of tools for establishing encrypted, resilient remote terminal sessions:
| Binary | Crate | Role |
|---|---|---|
mps |
moshpits | Server — listens for incoming connections and spawns PTYs |
mp |
moshpit | Client — connects to a running mps server |
mp-keygen |
moshpit-keygen | Key management — generates and inspects ed25519 key pairs |
Sessions are authenticated with ed25519 key pairs. TCP is used only for the initial key exchange; once the exchange completes the connection switches to UDP exclusively (ports 50000–59999) for all terminal I/O. The server tracks full terminal screen state with a server-side vt100 emulator; on reconnect the client receives a single clean screen snapshot and repaints instantly rather than replaying raw scrollback history.
Inspiration & Relation to Mosh
moshpit draws its core motivation from Mosh (Mobile Shell), the excellent remote terminal tool created by Keith Winstein and colleagues at MIT. Mosh demonstrated that a UDP-based transport with graceful handling of packet loss, reordering, and IP roaming could make remote terminal sessions feel dramatically more responsive and reliable than traditional SSH — particularly over high-latency or intermittent connections. moshpit was created as an exercise in rebuilding that idea from scratch in Rust, exploring different design trade-offs along the way.
What moshpit shares with Mosh
- UDP terminal data channel — terminal I/O is carried over UDP rather than a reliable stream, allowing the session to survive network interruptions without blocking on TCP retransmit timeouts.
- Resilience to connectivity loss — both tools keep the session alive across short network outages and IP address changes; the client reconnects automatically without user intervention.
- Authenticated encryption — all data on the wire is encrypted and authenticated; neither tool relies on a plain-text transport at any layer.
- Client / server split — a lightweight server component (
mps/mosh-server) runs on the remote host and manages the PTY; a client (mp/mosh) runs locally and drives the terminal. - Server-side screen state — the server maintains a vt100 model of the current PTY screen; on reconnect the client receives a single screen snapshot for an instant, noise-free repaint.
- Client-side prediction — keystrokes are echoed locally and cursor movement is predicted to hide round-trip latency; predicted characters are underlined until the server confirms them.
Where moshpit differs
| Concern | Mosh | moshpit |
|---|---|---|
| Language | C++ | Rust |
| Authentication | Delegated to SSH for the initial handshake; a one-time secret is passed back over SSH | Standalone ed25519 key-pair authentication — no SSH dependency |
| Transport model | Pure UDP after setup; Mosh's State Synchronization Protocol (SSP) keeps a diff of the full terminal screen state and sends only the latest snapshot | TCP is used solely for the ed25519 key exchange; all terminal I/O runs over UDP after the exchange completes. NAK-based selective retransmission ensures reliable, ordered delivery of the raw byte stream |
| Reconnect display sync | SSP sends the latest screen snapshot; client repaints from the diff immediately | Server maintains a vt100::Parser tracking the live PTY screen; on reconnect a single ScreenState frame delivers contents_formatted() bytes for an instant clean repaint. A 50 ms periodic task also sends ScreenState diffs during normal use so the client stays in sync even across network hiccups. |
| Client-side prediction | Mosh echoes keystrokes locally and predicts cursor movement to hide latency, underlining characters that have not yet been confirmed by the server | Same — keystrokes are echoed locally, cursor movement is predicted, and unconfirmed characters are underlined until the server output arrives |
| Encryption | AES-128-OCB authenticated encryption using a symmetric session key | Key exchange via an ed25519-based handshake; symmetric AES-256-GCM-SIV on the UDP channel with per-packet HMAC-SHA-512 authentication |
| Session multiplexing | One Mosh session per mosh-server process |
Same — one PTY per mps connection |
| Configuration | Minimal; primarily driven by command-line options | TOML config files with environment-variable overrides |
| UDP port range | 60001–61000 (by default) | 50000–59999 |
| License | GPL v3 | Apache 2.0 / MIT (your choice) |
Attribution: the name moshpit is a deliberate nod to Mosh, whose design and published research were a direct inspiration for this project. If you need production-grade, battle-tested remote terminal software, use Mosh. moshpit is an independent reimagining with different goals and trade-offs.
Connection model
Phase 1 — TCP key exchange
The client opens a TCP connection to the server's configured port (default 40404). The two sides run a mutual ed25519 key-pair authentication and key-exchange protocol over this connection. Once the handshake completes both halves of the TCP socket are released and the TCP connection is closed immediately — it is not kept alive, and is not used for anything after the key exchange.
Phase 2 — UDP session
All subsequent communication happens exclusively over UDP (server-side port range 50000–59999). Every frame is encrypted with AES-256-GCM-SIV and authenticated with a per-packet HMAC-SHA-512.
Reliable, ordered delivery is provided at the application layer using NAK-based selective retransmission:
- The receiver (
UdpReader) tracks the highest sequence number seen and maintains a reorder buffer. Any gap that persists beyond a 50 ms timeout triggers aNakframe — a compact list of missing sequence numbers — sent back to the sender over the same UDP channel. - The sender (
UdpSender) keeps a sliding retransmit buffer of the 512 most-recently transmitted wire-encoded packets. When aNakarrives the missing packets are looked up and resent immediately. - Each gap is retried up to 10 times (with the 50 ms floor doubling each attempt); after the limit is exceeded the gap is abandoned and the session proceeds.
Because retransmission is handled entirely within the UDP layer there is no head-of-line blocking from TCP: a lost packet delays only the frames that depend on it, not the rest of the stream.
Reconnection
If the UDP path is interrupted the client automatically reconnects — performing a new TCP key exchange for the same logical session — and the server delivers a single ScreenState frame containing the current terminal contents so the display repaints instantly without replaying scrollback history.
Current Releases
libmoshpit
moshpit
moshpits
moshpit-keygen
CI/CD
Installation (Arch Linux / AUR)
All three binaries are available as separate AUR packages. Install them with any AUR helper (e.g. yay, paru) or manually with makepkg.
| AUR package | Installs | Notes |
|---|---|---|
moshpit-keygen |
mp-keygen |
No dependencies; install this first if building manually |
moshpit |
mp (client) |
Depends on moshpit-keygen |
moshpits |
mps (server) |
Depends on moshpit-keygen |
Install with an AUR helper
# Install the server (pulls in moshpit-keygen automatically)
# Install the client (pulls in moshpit-keygen automatically)
# Or install both in one go
Install manually with makepkg
# 1. Clone and build moshpit-keygen first (shared dependency)
# 2. Clone and build the server
# 3. Clone and build the client
Removing packages
# Remove server and client (keep keygen)
# Remove everything including keygen
Installation (cargo)
Requires a Rust toolchain (stable, 1.91.1 or later). Install all three binaries directly from crates.io:
# Key management tool (install first — the others depend on it)
# Client
# Server
To install a specific version, append --version <x.y.z> to any of the commands above.
mp-keygen
mp-keygen creates and inspects the ed25519 key pairs used by both the server and client.
Subcommands
generate
Interactively generates a new ed25519 public/private key pair. The tool prompts for an output path and an optional passphrase.
Default key locations (when the default path is accepted at the prompt):
| Key | Default path |
|---|---|
| Private key | ~/.mp/id_ed25519 |
| Public key | ~/.mp/id_ed25519.pub |
fingerprint
Displays the SHA-256 fingerprint of a public key file.
verify
Verifies a public key fingerprint string. Pass --randomart to verify a randomart image instead.
# Verify a fingerprint string
# Verify a randomart image
Global flags
| Flag | Short | Description |
|---|---|---|
--verbose |
-v |
Increase log verbosity (repeatable) |
--quiet |
-q |
Decrease log verbosity (repeatable, conflicts with --verbose) |
moshpits server (mps)
Quick start
-
Generate a server host key pair (run once):
# The server uses a separate key stored at ~/.mp/mps_host_ed25519_key[.pub] # by default. You can generate it with mp-keygen using a custom path: # Enter a path such as: /home/user/.mp/mps_host_ed25519_key -
Create the config file at
~/.config/moshpits/moshpits.toml(see Configuration below). -
Start the server:
Command-line usage
mps [OPTIONS]
Options:
-v, --verbose Turn up logging verbosity (repeatable)
-q, --quiet Turn down logging verbosity (repeatable)
-e, --enable-std-output Enable logging to stdout/stderr
(not recommended when running as a daemon)
-c, --config-absolute-path <PATH> Absolute path to an alternate config file
-t, --tracing-absolute-path <PATH> Absolute path to an alternate tracing output file
-p, --private-key-path <PATH> Absolute path to the server private key
-k, --public-key-path <PATH> Absolute path to the server public key
-h, --help Print help
-V, --version Print version
Example invocations
# Start with defaults (reads ~/.config/moshpits/moshpits.toml)
# Start with verbose logging to stderr
# Use a custom config file
# Use non-default key files
moshpits configuration
Default config file: ~/.config/moshpits/moshpits.toml
Environment variable prefix: MOSHPITS_ (nested keys separated by _, e.g. MOSHPITS_MPS_PORT=40404)
# ~/.config/moshpits/moshpits.toml
# ── Logging ──────────────────────────────────────────────────────────────────
# Base verbosity offset applied to the tracing output file.
# 0 = INFO, positive values increase verbosity, negative decrease it.
= 0
= 0
# ── Server listen address ─────────────────────────────────────────────────────
[]
= "0.0.0.0" # IP address to listen on
= 40404 # TCP port to listen on for client connections
# ── Key files ─────────────────────────────────────────────────────────────────
# Defaults to ~/.mp/mps_host_ed25519_key and ~/.mp/mps_host_ed25519_key.pub
# when not set.
# private_key_path = "/path/to/mps_host_ed25519_key"
# public_key_path = "/path/to/mps_host_ed25519_key.pub"
# ── Tracing (log output) ──────────────────────────────────────────────────────
# stdout layer — controls the format of log lines written to stderr when
# --enable-std-output is active.
[]
= false # include the Rust module path in each log line
= false # include the thread ID
= false # include the thread name
= false # include the source file line number
= true # include the log level (ERROR, WARN, INFO, …)
# directives = "moshpits=debug,libmoshpit=info" # optional tracing filter
# file layer — controls the format and level of the persistent tracing file.
# Default log file: ~/.config/moshpits/logs/moshpits.log
[]
= 0
= 0
[]
= false
= false
= false
= false
= true
# directives = "moshpits=debug"
Configuration precedence (highest → lowest)
- Environment variables (
MOSHPITS_*) - Command-line flags
- Config file values
moshpit client (mp)
Quick start
-
Generate a client key pair (run once):
# Accept the default path: ~/.mp/id_ed25519 -
Add the client's public key to the server's
authorized_keysfile.On the server, append the contents of the client's
~/.mp/id_ed25519.pubto~$TARGET_USER/.mp/authorized_keys(one key per line):# On the client — display the public key to copy # On the server — create the directory and file with the correct permissions &&The public key line format written by
mp-keygen generateis:moshpit <base64-encoded-public-key> user@hostPermission requirements:
~/.mpmust be mode0700andauthorized_keysmust be mode0600, otherwise the server will reject the connection. -
Connect to the server:
# or with an explicit user
Command-line usage
mp [OPTIONS] <SERVER_DESTINATION>
Arguments:
<SERVER_DESTINATION> IP address (or user@address) of the server to connect to
Options:
-v, --verbose Turn up logging verbosity (repeatable)
-q, --quiet Turn down logging verbosity (repeatable)
-c, --config-absolute-path <PATH> Absolute path to an alternate config file
-t, --tracing-absolute-path <PATH> Absolute path to an alternate tracing output file
-p, --private-key-path <PATH> Absolute path to the client private key
-k, --public-key-path <PATH> Absolute path to the client public key
-s, --server-port <PORT> Server TCP port (default: 40404)
-h, --help Print help
-V, --version Print version
Example invocations
# Connect to a server on the default port (40404)
# Connect as a specific user
# Connect to a non-default port
# Verbose logging, custom key files
moshpit configuration
Default config file: ~/.config/moshpit/moshpit.toml
Environment variable prefix: MOSHPIT_ (e.g. MOSHPIT_SERVER_PORT=40404)
# ~/.config/moshpit/moshpit.toml
# ── Logging ───────────────────────────────────────────────────────────────────
= 0
= 0
# ── Server connection ─────────────────────────────────────────────────────────
= 40404 # TCP port of the moshpits server
= "192.168.1.10" # "ip" or "user@ip"; overridden by the
# positional argument on the command line
# ── Reconnection ──────────────────────────────────────────────────────────────
# Maximum back-off interval between automatic reconnect attempts (seconds).
# Clamped to the range [2, 86400]. Default: 3600 (1 hour).
= 3600
# ── Key files ─────────────────────────────────────────────────────────────────
# Defaults to ~/.mp/id_ed25519 and ~/.mp/id_ed25519.pub when not set.
# private_key_path = "/home/alice/.mp/id_ed25519"
# public_key_path = "/home/alice/.mp/id_ed25519.pub"
# ── Tracing (log output) ──────────────────────────────────────────────────────
[]
= false
= false
= false
= false
= true
# directives = "moshpit=debug,libmoshpit=info"
# Default log file: ~/.config/moshpit/logs/moshpit.log
[]
= 0
= 0
[]
= false
= false
= false
= false
= true
Configuration precedence (highest → lowest)
- Environment variables (
MOSHPIT_*) - Command-line flags
- Config file values
Ports and firewall
| Port range | Protocol | Direction | Purpose |
|---|---|---|---|
mps.port (e.g. 40404) |
TCP | Inbound to server | Key exchange only — connection switches to UDP after handshake |
| 50000–59999 | UDP | Inbound to server | Encrypted terminal data |
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.