alksocks 0.3.0

SOCKS5 (RFC 1928) proxy protocol over alkcall channels: targeted-forked fast-socks5 typestate core plus producer/consumer sessions. Producer/consumer protocol crate.
Documentation
# Changelog

All notable changes to this crate are documented here. The format is
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
this crate adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.3.0] - 2026-10-19

The dependency-major release: the alkcall substrate bumped 0.9.0 →
0.10.0 (the gateway ownership-seam release) and the alktunnels sibling
bumped 0.3.0 → 0.4.0 (its dependency-major ride on alkcall 0.10.0). No
alksocks API change, no wire change; zero code contact with the
0.10.0 breaking surface (compile + suite green unchanged), which is
why this is a minor bump rather than a patch: the semver-incompatible
dependency bumps plus this crate's public re-exports of alkcall types
(`SocksOpenError::Open`/`open_ref` carry `ChannelOpenError`) make it
semver-visible to downstream consumers (matching the alktunnels
0.3.0 → 0.4.0 ride).

### Changed

- **alkcall 0.9.0 → 0.10.0** (crates.io). The gateway
  ownership-seam release (review 012): `GatewayDispatch` gains
  `with_ownership_provider` / `with_static_ownership_fallback`, and
  the previously-silent static fallback becomes loud — an unwired
  spine refuses (an error envelope, `FORBIDDEN`, pre-dispatch) an op
  whose spec carries `access_control.resource_type`. None of it is on
  this crate's paths: alksocks does not use the gateway module —
  SOCKS5 opens serve via `ChannelCore::register_openable`/
  `register_openable_with_establisher` registry dispatch on the native
  wire, which 0.10.0 leaves untouched. All of this crate's alkcall
  contact points (`ChannelClient::open_channel*`,
  `ChannelOpenError`, `Establishment::default()`,
  `register_openable*`, `pump_bidi`, the `core` types) are unchanged
  between 0.9.0 and 0.10.0. 145 default / 171 all-features tests pass;
  wasm-clean verified.
- **alktunnels 0.3.0 → 0.4.0** (the dependency-major ride on alkcall
  0.10.0). No alktunnels API/wire change; the composed providers see
  none of it.
- **fuzz/shared harness pin** (the only bump contact): the harness
  declares its own `alkcall` pin (it constructs `ChannelOpenError`
  values for the invariant functions), so the two pins must stay in
  lockstep — bumped 0.9.0 → 0.10.0 with the substrate (a drifting pin
  produces a duplicate-type compile failure, not silent drift).

## [0.2.0] - 2026-10-14

The dependency-major release: the alkcall substrate bumped 0.8.1 →
0.9.0 (the stub-sweep release) and the alktunnels sibling bumped
0.2.1 → 0.3.0. No alksocks API change, no wire change; no code
contact with the 0.9.0 breaking surface at all (compile + suite green
unchanged), which is why this is a minor bump rather than a patch:
the semver-incompatible dependency bumps plus this crate's public
re-exports of alkcall types make it semver-visible to downstream
consumers (matching the alktunnels 0.2.1 → 0.3.0 ride).

### Changed

- **alkcall 0.8.1 → 0.9.0** (crates.io). The stub-sweep release: the
  marked-pub open contract (S-01), `channel/control` (S-02),
  `channel/resources/subscribe` (S-03), and `op/register`
  forward-through (S-04) replace the four always-failing stubs. None
  of the four stubs were on this crate's paths (no marked-pub
  registration, no control-handle subscription, no resource
  enumerator use, no generic `op/register` announcements). The 0.9.0
  breaking list (`ManagerError::InvalidAdopt` additive,
  `RelayError::PubTypedOpen` removal, the
  `register_imported`/`register_imported_all` return change,
  `Establishment` dropping its `Debug` derive) is untouched by this
  crate's API use — `Establishment::default()` is the only
  `Establishment` contact, in the session gate. 145 default / 171
  all-features tests pass; wasm-clean verified.
- **alktunnels 0.2.1 → 0.3.0** (the dependency-major ride on alkcall
  0.9.0). No alktunnels API/wire change; the composed providers see
  none of it.

## [0.1.0] - 2026-09-27

The initial release: SOCKS5 (RFC 1928) as a produced resource on
alkcall channels — the `-D` flavor as a first-class protocol crate,
over alkcall 0.8.1. The defining posture: the protocol never binds a
port; kernel binds exist only as explicit, feature-gated assembly
shapes. Composed against by the alktunnels 0.2.1 graduation (the
direct op + the bind-first/forwarded flow power the composed
providers). Fuzzing adopted (the `fuzz/` workspace — excluded from
the published package) over the crate's two attacker-reachable
parsers: the ASSOCIATE datagram codec and the RFC 1928 handshake;
campaigns clean, no crate bugs.

### Added

- **The RFC 1928 core (`src/rfc1928/`, ADR-013)** — the targeted fork
  of fast-socks5 v1.0.0's `T`-generic subset, extracted in-tree as
  fully owned code (MIT notice bundled; extraction per ADR-011): the
  wire consts, codecs, errors, the explicit typestate server API
  (`Socks5ServerProtocol<T, states::*>`, generic over
  `T: AsyncRead + AsyncWrite + Unpin`), and the client `use_stream`
  path. Two deliberate changes carried from the fork point: BIND
  support in the base (ADR-007) and the `new_reply` publication. No
  kernel-net code — the default crate is wasm-clean by construction.
