Files
alktunnels/tasks/tunnels/params.md
T
glm-5.3-flash f6fb3a3da2 feat: params — TunnelParams/Substrate wire types + tunnel_open_spec
- TunnelParams {resource, substrate} with deny_unknown_fields (ADR-001
  loud-rejection posture); Substrate enum ships unix in v1 (OQ-TN-14)
- tunnel_open_spec(): channels/tunnel/sub, Sub-typed, alk/tunnel
  channel-open marker, input schema enum [tcp,udp,unix], channel_id
  output schema (minimum 1, required), tunnel:open ACL (ADR-006),
  discovery description
- establishment_reason delegates to alkcall 0.7.0's typed
  ChannelOpenError::establishment_reason; error re-exported through
  params
- 6 unit tests: serde round-trip, unknown/missing-field rejection,
  unknown-substrate rejection, spec conformance to wire.md

Verified: cargo test, clippy --all-targets -D warnings (native +
wasm32), fmt --check, wasm32 check — all clean
2026-09-08 06:48:54 +00:00

126 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: tunnels/params
name: TunnelParams + open-op spec (schema, scope gate, constants)
status: completed
depends_on: [tunnels/crate-init]
scope: narrow
risk: low
impact: component
level: implementation
tags: [wire, params, open-op]
---
## Description
Implement `src/params.rs` per ADR-001 + wire.md §The Open Op: the
`TunnelParams` wire type, the `Substrate` discriminator, and the open-op
`OperationSpec` builder. This is the wire-stable surface (one-way door) —
exact conformance to the ADR is the point.
### Types
```rust
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TunnelParams {
pub resource: String,
pub substrate: Substrate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Substrate { Tcp, Udp, Unix }
```
- `deny_unknown_fields` is the loud-rejection posture (ADR-001: unknown
fields fail schema validation, never silently ignored).
- `Substrate::Unix` is included in the type AND in the open-op schema's
enum list v1 (`["tcp", "udp", "unix"]` — OQ-TN-14 resolved: unix has
an implementation path via the `local` feature). The schema list is
the authoritative gate for newer-substrate negotiation: an older
producer rejecting a newer substrate value is the SSH "unknown
channel type" posture (loud, not silent).
### The open-op spec (in producer.rs or params.rs — put it where
`tunnels/producer-open-op` can consume; the spec builder lives in
params.rs per the module map, producer.rs re-exports)
```rust
pub const OP_TUNNEL_OPEN: &str = "channels/tunnel/sub";
pub const TUNNEL_ALPN: &str = "alk/tunnel";
pub const TUNNEL_OPEN_SCOPE: &str = "tunnel:open";
pub fn tunnel_open_spec() -> OperationSpec
```
- `OperationType::Sub`; `Visibility::External`; channel-open marker
`ChannelOpenSpec::new(TUNNEL_ALPN)`.
- `input_schema`: `{resource: string (required), substrate: string enum
["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}`.
- `AccessControl.required_scopes: [TUNNEL_OPEN_SCOPE]` (ADR-006).
- `description`: a one-line human hint (round-trips through discovery;
the spec's SHOULD).
### Error surface
`src/error.rs`: `TunnelError` (thiserror) covering the codec + session
errors (filled incrementally by later tasks); plus the re-export of
`alkcall::channels::client::ChannelOpenError` and an
`establishment_reason(&CallError) -> Option<&str>` helper
(ADR-049 §4 surface; the POC consumer's shape).
## Acceptance Criteria
- [x] `TunnelParams` round-trips serde; unknown fields rejected
- [x] `tunnel_open_spec()` matches wire.md §The Open Op exactly (op id,
type, ALPN marker, schemas, ACL, description)
- [x] Schema validation test: valid params pass; missing fields,
unknown fields, unknown substrate values all fail
- [x] `establishment_reason` maps `channel:open_failed` details.reason
(POC-pinned shapes: `unknown_resource`, `dial_failed`,
`resource_shortage`)
- [x] Unit tests cover the serde + schema shapes
- [x] wasm32 check passes
## References
- docs/architecture/decisions/001-open-params-layout.md
- docs/architecture/wire.md §The Open Op
- docs/architecture/decisions/006-access-control-posture.md
- POC reference: `/workspace/alktunnels-udp-poc/src/params.rs` +
`producer.rs::tunnel_open_spec` (the shape, with `deny_unknown_fields`
added)
## Notes
- `TunnelParams` + `Substrate` per the task's exact type shapes
(`deny_unknown_fields`, `rename_all = "lowercase"`, `Unix` in the
enum). `Eq` added to `TunnelParams` (test equality).
- `tunnel_open_spec()` lives in params.rs per the module map;
producer.rs re-exports. Output schema pins `channel_id` with
`minimum: 1` + `required` (wire.md: integer > 0) — the POC's
`minimum: 0` / no-required was loose.
- `establishment_reason` delegates to alkcall 0.7.0's
`ChannelOpenError::establishment_reason` method (upstream has it);
`ChannelOpenError` re-exported from params.rs (pub use, since lib.rs
re-exports through it).
- Schema-validation tests use the `TunnelParams` deserializer as the
enum validator (same deny-unknown/enum posture as the registry's
JSON Schema check; no jsonschema dep added for v1 — the
input_schema string is asserted structurally in
`open_spec_matches_wire_md`).
- 6 unit tests: round-trip ×3 substrates, unknown-field rejection,
missing-field rejection, unknown-substrate rejection, spec-shape
conformance, schema enum validation.
## Summary
`src/params.rs` complete: the wire-stable `TunnelParams`/`Substrate`
types (ADR-001 exact shapes), `tunnel_open_spec()` (op id, Sub type,
External visibility, input/output schemas with the 3-value substrate
enum, `tunnel:open` ACL, channel-open marker, description),
constants, and the `establishment_reason` helper. Verified: `cargo
test` (6 passed), clippy --all-targets -D warnings (native + wasm32),
fmt --check, wasm32 check — all clean.