Files
dosh/docs/PUBLIC_READINESS.md
T
2026-06-19 16:00:51 -04:00

13 KiB

Dosh Public Readiness

Dosh's defensible public claim is fast terminal attach/reconnect plus native encrypted forwarding on Dosh-installed servers. It should not claim generic SSH compatibility unless it implements the SSH protocol, and public benchmark claims must be reproducible outside the author's homelab.

The plan for replacing the day-to-day SSH workflow with native Dosh authentication and forwarding is specified in docs/NATIVE_V1_SPEC.md, and the published threat model is in docs/THREAT_MODEL.md. Native v1 is now substantially implemented: native key-exchange + user auth, host-key pinning/trust, -L/-R/-D forwarding, dosh doctor, token-bucket auth rate limiting, and fuzz-smoke CI all exist (see the feature matrix and the verification-checklist status table below). It is not yet fully verified: long sleep/roaming soak and deeper fuzzing results are still open launch evidence. Until the verification checklist (NATIVE_V1_SPEC.md section 16) is green, Dosh's defensible public security claim remains fast encrypted native attach/reconnect and forwarding with SSH bootstrap fallback, not generic SSH compatibility.

Objective Benchmarks

Run the same-host SSH comparison:

make bench-docker-ssh

Run the Dosh, SSH, and Mosh comparison:

make bench-docker-mosh

The Mosh-inclusive target builds one Docker image with OpenSSH, Mosh, dosh-server, and dosh-auth. It uses the same generated key, localhost network path, SSH server, and container for all samples.

Reported metrics:

Metric Meaning
ssh_true_ms Time to run ssh host true with the same key/options.
dosh_attach_ms Time for dosh-client --attach-only to render the first frame and detach on the configured path. With --no-cache, this is cold SSH bootstrap plus UDP attach.
dosh_cached_attach_ms Time for a warmed Dosh client to attach using cached UDP credentials/tickets, render the first frame, and detach. This is the fast-path claim and should be approximately network RTT plus local process/render overhead.
mosh_start_true_ms Time for mosh host -- true to bootstrap, run true, and exit.

These are not identical workloads. They are still useful because they measure the startup path each tool must pay before useful remote work begins. Public numbers must include the command, machine, OS, CPU, network path, and sample count.

Do not cite cold dosh_attach_ms as the core speed claim. Cold Dosh still pays SSH startup/authentication. Cite dosh_cached_attach_ms for repeat attach/reconnect speed, and cite cold dosh_attach_ms only to show that fallback remains competitive with ordinary SSH.

Feature Matrix

Feature Mosh Dosh now Public status
SSH-based first authentication yes yes ready
Native UDP key auth (no SSH per attach) no yes, Ed25519, ECDSA P-256, and RSA-SHA2 via ssh-agent or OpenSSH identity files implemented; pending full verification
Dosh host-key pinning and trust no yes, known_hosts + dosh trust + mismatch hard-fail implemented
authorized_keys policy enforcement no yes, from=/no-port-forwarding/permitopen=, unsupported fail closed implemented
dosh doctor diagnostics no yes, config/auth/UDP/forwarding-policy check implemented
Encrypted UDP terminal data yes yes ready
Roaming by client address change yes yes implemented; hostile-network covered, 30-minute soak gate available
Survive sleep or network loss yes yes implemented; run make soak-local before public launch
Fast repeat attach without SSH no yes, via attach tickets core differentiator
Resident server daemon no yes core differentiator
One UDP port for all sessions port range by default yes ready
Fresh session by default yes yes ready
Named persistent sessions no built-in shared session model yes, plus opt-in server-restart holders implemented; holder mode needs stress-testing
Multiple clients on one session no yes implemented; needs conflict-policy docs
View-only clients no yes ready
Full-screen TUI correctness yes yes, scripted transport coverage for control sequences implemented; needs broader app matrix
Predictive local echo mature guarded printable-only opt-in not parity
Non-destructive disconnect UI yes yes, bottom-row save/restore status line implemented
Unicode edge-case handling strong basic terminal emulator dependent not parity
X11 forwarding no no non-goal unless tunneled separately
SSH agent forwarding no yes, explicit -A / forward_agent plus server allow-list implemented; opt-in only
Local TCP forwarding, -L no yes, native encrypted stream mux implemented; load/priority, replay/reorder, and stream retransmit tested
Remote TCP forwarding, -R no yes, loopback bind by default implemented; policy and stream mux covered
Dynamic SOCKS forwarding, -D no yes, SOCKS5 over native streams implemented over the same reliable stream mux
Forward-only / background forwarding, -N / -f no yes, -f requires -N implemented
Per-stream flow control / terminal priority no yes, windowed credit per stream implemented; covered by blocked-stream and local-forward load tests

