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).
This commit is contained in:
glm-5.3-flash committed 2026-10-29 04:24:50 +00:00
1 parent 6443feac3e
commit 3d0253d981
15 files changed
+2839 -236

No files matched your search

+133 -9
View File
@@ -1,7 +1,7 @@
---
id: w3-tunnel-listen
name: Wave 3 — tunnel leg 2 (the -R listen flow + the forwarded-tcpip data leg)
status: pending
status: completed
depends_on: [w3-tunnel-direct]
scope: moderate
risk: high
@@ -87,24 +87,24 @@ target):
## Acceptance Criteria
- [ ] `tcpip_forward` / `streamlocal_forward` (+ cancel forms) carry
- [x] `tcpip_forward` / `streamlocal_forward` (+ cancel forms) carry
the real mapped flow under `tunnel` (+ the runtime-Unix gate
where it binds); decline classes keep their wire shapes
- [ ] The family-ward listen composition decided against alktunnels
- [x] The family-ward listen composition decided against alktunnels
0.4.1's actual surfaces and recorded; the door-side inbound-open
mechanics closed (or a serve-config amendment landed + recorded;
or stopped-and-booked for the gate — nothing silent)
- [ ] The `forwarded-tcpip` data leg serves per the handler shape
- [x] The `forwarded-tcpip` data leg serves per the handler shape
(target-arg echo per pending listen, cap-gated acceptor, the
pump, teardown-at-birth on correlation-miss)
- [ ] The per-session listener bookkeeping bounded; the bound-port
- [x] The per-session listener bookkeeping bounded; the bound-port
out-param returns the actually-bound number
- [ ] The per-build decline-matrix rows green through the harness with
- [x] The per-build decline-matrix rows green through the harness with
the client-side accept hook in place (the wave-2 gate's booked
harness item discharged)
- [ ] The real-tool smoke posture decided + recorded; the wave-2 stub
- [x] The real-tool smoke posture decided + recorded; the wave-2 stub
listen rows migrated
- [ ] `cargo test` (+ per-feature + `--all-features` rows), clippy ×2
- [x] `cargo test` (+ per-feature + `--all-features` rows), clippy ×2
(`-D warnings`), fmt, `cargo doc --no-deps` pass
## References
@@ -132,4 +132,128 @@ target):
## Notes
> To be filled at implementation time.
**Landed.** The `-R` leg is served for real: the listen flow
(`tcpip_forward`/`streamlocal_forward` + cancel forms under `tunnel`)
and the `forwarded-tcpip`/`forwarded-streamlocal` data leg, verified
against the published alktunnels 0.4.1 / alkcall 0.10.1 surfaces
through the harness and (tooling present) a real OpenSSH `ssh -R`
smoke. 18 server-direction rows + 2 real-tool smokes green in
`tests/tunnel_mapping.rs`.
- **The composition decisions (recorded in `src/tunnel_map.rs`'s
module doc + `mapping.md` §Global-request rows):**
- *Who dials `tunnel/forwarded`*: the fabric (not the door) —
alktunnels' bind-first accept dispatcher
(`make_bindfirst_accept_handler` + the `ForwardedOpener` closure)
dials the op on the channel-0 call plane toward the listen opener
per arrival; the door reply carries the door-allocated channel id
the producer adopts (alkcall's odd/even split keeps the two open
directions collision-free: the door allocates odd as the connect
side, the fabric even as the accept side).
- *The door-side inbound-open mechanics*: closed over the published
surfaces — the `Arc<ChannelClient>` alone cannot carry them (a
client serves inbound ops on its channel-0 loop only when built
with `from_connection_with_serving` against an `OperationRegistry`
the assembly owns; no post-construction way in) — so the
serve-config's tunnel handle gained `tunnel_listen_seam`
(`TunnelListenSeam`, the wave-3 seam-config's second
course-correction): the same `Arc<OperationRegistry>` the assembly
wired into the leg's `ServingConfig`. alkssh registers the
forwarded openable itself (`register_tunnel_forwarded_openable`
over a `ChannelCore` wrapping a clone of the held client's
manager, alkcall's secure default per-identity policy) at
`SshAdapter::new` — a registration failure is a failed
construction (`AdapterError::ListenRegistration`), never a
half-configured adapter. A scratch probe (published crates only,
no `alktunnels/local`) proved the topology end to end before any
of this landed: door-side registration → fabric bind-first
producer → arrival → `ForwardedHandler` (params + `BiStream`) →
pump round trip, with the door's own pending calls still resolving
while serving is enabled.
- *The resource-naming convention (the flow's one open surface)*:
the vanilla wire bind endpoint rides family-ward as the resource
string verbatim (`"localhost:9200"` / `"0.0.0.0:9200"` / the
socket path) — the OpenSSH client normalizes the no-address case
to `"localhost"` before sending (sshd `channel_rfwd_bind_host`
parity), the door passes through, and the fabric's
establisher/bind closure (assembly policy — the dynamic-address
producer opt-in the door's convention fronts) treats it as the
bind endpoint. ADR-001's "resource is not an address" discipline
stays a producer-side naming rule; this is a recorded door-to-
fabric convention, not a wire change.
- **The `forwarded-tcpip` data leg** (the row the POC only exercised
as refusal): per arrival → token correlation through the shared
`ListenRouter` (one route serves unboundedly many arrivals;
removed only at the cancel forms/session teardown) → the in-crate
per-session channel cap admits at the accept surface — the acceptor
vocabulary's first shape, the wave-2 gate's booked item now
discharged (never-established arrival paths release inside the
arrival pump; established closes release through the packet loop's
`channel_close` as ever) → `Handle::channel_open_forwarded_tcpip`
echoing the listen request's own args: the wire address string
verbatim + the actually-bound port (sshd `c->path` +
`get_local_port` parity — the OpenSSH client matches that exact
string + port and dials its own target), the
`TunnelForwardedParams.peer` arrival mapped to the originator
fields (HostPort → address/port; Path → path + 0,
degenerate-but-typed) → `pump_bidi(ssh, fabric)` (half-close
semantics: EOF-shutdown in the completed direction; the vanilla
client closes its half when its target socket dies — the row's
stand-in — and the cap release follows through the normal close).
Correlation-miss → teardown-at-birth (drop the fabric half; the
open op's channel reaps — never a half-open SSH channel). Unix
arrivals (`streamlocal_forward` mints) open
`forwarded-streamlocal@openssh.com` with the path echoed (russh's
server `Handle` carries that channel-open at this pin; the client
hook exists) — the "no russh hook" concern in the wave-2 stub notes
was about the door's *inbound* handlers of that type, not the
server-initiated direction.
- **Bounded bookkeeping** (convention 9): the per-session listener
map is packet-loop-only (like the `channels`/`ptys` stores); its
bound is a dedicated counter against the same
`max_concurrent_channels` ceiling — a listen is at least one future
channel's potential; the listener/channels mix stays unconstrained
while both stay bounded (no unbound listener map). The route map is
bounded by construction: entries minted exactly at accepted listens,
removed 1:1 at the cancels and at the owning session's teardown (the
handler's `Drop`); a route to a dead handle only ever costs one
failed open → teardown-at-birth. `cancel` of an unknown
(endpoint, bound-port) pair answers `false` (sshd
cancel-of-unknown parity). Admissions have two sites and releases
three since this task — the cap's recorded single-writer reasoning
updated in `channel_cap.rs` (mutex-guarded, saturating, both).
- **The real-tool smoke**: implemented and green here (tooling
check-gated like the -L smoke's posture): a real `ssh -R
127.0.0.1:PORT:HTTP` (OpenSSH client) through the TCP-mounted
adapter → the fabric double with a real bind → arrival → the
forwarded-tcpip open → the client dials its own endpoint → curled
body parity. Never silently dropped.
- **The decline matrix** per build, all through the harness with the
client-side accept hook in place (the harness handler's
`server_channel_open_forwarded_tcpip` /
`server_channel_open_forwarded_streamlocal`, acceptor-driven — no
acceptor → clean decline, the family's client-side contract): the
wave-2 stub listen rows migrated (`tcpip_forward_decline_emits_*`
split per build: `unfeatured` vs `seam-unconfigured`); the
featured + configured rows serve/arrive/cancel (incl. the port-0
mint resolving into the `&mut u32` out-param and the echo); the
unset listen seam declines while the egress bridge still routes
(the row's control leg); the listener cap and the per-arrival
channel cap rows (refusal + release proof); the composed-identity
rows (the fabric-scope gate on the listen dial, the door-side ACL
gate on arrivals); the OQ-11 off-Unix rows; the correlation-miss
row; the unvanilla inbound `forwarded-tcpip` open declined
(event-level contract — russh's client has no arbitrary-type open
to drive that row on the wire).
- **`tests/common` grew**: the forwarding acceptor
(`ForwardedAcceptor` + `ForwardedOpen` + `drive_with_acceptor`)
and a minimal hand-rolled tracing subscriber (`trace_global` — the
family posture: no `tracing-subscriber` dependency; opted in per
row) as the leg-2 rows' failure-triage vehicle.
- **Verification (all green)**: `cargo test` across default /
`tunnel` / `git` / `session` / `sftp` / `tunnel,git` /
`tunnel,session` / `git,session` / `tunnel,sftp` / `git,sftp` /
`--all-features`; `cargo clippy --all-targets -- -D warnings` ×2
(default + `--all-features`) plus the combo builds (`tunnel,git` /
`tunnel,session` / `tunnel,sftp`); `cargo fmt --check`; `cargo doc
--no-deps` (0 warnings); `taskgraph validate` (26 tasks).