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

6.9 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
implementation/core-params-errors Core — params schema + scope constants + typed error surface completed
implementation/core-crate-deps
narrow low component 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.