Same treatment for the prose the compiler carries: doc and line comments
across src/ and tests/, test assertion and error-message strings (prose
surfaces: the tunnel-map registration/establish errors, the key-source
decode payload, assertion messages), scripts/matrix.sh comment lines,
the fuzz sidecar's comments and README (corpus seeds untouched), and the
Cargo.toml comment headers plus the crates.io description. The AGENTS.md
no-em-dash rule (convention 14) now holds for the whole tracked tree.
Verification: repo-wide git grep is clean (single exempt hit: the
verification command's own search pattern), cargo test (18 suites, zero
failures, default and per-feature builds via scripts/matrix.sh), clippy
--all-targets -D warnings and clippy all-features, fmt --check, doc
--no-deps, and the fuzz corpus gate (fuzz/shared, 11 tests) all green.
- src/client_accept.rs: the ReverseListener handles (ReverseEndpoint
carried; cancel() the wire-honest teardown) returned by the -R listen
rows; the shared cancel paths both the handles and the stateless wire
forms compose into; the confirmed cancel releases the listener count
exactly once; no Drop hides a wire op
- src/client.rs: the SshConnection internals behind the Arc
(ConnectionShared) — the handles' independent cancels compose on the
same stack; the §Lifecycle posture module docs (primitives-not-policy,
OQ-12; the abrupt-drop/clean-close facts, row-pinned)
- rows: drop-handle no-wire-state, handle-cancel + budget release,
cancel-mid-data-leg (tunnel build) + the every-build abrupt-drop row;
existing reverse rows adapted to the handles; the unix cancel rides
the handle flavor
- docs/architecture/client.md: the §Lifecycle primitives landing block;
task file filled
Verification: cargo test (default / session / git / tunnel / all-features)
all green; clippy ×2 -D warnings clean; fmt clean; cargo doc --no-deps clean
The client direction's substrate (client.md §The dial surface), the
mirror of w2-server-mount: dial(DialConfig) runs the fail-closed
four-step sequence — decrypt-at-dial key load (every KeySource
posture a distinct no-dial outcome), the bounded russh client Config
from Limits (keepalive 15 s / 3, the wall clock always Some, window/
packet/buffer verbatim), the injected host-key policy at
check_server_key (RejectAll connects to nothing; UnknownKey maps to
DialError::HostKeyRejected — its only producer), and exactly one
publickey-only auth attempt — and returns the authenticated
SshConnection handle everything else in the wave rides.
- DialConfig amendment (the sanctioned course-correction, recorded):
the required `username` field — the USERAUTH_REQUEST's user string;
no crate default, never identity, never logged (ADR-004 decision 5).
- The client Handler skeleton (src/client_handler.rs): every
server_channel_open_* hook overridden explicit-over-inherited with
a clean AdministrativelyProhibited reject (never russh's
accept-and-drop default, ADR-003 §4); should_accept_unknown_
server_channel the explicit false; decline events on the crate
vocabulary (no-acceptor / never-feature reasons).
- The in-process fake sshd (tests/common/sshd_double.rs): the
client-direction contract vehicle — a plain russh server on a real
loopback TCP listener, accept-set identity resolution, the auth_
succeeded handle carrier for the server-initiated-open rows, the
per-session outcome channel for the clean-close rows.
- tests/client_dial.rs: 10 contract rows — dial success, the five
key-source no-dial postures, RejectAll / pin-mismatch / case-2
policy rows, refused auth, the malformed-address dial-time refusal,
the bounded-config observable, the six-hook decline wire rows with
decline events, and the smoke: alkssh-client ↔ alkssh-server over a
real TCP loopback (real-sshd dial stays wave-5 e2e).
Verification: cargo test + per-feature + --all-features (14 binaries,
all green), cargo clippy ×2 (-D warnings), cargo fmt --check,
cargo doc --no-deps — all clean. taskgraph validate: 35 tasks valid.