Files
alktunnels/docs/architecture/open-questions.md
T
glm-5.3-flash 508fc6fc28 docs: complete oq-promotion-sync — AGENTS.md current with Phase 1, add TN-12 tracker
- AGENTS.md architecture bullet: spec exists (present tense), OQ tracker
  authoritative; convention 16 de-hedged (shape pinned by ADR-004/005);
  alkcall version drift fixed (v0.4.x/v0.5.x -> v0.7.x per Cargo.toml);
  Phase 1-work bullet -> Phase 1 complete, Phase 2 in tasks/
- phase-0-findings.md: promotion note atop the Open Questions section
  pointing at docs/architecture/open-questions.md; convergence
  checklist's final item checked
- tasks: add architecture/oq-tn-12-tracker (external-trigger tracker for
  OQ-TN-12 deferred(scope), per sdd_process.md deferred-OQ two-halves
  rule); OQ-TN-12 Blocked-on text now references the tracker id
- tasks: oq-promotion-sync completed (scope grew: 3 folded stale
  AGENTS.md refs found during review + the TN-12 tracker)

Verified: taskgraph validate (13 tasks), grep sweep clean for stale
'not exist yet'/version-drift language, cargo test + cargo fmt --check
2026-09-08 06:18:55 +00:00

243 lines
12 KiB
Markdown

---
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": <string>, "substrate": <string> }`.
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 split (OQ-TN-14): unix ships with the
`local` feature; stdio is OUT — alktty's pipe mode owns process
stdio (exit codes + signals are tty semantics). #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. Tracker task:
`architecture/oq-tn-12-tracker` (tasks/architecture/).
- **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)
- **Priority**: low
- **Status**: **resolved 2026-09-07 (split by substrate):**
- **Unix: in scope for the `local` feature v1** — same halves shape
as TCP (`dial_unix`), cheap, wire enum already carries `unix`
(ADR-001). Implemented by `tunnels/local-socket-halves`.
- **Stdio: OUT of scope — alktty owns it.** A spawned process's
stdin/stdout/stderr IS a terminal session in alktty's model:
`LocalTtyBackend`'s pipe mode (`tokio::process::Command` +
`Stdio::piped()`, alktty `tty-local.md`) is exactly the stdio
case — 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 (exit code, signals — a byte
tunnel has neither). Consumers needing to run commands remotely
compose: alkcall dial → alktty session (pipe mode) — the two
crates are siblings on the same channels substrate, not
competitors. (The OQ-TN-10 #3 "cheap item" was scoped before the
sibling relationship was this clear.)
- **Impacts**: `tunnels/local-socket-halves` (unix yes, stdio no);
the wire enum's `unix` value now has a v1 implementation path.
- **Cross-references**: ADR-003, ADR-004, overview.md; alktty
`tty-local.md` §pipe mode, alktty ADR-004 (exit code on a control
chunk)