Split the last open substrate-placement question: - Unix: ships with the local feature v1 (dial_unix — same halves shape as TCP; the wire enum already carried unix per ADR-001; the params task's schema list includes all three values) - Stdio: OUT of scope — a spawned process's stdin/stdout/stderr IS alktty's pipe mode (LocalTtyBackend + tokio::process + Stdio::piped, alktty tty-local.md): three multiplexed logical streams + the exit-code control chunk (alktty ADR-004) + signal forwarding (REQ-TTY-02). A stdio bridge here would be alktty's runner mode with the terminal stripped out — a strictly worse duplicate that also drops the semantics that matter (a byte tunnel has neither exit codes nor signals). Remote command execution composes via alktty on the same channels substrate. Updated: open-questions.md OQ-TN-14 (resolved), overview.md feature gate + deps + OQ summary, producer.md OQ ref, OQ-TN-10 promotion (#3 split), phase-0-findings + both POC summaries' resolution notes, params task (schema enum includes unix), local-socket-halves task (unix ships, stdio does NOT — with the composition rationale), oq-tn-14-tracker task repurposed (boundary-maintenance: re-opens only if a consumer needs stdio-without-process-semantics — which would need its own ADR, or if the alktty/alktunnels boundary needs sharpening). Verified: taskgraph valid (12 tasks, no cycles)
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-07 |
alktunnels — Architecture
Arbitrary bidirectional tunnels over alkcall channels: the
alk/tunnel-family producer/consumer protocol crate. This directory
holds the architecture spec docs and the ADRs. It is the sibling of
alktty's architecture (/workspace/@alkdev/alktty/docs/architecture/)
— same substrate (alkcall channels), same shape (a producer
open-handler + a typed consumer session), different service (tunnels:
raw byte/datagram transport, not terminal sessions).
Documents
| Document | Status | Description |
|---|---|---|
| overview.md | draft | Crate purpose, the resource model in brief, dependencies, ALPN, feature gates, module map |
| wire.md | draft | The wire format: open-op params JSON (ADR-001), the data-plane codec (raw pass-through for stream substrates; mandatory [len: u16 BE] framing for UDP — ADR-003), sentinels, half-close semantics |
| producer.md | draft | The producer half: the channels/tunnel/sub open op, the establisher (dial + listen variants), the pump handler, registration, scope gating |
| consumer.md | draft | The consumer half: TunnelSession (stream + datagram variants), the reverse-flow initiator, teardown ownership (ADR-005) |
| bast.md | draft | The BAST (Binary Abstract Syntax Tree) document for the data-plane's binary framing (the UDP [len: u16 BE] codec — ADR-003); the stream pass-through has no binary framing to describe |
| open-questions.md | draft | Centralized OQ tracker (OQ-TN-01..14; the Phase 0 ledger promoted) |
Applicable ADRs
alktunnels-native ADRs (001..005). Upstream ADRs (alkcall/alknet) are
referenced by their own numbers and are NOT ported — the alkcall
originals at /workspace/@alkdev/alkcall/docs/architecture/decisions/
remain authoritative for the shared surfaces (establishment, channels
wire format, pump_bidi, identity seam).
| ADR | Title | Status |
|---|---|---|
| 001 | Open-Op Params Layout: Resource + Substrate | Accepted |
| 002 | Single alk/tunnel ALPN (Option A) |
Accepted |
| 003 | Data-Plane Codec: Raw Pass-Through (Stream) / Mandatory Length Framing (UDP) | Accepted |
| 004 | No TunnelBackend Trait — Halves Functions at the Assembly Layer | Accepted |
| 005 | The Consumer Session Type Owns Teardown | Accepted |
| 006 | Access-Control Posture — the Open Gate Is the Security Boundary | Accepted |
Key Design Principles
-
A tunnel is a resource, not an address.
paramsidentify a produced resource + substrate discriminator; the producer owns the backing (local port, docker container, in-process service, unix socket). No URL-style general addressing in the wire — rich addressing enters only through-D/dynamic composition, which speaks its protocol inside the tunnel payload. See ADR-001 and wire.md. -
Role follows the resource; connection direction is independent. Whoever can reach the target is the producer (registers openable channels); whoever wants the bytes is the consumer.
-Land-Rare the same open op with the entry point on different machines — no protocol-level direction (the hub-owns-the-connection model, OQ-TN-03). Validated by the reverse-flow POC: the worker serves on the connect side; the hub initiates per accept. See producer.md and consumer.md. -
Raw pass-through for stream substrates; mandatory length framing for UDP. A tunnel has one data stream per direction — no sub-demux key, no tunnel-level header on stream substrates (0 B tunnel overhead; the channels 8-byte header is the only cost). UDP rides
[len: u16 BE]framing — mandatory for correctness (F-2; the rationale and the executable pin live in ADR-003). See ADR-003 and wire.md. -
The two-pump shape is
alkcall::channels::pump_bidi— never hand-rolled. The producer's pump handler and the consumer-side pump both consume the upstream helper (ADR-050); each pump shuts down the opposite sink on completion (half-close semantics fall out). TheOpenHandler's returnedJoinHandletracks the data-plane lifetime (early return = teardown-at-birth, R-02). See producer.md. -
No backend trait. Stream substrates need "produce boxed halves for a resource" — a function, not a trait (the UDP POC's OQ-TN-05 finding). The inversion point is the assembly layer wiring substrate halves into the establisher/pump; substrate types stay in feature-gated backend modules. The listen variant (producer accepts instead of dialing) is an establisher shape, not a trait. See ADR-004.
-
The consumer session type owns teardown. One session = one channel = one tunnel (the channel ID is the flow key). The adopted side has no wrapper awaiting its pump (reverse POC W3) — the session must hold the pump handle and reap the channel on completion/close. See ADR-005 and consumer.md.
-
Backpressure and limits are inherited, not redefined. Bounded per-channel buffers, the 256-channel cap, monotonic IDs, and the zero-length-sentinel EOF convention are alkcall channels invariants; the tunnel crate consumes them and does not build a second demux/mux (AGENTS.md convention 10).
-
Security posture: the open gate is the boundary. Scope-gated opens (
TUNNEL_OPEN_SCOPE = "tunnel:open", stable once published — ADR-006), alkcall's op-level ACL viaregister_openable; caller identity resolves in the 0.7.0 precedence order (token >ServingConfig.identity> transport); the establisher/pump handler see the per-call opener identity. No target allowlists, no tunnel-specific policy machinery in v1. See ADR-006 and producer.md. -
Wasm-clean default crate. The default crate (protocol only) compiles to
wasm32-unknown-unknown; socket/platform I/O lives in feature-gated backend modules (AGENTS.md conventions 4/14), same as alktty.
Relevant Open Questions
All open questions are tracked in
open-questions.md. The Phase 0 OQ ledger
(OQ-TN-01..10, docs/research/phase-0-findings.md) is promoted there;
the questions affecting the spec set are summarized in each doc's Open
Questions section.
References
- Phase 0 research:
docs/research/phase-0-findings.md(OQ ledger),docs/research/poc-summary.md(UDP POC),docs/research/reverse-poc-summary.md(reverse-flow POC),docs/research/ssh-socks5-survey.md(SSH/SOCKS5/udpgw prior art) - Upstream: alkcall ADR-047 (openable ALPNs are operations), ADR-049
(establishment phase + amendment 2 plan payload), ADR-050
(
pump_bidi), ADR-022 §2 (both-sides serving), ADR-047 §5 (connection-owner allocation), ledger CF-005/CF-006 (identity seam); alknet ADR-078 (two-pump contract), ADR-071/093 (channels wire format); alkcall ADR-042 (hub relay) - Upstream reviews/ledger (the spec cites these IDs):
/workspace/@alkdev/alkcall/docs/reviews/007-establishment-follow-ups-review.md(R-01 = theEstablishmentplan payload, R-02 = theOpenHandlerlifetime contract, R-03 =pump_bidi) and/workspace/@alkdev/alkcall/docs/reviews/consumer-findings-ledger.md(CF-005/CF-006 = the identity seam, resolved in 0.7.0) - Sibling precedent: alktty (
/workspace/@alkdev/alktty) — the producer/consumer-on-channels shape, wasm-clean default, feature-gated local backend, BAST convention - POC code:
/workspace/alktunnels-udp-poc(forward),/workspace/alktunnels-reverse-poc(reverse)