- **The wire codec (`wire`, ADR-003)** — the ASSOCIATE datagram frame
  (`[len: u16 BE][addr + data]`), framing applied exactly once at
  this layer; the `DatagramReader` incremental parser; the
  `MAX_INNER_LEN` oversize guard.
- **The wire-stable open-op surface (`params`, ADR-001/006)** — the
  `alk/socks5` ALPN, the `channels/socks5/sub` op id, the trivial
  `{}` params (the RFC conversation is the data plane; nothing to
  negotiate at open time), and the two scope constants
  (`socks5:open`, `socks5:bind` — two grants, never implied). The
  typed open-error vocabulary rides `error` (the ADR-049 reasons).
- **The three seams (`egress`, ADR-004)** — `DialFn` /
  `EgressFactory` / `BindAcceptFactory`: functions, not traits, at
  the three I/O planes; ownership rides the handle structurally
  (`TargetHandle` / `BindTargetHandle` / `BindAccept`); the egress
  readiness contract (`next_reply` `Pending` when nothing is ready)
  is load-bearing API.
- **The producer half (`producer`)** — the session-gate establisher
  (validates the opener identity; the target is unknown at open
  time), the command-dispatch pump handler (CONNECT pass-through /
  ASSOCIATE datagram stage / BIND phase model — reply#1 → quiet wait
  → exactly one accept → reply#2 → pass-through), the command gate
  (`socks5:bind` at BIND dispatch), the identity witness, and both
  wiring shapes: the free functions
  (`register_socks5_openable` / `socks5_session_establisher` /
  `make_socks5_pump_handler` / `serve_socks5_over`) and the builder
  (`Socks5Producer::builder()` — dial/egress/accept/bind_policy).
  Data planes ride `alkcall::channels::pump_bidi` (ADR-050).
- **The consumer half (`consumer`)** — the typed channels-native
  sessions, each owning its channel's teardown (`Drop` aborts +
  reaps; no `Clone`): [`Socks5ChannelSession`] CONNECT — raw
  pass-through, the `split`/`into_inner` postures,
  [`AssociateSession`] the framed datagram stage —
  `send_datagram`/`recv_datagram`/`split`, [`BindSession`] the
  reply#1/reply#2 reader + the `BindDataPlane` pass-through, and the
  token-carrying surface (`TokenClient` + the `*_with_token`
  constructor twins — the cred-bearing front door's re-origination).
- **The `local` feature (ADR-008/009)** — the socket-backed seam
  providers (`tcp_dial` / `udp_egress` / `bind_accept` — the explicit
  bind decisions, resolving via the non-blocking
  `tokio::net::lookup_host` resolver), plus both doors: the
  consumer-side front doors (`FrontDoor` — the NoAuth raw pipe + the
  RFC 1929 cred-bearing door with the credential→identity mapping,
  ADR-005; `UdpFrontDoor` — one association per bound relay socket,
  the RFC codec at the door boundary only, the peer-pin posture: the
  client's source IP pins on the first parsed datagram, replies
  follow the latest-seen port from that IP, foreign-IP datagrams are
  dropped per RFC 1928's MUST — OQ-SK2-09) and the producer-side
  listener (`ProducerDoor` — vanilla clients dial it, each accepted
  connection is served via `serve_socks5_over`; the ADR-014
  idle-first-byte window + max-sessions cap knobs).
- **The `composed` feature (ADR-008/009)** — the alktunnels 0.2.1
  composed providers: `direct_egress` (the direct-op egress — a lazy
  destination→tunnel table, `DEFAULT_MAX_TUNNELS` bounded), and
  `bindfirst_accept` + `register_forwarded_serving` (the BIND accept
  whose listener lives on a bind-first listen resource; the
  `CorrelationRegistry` correlates the forwarded op's peer metadata
  into `ForwardedSession`s).
- **The idle-timeout posture (ADR-014)** — every phase
  teardown-governed, no protocol-layer idle bound; the doors own the
  accept-path defense.
- **The suites** — the differential suite against the upstream
  reference checkout (`tests/rfc1928-differential.rs`), the
  protocol-core integration suite (`tests/core-integration.rs`), and
  the assembly-layer integration suite (`tests/assembly-integration.rs`).
- **Fuzzing (`fuzz/` workspace, `docs/research/fuzzing.md`)** — the
  alkcall §5 pattern adopted for this crate's two attacker-reachable
  parser classes: `datagram_codec` (the `wire` codec — no-panic,
  infallible decode, size bound, re-frame round-trip, the
  `retained_bytes` bound) and `rfc1928_codec` (the RFC 1928 handshake
  state machine, fed attacker bytes from both protocol directions),
  plus `params_parse`. Invariant logic lives in `fuzz/shared/` — a
  stable-toolchain crate replayed as plain `cargo test` (the corpus
  replay is the fuzz gate). 129 committed seeds; detached campaigns
  clean (`oom/timeout/crash: 0/0/0`, coverage saturated). The one
  diagnostic added: `DatagramReader::retained_bytes()`. `fuzz/` is
  excluded from the workspace and from the published package.

[`Socks5ChannelSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.Socks5ChannelSession.html
[`AssociateSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.AssociateSession.html
[`BindSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.BindSession.html

[Unreleased]: https://git.alk.dev/alkdev/alksocks/compare/v0.2.0...HEAD
[0.2.0]: https://git.alk.dev/alkdev/alksocks/compare/v0.1.0...v0.2.0
[0.1.0]: https://git.alk.dev/alkdev/alksocks/releases/tag/v0.1.0