Six implementation tasks + the gate under tasks/implementation/, chained sequentially (the auth hooks, dispatch, and channel cap all edit the same per-connection Handler impl): - w2-server-mount: SshAdapter::handle mount + russh Config construction from the serve-config (honest advertisement, host-key decrypt-at-construction, the Limits knob mapping) + the Handler skeleton at fail-closed defaults - w2-auth-bridge: the ADR-004 dispositions (ed25519-only explicit decline, fingerprint-only resolution, explicit declines for every non-publickey method) - w2-loopback-harness: the in-process client/server contract-test vehicle over the wave-1 doubles - w2-dispatch-decline-core: parse-only dispatch + the fail-closed decline core, contract-tested per feature build - w2-spawn-return: the structural constraint pinned by test + the exit-reporting mechanics via the cloned Session::handle() - w2-bounded-resources: the in-crate concurrent-channel cap at the handler surface - review-wave-2: the gate The plan carries the wave-2 decomposition record with four interpretations flagged for the gate (skeleton-level git probe, declining routing stubs, the adapter error surface, the harness transport choice). taskgraph validate: 19 tasks, clean.
3.4 KiB
3.4 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||
|---|---|---|---|---|---|---|---|---|---|---|
| w2-auth-bridge | Wave 2 — the auth bridge (dispositional handler surface) | pending |
|
moderate | low | component | implementation |
Description
Fill the Handler skeleton's auth hooks with the ADR-004 dispositions — the fingerprint → identity bridge, fail-closed on every path:
auth_publickey(fires post-verification; the key arrives asVerifiedPublicKey.public_keywith the signature.algorithmexposed): non-ed25519 algorithm → explicit reject (ADR-002 decision 4 — the explicit-over-inherited pattern, so the posture survives a pin bump); ed25519 → fingerprint via the crate's single fingerprint path (src/fingerprint.rs, the ADR-004 contract invariant) →IdentityProvider::resolve_from_fingerprint→Some(identity)→Auth::Acceptwith the resolved identity stored in the per-connection handler state (it rides the session for the wave-3 seam propagation —auth_succeededis the ride-along hook);None→ reject fail-closed. The resolved-identity-Nonepath never creates a weakly authorized session.auth_publickey_offered— accept-and-delegate (the russh default shape at this pin; keep it explicit so the counting stays honest — the hook does not consume an attempt, POC-1 verified).- Explicit declines —
auth_none,auth_password,auth_keyboard_interactive,auth_openssh_certificateeach get an explicitAuth::reject()implementation (family contract, not inherited defaults — a pin bump could silently change them). - The username carries no semantics (OQ-03) — resolution is fingerprint-only; the user string is never an identity input.
Auth declines are dispositions (Auth::Reject), not errors — the
error model stays key-source-only. Tests run against the wave-1
doubles (MemIdentityProvider + the ed25519 fixture keys) at the
handler level; the loopback harness proves the bridge end to end over
the wire in w2-loopback-harness.
Acceptance Criteria
auth_publickey: non-ed25519 → explicit reject; ed25519 → fingerprint →resolve_from_fingerprint→ accept-with-identity / reject fail-closed; the resolved identity lands in per-connection handler stateauth_publickey_offeredaccepts-and-delegates explicitlyauth_none/auth_password/auth_keyboard_interactive/auth_openssh_certificateeach explicitly reject- The username is never an identity input (fingerprint-only resolution; OQ-03)
- Handler-level tests: known-fingerprint accept (fixture key +
MemIdentityProvider), unknown-fingerprint reject, non-ed25519 explicit reject, every non-publickey method rejects cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --checkpass
References
- docs/architecture/decisions/004-auth-dispositions.md (the dispositions, decisions 1–5)
- docs/architecture/auth.md §Server direction (the bridge steps; the
0.65.0
VerifiedPublicKeyshape) - docs/architecture/decisions/002-russh-pin.md (decision 4 — ed25519-only, the explicit-decline rationale)
- docs/plans/implementation.md §Wave 2 (the auth-bridge bullet)
- russh 0.65.0 in-source:
Auth,VerifiedPublicKey,PublicKeyOffer, the auth hook signatures
Notes
To be filled by implementation agent
Summary
To be filled on completion