tests/harness.rs — the producer<->consumer wiring over one
tokio::io::duplex carrying the channels 8-byte chunk format, the
alkcall 0.8.0 establishment surface consumed for the first time by
this crate (register_openable_with_establisher + the call/adopt
dance) — the first-consumption risk retired.
- Identities: consumer (socks5:open) / bind (socks5:bind only) /
both-scopes (explicit, ADR-006) + TestIdProvider (token path).
- In-process seams, no sockets, no `local`: echo_dial /
refusing_dial (variant-preserving), echo_egress (readiness
contract verified — next_reply parks Pending when idle) /
refusing_egress, echo_accept / refusing_accept_factory /
refusing_accept (two-stage BIND refusal shapes, mapped through
to_reply_error()).
- Producer-side registration helper: register_socks5_openable_minimal
+ socks5_session_establisher + RegisterMode — the single place the
producer wiring lives; the CF-006 identity witness rides it and is
asserted end-to-end.
- RawRfcClient: the hand-rolled RFC 1928 client helper over any
AsyncRead + AsyncWrite, round-tripped against a byte-level
hand-rolled server (success + refusal replies).
- Smoke test: harness up -> producer registered -> consumer open +
adopt -> raw RFC CONNECT -> echo round-trip through pump_bidi.
Fail-closed probe: scope-less identity -> FORBIDDEN, no channel
anywhere (the load-bearing POC #1 set_identity wiring requirement,
explicit in the install hook and tested).
14 harness tests. Verification: cargo test (127 green), clippy
(all-targets + wasm32) clean, fmt clean, doc zero warnings,
test --all-features green, both wasm checks clean (harness is
dev-only).
- egress.rs: the I/O-plane inversion point, function-not-trait at the
three factories; Egress is the one trait (the per-association
returned object)
- dial: TargetHandle (boxed halves, +Sync per the ChannelPlan bound),
DialFn over the typed TargetAddr (the POCs' JSON stand-in retired),
SocksDialError with the five-variant RFC-faithful mapping
- egress: Egress (send payload-only; next_reply returns inner bytes;
the readiness contract — Pending when nothing is ready — documented
on the trait), EgressFactory, EgressError, the egress_factory
sync-lift constructor
- accept: BindAccept (the accept future owns its resources end-to-end;
the quiet wait's drop IS the client-gone teardown), BindTargetHandle
(ownership structural; the composed keep-alive owner rides inside
the halves provider-side), BindAcceptFactory, the two-stage error
pair (factory → reply#1; accept-stage → reply#2)
- no substrate type named anywhere in the module; ownership rides the
handle structurally (no _ bindings)
- 9 new tests: the four error→reply-code tables (all variants,
byte-compared), the sync-lift Ok/Err paths, the closure compile
shapes, the Send/Sync/Unpin assertions; lib.rs re-exports the
surface
Verified: cargo test (113 green), --all-features green, clippy -D
warnings clean (native + wasm32), fmt clean, cargo doc zero warnings,
wasm32 check clean.
- frame(addr, payload): [len: u16 BE][addr + data] over
TargetAddr::to_be_bytes() + payload; the joint bound enforced at
frame time (checked_add; 65507 + 255-char domain = 65766 > 65535 →
Oversize) with addr_encoding_len exposed (7 v4 / 19 v6 / 259 max
domain) and MAX_INNER_LEN pinned
- DatagramReader: incremental reassembly across chunk boundaries
(split frames, batched frames, partial headers), infallible,
returning per-frame Results; the addr sub-layout decoded by hand
(ATYP-keyed, UTF-8-validated) with typed decode errors —
BadAtyp / ShortAddress / DomainNotUtf8 / EmptyFrame — so the
producer stage fails the association loud on protocol garbage
(udp_undecodable_frame_ends_association, POC #2)
- module docs state the framing-once invariant (the egress speaks
inner bytes; this layer owns the single length prefix — the
double-framing bug class POC #2 caught), the F-2 invariant at
payload level (a zero-length payload is a legal empty datagram,
never EOF; wire len == 0 is unreachable garbage), and the EOF
layering (the channels-level sentinel, outside the codec)
- doc-sync: wire.md + bast.md residue corrected — "len == 0 is a
legal empty datagram" replaced with the payload-level F-2
statement (len == 0 unreachable since addr >= 7 bytes)
- lib.rs re-exports frame, DatagramReader, addr_encoding_len,
MAX_INNER_LEN, WireError (convention 15)
Verification: cargo test (58 lib + 37 differential), clippy
--all-targets -- -D warnings, fmt --check, doc --no-deps, and both
wasm checks (check + clippy on wasm32-unknown-unknown) — all clean.
- Cargo.toml: alkcall = "0.8.0" (convention 11 — pinned, first
consumption of the 0.8.0 surface), serde/serde_json, bytes, and
the std-only futures subset (BoxFuture seam shape). tokio's
wasm-clean subset and the empty local/composed features unchanged.
- lib.rs: the six skeleton modules per overview.md §Module Map
(params, wire, producer, consumer, egress, error) — stub files
with module docs naming their filling tasks; no re-exports yet
(each implementation task adds its own).
Verified: cargo test (76 tests), clippy --all-targets -D warnings,
fmt --check, cargo doc --no-deps, both wasm checks
(check + clippy on wasm32-unknown-unknown), and the dep-graph
spot-checks (no mio/socket2/tokio::net in the default normal graph;
normal tokio features = bytes/io-util/macros/rt/sync/time only).
alkcall 0.8.0 from crates.io confirmed wasm-clean via probe build
before wiring.
Task: tasks/implementation/core-crate-deps.md (completed)
Batch 1 of the Phase 1 decomposition: the default, wasm-clean crate
(producer + consumer + wire + seams + params/errors), per the
architecture spec set (ADRs 001-013) and the three POCs' validated
shapes. The assembly layer (local/composed providers, front doors) is
batch 2, decomposed after review-protocol-core opens.
- foundation: crate-deps (alkcall 0.8.0 first consumption), wire-codec
(the ASSOCIATE frame + the joint bound), seam-types (the three
ADR-004 seams), params-errors (the one-way-door surface)
- harness: duplex transport + identities + in-process seams (the
alkcall 0.8.0 establishment surface proven before the producer)
- verticals: producer/consumer splits per command (CONNECT,
ASSOCIATE, BIND) — each POC's scope, producer tasks test via the
raw RFC client, consumer tasks e2e through the harness
- closing: integration suite (three-command regression, teardown
matrix, identity matrix, docs sync) + review-protocol-core (the
batch gate)
taskgraph: 8 generations, no cycles, risk path 2.55 (no high-risk
tasks). Also records the wire.md/bast.md len=0 residue (payload-level
F-2 is the operative invariant) as a doc-sync deliverable of the
wire-codec task.
Verification: cargo test (76 passed), fmt clean; taskgraph
validate/cycles/parallel/critical clean.
The extraction review against vendor.md's cut table, ADR-011
§Amendment, and AGENTS.md conventions. All five file diffs vs the
fast-socks5 v1.0.0 reference checkout read hunk-by-hunk and
categorized (cut / carried changes / adjustments / provenance /
tests); wasm, clippy, fmt, doc all green; RFC 1928 spot-checks
(reply form, method order, UDP zero fallback, domain cap, FRAG
split) independently verified against the RFC text.
F-1 (critical, resolved here): ReplyError::from_u8 kept upstream's
unreachable!("ReplyError code unsupported.") arm while being
wire-reachable — Socks5Stream::read_request_reply feeds the raw
reply byte from the wire into it, and RFC 1928 §6 marks 0x09-0xFF
unassigned, so a misbehaving peer would panic the consumer task.
Unassigned codes now map to GeneralFailure (the RFC's catch-all),
pinned by a new test, and the panic-translation adjustment is
recorded in vendor.md's adjustment list (with the Config-Default
and write_all notes) in the same change.
Findings F-2..F-4 (dead commented code dropped with its region;
Cargo.toml feature-layout comments; the preserved resolve_dns error
string) reviewed and accepted. Review task file carries the full
record; the gate to the downstream task groups is open.
Verified: cargo test (38 + 32), cargo test --all-features, clippy
--all-targets and --target wasm32-unknown-unknown (-D warnings),
cargo check --target wasm32-unknown-unknown, cargo fmt --check,
cargo doc --no-deps.
Extract the T-generic client session from fast-socks5 v1.0.0's
client.rs into src/vendor/client.rs (ADR-011 §Amendment extraction,
vendor.md cut table row 2): Socks5Stream<S> with use_stream (the
greeting, method-choice read, password sub-handshake, request
encoder, reply reader, socket accessors), the AsyncRead/AsyncWrite
impls, and the pruned Config (skip_auth only).
- typed MissingTargetAddr variant replaces upstream's
anyhow::Error::msg(...).into() at client.rs:285 (the one SocksError
addition; the client.rs:528 Other(anyhow!) site went with the cut
Socks5Datagram)
- use_password_auth's unreachable!() is an error return (upstream's
None-arm message; convention 2), and the sub-negotiation reply's
version byte is now checked (was only a dropped debug!)
- write -> write_all in request_header (clippy unused_io_amount;
byte-identical on a healthy stream)
- cut (not copied): Socks5Datagram, the Socks5Stream<TcpStream>
convenience impl (connect*/connect_raw), Config::connect_timeout
- 14 first-party duplex tests vs raw-byte mock servers (uncorrelated
from the vendored server typestate): both handshakes, 0xff
refusal, version mismatch, domain/IP request forms, the full
failure-code table, UDP-ASSOCIATE zero fallback, skip_auth,
io-impl copy, socket accessors
- mod.rs: doc touch-ups (client module now landed; fixed the
pre-existing broken intra-doc link)
Verification: cargo test (37 pass), --all-features, clippy
--all-targets -D warnings (native + wasm32), cargo check --target
wasm32-unknown-unknown, cargo fmt --check, cargo doc --no-deps.
Extract fast-socks5 v1.0.0's non-net lib.rs region into
src/vendor/mod.rs (scaffolding-pinned root; the task's lib.rs target
name corrected — a second root file would conflict, see task Notes):
consts, Socks5Command (as_u8/from_u8 made pub), AuthenticationMethod,
SocksError pruned to the extracted region's consumers (no anyhow/
Redaction/InvalidHeader/ReplySocks4Error/ArgumentInputError), the
Result alias, ReplyError codec, UdpHeaderError + new_udp_header/
parse_udp_request. util/target_addr.rs gains TargetAddr/AddrError
(minus resolve_dns + its two variants)/ToTargetAddr/Addr/read_address;
util/stream.rs gains the read_exact!/ready! macros (de-exported to
pub(crate) use) + ConnectError + to_reply_error. SocksServerError
lands verbatim in server.rs early — SocksError's #[from] needs it
before the typestate task. 16 first-party codec tests (round-trips,
mapping tables, garbage-RSV/unknown-ATYP); no sockets, no env_logger.
Newer-clippy style lints allowed at module level in vendored files
(verbatim diff posture, rationale recorded).
Verification: cargo test (16 pass) + --all-features; clippy
--all-targets -D warnings (native + wasm32); cargo check
--target wasm32-unknown-unknown; cargo fmt --check; cargo doc.
Negative sweeps: no anyhow/log/tokio::net/socket2/tokio-stream/
async-trait under src/vendor/; no #[macro_export]; resolve_dns and
the pruned error variants absent.
The inline vendor (vendor.md, ADR-011 §Amendment) is the root of the
crate's dependency graph; this decomposes it before any downstream
group (params/wire/producer/consumer/doors).
- vendor-crate-scaffolding: Cargo.toml (wasm-clean tokio subset, no
alkcall yet), lib.rs, vendor/ skeleton, bundled MIT notice
- vendor-wire-vocabulary: consts, codecs, errors, macros — with the
extraction-boundary pins (error-variant pruning, pub command
codecs, macro de-exporting)
- vendor-server-typestate: Socks5ServerProtocol + the new_reply
publication (carried change #2); BIND's driving is producer-group
work, the vendor carries only the surface
- vendor-client-session: Socks5Stream::use_stream path + the
AsyncRead/AsyncWrite impls; Config pruned (connect_timeout cut);
tests against a raw-byte mock server so it runs parallel with the
typestate task
- vendor-differential-tests: RFC edge-case suite over duplex streams,
pinning upstream behavior per vendor.md's differential posture
- review-vendor-extraction: the gate before downstream groups —
file-by-file diff vs /workspace/fast-socks5 must contain only
cut/carried-change/adjustment hunks
Graph validated: all dependency ids resolve, no cycles; topological
order scaffolding → wire → (typestate ∥ client) → tests → review.