- 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.
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 |
|
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:
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 onSOCKS5_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 {}— serdedeny_unknown_fields, empty) for the consumer's open call.
- Constants:
src/error.rs— the typed open-error surface (alkcall ADR-049 §4):SocksOpenError— the consumer-side open-failure vocabulary mapping thechannel:open_failedreasons (dial_failed/unknown_resource/resource_shortage/handler_error/timeout) plus the registry'sFORBIDDEN/INVALID_INPUTclasses, shaped on alktunnels'TunnelOpenError+establishment_reasonhelper.- The session-level error type for the consumer sessions
(
SocksSessionError-shaped: open errors vs in-streamSocksErrorvs 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).
- 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 testgreen; 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 structSocks5Params {}(serdedeny_unknown_fields, +Serializeso tests round-trip it;as_json()names the canonical wire form),socks5_open_spec()(Sub/External/channel-open marker onalk/socks5; input schema{type: object, additionalProperties: false}; reply schemachannel_idinteger minimum 1 required;required_scopes = ["socks5:open"]), and theestablishment_reasonhelper re-export (the alktunnels posture — delegates toChannelOpenError::establishment_reason).src/error.rs:SocksOpenError(singleOpenvariant wrappingChannelOpenError— the same shape as alktunnels'TunnelOpenError; theFromimpl covers thechannel:open_failed/FORBIDDEN/INVALID_INPUTclasses), the typedSocksEstablishmentReasonvocabulary (ADR-049 §3's five codes,parse/as_str; unknown codes areNone— never silently aliased, the vocabulary is one-way),SocksOpenError::reason()as the typed view, andSocksSessionError(open errors vs in-streamRfc(SocksError)vsIo; the RFC reply-code refusals ride the session type per consumer.md §Error surface).SocksError/SocksServerError/ReplyErrorre-exported fromrfc1928(SocksServerErrorlives inrfc1928::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 sendsjson!({}));SocksEstablishmentReasonis 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::SocksOpenErrorfromparamsare 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 idchannels/socks5/sub, ALPNalk/socks5, scopessocks5:open/socks5:bind, trivial empty-object input schema with loudadditionalProperties: false, reply{channel_id: integer, minimum 1}required).Socks5Params {}withdeny_unknown_fields— empty object valid, unknown fields rejected.error:SocksOpenErrormapping the ADR-049 §4 reasons (dial_failed/unknown_resource/resource_shortage/handler_error/timeout) through the typedSocksEstablishmentReason(unknown codes never aliased); the registry'sFORBIDDEN/INVALID_INPUTclasses correctly non-establishment;SocksSessionErrorshaped 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.