--- status: draft last_updated: 2026-09-07 --- # alktunnels — Open Questions Centralized tracker. The Phase 0 OQ ledger (`docs/research/ phase-0-findings.md`, OQ-TN-01..10) is promoted here with statuses current as of the Phase 1 spec; new Phase 1 questions continue the OQ-TN numbering. Format per `docs/sdd_process.md`. ## Theme 1: Wire format and addressing ### OQ-TN-01: Target addressing format (open-op `params` shape) - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-01) - **Status**: resolved — [ADR-001](decisions/001-open-params-layout.md) - **Priority**: high - **Resolution**: 2026-09-07 (Phase 1). `params` = self-contained JSON object: `{ "resource": , "substrate": }`. The resource identifier is the producer's stable name (a registered resource id, not an address); substrate is the extensible discriminator (`tcp` | `udp` | `unix` extensible, additive). The producer owns the backing; no URL-style general addressing. Reframe + survey input (ssh-socks5-survey §OQ-TN-01 residue) + both POCs. - **Cross-references**: OQ-TN-02, OQ-TN-03, ADR-001, ADR-002 ### OQ-TN-02: Datagram substrates (UDP) — boundary preservation - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-02) - **Status**: resolved — [ADR-003](decisions/003-codec-and-udp-framing.md) - **Priority**: high - **Resolution**: split by path (2026-09-06 survey) + F-2 mandate (2026-09-07 reverse POC). Base open-op UDP resources: endpoint-at-open (one channel = one flow). Dynamic/`-D` UDP: per-datagram addressing inside the tunnel payload, assembly-layer. Boundary preservation: mandatory `[len: u16 BE]` per-datagram framing — mandatory for correctness, not cosmetics (F-2; the full rationale and the executable pin live in ADR-003). Under the codec, `len=0` is a legal empty datagram (forward POC validated); EOF is the channels-level sentinel; the layers never collide. - **Cross-references**: OQ-TN-01, OQ-TN-07, ADR-003 ### OQ-TN-03: Direction semantics (`-L` / `-R` / dynamic) - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-03) - **Priority**: high - **Status**: resolved — hub-owns-the-connection model (2026-09-05) + the reverse-flow POC (2026-09-07, `reverse-poc-summary.md`). No protocol-level direction: role follows the resource. `-L`/`-R` are the same open op with the entry point on different machines; no advertisement op is needed (the listener is the initiator's own local resource; a far-side listener is a producer-side listen establisher over the same open op). `-D` composes at the assembly layer ("tunnel a socks5 connection"), out of base-crate scope. - **Cross-references**: OQ-TN-04, OQ-TN-08, ADR-004, ADR-005 ### OQ-TN-04: No forced local binding - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-04) - **Priority**: medium - **Status**: resolved — binding is always assembly-layer and optional, on either side; the protocol never binds. API surface: dial flows with no local bind (both POCs), listen flows where the binding is the producer's establisher (listen variant, ADR-004), unbound/abstract flows (in-process pipes — the pump is generic over boxed halves). The concrete produce/consume surface is spec'd in producer.md / consumer.md (ADR-005). - **Cross-references**: OQ-TN-03, ADR-004, ADR-005 ## Theme 2: Crate architecture ### OQ-TN-05: Backend inversion point — `TunnelBackend` trait? - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-05) - **Status**: resolved — [ADR-004](decisions/004-no-backend-trait.md) - **Priority**: medium - **Resolution**: 2026-09-07 (Phase 1). No trait for substrate access: "produce boxed halves for a resource" is a function (UDP POC, OQ-TN-05 executable input). The only remaining thread — the hub re-produce composition — resolves the same way: a hub proxy is a consumer + producer composed at the assembly layer (the reverse POC's hub is exactly this shape minus re-export); a composition helper is deferred(scope) until a concrete hub-proxy consumer exists (OQ-TN-12). - **Cross-references**: OQ-TN-12, ADR-004 ### OQ-TN-06: The two-pump helper — extract now? - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-06) - **Priority**: medium - **Status**: resolved (upstream) — `alkcall::channels::pump_bidi` landed in alkcall 0.6.0 (ADR-050, review 007 R-03), shape as this OQ sketched it; this crate is cited as convergence input. alktunnels consumes the helper; hand-rolling is out. - **Cross-references**: ADR-005 (consumer pump), ADR-050 (upstream) ## Theme 3: Protocol surfaces ### OQ-TN-07: ALPN strategy - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-07) - **Status**: resolved — [ADR-002](decisions/002-alpn-strategy.md) - **Priority**: high - **Resolution**: 2026-09-07 (Phase 1). Option A: single `alk/tunnel` ALPN; the substrate discriminator in `params` selects the framing. Prior art (SSH one channel mechanism; SOCKS5 one control connection; udpgw self-describing framing) gives no reason for the split; option B's consumer-side API bifurcation buys nothing. - **Cross-references**: OQ-TN-01, OQ-TN-02, ADR-001, ADR-002 ### OQ-TN-08: Access control and ownership scope - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-08) - **Status**: resolved — [ADR-006](decisions/006-access-control-posture.md) (2026-09-07 — the posture promoted to a decision). The resource is owned by the far side (alkcall op-level ACL applies as-is; no tunnel-specific policy layer, no allowlists in v1; the ownership seam is the establisher seeing the per-call opener identity). Discovery = the ACL-filtered ops listing (openable channels are operations, ADR-047); `OperationSpec.description` round-trips through discovery (review 006 E-02). The live resource-enumeration half (alkcall OQ-40) stays deferred upstream. Identity: 0.7.0's precedence chain (token > `ServingConfig.identity` > transport), per-call opener identity on the open-op hooks (CF-005/CF-006). The `tunnel:open` scope constant is stable once published (ADR-006 §2). `-D` = "tunnel a socks5 connection" (target selection in the socks5 protocol). - **Cross-references**: OQ-TN-03, ADR-006, producer.md (scope gating) ### OQ-TN-09: Lifecycle, teardown, and error reporting - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-09) - **Priority**: high - **Status**: resolved (superseded by alkcall ADR-049, 0.5.0; narrowed through 0.6.0/0.7.0) — establishment is the open op's awaited phase; dial failure is a typed `channel:open_failed` call error (reason ∈ `dial_failed` / `unknown_resource` / `resource_shortage` / `handler_error` / `timeout`), never a phantom channel. Residual resolved by the POCs: half-open semantics = the two-pump contract (W4 validated); the consumer session owns teardown (W3, ADR-005); v1 needs no mid-stream control frame (pump-phase failures are EOF-shaped by design, ADR-049 §6). - **Cross-references**: ADR-003 (sentinels), ADR-005, alkcall ADR-049 ### OQ-TN-10: POC scope (Phase 0 closure) - **Origin**: [phase-0-findings.md](../research/phase-0-findings.md) (OQ-TN-10) - **Priority**: low - **Status**: resolved — UDP POC DONE (2026-09-06, 17 tests), reverse-flow POC DONE (2026-09-07, 14 tests; re-validated over alkcall 0.7.0, 16 tests). #3 (unix/stdio) folds into the real crate's test suite (pump agnosticism confirmed twice); #4 (helper spike) resolved by alkcall ADR-050. Findings F-1 (plan payloads `Send + Sync` — documented upstream) and F-2 (UDP codec mandatory) recorded; W1 filed upstream and RESOLVED (CF-005/CF-006). - **Cross-references**: ADR-003, OQ-TN-05, OQ-TN-06 ## Theme 4: Phase 1 residues (new) ### OQ-TN-11: Resource naming — collision domain and lifecycle of a resource id - **Origin**: OQ-TN-01's residue (stable resource name vs target address), carried into Phase 1 - **Status**: partially resolved — the collision-domain half is **resolved by [ADR-001](decisions/001-open-params-layout.md)**: the domain is the producer's registry (per-producer; two producers on the same connection may expose the same resource name independently — the open op resolves against the SERVING side's registry). The lifecycle half stays open: is a resource registry a producer-side assembly construct (the POCs' `HashMap` — the leading candidate), or does it need protocol-level register/rename/retire ops? - **Priority**: medium - **Impacts**: producer.md's `ResourceRegistry` shape and the discovery story (OQ-TN-08); a protocol-level registry op would be a new wire surface (one-way door). - **Resolution**: lifecycle half pending — no prior-art pressure (SSH lists no forwards in-band; discovery is the ops listing); decide when a consumer needs shared/lifecycle-managed resources. - **Cross-references**: ADR-001, OQ-TN-08 ### OQ-TN-12: Hub re-produce composition helper - **Origin**: OQ-TN-05's remaining thread (2026-09-05 hub model) - **Status**: deferred(scope) - **Priority**: low - **Blocked on**: a concrete hub-proxy consumer (a crate that re-produces a consumed tunnel resource) — the reverse POC's hub is the seed shape (consumer + `register_openable` on the same connection) but does not exercise re-export; if one materializes, decide whether a composition helper belongs in this crate or is assembly-layer wiring of the existing surface. - **Cross-references**: OQ-TN-05, ADR-004, alkcall ADR-042 (relay) ### OQ-TN-13: UDP truncation semantics at the codec boundary - **Origin**: forward POC Issues Surfaced #3 - **Status**: resolved — [ADR-003](decisions/003-codec-and-udp-framing.md) (2026-09-07): **fail-loud at the adapter level.** A receive buffer smaller than the datagram is an error, never a silent truncation — a truncated datagram would corrupt the length-framed stream's framing invariants (the reverse POC's F-2 rationale: the codec's wire discipline is what keeps empty datagrams unambiguous; silent truncation is the same class of invariant break). The codec's MTU bound (65535) makes truncation unreachable for well-formed peers; failing loud turns a spec violation into a typed error. - **Priority**: medium - **Impacts**: the `local` feature's UDP adapter and `TunnelSession::recv_datagram`'s error surface; consumer-visible (an error vs silent truncation is a behavioral difference), so the decision rides an Accepted ADR before the first consumer. - **Cross-references**: ADR-003, wire.md ### OQ-TN-14: Unix-socket and stdio substrate placement - **Origin**: OQ-TN-10 #3 (the cheap un-POC'd item) - **Status**: open - **Priority**: low - **Impacts**: the `local` feature's module surface (whether unix dial/listen helpers ship in v1); not a wire/ADR question (the wire enum already carries `unix` per ADR-001). - **Resolution**: pending — both POCs confirm pump agnosticism; unix is cheap (same halves shape as TCP); stdio bridging is a different lifecycle (no dial — the pipe IS the resource). Fold into the backend module task; unix leans yes for v1, stdio leans deferred. - **Cross-references**: ADR-003, ADR-004, overview.md