- resolve the rustdoc warning set (9 incl. private-link class in client_ops.rs + redundant explicit links + a duplicated tty_map paragraph): cargo doc warning-free on default and --all-features - docs honesty: the five recorded limits ride the published crate-root rustdoc (§Known limits: write_all no-deadline, PTY rule-5, port-0-only mint echo, no-reconnect policy, no-accept-all); crate docs state the feature graph + the fail-closed invariant - two no_run usage doctests (server mount + dial) — the README examples stay compile-checked in every matrix build - README.md lands (the door, feature graph, examples, custody posture, license); CHANGELOG.md carries the 0.1.0 founding entry - feature matrix green (6 test builds, clippy x2 -D warnings, fmt, doc) + the fuzz corpus gate (11 invariants) - cargo publish --dry-run --allow-dirty clean (76 files packaged + verified; file list checked against the package policy; manifest sanity recorded); the freeze inventory recorded for the gate Verification: scripts/matrix.sh (all builds), fuzz corpus gate, taskgraph validate
7.7 KiB
Changelog
All notable changes to this crate are documented here. The format is based on Keep a Changelog, and this crate adheres to Semantic Versioning.
[0.1.0] - 2026-10-30
The founding entry — the crate's first release. alkssh is the alk
family's SSH adapter (family ADR-003): a thin wrapper around russh
0.65 terminating the vanilla-ssh wire and bridging to/from alkcall
channels — the two-direction door for vanilla clients. The wire is
russh's; the crate's substance is the channel-type mappings
(session/pty → alktty, direct/forwarded-tcpip → alktunnels, git exec →
alkgit), each behind its cargo feature in a flat, independent graph
(ADR-003): a default build has no capabilities and serves nothing
but clean declines — every unfeatured or unimplemented request is
declined fail-closed, never half-served or silently fallen back, and
never advertised. Identity is the family's: a verified key's
fingerprint → alkcall IdentityProvider; keys the crate holds come
from alkvault-backed sources, encrypted at rest, decrypted at use,
never env vars (ADR-005). The crate is a door, not a backend — no
binary, no deployment policy; a downstream app embeds it, enables
features, and brings identity/ACL configuration.
Added
Phase 0/1 — research and architecture (the contract the implementation lands):
- SDD process, phase 0 research set (
docs/research/), and the three passed POCs (auth bridge, vault keys, mapping bake-off; findings inpoc-1..3+reverse-runner.md; POC source never merged). - Phase 1 architecture under
docs/architecture/: the spec docs and ADRs 001–006 (Accepted) — thealk/sshALPN pin; the russh 0.65.0 pin (aws-lc-rs, no compression, rsa off); the capability feature graph; auth dispositions; vault-key custody; the mapping/bridge/ dispatch decisions.
Wave 1 — foundations:
- The manifest: the russh 0.65 pin (
aws-lc-rs, default features off), the flat feature graph (session/git/tunnel/sftp,default = []). - The fail-closed error taxonomy (
thiserror):AdapterError,DialError,KeySourceError,OpError,OpenDeclineReason— declines are dispositions, never panics; nounwrapoutside tests. - Vault-backed key custody in code: the
KeySource/HostKeySourceseams and the ADR-005 implementations (VaultKeySource,VaultHostKeySource) — decrypt-at-use / decrypt-at-construction, theZeroizingseam, onedecode_secret_keyconversion. - The
HostKeyPolicyseam (OQ-05, policy-blind) with the fail-closed defaultRejectAll— no accept-all built-in exists in the crate. - The contract-invariant fingerprint computation (the crate's single
fingerprint path, the
SHA256:format both the auth bridge and the host-key policy consume). - The config surface:
ServeConfig,DialConfig,Limits(the bounded-resources budget model — wall clock always set, channel caps, keepalive posture) — unset fields decline at request time (OQ-08), construction never validates.
Wave 2 — the server stack:
SshAdapter— the mount point:SshAdapter::new(serve)builds the russh server from the config (fail-closed on key-source failure);adapter.handle(stream)serves one connection.- The auth bridge: russh auth hooks → ADR-004 dispositions —
verified-key fingerprint →
IdentityProvider::resolve_from_fingerprint, fail-closed on every path; bounded auth attempts. - Parse-only dispatch + the fail-closed decline core: every unroutable
or unfeatured request answers a clean, channel-scoped decline; the
spawned serving-path shapes (
channel_failurethen close). - Spawn-and-return plumbing (ADR-006 decision 4): serving paths run spawned (russh handlers return fast at this pin), with the exit-status-before-close mechanics.
- Bounded resources at the handler surface: per-connection channel caps (refuse-never-queue) and listener caps.
- The in-process loopback harness (the contract-test vehicle).
Wave 3 — the server mappings (the spine):
- Per-feature seam handles (
SessionSeam,GitSeam,TunnelListenSeam): unset seam → decline even with the feature on. - Decline-path tracing (
tracingevents on every decline class). - The git mapping (exec-only, ADR-006): the full exec grammar —
git-{upload,receive}-pack '/<repo>'parsed and dispatched to alkgit; a subsystem request can never carry the repository and declines;1is honestly reported for pushng. - The session mapping: pty/shell/exec through the alktty seam over a translation bridge (chunk-framed both directions), OQ-44's reserved ttymodes JSON, the exit-chunk → SSH exit derivation (rules 1–6), control rows (window-change, signal) on a bounded per-channel surface.
- Tunnel leg 1: the route-1 bridge —
direct-tcpip/direct-streamlocalopens ride the assembly'sTunnelSessionover the family fabric; fabric failure is a cleanConnectFailedreject. - Tunnel leg 2: the
-Rlisten flow —tcpip_forward/streamlocal_forwarddial the fabric, arrivals correlate through the token router (miss → teardown-at-birth), the data leg opensforwarded-tcpip/forwarded-streamlocalback toward the client, port-0 mints echo the actually-bound port.
Wave 4 — the client direction:
- The dial surface:
dial(DialConfig)— key load (decrypt-at-dial), bounded russh client config, the host-key policy call, exactly one publickey attempt;SshConnectionthe handle. - The op rows:
exec/pty_exec/shell(the alktty chunk plane),tunnel_direct/tunnel_direct_unix(byte duplex),git_service(pack duplex + consumed exit) — plus thesftpdecline row (always declines, every build). - The
-Rleg:reverse_listen/reverse_listen_unix+ theAcceptorRegistry(refuse-never-replace) and the acceptor traits — the hub asks the vanilla server to listen and serves the accepted channels. - Lifecycle primitives (OQ-12): explicitly cancellable listener
handles, clean
close()(Disconnect::ByApplication) — drop is never the clean path at this pin; no reconnect/restore policy (the crate ships primitives, the assembly composes). - Hardening riders: the git row's status-before-close by construction,
the channel-not-held guard wire rows, tty control-row coverage, the
ListenRoutertoken-collision pre-check.
Wave 5 — validation and release:
- The decline-matrix contract suite: every mapping-decline row × every
build it can appear in × both directions (the matrix runner
scripts/matrix.sh— one command over all feature builds). - Real-tool e2e both directions: OpenSSH (pty/shell/exec,
-L,-R,-D+ curl), git clone and push through the door, and a realsshdagainst the client direction (exec, direct-tcpip dial,-Rlisten + acceptor) — check-gated, skipped rows carry reasons. - The
fuzz/sidecar: the ingress surfaces (exec parse grammar, fingerprint format, key decode,TerminalParamsWire) with the committed-seed corpus gate (cargo test --manifest-path fuzz/shared/Cargo.toml). - Release readiness: warning-free crate docs with the known-limits records riding the published rustdoc, this README, and the publish dry-run posture.
Notes
sftpis a feature of decline: enabling it adds no capability — thesftpsubsystem request still declines in every build until the alkfs mapping lands (OQ-13; the decline stays contract territory).- The
envrequest declines unconditionally (OQ-09): session cwd/env are assembly-supplied, never wire-derived. - The russh pin (0.65,
aws-lc-rs,des/dsanever enabled) is a wire-relevant one-way door: bumps are deliberate, tested, and recorded (ADR-002).