main
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1e583ab55b |
docs(comments): sweep em dashes from source comments, test prose, and manifest
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. |
||
|
|
0d3fd48f5d |
feat(client): land the dial surface — the authenticated connection handle + the fail-closed client handler skeleton (w4-dial)
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. |
||
|
|
3d3bb5e9bf |
test(mapping): land the channel-not-held guard's wire rows on all three served rows (w4-channel-not-held-rows)
- git row: a second exec on a served git channel answers channel_failure inline — the serve rides on, no second serving path; race-free by construction (advertisement drained, serve parked at the gated list_refs) - tty rows: the pipe row (second exec) and the shell row (second shell request) — the same inline decline shape, channel still usable - the rows assert the decline event's exact request vocabulary (class request, reason channel-not-held, route git/pipe/absent, debug level, self-describing build) — the class's vocabulary coverage - tests/common/event_capture.rs: the observe-suite's capture convention shared across the mapping binaries - test-only: no production change; the guard held its documented shape in every featured build (verified default / git / session / session,git / tunnel / sftp / --all-features) Verified: cargo test (+ per-feature + --all-features) all green · cargo clippy --all-targets -- -D warnings ✓ · cargo clippy --all-features --all-targets -- -D warnings ✓ · cargo fmt --check ✓ · cargo doc --no-deps 0 warnings · taskgraph validate ✓ (35 tasks) |
||
|
|
3d0253d981 |
feat(tunnel): land the -R listen flow + the forwarded-tcpip data leg (w3-tunnel-listen)
The tunnel branch's other half is served for real against published alktunnels 0.4.1 / alkcall 0.10.1, with POC-3 §C.4 as the template: - tcpip_forward/streamlocal_forward (+ both cancel forms) map the vanilla listen request family-ward through TunnelListener::open on the held ChannelClient (bounded inline dial — the global request's reply + the &mut u32 bound-port out-param are the hook's); the wire bind endpoint rides as the resource string verbatim (OpenSSH client-side normalization parity). A reply without the minted listen token/bound report declines and reaps its fabric listener. - the door serves the fabric's inbound tunnel/forwarded opens: the serve config gains TunnelListenSeam (the seam-config second course-correction — the shared ServingConfig registry the assembly wired at from_connection_with_serving); alkssh registers the forwarded openable (ChannelCore over a clone of the held client's manager) at SshAdapter::new — a failed registration is a failed construction (AdapterError::ListenRegistration). - the forwarded-tcpip data leg: token correlation through the shared ListenRouter → the in-crate per-session channel cap at the accept surface (the acceptor vocabulary) → Handle:: channel_open_forwarded_tcpip streaming the listen request's own args + the actually-bound port (sshd parity) with TunnelForwardedParams.peer as the originator → pump_bidi(ssh, fabric). Correlation-miss → teardown-at-birth, never a half-open SSH channel. Unix arrivals ride forwarded-streamlocal@openssh.com. - bounded bookkeeping: the per-session listener map packet-loop-only with a dedicated max_concurrent_channels-ceiling counter; the route map bounded by construction (mints at accepted listens, removed at cancels + session teardown — the handler's Drop). - the harness grew the client-side accept hook (server_channel_open_forwarded_tcpip/_streamlocal, acceptor-driven) + a hand-rolled tracing subscriber; 18 contract rows + both real-tool smokes (ssh -L, ssh -R + curl) green in tests/tunnel_mapping.rs; the wave-2 stub listen rows migrated per build (unfeatured / seam-unconfigured). Verified: cargo test across default/git/session/sftp/tunnel and all combos + --all-features; clippy -D warnings (default, all-features, combos); cargo fmt --check; cargo doc --no-deps clean; taskgraph validate (26 tasks). |
||
|
|
b1e569d1a8 |
feat(session): land the session mapping — the translation bridge + the exit derivation (w3-tty-session)
The biggest single-row surface of the spine: pty/shell/pipe-exec serve
through the published alktty seam (drive_session_pre_negotiated) over a
translation bridge, re-landing POC-3 stage D as contract:
- src/tty_map.rs (session feature): the bridge (SSH data → Chunk::stdin
through a bounded mpsc + framing task; SSH EOF → the zero-length
stdin sentinel — verified against alktty's pump semantics; stdout →
data; stderr → extended_data(1); exit chunk → the derivation
contract), the locally-built negotiation (OQ-08: shell argv verbatim,
wrapper ++ [cmd], cwd/env session-side, ttymodes → the reserved {}
modes JSON), and the spawned serving path.
- serve_tty_row: driven chunks→SSH inline so the exit report is the
path's final act (status before close, ADR-006 decision 4); two
cancel signals (per-channel watch fired by channel_close, connection
teardown fired by SessionHandler::Drop) abort the alktty session
task — the ADR-056 kill-on-drop contract is the cancelled session's
cleanup.
- Derivation contract rules 1–6 in derive_exit + the crate-local
signum→name table: −N → exit-signal(name), −1 → exit-status(255),
core_dumped never, unmapped negatives → 255, the PTY-path rule-5
fidelity limit documented; wire rows pin the shapes.
- Negotiation-error frames (alktty's 0x00-length-prefixed error
framing) → negotiation-failed serving decline + exit-status(255) +
close — the one could-not-start claim this door makes (nothing ran).
- Control rows: window_change/signal → the per-channel BridgeSurface
(bounded mpsc, try_send, never awaited on the packet loop); signal
names verbatim (no decline shape — fire-and-forget); pre-serve
resizes update the stored pty params (final size wins).
- Unset-declines (OQ-08) keep the git row's serving-path shape
(failure then close, spawned): shell-unconfigured /
wrapper-unconfigured / seam-unconfigured; pipe-less shell SERVES
pipe-mode (the recorded vanilla-sshd posture); env_request decline
unchanged (OQ-09).
- tests: the hand-rolled TtyBackend shim + ownership witness doubles;
serving rows through the harness, the unset-field decline matrix,
derivation rows on the wire, negotiation-failure rows, the identity
ride asserted live at the seam, the kill witness; the real-tool
smoke — OpenSSH -tt against a real PTY via dev-dep alktty local
(test graph only), skip-with-reason posture. Wave-2 stub rows
migrated to the new decline reasons (dispatch_core, observe).
Verification: cargo test (113) / --features session (149) / git (142) /
tunnel (115) / --all-features (161) — all green; cargo clippy
--all-targets -- -D warnings ×5 feature configs; cargo fmt --check;
cargo doc --no-deps ×3 builds clean; taskgraph validate.
|
||
|
|
7aac2d11a2 |
feat(git): land the git mapping — the exec grammar + the alkgit dispatch (w3-git-mapping)
- dispatch: the full exec grammar replaces the wave-2 skeleton probe (quoted/unquoted paths, the .git normalization decision, unknown- service / service-only decline-with-reason class; the probe's unit rows migrate with their pinned verdicts preserved) - git_map: the git row dispatches serve_duplex_resolved over the channel duplex (repo id validated, never resolved; the exit-status mapping 0/1 decided per POC-3 §A.5; status before close from the spawned task), with the resolved identity riding the seam - handler: the store-and-take channel handoff (stored at the open, taken in the spawned serving task, released at close) and the max_exec_command_len pre-parse cap - config course correction: GitSeam carries the assembly-supplied alkgit Limits (the ADR-009 budget alkgit's contract assigns the assembler) - tests: tests/git_mapping.rs (byte-level process-mode advertisement, ls-refs round + exit 0, the collapsed-denial byte-order + exit 1, the identity-ride authorize-gate witness, the packless CAS push, the grammar/cap decline rows, the sequential-serve store row, and the real-tool git 2.43 clone smoke over a TCP mount with a skip-with-reason posture); dispatch_core/observe rows updated to the new decision set; tests/common/git_doubles.rs (the hand-rolled recording alkgit quartet) Verification: cargo test (default), --features session, --features git, --all-features; clippy -D warnings ×2; fmt --check; cargo doc --no-deps; taskgraph validate |
||
|
|
3b77193e85 |
feat(tests): land the loopback harness — the in-process contract-test vehicle (w2-loopback-harness)
- tests/common/harness.rs: the recorded transport decision (tokio duplex pair + russh connect_stream; recorded over TCP loopback for determinism, no-port portability) and placement decision (sibling of the wave-1 doubles, the crate's contract-test vehicle convention) - the client-side posture from the doubles: MemHostKeyPolicy bridged into russh's check_server_key (HarnessClientHandler, decision input byte-equal to the wave-4 dial wiring's), the Limits keepalive/ inactivity posture (client_config), fixture-key auth helpers - thin helpers: authenticate_publickey + remaining_methods, open_session_channel (open-failure reason observed in the Err), request_exec/shell/subsystem, next_message / await_exit_status / await_close, drive → LoopbackSession with clean unmount - acceptance proof in tests/loopback_harness.rs: 6 contract-stamped rows, incl. the full in-process connect → fixture-key auth → authenticated-session observation (host-key decision log + observed decline reason) and the fail-closed host-key decline Verification: cargo test (81 pass), cargo clippy --all-targets -- -D warnings, cargo fmt --check, taskgraph validate — all green. |
||
|
|
2202149fc0 |
feat(auth): land the ADR-004 dispositional auth bridge (w2-auth-bridge)
- 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 |
||
|
|
214690b9c2 |
test(doubles): land the wave-1 in-memory seams + unified ed25519 fixtures
The `w1-test-doubles` task: the in-memory seams every later wave's contract tests run against, behind their production seams. - tests/common/mod.rs — the doubles, test-only (nothing reachable from lib.rs, no feature): MemIdentityProvider (registered fingerprints -> alkcall Identity resolutions over the IdentityProvider trait; token path resolves none per ADR-004), TestVault (a wrapper over alkvault 0.1's shipped in-memory construction — resolving the flagged vault-double question: wrap, not re-impl), MemHostKeyPolicy (exact (host, fingerprint) rules + explicit default verdict, fail-closed by default, inspectable decision log), and the fixture table. Module docs record the location/vault/fixture decisions and the row/stamp discipline (greppable Contract stamps). - tests/vectors/ — one unified fixture set: four ssh-keygen-minted ed25519 keypairs (committed .key + .pub + fingerprints.txt recorded from ssh-keygen -lf, OpenSSH_9.6p1). The previous inline PEM constants in src/key_source.rs were rejected by OpenSSH itself (russh-only decodable); all keys were re-minted and the overlapping vector set retired — one fixture set now serves the parity tests, the custody round-trip, and every later auth/mapping test (src-unit tests share it via include_str!). - tests/doubles.rs — the end-to-end rows: register -> resolve, seal -> source -> decode (+ locked/absent/tampered postures), host-key set in order, policy verdicts honored-and-logged. - tests/fingerprint_parity.rs and src/key_source.rs tests moved onto the shared fixture files; src changes are #cfg(test) only. Verification: cargo test (33 + 9 + 5 green; --all-features also green), cargo clippy --all-targets -- -D warnings clean, cargo fmt --check clean, taskgraph validate ok (12 tasks). |