Files
dosh/docs/NATIVE_V1_SPEC.md
T

22 KiB

Dosh Native v1 Spec

v1 status (annotation, not part of the spec text below). Native v1 is substantially implemented and being stabilized; it is not yet fully verified. Milestone progress against section 15:

  • Milestone 1 — host identity and trust: done. Host key generation, dosh trust, known_hosts, and mismatch hard-fail are implemented.
  • Milestone 2 — native user auth: done. ClientHello/ServerHello/UserAuth/ AuthOk, ssh-agent and OpenSSH identity-file auth for Ed25519, ECDSA P-256, and RSA-SHA2, plus authorized_keys verification, exist.
  • Milestone 3 — default native auth: done. auth_preference = "native,ssh" is the default with explicit, visible SSH fallback. Local and Docker benchmark gates cover cached attach, SSH fallback, and native cold auth (Track C / BENCHMARKS.md).
  • Milestone 4 — forwarding: implemented. Stream mux, -L, -R, -D, -N, -f, per-stream flow control, ordered stream offsets/ACKs, and stream-data retransmission exist. Terminal-priority/load, replay/reorder, and dropped server-to-client StreamData recovery regressions are covered.
  • Milestone 5 — hardening: partly done. Per-IP token-bucket rate limiting, fail-closed protocol-version checks with a documented v1 policy, parser fuzz targets, fuzz-smoke/deep CI entry points, scripted TUI transport tests, forwarding load/priority/replay/loss tests, and independent persistent session restart tests exist. Published long-soak evidence is not yet complete. The threat model is published (docs/THREAT_MODEL.md).
  • Milestone 6 — workflow parity: mostly done. dosh doctor, host-trust management, and the encrypted-key prompt flow exist; cross-OS daily-driver soak is ongoing.

The section 16 verification checklist is not yet fully green — see the item-by-item status table in docs/PUBLIC_READINESS.md and the residual-risk list in docs/THREAT_MODEL.md. The section 17 public-claim gate is therefore not met.

Native Dosh is a remote-login protocol for Dosh-installed servers. It is intended to replace the user's day-to-day ssh host workflow for terminals and forwarding while keeping SSH as a compatibility and recovery fallback.

Native Dosh is not an RFC-compatible SSH implementation. It deliberately avoids the full SSH transport/channel protocol and implements the smaller set of behavior Dosh needs: authenticated login, encrypted terminal transport, reconnect/roaming, and TCP forwarding.

1. Product Contract

User-facing commands:

dosh host
dosh host command...
dosh -L 8080:127.0.0.1:80 host
dosh -R 9000:127.0.0.1:9000 host
dosh --session work host
dosh --view-only --session work host
dosh host tm

Compatibility expectations:

  • Existing SSH keys and ssh-agent are reused.
  • Server-side authorization uses ~/.ssh/authorized_keys.
  • Host trust is pinned in a Dosh known-hosts file and can be bootstrapped by SSH.
  • ~/.ssh/config host aliases, HostName, User, Port, IdentityFile, ProxyJump, and UserKnownHostsFile are honored where practical.
  • SSH bootstrap remains available with --auth=ssh and is used automatically when native auth is disabled or cannot complete.

Native v1 is complete only if a normal interactive user can switch their daily workflow from ssh to dosh without keeping SSH open in another tab for routine tasks.

Non-goals for v1:

  • RFC-compatible SSH server/client behavior.
  • Arbitrary SSH subsystems.
  • X11 forwarding.
  • SFTP/SCP compatibility.
  • Multi-user daemon mode with privileged account switching.
  • Replacing OpenSSH on hosts that do not run dosh-server.

2. SSH Workflow Parity Target

Native v1 targets the SSH workflows interactive users actually use, not the entire OpenSSH feature universe.

