Files
glm-5.3-flash f4b5f896b4 docs: sweep em dashes from all markdown prose
Renames the written-usage rule into practice across the contract surface:
docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files,
taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the
.opencode/agents/ prompts. Every replacement is a sense-preserving
rewrite (colons, semicolons, commas, parentheses, sentence splits, varied),
never a mechanical swap; no spaced hyphens were substituted. Enum fields,
frontmatter, tables, wire data, and cited identifiers survive verbatim;
en dashes inside numeric ranges keep their meaning.

Verification: taskgraph validate (43 tasks), repo grep clean (single
exempt hit: the verification command's own search pattern in AGENTS.md),
cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
2026-11-04 20:28:00 +00:00

6.2 KiB

status, last_updated
status last_updated
reviewed 2026-10-28

ADR-002: The russh pin (0.65.0, aws-lc-rs, no compression, ed25519-only)

Status

Accepted (2026-10-26). A one-way door in practice (SSH negotiation behavior crosses crate boundaries; bumps are deliberate, tested, and recorded per the repo's wire-stability convention). Amended same day after review: the earlier enabling of the rsa feature is reverted (rsa off). Amended 2026-10-27 (review pass): the key-algorithm position is ed25519-only; the family's posture is the binding constraint, not fleet compatibility (decision 4).

Context

russh.md documents the crate inventory and the pin questions; the 2026-10-26 re-verification section carries the current facts. The POCs validated against 0.64.1 (then-newest) with the ring backend; since then 0.65.0 has been published (2026-10-25) with six security fixes, three of them in classes an internet-facing SSH door cares about (client EXT_INFO unbounded allocation, peer zero-max-packet-size channel stall, pre-auth nested-certificate stack exhaustion), and two breaking changes confined to handler-signature shapes (PublicKeyOffer / VerifiedPublicKey; async-trait feature removed).

The family's crypto-posture context: alktls, alkhttp, and alknet all standardize on rustls with the aws_lc_rs feature; no family crate links ring. The provider choice matters only as binary-composition hygiene (when alkforge's binary links alkssh and the family's rustls stack, the goal is one shared primitive library) and has nothing to do with TLS on the SSH wire.

Decision

  1. Pin russh = "0.65" (caret-compatible within 0.65.x, matching the family's "0.10"-style convention; russh minors have broken API before, so the caret stops at the minor). 0.65.0 over 0.64.1: current published + the three security fixes above; the breaking changes land inside the handler surface alkssh implements anyway. POC evidence transfers (wire behavior unchanged); the implementation phase re-verifies the POC-1 decline matrix against 0.65 as contract tests.
  2. Crypto backend: aws-lc-rs. Family-confirmed (alktls / alkhttp / alknet all rustls(aws_lc_rs); nothing in-family links ring) and it is russh's own default. Verified in-scratch: builds clean in ~50 s with no external provisioning (prebuilt bindings), tree aws-lc-rs 1.18.2 / aws-lc-sys 0.46.0. The POCs' ring build is recorded as the fallback if a target platform ever fails to provision aws-lc, a deliberate, recorded deviation, never drift.
  3. No compression (flate2 off). The POCs already ran compression-free and vanilla OpenSSH was untroubled; SSH compression is largely pointless at modern link layers and is the CRIME-adjacent attack surface OpenSSH itself defaults off. Bounded-resources and surface-reduction posture agree.
  4. ed25519-only (rsa off, and no other scheme is supported). Amended 2026-10-27 from the earlier "ed25519-first" wording after review: the position is ed25519-only. One key algorithm carries the family's identity: the same ed25519 keys serve alktls's self-signed TLS certificates and iroh, and the downstream alknet rewrite rides ssh + ed25519, tcp + tls, quic + tls, and iroh, the same ed25519 identity. The legacy/alternative SSH key schemes (rsa, dsa; ecdsa variants and hardware-token sk variants share the defect) are issued, stored, or derived nowhere in the family, so supporting them here would be a wart backed by nothing else. Enabling rsa would additionally have pulled two RC-pinned deps and made alkssh the family's only RSA-holding crate. Concretely: the tree drops rsa =0.10.0-rc.18 and pkcs1 =0.8.0-rc.4 entirely (verified: the ["aws-lc-rs"]-only build is clean and RC-free except ssh-key =0.7.0-rc.11, which russh exact-pins on the russh::keys path). Wire behavior is honest and fail-closed: a client presenting a non-ed25519 key gets a clean USERAUTH_FAILURE, never a half-open session. Operational shape: alkssh implements no other scheme; an assembly registers ed25519 fingerprints only; and auth_publickey declines non-ed25519 keys explicitly (ADR-004's explicit-over-inherited pattern applied to the algorithm position, so the posture survives a pin bump that might otherwise accept more schemes). If a scheme ever matters, the amendment is one feature flag plus this ADR.
  5. des / dsa never. russh's own "Danger: insecure" features, which no alkssh configuration may enable.
  6. Manifest lands with the dependency: rust-version = "1.89" (russh's MSRV; supersedes the current 1.88) and, with it, the edition-2024 bump the POCs compiled under.

Consequences

  • Positive: the pin is current, carries the security fixes, and matches the family crypto posture; the tree is fully inventoried, and with rsa off it is RC-free except ssh-key =0.7.0-rc.11 (unavoidable: russh exact-pins it on the russh::keys path); MSRV/licensing are explicit.
  • Negative: clients presenting non-ed25519 keys (RSA among them) decline outright (clean USERAUTH_FAILURE), recorded as intentional; ssh-key's RC pin rides the russh keys path until russh moves off it; aws-lc-rs is the heavier build (proven acceptable here, with ring as the recorded fallback).
  • Neutral: at 0.65.0 the key arrives as VerifiedPublicKey.public_key with the client's signature .algorithm exposed; the ed25519-only posture (decision 4) acts on the key-algorithm fact at auth_publickey, and the .algorithm field remains available for further hardening if ever wanted. ADR-004 carries the matching amendment note; its other dispositions are unchanged.

References

  • docs/research/russh.md (inventory + the 2026-10-26 re-verification section with the scratch-build record)
  • docs/research/poc-1-auth-bridge.md (the 0.64.1 wire evidence; backend note), docs/research/poc-2-vault-keys.md, docs/research/poc-3-mapping-bakeoff.md (0.64.1 runtime facts)
  • ADR-001 (the pin context), ADR-003 (feature graph the russh features compose under), ADR-004 (auth dispositions on this surface)
  • Family: alktls/alkhttp/alknet manifests (the aws-lc-rs posture), family ADR-002 decision 5