docs: resolve OQ-TN-14 — unix in via local, stdio out (alktty owns process stdio)
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)
This commit is contained in:
@@ -150,9 +150,10 @@ OQ-TN numbering. Format per `docs/sdd_process.md`.
|
|||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Status**: resolved — UDP POC DONE (2026-09-06, 17 tests),
|
- **Status**: resolved — UDP POC DONE (2026-09-06, 17 tests),
|
||||||
reverse-flow POC DONE (2026-09-07, 14 tests; re-validated over
|
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
|
alkcall 0.7.0, 16 tests). #3 split (OQ-TN-14): unix ships with the
|
||||||
crate's test suite (pump agnosticism confirmed twice); #4 (helper
|
`local` feature; stdio is OUT — alktty's pipe mode owns process
|
||||||
spike) resolved by alkcall ADR-050. Findings F-1 (plan payloads
|
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)
|
`Send + Sync` — documented upstream) and F-2 (UDP codec mandatory)
|
||||||
recorded; W1 filed upstream and RESOLVED (CF-005/CF-006).
|
recorded; W1 filed upstream and RESOLVED (CF-005/CF-006).
|
||||||
- **Cross-references**: ADR-003, OQ-TN-05, OQ-TN-06
|
- **Cross-references**: ADR-003, OQ-TN-05, OQ-TN-06
|
||||||
@@ -215,13 +216,27 @@ OQ-TN numbering. Format per `docs/sdd_process.md`.
|
|||||||
### OQ-TN-14: Unix-socket and stdio substrate placement
|
### OQ-TN-14: Unix-socket and stdio substrate placement
|
||||||
|
|
||||||
- **Origin**: OQ-TN-10 #3 (the cheap un-POC'd item)
|
- **Origin**: OQ-TN-10 #3 (the cheap un-POC'd item)
|
||||||
- **Status**: open
|
|
||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Impacts**: the `local` feature's module surface (whether unix
|
- **Status**: **resolved 2026-09-07 (split by substrate):**
|
||||||
dial/listen helpers ship in v1); not a wire/ADR question (the wire
|
- **Unix: in scope for the `local` feature v1** — same halves shape
|
||||||
enum already carries `unix` per ADR-001).
|
as TCP (`dial_unix`), cheap, wire enum already carries `unix`
|
||||||
- **Resolution**: pending — both POCs confirm pump agnosticism; unix
|
(ADR-001). Implemented by `tunnels/local-socket-halves`.
|
||||||
is cheap (same halves shape as TCP); stdio bridging is a different
|
- **Stdio: OUT of scope — alktty owns it.** A spawned process's
|
||||||
lifecycle (no dial — the pipe IS the resource). Fold into the
|
stdin/stdout/stderr IS a terminal session in alktty's model:
|
||||||
backend module task; unix leans yes for v1, stdio leans deferred.
|
`LocalTtyBackend`'s pipe mode (`tokio::process::Command` +
|
||||||
- **Cross-references**: ADR-003, ADR-004, overview.md
|
`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)
|
||||||
@@ -101,7 +101,7 @@ alktunnels (default — wasm-clean)
|
|||||||
└── (no backend deps — sockets/process live in feature-gated modules)
|
└── (no backend deps — sockets/process live in feature-gated modules)
|
||||||
|
|
||||||
alktunnels (local feature) — non-wasm by design
|
alktunnels (local feature) — non-wasm by design
|
||||||
└── adds: tokio/net (TCP dial, UDP bind/connect, unix sockets — unix pending OQ-TN-14)
|
└── adds: tokio/net (TCP dial, UDP connect, unix sockets — unix per OQ-TN-14)
|
||||||
```
|
```
|
||||||
|
|
||||||
Same posture as alktty: the default crate is protocol-only and
|
Same posture as alktty: the default crate is protocol-only and
|
||||||
@@ -115,7 +115,7 @@ trivial here, since the codec is a 2-byte length prefix).
|
|||||||
| Feature | Contents | Wasm |
|
| Feature | Contents | Wasm |
|
||||||
|---------|----------|------|
|
|---------|----------|------|
|
||||||
| *(default)* | params, wire codec, open-op spec, establisher shapes, `TunnelSession` — protocol only | yes |
|
| *(default)* | params, wire codec, open-op spec, establisher shapes, `TunnelSession` — protocol only | yes |
|
||||||
| `local` | TCP/UDP/unix dial + listen helpers — halves-producing dial/listen functions (ADR-004; unix pending OQ-TN-14) | no |
|
| `local` | TCP/UDP/unix dial + listen helpers — halves-producing dial/listen functions (ADR-004). Unix ships (OQ-TN-14); stdio does NOT — a spawned process's stdio is alktty's pipe mode (exit codes, signals), not a tunnel | no |
|
||||||
|
|
||||||
## Module Map
|
## Module Map
|
||||||
|
|
||||||
@@ -157,7 +157,8 @@ Key questions affecting this document:
|
|||||||
- **OQ-TN-11**: resource naming collision domain + lifecycle (partially resolved)
|
- **OQ-TN-11**: resource naming collision domain + lifecycle (partially resolved)
|
||||||
- **OQ-TN-12**: hub re-produce composition helper (deferred(scope))
|
- **OQ-TN-12**: hub re-produce composition helper (deferred(scope))
|
||||||
- **OQ-TN-13**: UDP truncation semantics (resolved — fail-loud, ADR-003)
|
- **OQ-TN-13**: UDP truncation semantics (resolved — fail-loud, ADR-003)
|
||||||
- **OQ-TN-14**: unix/stdio substrate placement (open)
|
- **OQ-TN-14**: unix/stdio placement (resolved — unix in via
|
||||||
|
`local`, stdio out: alktty's pipe mode owns process stdio)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ Tunnels reach local networks — the open gate is the security boundary
|
|||||||
- **OQ-TN-11**: resource naming collision domain + lifecycle
|
- **OQ-TN-11**: resource naming collision domain + lifecycle
|
||||||
(partially resolved — the per-producer registry is the leading
|
(partially resolved — the per-producer registry is the leading
|
||||||
candidate)
|
candidate)
|
||||||
- **OQ-TN-14**: unix/stdio substrate placement (open — backend module
|
- **OQ-TN-14**: resolved (split: unix in, stdio out — alktty's pipe
|
||||||
task, not a wire question)
|
mode owns process stdio; see open-questions.md)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ this document is the normative WHAT.
|
|||||||
(NOT an address; the producer's registry maps it to its backing).
|
(NOT an address; the producer's registry maps it to its backing).
|
||||||
- `substrate`: string, required, enum `["tcp", "udp", "unix"]` — the
|
- `substrate`: string, required, enum `["tcp", "udp", "unix"]` — the
|
||||||
extensible discriminator (ADR-001); selects the data-plane framing.
|
extensible discriminator (ADR-001); selects the data-plane framing.
|
||||||
|
The schema ships with all three values in v1 (unix has an
|
||||||
|
implementation path — OQ-TN-14). An older producer rejecting a
|
||||||
|
newer substrate value is the SSH "unknown channel type" posture —
|
||||||
|
loud, not silent.
|
||||||
- `input_schema` pins both as required strings; unknown substrate
|
- `input_schema` pins both as required strings; unknown substrate
|
||||||
values fail schema validation (typed error — loud, not silent).
|
values fail schema validation (typed error — loud, not silent).
|
||||||
- Extensibility: a new substrate is a new enum value, not a format
|
- Extensibility: a new substrate is a new enum value, not a format
|
||||||
|
|||||||
@@ -886,10 +886,11 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
(2026-09-07, `reverse-poc-summary.md` — the `-R` shape validated
|
(2026-09-07, `reverse-poc-summary.md` — the `-R` shape validated
|
||||||
end-to-end over alkcall 0.6.0, re-validated over 0.7.0 with the
|
end-to-end over alkcall 0.6.0, re-validated over 0.7.0 with the
|
||||||
CF-005/CF-006 identity remediation; findings F-1/F-2; W1 filed
|
CF-005/CF-006 identity remediation; findings F-1/F-2; W1 filed
|
||||||
upstream and RESOLVED). Remaining items (#3 unix/stdio, #4
|
upstream and RESOLVED). Remaining items resolved in Phase 1:
|
||||||
helper spike) are spec-scope: #3 folds into the real crate's
|
#3 split — unix ships with the `local` feature (OQ-TN-14);
|
||||||
tests (pump agnosticism confirmed twice), #4 resolved by
|
stdio is OUT (alktty's pipe mode owns process stdio — exit
|
||||||
alkcall 0.6.0 (ADR-050)
|
codes + signals are tty semantics); #4 resolved by alkcall
|
||||||
|
0.6.0 (ADR-050)
|
||||||
- [x] Two-pump helper convergence input gathered (OQ-TN-06) — the POC
|
- [x] Two-pump helper convergence input gathered (OQ-TN-06) — the POC
|
||||||
gives both shapes (producer `pump_halves`, consumer
|
gives both shapes (producer `pump_halves`, consumer
|
||||||
`take_halves` + copy): same shape modulo channel side; the
|
`take_halves` + copy): same shape modulo channel side; the
|
||||||
|
|||||||
@@ -293,7 +293,9 @@ review and ADRs:
|
|||||||
2. **Unix-socket and stdio substrates** — the OQ-TN-10 #3 "cheap"
|
2. **Unix-socket and stdio substrates** — the OQ-TN-10 #3 "cheap"
|
||||||
item. The pump is substrate-agnostic by construction (finding #5);
|
item. The pump is substrate-agnostic by construction (finding #5);
|
||||||
these are wiring, not mechanics. Fold into the real crate's test
|
these are wiring, not mechanics. Fold into the real crate's test
|
||||||
suite rather than a separate POC.
|
suite rather than a separate POC. (Phase 1 resolution: unix in via
|
||||||
|
the `local` feature; stdio OUT — alktty's pipe mode owns process
|
||||||
|
stdio. OQ-TN-14.)
|
||||||
3. **The reverse-flow (`-R`) advertisement** — OQ-TN-10 #2 remains
|
3. **The reverse-flow (`-R`) advertisement** — OQ-TN-10 #2 remains
|
||||||
open; the SSH `tcpip-forward` template in the survey is the
|
open; the SSH `tcpip-forward` template in the survey is the
|
||||||
lifecycle shape to validate there.
|
lifecycle shape to validate there.
|
||||||
|
|||||||
@@ -223,7 +223,9 @@ alktunnels-reverse-poc/
|
|||||||
listen establisher over the same open op — spec-shaped, not POC-
|
listen establisher over the same open op — spec-shaped, not POC-
|
||||||
shaped.
|
shaped.
|
||||||
3. **Unix socket + stdio substrates** — OQ-TN-10 #3 (cheap; the pump
|
3. **Unix socket + stdio substrates** — OQ-TN-10 #3 (cheap; the pump
|
||||||
is substrate-agnostic and both POCs now confirm it).
|
is substrate-agnostic and both POCs now confirm it). Phase 1
|
||||||
|
resolution: unix in via the `local` feature; stdio OUT — alktty's
|
||||||
|
pipe mode owns process stdio (OQ-TN-14).
|
||||||
4. ~~**W1 remediation**~~ — **resolved 2026-09-07 (alkcall 0.7.0)**:
|
4. ~~**W1 remediation**~~ — **resolved 2026-09-07 (alkcall 0.7.0)**:
|
||||||
the remediation IS now exercised from this POC's topology
|
the remediation IS now exercised from this POC's topology
|
||||||
(transport identity, `ServingConfig.identity` override, precedence,
|
(transport identity, `ServingConfig.identity` override, precedence,
|
||||||
|
|||||||
@@ -1,52 +1,60 @@
|
|||||||
---
|
---
|
||||||
id: architecture/oq-tn-14-tracker
|
id: architecture/oq-tn-14-tracker
|
||||||
name: "[external-trigger, deferred-oq] OQ-TN-14 unix/stdio placement tracker"
|
name: "OQ-TN-14 follow-through: unix lands with local v1; stdio stays alktty's"
|
||||||
status: pending
|
status: pending
|
||||||
depends_on: []
|
depends_on: []
|
||||||
scope: single
|
scope: single
|
||||||
risk: trivial
|
risk: trivial
|
||||||
impact: isolated
|
impact: isolated
|
||||||
level: research
|
level: research
|
||||||
tags: [architecture, deferred-oq, external-trigger, unix, stdio]
|
tags: [architecture, external-trigger, unix, stdio, alktty]
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
External-trigger tracker for OQ-TN-14 (`docs/architecture/open-questions.md`
|
OQ-TN-14 is resolved (open-questions.md, 2026-09-07): unix ships with
|
||||||
— unix-socket and stdio substrate placement, currently `open` leaning
|
the `local` feature v1 (`tunnels/local-socket-halves`); stdio is OUT of
|
||||||
unix-yes-v1). This task is NOT actionable implementation work; it tracks
|
scope — a spawned process's stdio is alktty's pipe mode (exit codes +
|
||||||
whether the unblocking condition has arrived. If the condition arrives, this
|
signals are tty semantics, not tunnel semantics); remote command
|
||||||
task flips the OQ (and the implementation task `tunnels/local-unix-halves`
|
execution composes via alktty.
|
||||||
below) from its deferred/open posture to actionable.
|
|
||||||
|
|
||||||
Unblocking conditions (any one):
|
This task stays open as the boundary-maintenance tracker (research
|
||||||
|
level — not implementation work). It re-opens the question only if a
|
||||||
|
concrete consumer demonstrates the resolved posture is wrong:
|
||||||
|
|
||||||
1. A consumer needs unix-socket tunneling through alktunnels (a hub exposing
|
1. A consumer needs stdio tunneling with NO process semantics (no exit
|
||||||
a docker container's unix socket, or a local daemon bridge).
|
code, no signals) — e.g. piping an already-running long-lived
|
||||||
2. The `tunnels/crate-init` task lands and the `local` feature module needs
|
process's existing stdio. That is a different resource shape (the
|
||||||
its v1 surface finalized (the decision point baked into that task).
|
pipe IS the resource, no dial); it would need its own ADR, not a
|
||||||
|
silent addition to `local`.
|
||||||
|
2. A consumer reports confusion about when to use alktunnels vs alktty
|
||||||
|
for a stdio-like flow — the composition boundary in OQ-TN-14 needs
|
||||||
|
sharpening in the docs, not code.
|
||||||
|
|
||||||
If neither arrives by the end of the v1 implementation phase, the OQ stays
|
If neither arrives, this task closes with the implementation phase.
|
||||||
open and `local` ships TCP+UDP only (the lean posture ADR-004 endorses).
|
|
||||||
|
|
||||||
## Work
|
## Work
|
||||||
|
|
||||||
- Re-read OQ-TN-14 + `docs/architecture/overview.md` §Feature Gates when
|
- Re-read OQ-TN-14 + alktty's `tty-local.md` §pipe mode at triage;
|
||||||
triaging; confirm whether the unix helper ships with `local` v1.
|
confirm the boundary held (no stdio-shaped resource got registered
|
||||||
|
as a tunnel resource).
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- [ ] Tracked (this file existing and being reviewed at triage time IS the
|
- [ ] Tracked (this file existing and being reviewed at triage time IS
|
||||||
tracking mechanism — no code or doc change is expected until the
|
the tracking mechanism — no code or doc change is expected unless
|
||||||
condition arrives)
|
a boundary condition above arrives)
|
||||||
|
|
||||||
## Out of scope
|
## Out of scope
|
||||||
|
|
||||||
- Implementing the unix/stdio helpers (see `tunnels/local-unix-halves`)
|
- Implementing stdio bridging (would need its own ADR per the
|
||||||
|
conditions above)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- docs/architecture/open-questions.md OQ-TN-14
|
- docs/architecture/open-questions.md OQ-TN-14 (the resolution)
|
||||||
|
- alktty `docs/architecture/tty-local.md` §pipe mode (the owner of
|
||||||
|
process stdio)
|
||||||
- docs/sdd_process.md §Deferred OQs and the blocker-task half
|
- docs/sdd_process.md §Deferred OQs and the blocker-task half
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ pub struct UdpAssociateHalves { sock: Arc<UdpSocket> } // the POC's associate s
|
|||||||
`poll_recv` into a too-small caller buffer must fail loud
|
`poll_recv` into a too-small caller buffer must fail loud
|
||||||
(OQ-TN-13's resolved posture — ADR-003). The codec wraps at this
|
(OQ-TN-13's resolved posture — ADR-003). The codec wraps at this
|
||||||
boundary; the pump never sees UDP specifics.
|
boundary; the pump never sees UDP specifics.
|
||||||
|
- **Unix**: `dial_unix` ships (OQ-TN-14 resolved — same halves shape
|
||||||
|
as TCP). **Stdio does NOT** — a spawned process's stdio is alktty's
|
||||||
|
pipe mode (exit codes + signals are tty semantics, not tunnel
|
||||||
|
semantics); remote command execution composes via alktty, not here.
|
||||||
- **Error mapping** to `TunnelEstablishError` variants (the POC's
|
- **Error mapping** to `TunnelEstablishError` variants (the POC's
|
||||||
`Into<EstablishmentError>` path).
|
`Into<EstablishmentError>` path).
|
||||||
- `TargetHandle` re-used from producer.rs (the halves type lives in
|
- `TargetHandle` re-used from producer.rs (the halves type lives in
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ pub enum Substrate { Tcp, Udp, Unix }
|
|||||||
|
|
||||||
- `deny_unknown_fields` is the loud-rejection posture (ADR-001: unknown
|
- `deny_unknown_fields` is the loud-rejection posture (ADR-001: unknown
|
||||||
fields fail schema validation, never silently ignored).
|
fields fail schema validation, never silently ignored).
|
||||||
- `Substrate::Unix` is included in the type (the wire enum per ADR-001)
|
- `Substrate::Unix` is included in the type AND in the open-op schema's
|
||||||
but NOT in the open-op schema's enum list v1 (`["tcp", "udp"]`) until
|
enum list v1 (`["tcp", "udp", "unix"]` — OQ-TN-14 resolved: unix has
|
||||||
OQ-TN-14 resolves — an older producer rejecting a newer substrate is
|
an implementation path via the `local` feature). The schema list is
|
||||||
the SSH "unknown channel type" posture; the schema list is the
|
the authoritative gate for newer-substrate negotiation: an older
|
||||||
authoritative gate. Document this asymmetry in the type docs (type
|
producer rejecting a newer substrate value is the SSH "unknown
|
||||||
extensible, schema conservative).
|
channel type" posture (loud, not silent).
|
||||||
|
|
||||||
### The open-op spec (in producer.rs or params.rs — put it where
|
### The open-op spec (in producer.rs or params.rs — put it where
|
||||||
`tunnels/producer-open-op` can consume; the spec builder lives in
|
`tunnels/producer-open-op` can consume; the spec builder lives in
|
||||||
@@ -56,7 +56,8 @@ pub fn tunnel_open_spec() -> OperationSpec
|
|||||||
- `OperationType::Sub`; `Visibility::External`; channel-open marker
|
- `OperationType::Sub`; `Visibility::External`; channel-open marker
|
||||||
`ChannelOpenSpec::new(TUNNEL_ALPN)`.
|
`ChannelOpenSpec::new(TUNNEL_ALPN)`.
|
||||||
- `input_schema`: `{resource: string (required), substrate: string enum
|
- `input_schema`: `{resource: string (required), substrate: string enum
|
||||||
["tcp","udp"] (required)}` — JSON Schema Draft shape as the POCs used.
|
["tcp","udp","unix"] (required)}` — JSON Schema Draft shape as the
|
||||||
|
POCs used (unix in the list per OQ-TN-14's resolution).
|
||||||
- `output_schema`: `{channel_id: integer > 0}`.
|
- `output_schema`: `{channel_id: integer > 0}`.
|
||||||
- `AccessControl.required_scopes: [TUNNEL_OPEN_SCOPE]` (ADR-006).
|
- `AccessControl.required_scopes: [TUNNEL_OPEN_SCOPE]` (ADR-006).
|
||||||
- `description`: a one-line human hint (round-trips through discovery;
|
- `description`: a one-line human hint (round-trips through discovery;
|
||||||
|
|||||||
Reference in New Issue
Block a user