alkcall 0.7.0 resolved every open downstream-consumer finding, including this crate's reverse-flow POC W1 (filed as ledger CF-005, with the CF-006 corollary): - CF-005 (a): ServingConfig.identity — the explicit override - CF-005 (b): transport Connection::identity() propagates to channel 0 — set_identity before dialing is the mTLS/QUIC path - CF-006: the establisher/pump handler receive the per-call opener identity (dispatch-resolved), not the install-time context - CF-007: ADR-016 code list completed (doc-only) - F-1 (POC finding): the ChannelPlan type doc now carries the Send + Sync payload constraint alktunnels updates: - Cargo.toml: alkcall 0.6.0 -> 0.7.0 (lockfile resolves 0.7.0) - AGENTS.md convention 11: pin note updated; the 0.7.0 identity surface marked load-bearing (precedence order + per-call opener) - AGENTS.md architecture context: CF-005/CF-006 row added; the 'What the POC does NOT settle' list replaced with the accurate settled state (Phase 0 complete; Phase 1 = the spec) - reverse-poc-summary: W1 marked RESOLVED with the remediation validated from the POC's topology (16 tests over 0.7.0: transport identity alone authorizes, ServingConfig.identity overrides, identity-less fails closed, CF-006 witness); F-1 marked documented upstream; what-the-POC-does-not-validate and verification blocks updated - phase-0-findings OQ-TN-10: reverse POC entry notes the 0.7.0 re-validation and W1 resolution The reverse POC (/workspace/alktunnels-reverse-poc, 8b35818) re- validated: the default harness now authorizes on transport identity alone — the 0.6.0-era auth_token workaround is dead code. Verification: cargo test, clippy --all-targets -D warnings, fmt --check, wasm32 check, doc --no-deps — all clean
14 KiB
status, last_updated
| status | last_updated |
|---|---|
| complete | 2026-09-07 |
alktunnels: Reverse-Flow (-R) Tunnel POC Research Summary
Status: Research complete — the -R shape (the hub opens tunnel
channels toward a connect-side worker that serves its own open op)
validated end-to-end over alkcall 0.6.0 (2026-09-07 pass, 14 tests)
and re-validated over alkcall 0.7.0 (16 tests) after the CF-005/CF-006
identity remediation landed — the W1 auth_token workaround is dead;
the primary caller-identity path is transport identity. Clippy
-D warnings clean; fmt clean. The review 007 non-finding trace
("no upstream mechanism is missing") is confirmed by execution —
and the POC surfaced two real findings the forward POC could not
(one an upstream sharpness question — F-1, documented upstream in
0.7.0; one a spec-mandate — F-2).
Date: 2026-09-07
Scope: Validates the OQ-TN-10 #2 POC — the SSH tcpip-forward
template (register → per-accept channel-open → cancel) mapped onto
alkcall's both-sides serving semantics. Probes four wrinkles (W1..W4)
selected from the mechanism trace; findings F-1/F-2 below.
Executive summary
A POC (alktunnels-reverse-poc, /workspace/alktunnels-reverse-poc)
validated the reverse flow end-to-end over alkcall 0.6.0 (re-validated
over 0.7.0 — see §W1):
- The worker (connect side, serving side) — dials the transport,
ChannelClient::from_connection_with_serving+ChannelOperations::register_on(generic channel ops on the serving registry, so the hub canchannel/close) + post-hocregister_openable_with_establisher(W2). Its establisher dials the resource target and returns the handle viaEstablishment::new(plan)— R-01's plan flow, no handoff map. Its pump handler awaitsalkcall::channels::pump_bidiinline (R-02/R-03). This is ADR-022 §2 both-sides semantics exercised for real: connect side AND serving side. - The hub (accept side, reverse-flow initiator) —
ChannelsAdapter+ install hook that captures the hub's channel-0CallConnectionandChannelManager. Per local accept: call the worker's open op (call_with_payload— the caller identity rides the transport identity by default (0.7.0); the payloadauth_tokenis the optional hub-forwarding path), adopt the worker-allocated channel ID, pump the accepted socket against the adopted halves viapump_bidispawned locally. - The ADR-047 §5 orientation under test holds: the reverse channels are allocated by the worker (connect side — odd IDs) and adopted by the hub (accept side). "Connection owner allocates" exercised in the Pub-like orientation.
- 16 tests (0.7.0 pass): TCP round trip, 1 MiB backpressure,
half-close semantics, concurrent same-resource channels (the R-01
race-killer proven: two opens of one resource, distinct channels,
distinct dialed handles), UDP round trip, TCP+UDP concurrent,
typed establishment errors (
unknown_resource,dial_failed), the identity precedence chain (transport identity alone /ServingConfig.identityoverride / token override / identity-less fail-closed), CF-006 per-call opener witness, out-of-bandchannel/close, worker outbound calls resolving while serving, late-registration visibility, and two finding-pinning tests (F-1 compiler-pinned, F-2 below).
Wrinkle probes (W1..W4) — results
- W1 identity on the serving path — CONFIRMED 2026-09-07 against
0.6.0; RESOLVED by alkcall 0.7.0 (ledger CF-005 + CF-006).
Original finding:
from_connection_with_servingbuilt channel 0 internally; itsConnection::identity()was empty andServingConfighad no auth capture point — the serving dispatcher resolved the caller identity ONLY from the payloadauth_token→ServingConfig.identity_provider, and the establisher/pump handler received the install-timeAuthContext, never the per-call opener. The 0.7.0 remediation (validated from this POC's topology, 16 tests):ServingConfig.identity: Option<Identity>— the explicit override (remediation (a)).- The transport
Connection::identity()(set before dialing — the mTLS/QUIC posture) propagates to channel 0 automatically (remediation (b)). Validated: the POC's default harness now authorizes the scope-gated open on transport identity ALONE — no token payload. - Precedence (alkcall cf005 tests + POC probes): payload token >
ServingConfig.identity> transport identity; none → fail-closedFORBIDDEN(validated). - CF-006 corollary: the establisher/pump handler now receive the
per-call opener identity (validated via an identity witness —
the establisher saw
hub, and the override identity under (a)). On hub-forwarded opens the establisher sees the end client, not the hub. Status: resolved — the POC'sauth_tokenworkaround is dead code; the primary path is transport identity.
- W2 post-hoc openable registration — works. The worker registers
its openable AFTER
from_connection_with_servingreturns; the dispatcher reads through the sharedArc<OperationRegistry>per dispatch. Proven by every open succeeding + a directregistration()assertion. No finding — the assembly order (construct client → register ops → serve) is viable as-is. - W3 adopter self-reaping — confirmed asymmetry, POC-local fix.
The serving side's channel is wrapper-managed (teardown cascades on
pump completion or out-of-band close). The ADOPTING side has no
such machinery:
adopt_channelinstalls routing state nothing awaits. The hub must hold its pump handle and reap (teardown_channel) itself —ReverseTunnel::join_and_reap/closein the POC. Spec note: the real crate's consumer half needs a session type with the same ownership (the adopted channel + pump handle + reaper). Not an upstream gap (the hub's lifecycle is assembly-layer by design, OQ-TN-04) — but it is the reasonTunnelSessionin the spec must own teardown explicitly. - W4 EOF sentinel propagation — works both ways. Half-close
semantics test: local
shutdown()→ EOF sentinel into the mux → worker pump sees the zero-length read → shuts the target write down; the target's echo still flows back before the reverse leg completes.pump_bidi's shutdown-on-completion contract holds across the reverse path unchanged.
Findings
F-1 — ChannelPlan's Send + Sync bound constrains plan payloads (compiler-pinned)
ChannelPlan = Arc<dyn Any + Send + Sync> (ADR-049 amendment 2).
A dialed-socket handle whose halves are
Box<dyn AsyncRead + Send + Unpin> does NOT satisfy the bound —
Arc<T> requires T: Send + Sync, and dyn AsyncRead is not
Sync by default. The POC widened the halves to + Sync (socket
halves are Sync, so dial-shaped payloads are unaffected).
Implications for Phase 1: tunnel plan payloads (target handles) must
be Sync, which is fine for socket-backed substrates but would
exclude non-Sync handles (e.g. process pipes as boxed trait objects)
without a wrapper. Documented upstream 2026-09-07 (alkcall 0.7.0):
the ChannelPlan type doc now carries the Send + Sync payload
constraint. No further action needed — the bound is correct (the
plan crosses the wrapper task boundary).
F-2 — empty UDP datagrams collide with the EOF sentinel in raw pass-through (spec-mandate)
The raw pass-through pump shape (tokio::io::copy between the UdpHalf
adapter and the channel) cannot carry an empty datagram: poll_recv
returning zero bytes is Ok(0) from the adapter — indistinguishable
from EOF for copy, which treats it as end-of-stream and shuts the
pump down. An empty datagram and the zero-length EOF sentinel are the
SAME wire shape at the pump level. Pinned by test
(reverse_udp_empty_datagram_finding asserts the non-round-trip so
the conclusion is executable, not anecdotal).
Consequence: the forward POC's codec decision — [len: u16 BE]
framing for UDP — is now validated from a SECOND angle: not only does
it preserve boundaries, it is MANDATORY for correctness (an empty
datagram is 2 bytes under the codec, unambiguous with EOF). The
Phase 1 spec must state: UDP rides the length-framed codec, never raw
pass-through. Stream substrates (TCP, unix) keep raw pass-through —
a zero-length read there is genuinely EOF.
The -R registration template (SSH tcpip-forward analogue)
SSH: global request tcpip-forward → per-accept forwarded-tcpip
channel opens toward the forwarder → cancel-tcpip-forward.
Mapped onto the validated surface (OQ-TN-03's role-follows-resource model — no protocol-level direction):
| SSH | alktunnels reverse shape (validated) |
|---|---|
tcpip-forward registration |
hub binds the listener locally (OQ-TN-04: binding is assembly-layer; no advertisement op needed for the hub's OWN listeners) |
| server-side accept loop | hub accept loop (assembly layer) |
per-accept forwarded-tcpip open |
hub calls the worker's served open op per accept (call_with_payload) + adopt_channel |
| target dial | worker's establisher dials the resource (establishment phase — typed errors) |
cancel-tcpip-forward |
hub closes: pump teardown + teardown_channel (and/or channel/close out-of-band on the worker) |
| originator pair (informational) | not needed — ACL rides the channels open-op machinery |
Key validation: no advertisement surface is needed in the base
crate. The "please listen on your side" step of SSH's template
dissolves when the listener is the hub's own local resource — the hub
does not ask the worker to listen; the worker serves open ops, and
the hub opens channels whenever its own accepts fire. An advertisement
op only becomes necessary if the LISTENER lives on the far side
("worker, expose port 8080 on your end") — which is the worker
exposing a produced resource = the forward shape with the listener as
the resource (producer-side listen is an establisher variant, not a
new mechanism). This closes the last open thread of OQ-TN-03's
original question set (the -R advertisement/lifecycle surface).
What was built
alktunnels-reverse-poc/
Cargo.toml — alkcall 0.6.0; tokio wasm-clean subset + net (POC-local)
src/
lib.rs — module docs; wrinkle overview
producer.rs — worker half: tunnel_open_spec, tunnel_establisher
(Establishment::new(plan) — R-01; per-call
identity witness — CF-006), make_tunnel_
pump_handler (pump_bidi inline — R-02/03),
register_tunnel_openable (post-hoc, W2)
producer/
params.rs — TunnelParams {resource, substrate} + the UdpHalf
adapter (Send + Sync — F-1)
consumer.rs — hub half: open_reverse_channel (call_with_payload
with optional auth_token — now the fallback
path; primary is transport identity, 0.7.0),
ReverseTunnel::open_and_pump (adopt +
pump_bidi), join_and_reap/close (W3),
bi_stream_from_halves
harness.rs — wire()/wire_with(): worker =
from_connection_with_serving (transport identity
+ ServingConfig identity/provider — the 0.7.0
precedence chain) + ChannelOperations::
register_on + post-hoc openable; hub = adapter +
capturing install hook (manager + CallConnection)
tests/
tunnel_poc.rs — 16 tests (see Executive summary + §W1)
What the POC does NOT validate
- A real transport —
tokio::io::duplexstands in for TCP/TLS/QUIC (unchanged from the forward POC scope). - The advertisement op for far-side listeners — resolved as "not needed" (see the template table); if a deploy mode ever wants "worker, expose a port on YOUR side," that is a producer-side listen establisher over the same open op — spec-shaped, not POC- shaped.
- Unix socket + stdio substrates — OQ-TN-10 #3 (cheap; the pump is substrate-agnostic and both POCs now confirm it).
W1 remediation— resolved 2026-09-07 (alkcall 0.7.0): the remediation IS now exercised from this POC's topology (transport identity,ServingConfig.identityoverride, precedence, fail-closed, CF-006 witness). Remaining un-exercised: fingerprint- based identity resolution (resolve_from_fingerprint— the POC's duplex transport has no TLS layer to fingerprint).- Multi-hop (hub relay traversal) — single hop; relay forwarding (alkcall ADR-042) is transparent per leg.
- Wasm — POC-local
tokio/net; the real crate's protocol-only core stays wasm-clean (unchanged conclusion).
Verification
0.6.0 pass: 14 tests; clippy -D warnings clean; fmt clean.
0.7.0 pass: 16 tests (W1 remediation probes added, foreign-token
probe obsoleted by the precedence chain); clippy -D
warnings clean; fmt clean; repeat-run stable (3×).
References
- alkcall 0.6.0 — review 007 remediation as consumed here: ADR-049
amendment 2 (
Establishment::new(plan), R-01), ADR-050 (channels::pump_bidi, R-03), R-02 JoinHandle contract. - alkcall review 007 §Part B — the reverse-flow non-finding trace
(
from_connection_with_serving+register_on+ serving-side allocation) — confirmed by execution. - alkcall ADR-022 §2 — both-sides serving semantics (the worker's shape). ADR-047 §5 — connection-owner allocation (the odd-ID orientation). ADR-037/ADR-042 — channel ops, relay.
- alktunnels
docs/research/phase-0-findings.md— OQ-TN-03 (direction semantics — the advertisement thread this POC closes), OQ-TN-04 (no forced binding — the hub binds, the worker never does), OQ-TN-09 (half-open residual — validated W4), OQ-TN-10 #2 (this POC). - alktunnels
docs/research/ssh-socks5-survey.md§RFC 4254 §7.1 — thetcpip-forwardtemplate mapped in the table above. - forward POC (
poc-summary.md) — the codec decision F-2 strengthens; the UdpHalf adapter reused; the plan flow replacesHandleHandoff.