tasks/architecture/: - oq-promotion-sync (planning): back-pointers from the phase-0 ledger + AGENTS.md to the promoted OQ tracker (the convergence checklist's final half) - oq-tn-14-tracker: the Safe-Exit external-trigger tracker task for OQ-TN-14 (unix/stdio placement; [external-trigger, deferred-oq], risk trivial, level research per the two-halves rule) tasks/tunnels/ (the implementation graph, 8 generations): - crate-init: module skeleton per overview.md's module map - params: TunnelParams + open-op spec (ADR-001 wire-stable surface) - wire-codec: frame_datagram/DatagramReader + the 8 POC-pinned test families (ADR-003) - producer-open-op: establisher (dial, plan flow R-01) + pump handler (pump_bidi inline R-02) + registration; POC-ported integration tests - consumer-session: TunnelSession (open/adopt, data planes, teardown matrix — ADR-005); generalizes the reverse POC's ReverseTunnel - producer-listen: the listen establisher + AcceptQueue contract (ADR-004 shape 2) - local-socket-halves: the local feature (TCP/UDP/unix halves functions; truncation fail-loud per OQ-TN-13; unix ships per OQ-TN-14's lean-yes, stdio deferred) - review-core-crates: review-injection point before the downstream tasks build on the high-risk producer/consumer shapes - end-to-end-suite: 6 suites / >=20 tests consolidating both POC suites against the public API (the spec's executable form) - review-impl: the phase-gate review (wire/API/conventions/docs sync; findings doc per the alkhttp/alkcall house pattern) Graph verified with taskgraph: 12 tasks valid, no cycles, 8 generations; critical path = oq-promotion-sync -> crate-init -> params -> wire-codec -> producer-open-op -> consumer-session -> review-core-crates -> review-impl; risk concentrated in the two session tasks (both POC-validated); parallel groups available at generations 1 and 6
3.8 KiB
3.8 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tunnels/local-socket-halves | local feature — real socket halves functions (TCP dial, UDP connect, unix) | pending |
|
moderate | medium | component | implementation |
|
Description
Add the local feature module (src/local/) per overview.md §Feature
Gates + ADR-004: the real-socket halves functions the assembly layer
injects as DialFn/AcceptFn closures. The POCs' substrate adapters
are the reference (they ran all of this); the task is packaging them
into the feature-gated module with the crate's conventions.
Surface
// src/local/mod.rs (feature = "local"; non-wasm by design)
pub async fn dial_tcp(target: &str) -> Result<TargetHandle, TunnelEstablishError>;
// TcpStream::connect (5s timeout per the POCs) → into_split → boxed halves
pub async fn connect_udp(target: &str) -> Result<TargetHandle, TunnelEstablishError>;
// UdpSocket::bind(("0.0.0.0", 0)) → connect(target) → the FRAMED adapter
// (UdpHalf + codec — ADR-003; truncation fail-loud per OQ-TN-13)
pub async fn dial_unix(path: &str) -> Result<TargetHandle, TunnelEstablishError>;
// UnixStream::connect → into_split (OQ-TN-14: ships with local v1 — cheap,
// same halves shape as TCP)
pub struct TcpListenerHalves { listener: tokio::net::TcpListener }
impl TcpListenerHalves {
pub async fn bind(addr: &str) -> Result<Self, ...>;
pub fn accept_fn(&self, queue: &AcceptQueue) -> impl Future; // the accept loop feeding the queue
}
pub struct UdpAssociateHalves { sock: Arc<UdpSocket> } // the POC's associate shape
- UDP adapter (
UdpHalf): port from the forward POC (params.rs'sUdpHalfRead/UdpHalfWrite) with the truncation fix:poll_recvinto a too-small caller buffer must fail loud (OQ-TN-13's resolved posture — ADR-003). The codec wraps at this boundary; the pump never sees UDP specifics. - Error mapping to
TunnelEstablishErrorvariants (the POC'sInto<EstablishmentError>path). TargetHandlere-used from producer.rs (the halves type lives in producer.rs; local/ depends on it, never vice versa).
Feature wiring
[features]
default = []
local = ["tokio/net"]
tokio/netis the only dep addition (no new external crates; the POCs provedrt+sync+io-util+macros+time+netcovers everything).- The default crate stays wasm-clean:
local-gated code must not be importable fromproducer.rs/consumer.rs(convention 16 — backend modules are never imported from the shared/producer/consumer modules). - Unix is IN v1 (OQ-TN-14's lean-yes posture; same halves shape as TCP); stdio bridging is NOT (different lifecycle — deferred).
Tests
Behind the feature: TCP dial round-trip through the real establisher path, UDP associate + datagram round-trip with the framed adapter, unix dial round-trip, truncation fail-loud (a short recv surfaces as an error), the 1400-byte MTU datagram through bounded buffers (the POC's sizing test).
Acceptance Criteria
cargo test --features localgreen;cargo test(default) unaffectedcargo test --all-featuresgreen (AGENTS.md convention 14)- wasm32 check on the DEFAULT crate passes;
localis non-wasm by design (documented) - Truncation fails loud (the OQ-TN-13 test)
- No socket type appears outside
src/local/ - Clippy/fmt clean
References
- docs/architecture/overview.md §Feature Gates, §Dependencies
- docs/architecture/decisions/004-no-backend-trait.md (the injection point), 003 (the framed adapter)
- POC reference:
/workspace/alktunnels-udp-poc/src/producer.rs(UdpHalf, the dial paths),params.rs(the UDP adapter)
Notes
Agent fills during implementation.
Summary
Agent fills this on completion.