- 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
152 lines
7.7 KiB
Markdown
152 lines
7.7 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this crate are documented here. The format is
|
||
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
||
this crate adheres to [Semantic Versioning](https://semver.org/).
|
||
|
||
## [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 in
|
||
`poc-1..3` + `reverse-runner.md`; POC source never merged).
|
||
- Phase 1 architecture under `docs/architecture/`: the spec docs and
|
||
ADRs 001–006 (Accepted) — the `alk/ssh` ALPN 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; no `unwrap` outside tests.
|
||
- Vault-backed key custody in code: the `KeySource` /
|
||
`HostKeySource` seams and the ADR-005 implementations
|
||
(`VaultKeySource`, `VaultHostKeySource`) — decrypt-at-use /
|
||
decrypt-at-construction, the `Zeroizing` seam, one
|
||
`decode_secret_key` conversion.
|
||
- The `HostKeyPolicy` seam (OQ-05, policy-blind) with the fail-closed
|
||
default `RejectAll` — 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_failure` then 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 (`tracing` events 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; `1` is honestly reported for push `ng`.
|
||
- 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-streamlocal` opens ride the assembly's `TunnelSession` over
|
||
the family fabric; fabric failure is a clean `ConnectFailed` reject.
|
||
- Tunnel leg 2: the `-R` listen flow — `tcpip_forward` /
|
||
`streamlocal_forward` dial the fabric, arrivals correlate through
|
||
the token router (miss → teardown-at-birth), the data leg opens
|
||
`forwarded-tcpip`/`forwarded-streamlocal` back 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; `SshConnection` the 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 the `sftp`
|
||
decline row (always declines, every build).
|
||
- The `-R` leg: `reverse_listen` / `reverse_listen_unix` + the
|
||
`AcceptorRegistry` (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
|
||
`ListenRouter` token-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 real
|
||
`sshd` against the client direction (exec, direct-tcpip dial, `-R`
|
||
listen + 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
|
||
|
||
- `sftp` is a feature of decline: enabling it adds no capability —
|
||
the `sftp` subsystem request still declines in every build until
|
||
the alkfs mapping lands (OQ-13; the decline stays contract
|
||
territory).
|
||
- The `env` request declines unconditionally (OQ-09): session cwd/env
|
||
are assembly-supplied, never wire-derived.
|
||
- The russh pin (0.65, `aws-lc-rs`, `des`/`dsa` never enabled) is a
|
||
wire-relevant one-way door: bumps are deliberate, tested, and
|
||
recorded (ADR-002). |