SSH Config Inheritance

Dosh intentionally delegates SSH parsing and authentication to OpenSSH. Bootstrap uses the user's normal ssh command, so ~/.ssh/config options such as Host, HostName, User, Port, IdentityFile, ProxyJump, and UserKnownHostsFile continue to apply.

Dosh also calls ssh -G <alias> to infer the UDP target host when no dosh_host is configured. To write explicit Dosh host entries from SSH aliases:

dosh import-ssh palav homelab

This appends entries to ~/.config/dosh/hosts.toml without trying to become an OpenSSH config parser.

Forwarding (Implemented)

SSH forwarding cannot be copied by keeping the bootstrap SSH connection open, because that would remove Dosh's fast reconnect advantage and would break after roaming. Dosh forwarding therefore runs as native encrypted stream channels over the Dosh transport, and is now implemented.

CLI Meaning
dosh -L 8080:127.0.0.1:80 host Local listener on the client; server connects to target.
dosh -R 8080:127.0.0.1:80 host Remote listener on the server (loopback by default); client connects to target.
dosh -D 1080 host SOCKS5 listener on the client; server connects to whatever the SOCKS request names.

Implemented:

  • StreamOpen/StreamOpenOk/StreamOpenReject/StreamData/StreamWindowAdjust/ StreamEof/StreamClose packet types.
  • Per-stream windowed flow control (initial 1 MiB credit) separate from terminal frames, so bulk forwarding does not block PTY input/output.
  • Ordered reliable StreamData delivery with per-stream byte offsets, cumulative received-offset ACKs, and retransmission re-encrypted as fresh transport packets.
  • Forwarding bound to the native-authenticated user; forwarding refuses to run under --local-auth and requires the native auth path.
  • Server-side policy enforcement: allow_tcp_forwarding, allow_remote_forwarding, loopback-only remote bind unless allow_remote_non_loopback_bind, plus per-key no-port-forwarding and permitopen=.
  • -N forward-only (no PTY) and -f background (requires -N, backgrounds only after listeners are ready).

Still open before claiming forwarding parity:

  • More real-host load soak. The integration suite already covers terminal-priority behavior while a local forward is under blocked-stream pressure, plus hostile replay/reorder and server-to-client stream retransmission after UDP loss.

Native v1 Verification Checklist Status

This maps each item in NATIVE_V1_SPEC.md section 16 to its status based on what the code in src/ actually does. Done = implemented and exercised by tests or obvious from code; in progress = partially implemented; pending = not yet implemented.

