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

140 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: w5-e2e-client
name: "Wave 5: real-tool e2e, client direction (the door against a real sshd)"
status: completed
depends_on: [review-wave-4]
scope: moderate
risk: medium
impact: phase
level: implementation
---
## Description
The client-direction real-tool e2e, which takes the reverse-runner
shape
re-landed against a **real `sshd`** (`docs/plans/implementation.md`
§Wave 5, the real-tool e2e bullet's client half; the wave-4 client
rows ran against the in-process fake `sshd` double, and the wave-4
gate named real-`sshd` e2e wave-5 territory explicitly). A dedicated
new binary (decide + record the placement, for which `tests/e2e_client.rs`
reusing `tests/common` is the recommended posture, disjoint from the
matrix task's binaries), under the check-gated
**skipped-with-reason** posture:
- **The sshd fixture**: a spawned real OpenSSH server the dial
targets: host key generated (ed25519), a listen port picked, the
`HostKeyPolicy` accepting the fixture's known fingerprint (the
case-1 pin posture, with the policy seam consumed against a real
presented key), a generated ed25519 user key authorized in the
fixture's `authorized_keys` (the decrypt-at-dial path over the
in-memory vault double). The fixture's exact shape (spawned
per-test vs a shared per-binary instance; the sshd_config knobs,
publickey-only, no password) decided + recorded.
- **The op rows against real sshd**: exec (exit-status parity
through a real server's reaping), the direct-tcpip dial (a real
TCP connection through the server's forwarding, `-L`-shaped),
**the `-R` leg**: `tcpip_forward` with the port-0 mint against a
real server's actually-bound reply, the server-initiated
`forwarded-tcpip` open served through the acceptor registry (the
reverse-runner consumer pattern end to end), the cancel form
tearing the real listen down.
- **The decline rows against a real server**, the sftp client-side
placeholder decline (`subsystem` sftp → the typed decline), the
unfeatured direct-tcpip decline (the connection stays usable,
which is the typed-open decline mirror's real-server row), the host-key
policy mismatch hard-decline (a wrong-fingerprint fixture → no
dial, the case-1 posture against a real presented key).
- **Every skip reasoned** (no `sshd` in the environment / fixture
constraints), every finding fixed in-task or booked for the gate,
never absorbed silently.
## Acceptance Criteria
- [x] The sshd fixture lands (spawned real server, ed25519 host +
user keys, the policy pin posture); its shape decided +
recorded
- [x] Exec, direct-tcpip, and the `-R` leg (port-0 mint + accepted
arrival + cancel) green against real sshd (or reasoned-skipped)
- [x] The decline rows green: sftp placeholder, unfeatured
direct-tcpip (connection stays usable), host-key mismatch
hard-decline
- [x] Every finding fixed in-task or booked for the gate (recorded)
- [x] `cargo test` (+ the builds the new binary rides), clippy ×2
(`-D warnings`), fmt, `cargo doc --no-deps` green
## References
- docs/plans/implementation.md §Wave 5 (the real-tool e2e bullet),
§Wave 4 (the fake-`sshd` posture this task's real server replaces
for e2e)
- docs/architecture/client.md (the dial surface, op rows, the
acceptor), docs/research/reverse-runner.md (the consumer pattern
the client direction serves)
- docs/research/poc-2*.md / the reverse-runner extraction (the
real-`sshd` client-direction evidence this re-lands against the
shipped crate)
- tests/client_dial.rs, tests/client_ops.rs,
tests/client_reverse_listen.rs (the double-driven rows this task
mirrors against the real server), tests/common/sshd_double.rs (the
double's semantics, which the real fixture must match)
## Notes
**The sshd fixture decided**: `tests/e2e_client.rs` (the new binary), reusing `tests/common`, and the
wave-4 client binaries stay single-writer per the decomposition's file map; this task edited only
its own binary (`tests/e2e_client.rs`, an in-file `support` module). **Fixture shape: per-row
spawned real `sshd`**, since each row's config knobs differ (`AllowTcpForwarding` yes/no) and each row
dials its own server, so a shared per-binary instance buys nothing; per-row isolation (own workdir,
own minted ed25519 host key via `ssh-keygen`, own probe-verified port from the e2e-server's
monotonic allocator posture) beats the spawn cost. Knob set: publickey-only, `StrictModes no` (the
throwaway workdir carries none of the real home-dir hygiene), `UsePAM no`, `UseDNS no`,
`PidFile none`, `PermitRootLogin prohibit-password`, host key + authorized_keys (fixture `alpha`'s
public blob) at absolute paths, per-row `AllowTcpForwarding`. Readiness is a bounded TCP probe; a
server that never comes up is the row's **reasoned skip** with the server's own `-e` log tail as
the reason (worked as non-root in this environment, verified against OpenSSH 9.6p1). The dial
rides the decrypt-at-dial path over the in-memory vault double (`VaultKeySource` over `TestVault`,
`alpha` sealed) and the case-1 pin posture (`MemHostKeyPolicy::pin`) accepts only the minted host
fingerprint.
**Row inventory** (all check-gated, `#[cfg(unix)]` + feature-gated per row group; deadline-bounded
end to end (the POC-1 §5 posture); `Contract stamp:` markers cite `client.md` / `reverse-runner.md`):
| Row | Build(s) | What it pins |
|---|---|---|
| `exec_row::real_sshd_exec_pumps_streams_and_reports_the_exit_status` | session (+all) | the wave-4 double-driven exec row re-landed: stdout/stderr separated through a real server, the **nonzero** exit (3) reported in-band last, the real server's reaping, parity not a canned 0; the case-1 pin accept decision on record against the really-presented key |
| `direct_tcpip_row::real_sshd_direct_tcpip_round_trips_a_live_tcp_connection` | tunnel (+all) | the `-L`-shaped dial through a real server's forwarding into a live loopback echo server, bytes end to end |
| `reverse_rows::real_sshd_port_zero_mint_serves_the_accepted_arrival_and_the_data_leg` | tunnel (+all) | the port-0 mint surfaces the actually-bound port (verified by dialing it), the server-initiated `forwarded-tcpip` arrives into the installed acceptor, the external dialer's bytes round trip, giving the reverse-runner consumer pattern end to end |
| `reverse_rows::real_sshd_cancel_tears_the_listen_down_and_unknown_cancels_refuse` | tunnel (+all) | explicit-port listen actually bound; the listener handle's `cancel()` frees the **real** bound port (later dials refused); a stateless cancel of the unknown listener is `ListenRefused`; connection stays usable |
| `declines::real_connection_sftp_placeholder_declines_with_the_connection_intact` | **every build** | the OQ-13 placeholder against a real authenticated connection: typed decline before any wire op, connection intact |
| `declines::real_presented_key_mismatch_hard_declines_and_the_right_pin_dials` | **every build** | the wrong-pinned policy hard-declines the dial (`DialError::HostKeyRejected`), the policy log records the **real** fingerprint as rejected, and the correctly-pinned dial to the same server succeeds, and the decline is the policy's decision, not the server's |
| `unfeatured_direct_decline::real_server_refused_direct_tcpip_declines_and_the_connection_stays_usable` | tunnel+session (all-features) | a real server with `AllowTcpForwarding no` refuses the typed open → `OpenDeclined(Prohibited)`, and a following exec is served, the connection-stays-usable fact end to end |
**Decisions recorded** (for the wave-5 gate):
- The "connection stays usable" decline row is gated `tunnel` **and** `session` (rides
`--all-features` only): the post-decline exec is the stays-usable proof's vehicle, and exec is
a `session`-gated op. Under tunnel-only builds the row is absent; the fake-double mirror
(`tests/client_ops.rs`) carries the tunnel-only stays-usable rows.
- The sftp and host-key-mismatch rows run in **every** build (feature-free ops on
`SshConnection`), so even the no-feature matrix build exercises two real-server rows.
- The fixture skips are honest: no `sshd` in the environment / no `ssh-keygen` / spawn refusal /
never-listening (the server's `-e` log tail carried in the reason), never silently green.
**Findings**: no src/ changes needed, as every observed issue was e2e-graph
local
(compile/fmt-level only). No bookings for the gate beyond the decisions above.
## Summary
Completed 2026-10-30. The client-direction real-tool e2e landed as `tests/e2e_client.rs` (7 rows,
fixture shape above): exec (exit-status parity, nonzero through real reaping), the direct-tcpip
dial, the `-R` leg (port-0 mint + accepted arrival + data leg + cancel tearing the real listen
down + unknown-cancel `ListenRefused`), and the three decline rows (sftp placeholder, host-key
mismatch hard-decline with the real presented fingerprint on record, server-refused direct-tcpip
with the connection staying usable). Verification: full `scripts/matrix.sh` run green: `cargo
test` per build (none / git / session / tunnel / sftp / --all-features; the new binary compiles
in every build and runs only its rows' builds: 2 / 2 / 3 / 5 / 2 / 7 rows), both clippy runs
`-D warnings` clean, fmt clean, `cargo doc --no-deps` clean; plus 10 consecutive all-features
e2e_client runs green (no flakes; per-row workdirs + the e2e-server's port allocator carried
over). Skips are reasoned prints only; on this host sshd 9.6p1 is present, so every row ran.