Must work in v1:

  • Interactive shell: dosh host.
  • Remote command: dosh host command....
  • Fresh terminal by default.
  • Named persistent terminal: dosh --session work host.
  • Shared/view-only session: dosh --view-only --session work host.
  • Local forwarding: dosh -L [bind:]listen:target:target_port host.
  • Remote forwarding: dosh -R [bind:]listen:target:target_port host.
  • Dynamic forwarding: dosh -D [bind:]listen host, if stream flow control is proven; otherwise it is the first v1.1 item and must be called out honestly.
  • Multiple forwards in one connection.
  • Forward-only mode: dosh -N -L ... host.
  • Background forwarding: dosh -f -N -L ... host, or an equivalent supervised user-service mode.
  • SSH-agent authentication.
  • Encrypted OpenSSH private-key authentication with prompt.
  • ~/.ssh/config host aliases for Host, HostName, User, Port, IdentityFile, ProxyJump, UserKnownHostsFile, and IdentitiesOnly.
  • Dosh host config overrides for user, Dosh-specific UDP host/port/auth policy.
  • Clear host-key trust and mismatch errors.
  • Clear auth failure errors that name the attempted key source.
  • Stable reconnect after sleep, network switch, NAT rebinding, and server packet loss.
  • dosh update.
  • dosh doctor host for config/auth/UDP reachability diagnostics.
  • dosh sessions host for session visibility.
  • Optional command extensions such as dosh host tm for companion tools that are installed separately from Dosh.

Should work in v1 if it does not compromise the transport schedule:

  • Agent forwarding with an explicit opt-in flag and clear warning.
  • ProxyJump through SSH for bootstrap/trust and through Dosh-native relay later.
  • SendEnv/SetEnv equivalent for explicit environment variables.
  • Clipboard integration as a separate opt-in channel.

Explicitly not v1:

  • X11 forwarding.
  • SFTP/SCP wire compatibility.
  • Full OpenSSH config language.
  • Full sshd replacement for arbitrary SSH clients.
  • Forced-command subsystems beyond fail-closed enforcement.

3. Stability Contract

Native v1 must be boring under real use:

  • A closed laptop must not kill the remote session.
  • A client crash must not kill the remote session.
  • A server restart must not kill the remote session when persist_sessions is on (currently opt-in until stress-tested): each session's shell runs in a detached per-session holder process whose PTY master fd the server passes back to itself via SCM_RIGHTS, so the shell + scrollback survive a server crash/upgrade/systemctl restart and a reattaching client lands on the same shell with its screen restored. With persist_sessions = false the old behavior applies: live PTYs drop on restart, but the client must still fail clearly and reconnect cleanly afterward.
  • Decrypt failures from stale packets must be ignored or trigger reconnect, never terminate the terminal by themselves.
  • Terminal cleanup must restore cursor, mouse mode, bracketed paste, alternate screen, and raw mode on exit.
  • Forwarding streams must close cleanly without leaving orphan listeners.
  • Every public error must be actionable: host trust, auth failure, UDP blocked, forwarding denied, version mismatch, or server unavailable.
  • dosh host must never attach to another active unnamed terminal by accident.
  • The default install must be secure without hand-editing configs.
  • Upgrades must preserve existing trusted host keys and credentials unless explicitly rotated.

4. Security Contract

Native Dosh must match the security properties users rely on from SSH for this use case:

  • Server authentication before terminal data is trusted.
  • User authentication by possession of an authorized private key or agent key.
  • Forward secrecy for terminal and forwarding traffic.
  • AEAD encryption and authentication for every post-handshake packet.
  • Replay protection for handshake and transport packets.
  • Host-key pinning with explicit first-use behavior.
  • No plaintext terminal bytes after handshake begins.
  • No custom cryptographic primitives.
  • Clear downgrade behavior: native auth failure must not silently fall back to an unauthenticated mode.

Native Dosh does not claim SSH's full protocol security surface. It claims equivalent security for Dosh terminal and forwarding sessions on Dosh-installed servers.

5. Threat Model

In scope:

  • Passive network observer.
  • Active network attacker that can spoof, drop, replay, reorder, or modify packets.
  • NAT rebinding and client IP/port changes.
  • Stolen attach-ticket cache without the user's private key.
  • Server restart and key rotation.
  • Malicious unauthenticated client flooding auth attempts.
  • Compromised low-privilege local user trying to read Dosh caches on a shared client.

Out of scope:

  • Compromised client machine.
  • Compromised server account.
  • Malicious kernel, terminal emulator, or PTY implementation.
  • Protecting against a server that is already authorized and then becomes malicious.

6. Cryptographic Building Blocks

