The wave-2 gate's booked finding: observability on the decline paths.
`tracing` rides the manifest per the ADR-002 toolkit but nothing in
src/ emitted — a decline's "reason" was only the routing decision
documented per build.
- src/observe.rs: the event vocabulary, adopting the family posture
read from alktty/alktunnels/alkcall/alkhttp (free tracing fns,
message-last lowercase, flat fields; no info!/trace!, no macros,
no spans): feature_build + row + channel_id + decline_class
(open/request/serving/auth) + reason + exec_route (exec rows);
declines at debug, the serving-path failure arm the vocabulary's
one warn
- handler.rs: every decline site emits — auth dispositions
(non-ed25519 / fingerprint-unresolved / the four unimplemented
methods), open-level declines (unfeatured / never-feature /
cap-exceeded / tunnel connect-failed stub), request-level
capability declines (exec route-aware, subsystem, env, x11, agent,
the -R listen forms), and the seam-stub serving declines with the
spawned-path lifecycle (started / ended / failed — the shape the
wave-3 mappings inherit)
- tests/observe.rs: the event-presence pin — a hand-rolled global
capture subscriber (the recorded test convention; no event-capture
precedent exists in the family), field-matching rows across the
default/git/session/tunnel/--all-features builds, and the safety
scan (no username, no fingerprint ever in an event)
- helper-level exact shapes pinned in src/observe.rs unit rows
Verification: cargo test (default, --features git/session/tunnel,
--all-features all green), clippy x2 --all-targets -D warnings,
fmt --check, cargo doc --no-deps 0 warnings, taskgraph validate.
- new private channel_cap module: per-session active-channel count;
admit (check+increment at accept sites) / has_room / saturating
release, single-writer (inline packet-loop hooks), unit-tested
- handler wires the cap at every channel_open_* hook: the session
accept path admits-and-counts at the accept point; the tunnel rows
cap-gate before the seam stub (a counted-but-unopened stub would
leak the cap); x11 never consumes a count; the count releases
through the new channel_close hook
- dispatch::cap_exceeded_open_decline -> AdministrativelyProhibited
(the assembly's policy ceiling; OpenSSH MaxSessions parity)
- bounded-resources contract rows: cap reached -> clean decline,
close -> recovery open succeeds (wire-order deterministic),
zero cap refuses without poisoning, exhausted cap answers the
tunnel row with the cap reason; max_auth_attempts verified
behaviorally (within-ceiling failures keep the advertisement, the
past-ceiling attempt is the NoMoreAuthMethodsAvailable teardown);
window/packet/buffer/inactivity knobs recorded as covered by the
config-mapping tests
- max_exec_command_len stays carried, not enforced (wave-3 grammar)
Verification: cargo test (default), --features session / git /
tunnel / --all-features, clippy --all-targets (-D warnings, also
--all-features), fmt --check, cargo doc --no-deps - all green
- src/serve.rs (new): the ADR-006 decision-4 mechanics in code —
spawn_serving_path (the one choke point; nothing awaited inline)
and report_exit_status_then_close (exit-status before close via
the cloned Session::handle()), #[doc(hidden)] contract-test
re-exports; a unit row pins spawn-returns-while-pending.
- src/handler.rs: the four session-row seam stubs became spawned
serving paths (failure-then-close unchanged; stubs are plain fns —
an async stub holding &Session across the spawn is non-Send); open-
level declines and the tunnel seam stub stay inline by class, per
the new module-doc section.
- tests/spawn_return.rs (new): two end-to-end wire rows — the client
observes exit-status(7) strictly before close and after the handler
returned; the handler-returns-fast row routes follow-up traffic
while the serving path is still gated (POC-3 deadlock inversion).
- tasks/implementation/w2-spawn-return.md — completed with notes.
Verification (all green): cargo test; per-feature dispatch matrices
(--features git/session/tunnel/sftp, --all-features); cargo clippy
--all-targets/-all-features with -D warnings; cargo fmt --check;
cargo doc --no-deps (no warnings); taskgraph validate.
- auth_publickey: ed25519-only gate → single fingerprint path →
resolve_from_fingerprint → accept-with-identity (stored per-
connection, read by wave 3) / reject fail-closed; the gate is
explicit-over-inherited (ADR-002 decision 4) and precedes resolution
- auth_publickey_offered: explicit accept-and-delegate (counting
stays honest, POC-1)
- auth_none / auth_password / auth_keyboard_interactive /
auth_openssh_certificate: four explicit family-contract declines
- username carried by no hook — resolution is fingerprint-only (OQ-03)
- SessionHandler constructible via #[doc(hidden)] re-export (the
w2-server-mount note's widening); resolve_bridge structured out of
the #[non_exhaustive]-arg hooks so contract tests drive the bridge
core directly
- tests/auth_bridge.rs: 21 rows — wire rows over the real adapter
mount (accept, fail-closed reject, username independence, revoke
between attempts, none/password/certificate declines) + direct
rows over the wave-1 doubles
- cert fixture (throwaway ca + alpha-cert.pub) recorded in
fingerprints.txt; MemHostKeySource double in tests/common
Verification: cargo test (76 across the suite), --all-features,
clippy --all-targets (both feature sets, -D warnings), fmt --check,
doc --no-deps, taskgraph validate — all green
- SshAdapter:🆕 decrypt-at-construction host-key load (ADR-005
decision 6) — locked / absent / decryption / unknown-version /
decode all refuse with AdapterError::Construction(KeySourceError
preserved), no adapter; an empty key set refuses as Absent even
from a custom source.
- russh 0.65 server Config built from ServeConfig: methods exactly
{publickey} (MethodSet from [MethodKind::PublicKey] — honest
advertisement, ADR-004 decision 4); host keys ride Config::keys;
Limits knobs (window_size, maximum_packet_size,
channel_buffer_size, max_auth_attempts) one-for-one;
inactivity_timeout always Some (bounded-resources posture). All
other knobs stay russh's default posture: rekey Limits (RFC-4253
defaults, russh-enforced), auth_rejection_time, server_id
(russh default — recorded), server keepalive (client-direction
posture only).
- SshAdapter::handle: ADR-001 mount — generic duplex stream in,
run_stream per stream, run to completion (Result<(), AdapterError>;
no ALPN/relay code; the serving loop is spawned inside russh, so
ADR-006's spawn-and-return holds without double-spawning).
- SessionHandler skeleton (pub(crate)): per-connection
russh::server::Handler with type Error = AdapterError; every hook
at the inherited fail-closed default; carries Arc<ServeConfig> for
w2-auth-bridge / w2-dispatch-decline-core.
- AdapterError (error.rs): Construction + Wire (From<russh::Error>)
— the wave-1 error-model's reserved wave-2 shape.
- Tests: advertisement-exactly-publickey; knob mapping (tuned +
default postures); key-set parity; construction failure matrix;
EOF-on-mount; wire smoke (russh client over duplex: full KEX,
fail-closed auth decline, exactly the {publickey} method list on
the wire).
Verified: cargo test (61 across suites), cargo test --all-features,
clippy --all-targets -- -D warnings (default + all-features),
fmt --check, cargo doc --no-deps, taskgraph validate.