feat(tunnel): adopt the forwarded-op establisher gate (alktunnels 0.5.0, review-003 W2)

- install_forwarded_surface registers
  register_tunnel_forwarded_openable_with_establisher (ADR-008
  Amendment 2's recommended shape) with a ForwardedGate capturing the
  ListenRouter: a stale/missing listen token fails the open typed at
  the call plane (channel:open_failed, unknown_resource) BEFORE any
  channel is born — the phantom-channel shape is closed at the open;
  malformed arrivals are likewise typed (handler_error) instead of
  success-then-silent-birth-teardown.
- the arrival pump's handler-side correlation re-lookup stays as the
  residual-race + cap-admission teardown-at-birth fallback (the base
  shape's posture, retained; the narrow gate-pass->handler-run race
  documented on the pump).
- the unscoped-arrival ACL row double-pins the pinned gate order
  (ACL before establisher — a genuinely minted token refused by ACL
  alone).
- rows: a_correlation_miss_fails_the_open_typed_before_any_channel
  (miss: typed refusal, no phantom channel, no acceptor row),
  a_gate_pass_arrival_serves_the_route_unchanged (the serving flow
  unchanged), and the lib unit pin
  the_pump_residual_miss_teardown_at_births_the_fabric_half.
- the harness opener now maps the wire refusal's details.reason back
  into the TunnelEstablishError taxonomy (the real opener's map) —
  the flat HandlerError swallow would erase exactly what the gate
  rows pin.

Verification: cargo test --all-features --test tunnel_mapping (22,
incl. both real-tool smokes), cargo test --features tunnel --lib
tunnel_map, full cargo test (default + --all-features), clippy x2
-D warnings, fmt.
This commit is contained in:
glm-5.3-flash committed 2026-11-04 09:45:28 +00:00
1 parent 36d08868c9
commit 4668061b19
2 files changed
+259 -43

No files matched your search

+111 -8
View File
@@ -1,7 +1,9 @@
//! The `direct-tcpip` / `direct-streamlocal` mapping — the tunnel
//! branch's route-1 bridge (`w3-tunnel-direct`, ADR-006 decision 1):
//! the adapter-side bridge POC-3 stage C proved, re-landed as contract
//! against published alktunnels 0.4.1.
//! against published alktunnels 0.4.1, with the forwarded serving
//! surface adopted to alktunnels 0.5.0's establisher shape (the
//! review-003 W2 adoption — the `ForwardedGate` correlation gate).
//!
//! ## The route-1 bridge (ADR-006 decision 1)
//!
@@ -39,7 +41,8 @@
//! — **the fabric listens**; per arrival the data flows back toward
//! the vanilla SSH client, which dials its own target. Three moving
//! parts, all decided against alktunnels 0.4.1's actual surfaces with
//! POC-3 §C.4 as the template:
//! POC-3 §C.4 as the template (the gate adoption rides 0.5.0's
//! establisher variant, above):
//!
//! 1. **The listen dial** (`tcpip_forward` / `streamlocal_forward`):
//! the door maps the vanilla listen request family-ward as
@@ -82,12 +85,21 @@
//! client's manager + alktunnels' `tunnel_forwarded_spec()` +
//! `make_forwarded_op_handler` over the crate's arrival pump, the
//! wire-level ACL (`tunnel:forwarded` scope) and the per-connection
//! channel budget enforced inside alkcall's dispatch.
//! channel budget enforced inside alkcall's dispatch — registered
//! in the **establisher variant** (alktunnels 0.5.0's
//! `register_tunnel_forwarded_openable_with_establisher`, the
//! review-003 W2 adoption): the router's correlation lookup rides
//! the establisher's `ForwardedGate`, so a stale/missing token
//! fails the open typed at the call plane (`channel:open_failed`,
//! `unknown_resource`) before any channel is born; the handler's
//! re-lookup keeps the residual-race +
//! cap-admission teardown-at-birth fallback.
//! 3. **The arrival pump** — the `forwarded-tcpip` data leg: the
//! registered [`ForwardedHandler`]-closure correlates the
//! arrival's `listen` token through [`ListenRouter`] (the route was
//! registered at listen acceptance with the owning session's
//! handle + cap); a miss is **teardown-at-birth** (drop the fabric
//! handle + cap); a miss here is the gate-pass residual race —
//! still **teardown-at-birth** (drop the fabric
//! half, never a half-open SSH channel). On a hit, per vanilla `-R`
//! convention: the door opens a `forwarded-tcpip` channel *toward
//! the client* echoing **the listen request's own args** — the
@@ -216,7 +228,10 @@ use alkcall::core::auth::AuthContext;
use alkcall::core::types::BiStream;
use alktunnels::consumer::{TunnelListener, TunnelSession};
use alktunnels::params::{Substrate, SubstrateAddr, TunnelForwardedParams, TunnelParams};
use alktunnels::producer::{ForwardedHandler, register_tunnel_forwarded_openable};
use alktunnels::producer::{
ForwardedGate, ForwardedHandler, TunnelEstablishError,
register_tunnel_forwarded_openable_with_establisher,
};
use parking_lot::Mutex;
use russh::Channel;
use russh::server::{ChannelOpenHandle, Handle};
@@ -431,7 +446,25 @@ impl Default for ListenRouter {
/// deployment policy; an assembly wanting a different per-identity
/// cap shapes it through its own `ServingConfig`/`registry`
/// composition upstream) — and spawns alktunnels' forward-op handler
/// over the crate's arrival pump. The registration is
/// over the crate's arrival pump.
///
/// The registration shape is the **establisher variant**
/// (`register_tunnel_forwarded_openable_with_establisher` — alktunnels
/// 0.5.0, review 003 W2's adoption, ADR-008 Amendment 2's recommended
/// form): the router's correlation lookup rides a [`ForwardedGate`]
/// capturing the router, so a stale/missing `listen` token fails the
/// open at alkcall's call plane as a typed `channel:open_failed`
/// (`details.reason == "unknown_resource"`) **before any channel is
/// born** — the phantom-channel class (call-plane success, then
/// handler-side decline) is closed at the open; door-side arrival
/// correlations that pass the gate but lose the narrow gate-pass→
/// handler-run race, plus every cap-admission decline, still take the
/// handler's teardown-at-birth path (the residual-race posture the
/// base registration also carries). A malformed arrival is likewise
/// typed (`handler_error`) instead of the base shape's
/// success-then-silent-birth-teardown.
///
/// The registration is
/// one-per-op-id-per-registry: alkcall's `register` silently
/// replaces an existing entry, so the door **pre-checks the op** —
/// a `tunnel/forwarded` registration that already exists (e.g. a
@@ -450,6 +483,21 @@ pub(crate) fn install_forwarded_surface(serve: &mut config::ServeConfig) -> Resu
return Err("tunnel listen seam configured without a fabric leg".to_string());
};
let router = Arc::new(ListenRouter::default());
let gate: ForwardedGate = {
let router = Arc::clone(&router);
Arc::new(move |params: &TunnelForwardedParams| {
if router.route_for(params.listen).is_some() {
Ok(())
} else {
Err(TunnelEstablishError::UnknownResource(format!(
"no accepted listen routes the arrival's token {} \
— the forwarding session is gone or was never minted \
(alkssh: stale/missing `-R` token)",
params.listen
)))
}
})
};
let handler: ForwardedHandler = {
let router = Arc::clone(&router);
Arc::new(move |params: TunnelForwardedParams, bidi: BiStream| {
@@ -472,10 +520,11 @@ pub(crate) fn install_forwarded_surface(serve: &mut config::ServeConfig) -> Resu
alktunnels::params::OP_TUNNEL_FORWARDED
));
}
register_tunnel_forwarded_openable(
register_tunnel_forwarded_openable_with_establisher(
&core,
&seam.registry,
AuthContext::anonymous(b"alk/tunnel"),
gate,
handler,
)
.map_err(|failure| format!("tunnel/forwarded openable registration: {failure}"))?;
@@ -491,7 +540,12 @@ pub(crate) fn install_forwarded_surface(serve: &mut config::ServeConfig) -> Resu
///
/// 1. **Correlation** — the route for the arrival's minted token;
/// a miss is teardown-at-birth (drop the fabric half; the alkcall
/// wrapper's awaited join ends, the channel reaps).
/// wrapper's awaited join ends, the channel reaps). The gate
/// (the establisher's call-plane correlation) misses a stale
/// token before any channel is born; this handler-side re-lookup
/// is the narrow gate-pass→handler-run **residual race** — the
/// same miss class, past the open — and the never-established cap
/// declines below take the same path.
/// 2. **The in-crate cap admission** (bounded resources at the accept
/// surface — the wave-2 gate's booked acceptor vocabulary): no
/// room → the fabric half drops, the arrival never opens an SSH
@@ -686,3 +740,52 @@ pub(crate) struct PendingListen {
/// the session's count dies with the handler, per the channels
/// cap's same posture).
pub(crate) type ListenerCap = ChannelCap;
#[cfg(test)]
mod tests {
use alktunnels::params::{Substrate, SubstrateAddr, TunnelForwardedParams};
use super::{ListenRouter, pump_forwarded_arrival};
/// The arrival pump's handler-side miss (the narrow gate-pass→
/// handler-run residual race the establisher shape retains) still
/// teardown-at-births: a token with no route at handler time drops
/// the fabric half — the adoptee reads EOF, never a half-served
/// arrival.
#[tokio::test]
async fn the_pump_residual_miss_teardown_at_births_the_fabric_half() {
let router = ListenRouter::default();
let (door, fabric_half) = tokio::io::duplex(64 * 1024);
let (reader, writer) = tokio::io::split(door);
let bidi = alkcall::core::types::BiStream::from_joined(reader, writer);
let pump = pump_forwarded_arrival(
std::sync::Arc::new(router),
TunnelForwardedParams {
listen: 424_242,
substrate: Substrate::Tcp,
peer: SubstrateAddr::HostPort {
host: "127.0.0.1".to_owned(),
port: 1234,
},
},
bidi,
);
tokio::time::timeout(std::time::Duration::from_secs(10), pump)
.await
.expect("the miss returns in time (the pump drops the half and ends)");
let mut adoptee = fabric_half;
let read = tokio::time::timeout(
std::time::Duration::from_secs(10),
tokio::io::AsyncReadExt::read_u8(&mut adoptee),
)
.await
.expect("the adoptee's EOF is observable in time");
assert!(
read.is_err(),
"the adopted fabric half EOFs (teardown-at-birth, not a half-serve)"
);
}
}
+148 -35
View File
@@ -1,9 +1,11 @@
//! The `w3-tunnel-direct` contract rows — the tunnel branch served for
//! real: the route-1 bridge (ADR-006 decision 1) lands
//! `direct-tcpip`/`direct-streamlocal` through the published alktunnels
//! 0.4.1 seam (`TunnelSession::open_direct` → accept only after the
//! fabric open succeeds → `take_halves()` pumped against the taken
//! channel), observed through the loopback harness against the **fabric
//! 0.5.0 seam (alkssh 0.2.0's W2 adoption: `TunnelSession::open_direct`
//! → accept only after the fabric open succeeds → `take_halves()`
//! pumped against the taken channel; the forwarded serving surface on
//! the establisher shape — the `ForwardedGate` correlation gate),
//! observed through the loopback harness against the **fabric
//! double** (`w3-tunnel-direct`'s recorded test composition: the
//! POC-3 §C.4 in-process recipe — `ChannelsAdapter` +
//! `InstallChannelZero` + the alktunnels openables + a hand-rolled
@@ -204,11 +206,11 @@ mod serving {
/// Drives the fabric's forwarded-open closure directly:
/// `Ok` carries the adopted fabric-side halves (the door
/// allocated the channel — the correlation-miss row's
/// allocated the channel — the gate-pass/residual-race
/// teardown-at-birth is then observable as an EOF on the
/// `recv` half), `Err` is the door-side refusal (the
/// composed-gate row's ACL answer — the arrival never became
/// anything).
/// unknown-token gate miss / the composed-gate row's ACL
/// answer — the arrival never became anything).
/// The next minted correlation token (one per accepted listen,
/// in mint order — the rows' real-token vehicle).
pub(super) async fn minted_token(&self) -> u32 {
@@ -495,8 +497,47 @@ mod serving {
let response = call
.call(alktunnels::params::OP_TUNNEL_FORWARDED, payload)
.await;
// The real opener maps the wire refusal's
// reason through the `From<TunnelEstablish
// Error> for EstablishmentError` table — the
// typed `unknown_resource` (the gate's miss
// class) must survive the client side here,
// so the double maps the details.reason back
// into the establisher's taxonomy (the flat
// `HandlerError` swallow would erase exactly
// what the gate rows pin).
let out = response.result.map_err(|e| {
TunnelEstablishError::HandlerError(format!("forwarded open: {e:?}"))
if e.code == "channel:open_failed" {
match e
.details
.as_ref()
.and_then(|d| d.get("reason"))
.and_then(|r| r.as_str())
.unwrap_or_default()
{
"unknown_resource" => {
TunnelEstablishError::UnknownResource(e.message.clone())
}
"dial_failed" => {
TunnelEstablishError::DialFailed(e.message.clone())
}
"resource_shortage" => {
TunnelEstablishError::ResourceShortage(
e.message.clone(),
)
}
"handler_error" => {
TunnelEstablishError::HandlerError(e.message.clone())
}
_ => TunnelEstablishError::HandlerError(format!(
"forwarded open: {e:?}"
)),
}
} else {
TunnelEstablishError::HandlerError(format!(
"forwarded open: {e:?}"
))
}
})?;
let id =
out.get("channel_id")
@@ -1486,26 +1527,19 @@ mod serving {
session.unmount().await;
}
/// - *Contract stamp:* the arrival pump's correlation-miss class
/// (`w3-tunnel-listen` — teardown-at-birth, never a half-open SSH
/// channel): an arrival whose `listen` token has no route
/// (nothing was ever minted for this door) is dropped by the
/// door-side handler — the open op's channel tears down at
/// birth, and the fabric side's adopted halves EOF immediately.
/// - *Contract stamp:* the forwarded gate's miss class (alktunnels
/// 0.5.0, review 003 W2 adoption) at the alkssh level: an arrival
/// whose `listen` token has no route (nothing was ever minted for
/// this door) fails the open at the call plane as a typed
/// `channel:open_failed` (`TunnelEstablishError::UnknownResource`
/// — the wire's `unknown_resource` reason) **before any channel
/// is born** — the phantom-channel shape (call-plane success,
/// then handler-side decline) is closed at the open: no channel
/// allocation, no adopted halves, no acceptor row.
#[tokio::test]
async fn a_correlation_miss_adopts_no_ssh_channel() {
async fn a_correlation_miss_fails_the_open_typed_before_any_channel() {
let double = wire_fabric_with_listen(true, true).await;
let provider = Arc::new(MemIdentityProvider::new());
provider.register(
fixture("alpha").fingerprint,
identity("tunnel-rider", &["ssh"]),
);
let mut serve = harness_serve_config(provider, &[fixture("bravo")], Limits::default());
serve.channel_client = Some(Arc::clone(&double.client));
serve.tunnel_listen_seam = Some(TunnelListenSeam::new(Arc::clone(&double.registry)));
let _adapter = SshAdapter::new(serve)
.await
.expect("the -R wired adapter constructs (the registration is adapter-scope)");
let (session, mut opens) = listen_session(&double, Limits::default()).await;
let params = alktunnels::params::TunnelForwardedParams {
listen: 424_242,
@@ -1515,20 +1549,95 @@ mod serving {
port: 1234,
},
};
let (mut send, mut recv) = double
let outcome = double.open_forwarded_directly(params).await;
let err = match outcome {
Ok(_) => {
panic!("the gate miss fails the open typed — no phantom channel exists to adopt")
}
Err(error) => error,
};
assert!(
matches!(err, TunnelEstablishError::UnknownResource(_)),
"expected UnknownResource at the call plane, got {err:?}"
);
// No arrival rode the acceptor — the open died at the call
// plane; the handler never ran.
tokio::time::timeout(Duration::from_millis(500), opens.recv())
.await
.expect_err("a gate-missed arrival never opens an SSH channel");
assert!(opens.is_empty());
session.unmount().await;
}
/// - *Contract stamp:* the forwarded gate's pass posture (alktunnels
/// 0.5.0, review 003 W2 adoption): an arrival whose token is
/// genuinely routed behaves exactly as the base shape did — the
/// open succeeds at the call plane, the door allocates the SSH
/// channel at the acceptor, and the fabric-side halves are the
/// arrival's data plane (the gate adds an establishment phase;
/// the serving flow is unchanged).
#[tokio::test]
async fn a_gate_pass_arrival_serves_the_route_unchanged() {
let double = wire_fabric_with_listen(true, true).await;
let (session, mut opens) = listen_session(&double, Limits::default()).await;
let port = pick_port().await;
let _bound = tcpip_forward(&session.client, "127.0.0.1", u32::from(port))
.await
.expect("the listen is minted");
let token = double.minted_token().await;
let params = alktunnels::params::TunnelForwardedParams {
listen: token,
substrate: alktunnels::params::Substrate::Tcp,
peer: alktunnels::params::SubstrateAddr::HostPort {
host: "127.0.0.1".to_owned(),
port: 1234,
},
};
let (mut send, _recv) = double
.open_forwarded_directly(params)
.await
.expect("the door-side openable allocated the arrival's channel");
.expect("the gate-passed arrival adopts its fabric-side halves");
// The serving flow is the unchanged shape: the arrival becomes
// an SSH `forwarded-tcpip` open at the acceptor, and data rides
// the fabric half through to it.
let open = tokio::time::timeout(Duration::from_secs(10), opens.recv())
.await
.expect("the gate-passed arrival observed in time")
.expect("the acceptor's stream stays alive");
let mut channel = match open {
ForwardedOpen::Tcpip { channel, .. } => channel,
other => panic!("the gate-passed arrival opened something else: {other:?}"),
};
send.write_all(b"orphan arrival")
.await
.expect("the adopted halves still write (the door dropped its end — EOF, not refusal)");
.expect("the adopted halves still write (the serving flow ran)");
drop(send);
// The door-side handler dropped the fabric half at birth: the
// EOF arrives bounded, the channel never half-serves.
tokio::time::timeout(Duration::from_secs(10), recv.read_u8())
.await
.expect("teardown-at-birth is observable in time")
.expect_err("the adoptee's read half EOFs (the channel reapped)");
let mut seen: Vec<u8> = Vec::new();
tokio::time::timeout(Duration::from_secs(15), async {
while seen.len() < 13 {
match channel.wait().await {
Some(ChannelMsg::Data { data }) => seen.extend_from_slice(&data),
Some(_) => continue,
None => return,
}
}
})
.await
.expect("the fabric half rides in time");
assert_eq!(
seen,
b"orphan arrival".to_vec(),
"the gate-passed arrival's data rode the unchanged serving flow"
);
session.unmount().await;
}
/// - *Contract stamp:* AGENTS.md convention 7 (the visible-surface
@@ -1537,7 +1646,11 @@ mod serving {
/// caller's identity — an identity lacking the
/// `tunnel:forwarded` scope is refused by alkcall's dispatch (the
/// openable's own spec) and no SSH channel opens, even for a
/// genuinely minted token.
/// genuinely minted token. The genuinely-minted-token shape
/// double-pins the gate ordering now (the establisher variant's
/// pinned order, alktunnels 0.5.0 / ADR-008 Amendment 2): the ACL
/// refuses before the establisher's gate ever looks up the token —
/// an unscoped arrival could not leak a gate pass.
#[tokio::test]
async fn the_door_side_acl_refuses_an_unscoped_arrival() {
let double = wire_fabric_with_listen(true, false).await;