Add docs/THREAT_MODEL.md deriving a publishable threat model from NATIVE_V1_SPEC.md sections 4-6: assets, in/out-of-scope attackers, security properties vs SSH (including where Dosh aims to exceed it), cryptographic building blocks as implemented, and an honest accepted-residual-risks / known-gaps section. Refresh docs/PUBLIC_READINESS.md: feature matrix now reflects native auth, host-key trust, authorized_keys policy, doctor, and -L/-R/-D forwarding; add a per-item "Native v1 verification checklist status" table mapping spec section 16 to done/in-progress/pending from code. Update README.md status with a native v1 section and honest claim posture pointing to THREAT_MODEL.md. Annotate NATIVE_V1_SPEC.md with a v1 status block summarizing milestone progress. Point SPEC.md security model and header at native auth and the threat model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Dosh Public Readiness
Dosh's defensible public claim is fast terminal attach and reconnect. It should not claim full Mosh replacement status until the feature matrix below is green and the comparison benchmark is 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,
and dosh doctor all exist (see the feature matrix and the verification-checklist
status table below). It is not yet fully verified: per-IP token-bucket rate
limiting, protocol-version negotiation hardening, fuzzing in CI, and external review
are still open. Until the verification checklist (NATIVE_V1_SPEC.md section 16) is
green and that review is done, Dosh's defensible public security claim remains fast
encrypted native attach/reconnect with SSH-equivalent transport security and an
explicit SSH bootstrap fallback — not a fully verified, externally reviewed SSH
replacement.
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 via ssh-agent or encrypted key | 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 | needs more hostile-network tests |
| Survive sleep or network loss | yes | yes | needs long-running soak tests |
| 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 | ready |
| Multiple clients on one session | no | yes | needs conflict-policy docs |
| View-only clients | no | yes | ready |
| Full-screen TUI correctness | yes | improving | must stay green before public push |
| Predictive local echo | mature | guarded printable-only opt-in | not parity |
| Non-destructive disconnect UI | yes | not currently | needed |
| Unicode edge-case handling | strong | basic terminal emulator dependent | not parity |
| X11 forwarding | no | no | non-goal unless tunneled separately |
| SSH agent forwarding | no | no | planned as forwarding channel |
Local TCP forwarding, -L |
no | yes, native encrypted stream mux | implemented; needs hostile-network tests |
Remote TCP forwarding, -R |
no | yes, loopback bind by default | implemented; needs hostile-network tests |
Dynamic SOCKS forwarding, -D |
no | yes, SOCKS5 over native streams | implemented; needs hostile-network tests |
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; needs 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/StreamClosepacket types.- Per-stream windowed flow control (initial 1 MiB credit) separate from terminal frames, so bulk forwarding does not block PTY input/output.
- Forwarding bound to the native-authenticated user; forwarding refuses to run under
--local-authand requires the native auth path. - Server-side policy enforcement:
allow_tcp_forwarding,allow_remote_forwarding, loopback-only remote bind unlessallow_remote_non_loopback_bind, plus per-keyno-port-forwardingandpermitopen=. -Nforward-only (no PTY) and-fbackground (requires-N, backgrounds only after listeners are ready).
Still open before claiming forwarding parity:
- A dedicated dropped/reordered/replayed-UDP forwarding test suite.
- Load tests proving large forwarded transfers add no visible terminal input lag.
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. |
| 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 |
pending | Benchmark gate not yet run for native cold path (Track C / BENCHMARKS.md). |
| Cached attach near network RTT | pending | Same benchmark dependency. |
-L works without delaying terminal input |
in progress | Implemented with per-stream window; load proof pending. |
-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 | Long client_timeout_secs + resume; 30-min soak evidence pending. |
| 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 | in progress | Per-stream flow control exists; load test pending. |
| Fuzz targets run in CI | pending | No fuzz/ dir; CI runs fmt/test/build/bench only. |
| 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: in progress (another track). Today only
handshake-map eviction and a static
rate_limit_remaininghint exist. - Protocol VERSION negotiation: in progress. Single version, fail-closed reject; no multi-version negotiation yet.
- ECDSA P-256 / SHA-2 RSA user keys: pending. Ed25519 only today.
Before Public Launch
- Keep
cargo test,make bench-docker-ssh, andmake bench-docker-moshgreen. - Add a non-destructive disconnect indicator.
- Run scripted TUI tests for alternate-screen apps, arrow keys, resize, mouse mode, bracketed paste, and terminal cleanup.
- Publish benchmark output with raw samples, not just averages.
- Mark prediction as experimental until it has a real framebuffer model.
- Land full per-IP token-bucket auth rate limiting and wire fuzz targets into CI.
- Complete the native-v1 verification checklist above and an external security review
before making any "native SSH replacement" claim (
NATIVE_V1_SPEC.mdsection 17).