--- status: reviewed last_updated: 2026-10-28 --- # Overview: alkssh ## What this crate is alkssh is the alk family's SSH adapter (family ADR-003): a thin wrapper around **russh** that terminates the vanilla-SSH wire and bridges to/from family protocols. It is **a door in both directions, vanilla on the outside** (vision principle 2; ADR framing in `mapping.md` / `client.md`): - **Server direction**: a door *for vanilla clients*. Vanilla `ssh`, `git`, and (never for us, by design) sftp tooling connect as they would to any sshd; each accepted SSH channel/request is mapped onto exactly one family protocol seam and exactly one cargo feature. Primary current purpose: the alkforge git door. - **Client direction**: a door *to vanilla servers*. A family app (the reverse-runner hub) dials vanilla `sshd` (rented fleets, runpod/vast.ai-style boxes) and the ops map family-side onto alktty/alktunnels/alkgit types; the runner stays vanilla by design. The crate's substance is glue and mapping; no SSH framing is authored in-family (family ADR-003 decision 1); russh owns the wire, and the pin is ADR-002. ## Position in the family | Concern | Owner | |---|---| | SSH wire protocol (KEX, ciphers, channel mux, userauth state machines) | russh (pinned, ADR-002) | | SSH channel-type → family-protocol mapping | **this crate** (the spine, `mapping.md` / `client.md`) | | Identity (fingerprint → `Identity`) | alkcall `IdentityProvider`, assembly-implemented; never the vault (vault = secrets *we* hold) | | Secrets we hold (client dial keys, host keys) | alkvault-backed sources (ADR-005) | | The `alk/ssh` ALPN | ADR-001 (edge-termination contract; relay/browser legs have no consumer in scope, OQ-01 resolved) | | Deployment policy (which features, which ACLs) | the embedding assembly (alkforge) | Composability boundary: alkssh is a door, not a backend. It depends on alkcall types at the wire layer and the mapped crates' session or client surfaces, and carries no deployment policy. A downstream app embeds it, enables features (ADR-003), brings identity/ACL config. ## Public surface (categories) The crate is a library (`[lib]`, no binary, a stated non-goal); the surface categories the phase-1 specs define: - **Server stack** (`mapping.md`): serve configuration (feature-gated seam wiring, bounded-resource limits, honest-advertisement auth config) + the serving loop that accepts vanilla connections and dispatches per the mapping table. - **Client stack** (`client.md`): dial surface (address, key source, host-key policy, keepalive/timeout config), the op-row consumers (session exec/pty, tunnel dial, `-R` listen + acceptor), and lifecycle primitives (bound-port handles, cancel; OQ-12: no reconnect policy is owned). - **Key sources** (`auth.md`): vault-backed source construction per ADR-005 for both directions (client keys *and* host keys). - **Identity plumbing** (`auth.md`): the fingerprint→identity bridge at the server; the identity→seam propagation for mapped channels. Feature graph (ADR-003): `session`, `git`, `tunnel`, `sftp` (placeholder); flat, independent, `default = []` (no capabilities), two-direction symmetry, fail-closed declines contract-tested on every side of every feature. ## Bounded-resources policy (family convention) Every session is bounded by wall clock, size, and channel count (AGENTS.md convention 9; the knobs are russh's `Config` per ADR-002's surface notes): | Resource | Knob | Default posture | |---|---|---| | Session wall clock | `inactivity_timeout` | set (assembly-tunable) | | Rekey volume/time | `Limits` | russh-enforced (post-0.64-full-enforcement) | | Per-channel memory | `window_size`, `maximum_packet_size`, `channel_buffer_size` | set | | Auth attempts | `max_auth_attempts` | 10 default (ADR-004: the counting `none` probe) | | Keepalives (client direction, cloud fleets) | `keepalive_interval`/`keepalive_max` | 15 s / 3 (reverse-runner evidence: NAT'd runners) | | Concurrent channels per session | ours (in-crate) + per-identity policy when bridged | ours to cap | No unbounded loops, no unbounded buffers; the fail-closed declines plus the knobs above are the whole posture. ## Non-goals Per `vision.md`: no in-family SSH protocol machinery (ever); not an sshd (no local accounts, no OpenSSH `authorized_keys` semantics, no PAM); no sftp serving until alkfs lands (OQ-13 resolved; the feature exists as a tested declining placeholder); no binary/CLI; no password or keyboard-interactive auth in either direction; no x11 or agent forwarding at all (not even feature-gated, ADR-003 §5). ## Lifecycle note This crate follows the family crate template (alktty root pattern): `src/` modules re-exported from `lib.rs`, one module per file, public API = crate-root re-exports. The dependency set (russh per ADR-002, alkcall types, per-feature sibling deps: alktty/alktunnels/alkgit) and the manifest (`rust-version` 1.89, edition 2024) land at implementation start; ADR-002 records the manifest decisions. ## References - [README.md](README.md) (index), [open-questions.md](open-questions.md) (OQ-01…13) - ADRs 001–006 in [decisions/](decisions/) - `docs/research/`: vision, POC findings (1/2/3), russh inventory, auth bridge, mapping table (validated), reverse-runner extraction - Family ADR-003 (the structural decision), family ADR-002 (alkforge; why exclusion matters)