- docs/plans/implementation.md: five-wave decomposition of the architecture (foundations -> server stack -> server mappings || client direction -> validation & release), following the alkgit sibling pattern (decompose-implement-gate rhythm, per-wave decomposition records, review gates at wave boundaries) - scope authority: v0.1.0 publishes the crate; assembly wiring stays out; sftp decline placeholder is shipped behavior - wave 1 decomposed into eight task files under tasks/implementation/ (manifest, error model, key sources, host-key policy, fingerprint, config surface, test doubles, review gate) with recorded interpretations flagged for the gate Verification: taskgraph validate (12 tasks, no cycles); cargo test, clippy -D warnings, fmt --check green
2.5 KiB
2.5 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| w1-host-key-policy | Wave 1 — the HostKeyPolicy seam (OQ-05, policy-blind) | pending |
|
narrow | low | project | implementation |
Description
Land the client-side host-key policy seam (auth.md §Host-key policy,
OQ-05 resolution) — the trust-decision contract the dial surface calls:
- The
HostKeyPolicytrait — the crate surfaces the presented host key (host, port, fingerprint) to an assembly-provided policy that decides accept/reject. The two OQ-05 cases are the implementor's: case-1 pins (known beforehand — hard-decline on mismatch) and case-2 policies (unknown beforehand — prompt-and-record, learn-and-record, provider-metadata match, accept-and-continue for ephemeral fleets). The crate is policy-blind: it never decides trust, never hard-codes a trust model. - The crate default is fail-closed: reject unknown (russh's own reject-all default, kept as the built-in default policy). No accept-all built-in crate option — a permissive case-2 policy is the assembly's to write.
- The trait receives the fingerprint string (the wave-1 fingerprint
computation is the caller's input path, wave-4 dial); the exact
decision type (bool vs a richer verdict carrying a reason) is the
task's to pin against the russh
check_server_keyhook shape at this pin — flagged for the wave-1 gate. - The russh client-handler consumption (
check_server_keywiring) is wave-4 dial-surface material — this task lands the seam and its default only.
Acceptance Criteria
HostKeyPolicytrait in place; the presented-key context (host, port, fingerprint) is the decision input- Built-in default policy rejects unknown keys (fail-closed); no accept-all option exists in the crate
- Doc comments carry the two-case model and the policy-blind rule
- Unit tests: default policy rejects; a custom policy's verdict is honored
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --checkpass
References
- docs/architecture/auth.md §Host-key policy (OQ-05 resolution)
- docs/architecture/client.md §The dial surface, §What the crate does not include (trust decisions)
- docs/architecture/decisions/005-at-rest-key-custody.md (decision 6 — the client host-pin is public config, never vault material)
- docs/research/auth-bridge.md §Host keys and
check_server_key
Notes
To be filled by implementation agent
Summary
To be filled on completion