Allowed primitives:

  • Handshake pattern: Noise NK or XX through a maintained Rust Noise framework, or a small audited construction over x25519-dalek plus transcript binding.
  • KEX: X25519.
  • Signatures for user auth: Ed25519 and ECDSA P-256 via SSH-agent and OpenSSH key formats. RSA may be accepted only for compatibility and must use SHA-2 signatures.
  • AEAD: ChaCha20-Poly1305 by default; AES-GCM optional when hardware support is known.
  • KDF: HKDF-SHA256.
  • Hash/transcript: SHA-256.
  • Randomness: OS CSPRNG only.

Disallowed:

  • Homegrown ciphers, MACs, padding, or key derivation.
  • Reusing a nonce/key pair.
  • Unauthenticated encryption.
  • MD5/SHA-1 signatures for user auth.

7. Identity And Trust

Server Identity

Each dosh-server has a persistent host key:

~/.config/dosh/host_key
~/.config/dosh/host_key.pub

Default host-key algorithm: Ed25519.

Client pins host keys in:

~/.config/dosh/known_hosts

Entry format:

host-pattern key-type base64-public-key first-seen=unix-seconds source=tofu|ssh|manual

First-use policy:

  • Default for public internet: refuse unknown native host key and suggest dosh trust host or SSH bootstrap.
  • Default for local/private hosts may be TOFU only when trust_on_first_use = true.
  • dosh trust host may verify the Dosh host key over the existing SSH bootstrap path.

Host-key mismatch:

  • Hard fail.
  • Print old fingerprint, new fingerprint, and known-hosts file path.
  • Never auto-replace.

User Identity

Native auth user identity is the login user resolved from:

  1. Explicit CLI user: user@host.
  2. Dosh host config.
  3. ssh -G host user.
  4. Local username.

Server verifies user keys against:

~/.ssh/authorized_keys
~/.config/dosh/authorized_keys

~/.config/dosh/authorized_keys is optional and may restrict Dosh access without changing SSH access.

Authorized-key options required in v1:

  • from=
  • command= must reject native Dosh terminal login unless explicitly supported later.
  • restrict
  • no-port-forwarding
  • permitopen=

Unsupported restrictive options must fail closed.

8. Native Auth Handshake

Native auth runs over UDP on the same Dosh port. It establishes a short-lived authenticated control channel and returns the same terminal attach material that SSH bootstrap returns today.

Target path:

client -> server: ClientHello
server -> client: ServerHello
client -> server: UserAuth
server -> client: AuthOk + first terminal snapshot

The server may combine AuthOk and AttachOk to get terminal-ready in the final handshake flight.

ClientHello

Fields:

  • protocol version
  • client random
  • client ephemeral X25519 public key
  • requested host alias
  • requested user
  • requested session
  • requested mode
  • terminal size
  • supported AEAD algorithms
  • supported user key algorithms
  • optional cached host-key id
  • optional attach ticket envelope

ServerHello

Fields:

  • protocol version
  • server random
  • server ephemeral X25519 public key
  • server host public key
  • server host-key signature over transcript
  • chosen AEAD
  • server key epoch
  • auth challenge
  • rate-limit metadata when applicable

The client must verify the host key before sending user authentication.

UserAuth

Fields:

  • selected public key
  • key algorithm
  • signature over transcript and auth challenge
  • optional agent identity metadata
  • optional requested forwarding declarations

The signature must bind:

  • both ephemeral keys
  • both randoms
  • server host key
  • requested user/session/mode/terminal size
  • selected algorithms
  • protocol version

AuthOk

Fields:

  • assigned ClientId
  • session name
  • mode
  • session key id
  • encrypted session key material or derived key confirmation
  • attach ticket
  • attach ticket PSK encrypted to the handshake key
  • initial output sequence
  • first snapshot
  • server policy flags

AuthOk is AEAD-encrypted under the handshake traffic key.

9. Key Schedule

Handshake transcript:

H = SHA256(protocol_label || ClientHello || ServerHello || UserAuth)

Shared secret:

dh = X25519(client_ephemeral, server_ephemeral)

Handshake key:

handshake_key = HKDF-SHA256(dh, H, "dosh/native/handshake/v1")

Session traffic keys:

c2s_key = HKDF-SHA256(handshake_key, H, "dosh/native/c2s/v1")
s2c_key = HKDF-SHA256(handshake_key, H, "dosh/native/s2c/v1")

Attach ticket PSKs and rotated session keys must be derived independently from server secret material and fresh randomness. They must not reuse handshake traffic keys.