Spec section 16 item Status Evidence / note
Unknown host key fails unless TOFU explicitly enabled done Client refuses KnownHostStatus::Unknown unless trust_on_first_use.
Known host-key mismatch hard fails done KnownHostStatus::Mismatch aborts; trust_host refuses overwrite without --replace.
Native Ed25519 auth via ssh-agent done src/ssh_agent.rs signs the user-auth transcript.
Native Ed25519 auth via encrypted key prompt done load_ed25519_identity_with_passphrase decrypts OpenSSH keys.
Native ECDSA P-256 auth done ssh-agent and OpenSSH identity-file paths are wired; native_user_auth_accepts_ecdsa_p256_private_key verifies the direct key path.
Native RSA-SHA2 auth done ssh-agent requests rsa-sha2-512; direct OpenSSH RSA identities sign with rsa-sha2-512; legacy SHA-1 ssh-rsa signatures are rejected.
Removed authorized key can no longer authenticate done Covered by native_user_auth_accepts_authorized_key_and_rejects_removed_key.
Unsupported authorized-key options fail closed done ensure_native_allowed bails on any unsupported option.
Replayed handshake packets rejected done Handshake is transcript-bound and signature-verified; pending entries TTL-evicted.
Replayed transport packets rejected done ReplayWindow (128-wide) over per-direction counter.
Stale encrypted packets after reconnect ignored, not fatal done session_key_id mismatch drops the packet instead of erroring fatally.
Client IP/port change preserves session done Server matches by ClientId/session key id, updates endpoint.
Native cold auth beats cold ssh host true done Docker gate runs dosh-bench --cold-native after one-time dosh trust and requires Dosh mean <= SSH mean.
Cached attach near network RTT done Local loopback samples are ~3 ms; Docker cached gate is under 25 ms. See docs/BENCHMARKS.md.
-L works without delaying terminal input done Per-stream windowing plus native_local_forward_bulk_load_does_not_delay_interactive_terminal and blocked-stream priority regression.
-R enforces bind and permission policy done remote_bind_allowed + start_remote_forwards policy checks.
-N -L does not allocate a PTY done forward-only mode skips PTY allocation.
-f -N -L backgrounds only after listener readiness done spawn_background_forwarder waits for a readiness token.
Multiple forwards in one command done Forward lists parsed and started together.
dosh doctor identifies UDP-blocked/auth-denied/mismatch/forwarding-denied done run_doctor_command reports each state.
Closing laptop 30+ min does not kill session in progress sleep_roaming_soak_30m is intentionally ignored for normal cargo test; make soak-local runs it for 1800s launch evidence.
Three concurrent terminals independent unless named done Generated session names per attach; named sessions shared on purpose.
Large forwarded transfers add no visible input lag done Covered by the local-forward bulk-load integration test; still needs real-host soak before launch claims.
Fuzz targets run in CI done fuzz/ has parser/auth targets; CI runs 20s per target on push/PR and 300s per target on weekly/manual runs when cargo-fuzz is available.
Threat model updated with accepted residual risks done docs/THREAT_MODEL.md.

Additional security hardening tracked outside the section 16 list:

  • Full per-IP token-bucket rate limiting: implemented for native auth and covered by unit/integration tests. It still needs real-host tuning under abusive traffic.
  • Protocol VERSION handling: v1 policy is single-version, fail-closed reject at the packet/header and native-handshake layers. See docs/PROTOCOL_VERSIONING.md.
  • ECDSA P-256 / SHA-2 RSA user keys: implemented for ssh-agent and OpenSSH identity-file native auth. RSA is compatibility-only and uses SHA-2 signatures; legacy SHA-1 ssh-rsa signatures are not accepted.

Before Public Launch

  • Keep cargo test, make bench-docker-ssh, and make bench-docker-mosh green.
  • Run make soak-local before launch to produce 30-minute sleep/roaming evidence.
  • Run make fuzz-deep before launch, or use the scheduled/manual CI fuzz pass, and publish the target durations.
  • Keep scripted TUI tests green and add a broader app matrix for real alternate-screen tools.
  • Publish benchmark output with raw samples, not just averages.
  • Mark prediction as experimental until it has a real framebuffer model.
  • Stress-test persist_sessions = true before making restart-survivable holders the default.
  • Tune the native auth token bucket under abusive real-host traffic.
  • Complete the native-v1 verification checklist above before making any "native SSH replacement for Dosh-installed servers" claim (NATIVE_V1_SPEC.md section 17).