Files
alksocks/tasks/implementation/core-params-errors.md
T
glm-5.3-flash 1ccd5bbe0c params/error: the wire-stable open-op surface + the typed open-error vocabulary
- params: the ADR-001/ADR-006 constants (`channels/socks5/sub`,
  `alk/socks5`, `socks5:open`, `socks5:bind`), `socks5_open_spec()`
  (Sub/External/channel-open marker; trivial empty-object input schema
  with loud `additionalProperties: false`; reply
  `{channel_id: integer, minimum 1}` required; `socks5:open` scope),
  `Socks5Params {}` (deny_unknown_fields — empty stays valid forever),
  the `establishment_reason` helper re-export.
- error: `SocksOpenError` (the ADR-049 §4 typed surface, shaped on
  alktunnels' `TunnelOpenError`), the typed
  `SocksEstablishmentReason` vocabulary (the five reason codes,
  parse/as_str; unknown codes never aliased), `SocksOpenError::reason()`
  as the typed view, and `SocksSessionError` (open vs in-stream `Rfc`
  vs `Io` — a refused target is an RFC reply code through the session
  type, consumer.md §Error surface). `SocksError`/`SocksServerError`/
  `ReplyError` re-exported from the forked core.
- lib.rs re-exports the new public surface (convention 15).
- 9 new tests: spec shape, constants, serde round-trip +
  unknown-field rejection, the reason mapping table, the
  non-establishment classes (`FORBIDDEN`/`INVALID_INPUT`/
  `too_many_channels`), future-reason non-aliasing, the three-plane
  session surface.
- task `implementation/core-params-errors`: status → completed;
  Notes + Summary filled.

Verification: cargo test (104 green), clippy --all-targets -D
warnings clean, fmt --check clean, doc --no-deps zero warnings,
both wasm checks (check + clippy on wasm32-unknown-unknown) clean.
2026-09-23 15:17:20 +00:00

151 lines
6.9 KiB
Markdown

---
id: implementation/core-params-errors
name: Core — params schema + scope constants + typed error surface
status: completed
depends_on: [implementation/core-crate-deps]
scope: narrow
risk: low
impact: component
level: implementation
---
## Description
Implement `src/params.rs` and `src/error.rs` per wire.md §The Open
Op, ADR-001, and ADR-006 — the wire-stable surface (the one-way door)
and the typed open-error vocabulary. Template:
`/workspace/@alkdev/alktunnels/src/params.rs` (the sibling's spec
builder + scope-constant posture).
Deliverables:
1. **`src/params.rs`**:
- Constants: `OP_SOCKS5_OPEN = "channels/socks5/sub"`,
`SOCKS5_ALPN = "alk/socks5"`, `SOCKS5_OPEN_SCOPE =
"socks5:open"`, `SOCKS5_BIND_SCOPE = "socks5:bind"` (ADR-001/
ADR-006; the scope-string convention `<alpn-family>:
<capability>`; stable once published).
- `socks5_open_spec() -> OperationSpec` — `Sub`-typed, external,
channel-open marker on `SOCKS5_ALPN`, **trivial params**:
input schema `{type: object}`, `additionalProperties: false`
(empty params stay valid forever — ADR-001's additive-only
extension rule); reply schema `{"channel_id": integer,
minimum 1}`; `required_scopes = ["socks5:open"]`. Follow the
alktunnels spec-builder shape exactly (the registry validates
against these schemas).
- The trivial params struct (`Socks5Params {}` — serde
`deny_unknown_fields`, empty) for the consumer's open call.
2. **`src/error.rs`** — the typed open-error surface (alkcall
ADR-049 §4):
- `SocksOpenError` — the consumer-side open-failure vocabulary
mapping the `channel:open_failed` reasons (`dial_failed` /
`unknown_resource` / `resource_shortage` / `handler_error` /
`timeout`) plus the registry's `FORBIDDEN`/`INVALID_INPUT`
classes, shaped on alktunnels' `TunnelOpenError` +
`establishment_reason` helper.
- The session-level error type for the consumer sessions
(`SocksSessionError`-shaped: open errors vs in-stream
`SocksError` vs I/O) — the exact variant set is the task's
judgment; keep it faithful to what the sessions actually
surface (a refused session never yields a phantom channel; a
refused target is an RFC reply code surfaced through the
session type — consumer.md §Error surface).
3. **lib.rs re-exports** for the new public surface (convention 15).
Tests: spec-builder shape assertions (op id, ALPN marker, scopes,
schemas — the trivial-params schema rejects unknown fields), params
serde round-trip (empty object valid; unknown field rejected), the
error-reason mapping table.
## Acceptance Criteria
- [ ] Constants + spec builder per ADR-001/ADR-006 exactly (op id,
ALPN, both scopes, trivial schemas)
- [ ] Input schema rejects unknown fields (the loud-rejection
posture)
- [ ] Typed open-error surface maps the ADR-049 §4 reasons
- [ ] lib.rs re-exports the public surface
- [ ] `cargo test` green; clippy/fmt clean; both wasm checks clean
## References
- docs/architecture/wire.md §The Open Op (the normative op shape)
- docs/architecture/decisions/001-alpn-and-open-op.md (trivial
params; the one-way door)
- docs/architecture/decisions/006-access-control.md (the two scopes)
- docs/architecture/decisions/011-inline-vendor.md §4 (the typed
open-error surface)
- Template: `/workspace/@alkdev/alktunnels/src/params.rs`
(`tunnel_open_spec`, the scope constants, `establishment_reason`)
- POC sketch: `/workspace/alksocks-connect-poc/src/params.rs`
## Notes
> Filled by the implementation agent (2026-09-23).
- `src/params.rs`: the four wire-stable constants (`OP_SOCKS5_OPEN`,
`SOCKS5_ALPN`, `SOCKS5_OPEN_SCOPE`, `SOCKS5_BIND_SCOPE`), the
trivial-params struct `Socks5Params {}` (serde
`deny_unknown_fields`, + `Serialize` so tests round-trip it;
`as_json()` names the canonical wire form), `socks5_open_spec()`
(Sub/External/channel-open marker on `alk/socks5`; input schema
`{type: object, additionalProperties: false}`; reply schema
`channel_id` integer minimum 1 required; `required_scopes =
["socks5:open"]`), and the `establishment_reason` helper re-export
(the alktunnels posture — delegates to
`ChannelOpenError::establishment_reason`).
- `src/error.rs`: `SocksOpenError` (single `Open` variant wrapping
`ChannelOpenError` — the same shape as alktunnels'
`TunnelOpenError`; the `From` impl covers the
`channel:open_failed`/`FORBIDDEN`/`INVALID_INPUT` classes), the
typed `SocksEstablishmentReason` vocabulary (ADR-049 §3's five
codes, `parse`/`as_str`; unknown codes are `None` — never silently
aliased, the vocabulary is one-way), `SocksOpenError::reason()` as
the typed view, and `SocksSessionError` (open errors vs in-stream
`Rfc(SocksError)` vs `Io`; the RFC reply-code refusals ride the
session type per consumer.md §Error surface). `SocksError`/
`SocksServerError`/`ReplyError` re-exported from `rfc1928`
(`SocksServerError` lives in `rfc1928::server`, not the module
root).
- lib.rs re-exports both modules' public surface (convention 15).
- Deliberate deviations from the task text: the params struct
derives `Serialize` (test round-trip needs it; harmless on the
wire — the consumer sends `json!({})`); `SocksEstablishmentReason`
is a separate named type rather than an inline match so the
mapping table is testable in both directions; unknown reason codes
do NOT alias a variant (one-way vocabulary discipline).
- rustdoc links to `error::SocksOpenError` from `params` are
fully-qualified (`crate::error::…`) — plain `[SocksOpenError]`
does not resolve cross-module in this crate's layout.
## Summary
> Filled on completion (2026-09-23).
Implemented the wire-stable open-op surface and the typed
open-error vocabulary:
- `params`: constants + `socks5_open_spec()` per ADR-001/ADR-006
exactly (op id `channels/socks5/sub`, ALPN `alk/socks5`, scopes
`socks5:open`/`socks5:bind`, trivial empty-object input schema with
loud `additionalProperties: false`, reply
`{channel_id: integer, minimum 1}` required).
- `Socks5Params {}` with `deny_unknown_fields` — empty object valid,
unknown fields rejected.
- `error`: `SocksOpenError` mapping the ADR-049 §4 reasons
(`dial_failed`/`unknown_resource`/`resource_shortage`/
`handler_error`/`timeout`) through the typed
`SocksEstablishmentReason` (unknown codes never aliased); the
registry's `FORBIDDEN`/`INVALID_INPUT` classes correctly
non-establishment; `SocksSessionError` shaped open-vs-Rfc-vs-Io
per consumer.md §Error surface.
- 9 new tests (spec shape, constants, serde round-trip + unknown-field
rejection, the reason mapping table, non-establishment classes,
future-reason non-aliasing, the three-plane session surface).
- lib.rs re-exports the public surface.
Verification: `cargo test` (104 green: 67 lib + 37 rfc1928 + 0
doc), `cargo clippy --all-targets -- -D warnings` clean,
`cargo fmt --check` clean, `cargo doc --no-deps` zero warnings, both
wasm checks (`cargo check` + `cargo clippy` on
`wasm32-unknown-unknown`) clean.