10. Attach Tickets And Cache

Native attach tickets replace most cold auth after first login.

Ticket properties:

  • Server-sealed AEAD blob.
  • Scoped to server host key, user, session, mode, client key fingerprint, server key epoch, and policy flags.
  • Paired with a client-held random PSK.
  • Default TTL: 24 hours for trusted personal machines, configurable down to zero.
  • Stored mode 0600.
  • Revoked by server host-key rotation, server secret rotation, or user key removal.

Client cache path:

~/.local/share/dosh/credentials/

Cache entries must include:

  • host identity fingerprint
  • user
  • session
  • mode
  • ticket expiry
  • last rendered sequence
  • client id
  • session key id
  • attach ticket
  • attach ticket PSK

11. Transport

Post-auth terminal traffic continues to use the current Dosh UDP packet model:

  • fixed binary header
  • AEAD body
  • monotonic packet sequence
  • ack field
  • replay window
  • server snapshots for recovery

Required v1 changes:

  • Separate packet namespaces for terminal frames, control messages, and forwarding streams.
  • Explicit key_epoch or session_key_id in packet metadata so stale packets can be ignored without fatal decrypt errors.
  • Rekey command after configurable packet count or wall-clock interval.
  • Connection migration must be accepted after any valid encrypted packet from a new source address.

12. Forwarding

Native forwarding is a Dosh stream multiplexer over the encrypted transport.

CLI:

dosh -L [bind_host:]listen_port:target_host:target_port host
dosh -R [bind_host:]listen_port:target_host:target_port host
dosh -D [bind_host:]listen_port host
dosh -A host   # forward the local ssh-agent (opt-in, server-gated)

Stream packet types:

  • StreamOpen
  • StreamOpenOk
  • StreamOpenReject
  • StreamData
  • StreamWindowAdjust
  • StreamEof
  • StreamClose

Forwarding rules:

  • Terminal traffic has priority over stream bulk data.
  • Each stream has independent flow control.
  • StreamData carries a per-stream byte offset and is delivered to TCP in order.
  • StreamWindowAdjust carries a cumulative received byte offset; peers free retransmit buffers and replenish send credit only for acknowledged contiguous bytes.
  • Unacknowledged stream bytes are retransmitted as newly encrypted transport packets with fresh packet sequence numbers/nonces.
  • Backpressure must not block PTY input or output.
  • Server enforces no-port-forwarding and permitopen=.
  • Remote listeners bind to loopback by default.
  • Non-loopback remote bind requires explicit config.
  • -N opens forwarding without creating a PTY.
  • -f backgrounds only after all requested listeners are successfully active.
  • Listener setup failures fail the entire command unless --partial-forwarding is explicitly requested.
  • Forwarding reconnect must preserve listeners and re-open streams after network migration when protocol state allows it.
  • Stream packet scheduling must enforce terminal priority; a large port-forward copy cannot make shell keystrokes lag.

12.1 SSH-Agent Forwarding (opt-in, security-gated)

  • Activated only on explicit client opt-in (-A / forward_agent = true) and server allow_agent_forwarding = true. Off by default on both ends.
  • The client requests a ForwardingKind::Agent entry during auth. If the server policy allows it, the server binds a per-session proxy unix socket in a process-private directory (dir 0700, socket 0600) and exports its path as the spawned shell's SSH_AUTH_SOCK.
  • Each connection from a remote process to that proxy socket is tunneled to the client over a server-initiated StreamOpen carrying the reserved target sentinel @dosh-agent (port 0). The client splices it into its local SSH_AUTH_SOCK (a unix socket, not a TCP target). Data/window/close reuse the existing stream packets unchanged.
  • Only applies to a freshly spawned shell; an already-running attached/prewarmed session keeps its existing environment (same constraint as ssh/mosh).
  • SECURITY: forwarding exposes the local agent to the remote host for the session's lifetime, which is why it is double-gated and never the default.

13. Diagnostics And Operations

Native v1 must include operations commands:

dosh doctor host
dosh trust host
dosh trust --remove host
dosh sessions host
dosh update

dosh doctor host checks:

  • host alias resolution
  • Dosh known-host state
  • SSH fallback reachability
  • native UDP reachability
  • server version
  • native auth enabled/disabled
  • usable keys from ssh-agent and identity files
  • server authorization result without opening a terminal
  • configured forwarding policy

