The phase 0 document set (documented only; no POCs run yet): - docs/research/vision.md — guiding principles (russh owns the wire, door-not-backend, fail-closed everything, one seam per mapping, family identity not SSH identity); non-goals (no sshd, no sftp until alkfs, no password auth); the five scope questions phase 0/1 must answer - docs/research/russh.md — 0.64.1-era inventory: server/client Handler method tables, Config knobs, keys module (ssh-key re-export, fingerprint API), the RC-pinned dependency tree (ssh-key, rsa, pkcs1), MSRV/edition/license posture; alknet-main referenced as a rough example only, explicitly not decision input - docs/research/auth-bridge.md — the alkcall mapping: no resolve_from_signature exists; the actual surface is IdentityProvider::resolve_from_fingerprint fed by russh-verified key fingerprints in auth_publickey; auth-method disposition matrix (only publickey implemented, all defaults overridden); client side mirrors alkhttp ADR-014 (key sources, not static keys; vault client-side only); the vault-to-russh-key bridge named as the missing design piece - docs/research/mapping-table.md — the spine: channel-type rows with feature candidates and the verified sibling serve/consume entries (alktty drive_session, alkgit serve_duplex_resolved, alksocks serve_socks5_over, alktunnels consumer wrappers + the no-serve-over- stream absence note); request-level rows; parse-then-route dispatch inside the session channel; the alk/ssh ALPN topology question (ADR-004 reserved / ADR-034 stream_types [0,1] / ADR-042 hub never runs it); alktunnels route choice flagged for ADR - docs/research/pocs.md — POC-1 (auth bridge + fail-closed declines against vanilla OpenSSH), POC-2 (vault-to-russh key bridge: PrivateKey extraction vs vault-backed Signer), POC-3 (mapping bake-off stages A-D: git exec, socks -D, tunnel -L, pty); all standalone mode; findings docs are the deliverables Verification: cargo doc --no-deps, cargo fmt --check, cargo test (docs-only change; lib untouched)
5.4 KiB
Vision
Status: Draft (phase 0 research).
What this is
alkssh is the alk family's SSH adapter: a thin wrapper around russh terminating the vanilla-ssh wire and bridging to/from alkcall channels (family ADR-003). It is the alkhttp-style adapter for the SSH protocol — the vanilla-ssh front door for vanilla clients, and the family's vanilla-ssh client where a family app dials an upstream SSH server.
The substance almost entirely exists in the family. What alkssh adds is the glue + the mapping:
- vanilla ssh (ssh(1), git(1), sftp clients) connect to an alkssh endpoint as they would to any sshd;
- each SSH channel type is mapped onto a family protocol half
(see
mapping-table.md); - identity arrives the same way it does everywhere else in the family:
a verified SSH public key's fingerprint resolved through alkcall's
IdentityProvider(seeauth-bridge.md).
The alkforge-relevant property is exclusion: an alkforge ssh
endpoint built with only the git feature is structurally incapable of
serving a shell, tunnels, or socks. Every mapping is a cargo feature;
unfeatured channel opens decline fail-closed (family ADR-003 decision
3). This is why alkssh leads alkforge's phase 0 — the adapter is glue,
not protocol invention.
Guiding principles
- russh owns the SSH wire. No SSH framing is authored in-family (family ADR-003 decision 1). KEX, cipher negotiation, channel multiplexing, and userauth state machines come from russh; third- party-upstream posture applies (file asks upstream; never silently absorb a workaround).
- A door, not a backend. alkssh speaks family-protocol types only at the mapped seams. It carries no deployment policy: a downstream app (alkforge) embeds it, enables features, and brings identity/ACL configuration.
- Fail-closed everything. The feature graph, the auth-method disposition, and the channel-request disposition all default to a clean decline. Nothing is advertised that isn't served; nothing is half-served. Every decline path is contract territory and is tested.
- One seam per mapping. Each family protocol already exposes a
generic duplex-stream or
Connectionserving entry (mapping-table.md); a russh channel's read/write halves plug in at exactly one point per mapping. No channel-type knowledge flows upstream into the protocol crates. - Identity is the family's, not SSH's. SSH user names,
~/.ssh/conventions, and password auth are not the alk family's identity model. A verified key's fingerprint →Identityvia alkcall'sIdentityProvideris the only resolution path; secrets an alkssh client holds come from alkvault-backed sources, never env vars (family ADR-002 decision 5).
Non-goals
- Authoring SSH protocol machinery — no in-family framing, cipher, or KEX code, ever (russh upstream decides what SSH is).
- Being an sshd. No local user accounts, no
authorized_keysfile semantics of the OpenSSH variety, no PAM-shaped anything. Deployment posture belongs to the embedding app. - Serving sftp — deferred to alkfs (family ADR-002); the subsystem decline is the placeholder contract.
- A binary / CLI. Downstream apps embed the crate; there is no
alksshexecutable in this crate. - Password / keyboard-interactive auth for clients. The family
identity model has no password concept; these methods decline
server-side (see
auth-bridge.md) and exist client-side only as non-goals until a consuming use case arrives.
Scope questions phase 0 must answer
These frame the research docs; each is a candidate open question for
phase 1's open-questions.md:
- The
alk/sshALPN's topology role — alkcall ADR-004 reservesalk/ssh→SshAdapter, ADR-034's channels table gives it stream_types[0, 1]("SSH multiplexes internally, including its own control"), and ADR-009 sketches a browser-WASM route where the ALPN transports an SSH stream to anSshAdapter. But family ADR-003 describes alkssh terminating vanilla SSH at an edge. Whetheralk/sshcarries raw vanilla-SSH bytes between alkssh instances (a relay leg), serves the browser route, or both — and how that interacts with ADR-042's "the hub never runs a handler foralk/ssh" relay ruling — is unresolved (seemapping-table.md§topology). - russh pin — version, crypto-backend feature, and the
RC-pinned transitive dependency situation (see
russh.md). - Feature names + graph — the
session/tunnel/dynamic--forward/git/sftpnaming and their independence (no feature implies another; flat until architecture says otherwise). - Auth dispositions — which russh auth methods the server
implements vs declines; the exact
auth_publickey→ fingerprint → identity contract; the client-side vault→russh key bridge (seeauth-bridge.md). - The mapping table details — per channel type and per request,
the family seam, the parameter mapping, and the decline contract
(see
mapping-table.md); POC validation perpocs.md.
References
- family ADR-003 (
/workspace/@alkdev/alkfamily/docs/architecture/decisions/003-alkssh-russh-adapter.md) - family ADR-002 (alkforge; why the exclusion property matters)
- family open-questions OQ-08 (partially resolved; remaining scope is this repo's phase 0)
russh.md,auth-bridge.md,mapping-table.md,pocs.mdin this directory