dosh trust host checks:

  • fetch Dosh host key through SSH fallback when available
  • show fingerprint before writing trust
  • refuse overwrite on mismatch unless --replace is explicit

14. Config

Client:

auth_preference = "native,ssh"
trust_on_first_use = false
native_auth_timeout_ms = 700
known_hosts = "~/.config/dosh/known_hosts"
credential_cache = "~/.local/share/dosh/credentials"
identity_files = ["~/.ssh/id_ed25519"]
use_ssh_agent = true
forward_agent = false
send_env = ["LANG", "LC_*", "TERM", "COLORTERM"]
set_env = {}
forwardings = []

[extensions.tm]
command = "tm {args}"
description = "Open an optional server-side tmux dashboard"

Server:

native_auth = true
host_key = "~/.config/dosh/host_key"
authorized_keys = ["~/.ssh/authorized_keys", "~/.config/dosh/authorized_keys"]
native_auth_rate_limit_per_minute = 30
attach_ticket_ttl_secs = 86400
allow_tcp_forwarding = true
allow_remote_forwarding = false
allow_remote_non_loopback_bind = false
allow_agent_forwarding = false
accept_env = ["LANG", "LC_*", "TERM", "COLORTERM"]

15. Migration Plan

Milestone 1: host identity and trust

  • Generate Dosh host key on server install.
  • Add dosh trust host.
  • Add known-hosts file and mismatch handling.
  • Keep SSH bootstrap as the only auth path.

Milestone 2: native user auth

  • Implement ClientHello/ServerHello/UserAuth/AuthOk.
  • Support ssh-agent Ed25519 first.
  • Verify against authorized_keys.
  • Add --auth=native|ssh|auto.

Milestone 3: default native auth

  • Make auth_preference = "native,ssh" default.
  • Keep SSH fallback explicit and visible.
  • Add benchmark gates for native cold auth.

Milestone 4: forwarding

  • Add stream mux.
  • Add -L, -N, and -f.
  • Add -R.
  • Add -D only after flow control is proven.

Milestone 5: hardening

  • Fuzz packet parsing, authorized-key parsing, known-host parsing, and handshake state.
  • Add hostile-network integration tests.
  • Keep a public hardening checklist and threat model before public security claims.

Milestone 6: workflow parity

  • Implement dosh doctor.
  • Implement complete host-trust management.
  • Implement private-key prompt flow.
  • Implement forwarding policy diagnostics.
  • Run daily-driver soak on macOS and Linux clients.

16. Verification

Native v1 is not complete until all are true:

  • Unknown host key fails by default unless TOFU is explicitly enabled.
  • Known host key mismatch hard fails.
  • Native Ed25519 auth succeeds via ssh-agent.
  • Native Ed25519 auth succeeds via encrypted private key prompt.
  • Removed authorized key can no longer authenticate.
  • Restrictive unsupported authorized-key options fail closed.
  • Replayed handshake packets are rejected.
  • Replayed transport packets are rejected.
  • Stale encrypted packets after reconnect are ignored, not fatal.
  • Client IP/port change preserves the session.
  • Native cold auth benchmark beats cold ssh host true on the same host.
  • Cached attach remains near network RTT plus local render overhead.
  • -L forwarding works without delaying terminal input.
  • -R forwarding enforces bind and permission policy.
  • -N -L forward-only mode does not allocate a PTY.
  • -f -N -L backgrounds only after listener readiness.
  • Multiple forwards in one command work.
  • dosh doctor identifies UDP-blocked, auth-denied, host-key-mismatch, and forwarding-denied states.
  • Closing the laptop for at least 30 minutes does not kill the remote session.
  • Three concurrent Dosh terminals remain independent unless explicitly named.
  • Large forwarded transfers do not add visible terminal input lag.
  • Fuzz targets run in CI.
  • Threat model is updated with any accepted residual risks.

17. Public Claim Gate

Dosh may claim "native SSH replacement for Dosh-installed servers" only after:

  • Native auth is default on at least one real host.
  • SSH fallback remains available.
  • The verification checklist is green.
  • The threat model is published.
  • Benchmarks include raw samples for SSH cold, Dosh native cold, Dosh cached attach, and Mosh startup.

Dosh must not claim generic SSH compatibility unless it implements the SSH protocol.