Files
glm-5.3-flash e196ed43e3 docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/
  (the historical reviews stay verbatim, matching the alktunnels and
  alktty precedent): src doc comments and inline comments (587),
  docs/architecture (319), docs/research + sdd_process (484), tests
  (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77),
  AGENTS.md (85), and the .opencode/agents prompts (109)
- Fenced content follows the precedent: comments, ASCII-diagram label
  separators, and template placeholders rewritten; wire data, JSON/BAST
  documents, byte diagrams, and transcripts survive verbatim; assert,
  expect, panic, error-display, and format message literals rewritten
  as prose; no code, fixture, or test data change
- src/rfc1928/LICENSE: the two dashes in alksocks' own provenance
  header rewritten; the MIT legal text untouched
- AGENTS.md adds Project Convention 18: no em dashes in prose; varied
  restructure, no spaced-hyphen substitutes, fenced prose rule, en
  dash numeric ranges exempt, docs/reviews/ exempt from the greps
- tasks/*.md frontmatter: the name values rewritten by the sweep are
  now quoted (a raw colon inside an unquoted YAML scalar parsed as
  null); taskgraph validate reports 37 tasks, no issues

Verification: cargo test; cargo clippy --all-targets -D warnings;
cargo fmt --check; cargo doc --no-deps; fuzz corpus replay
(cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and
clippy for wasm32-unknown-unknown with -D warnings; em dash grep over
the repo returns matches only in docs/reviews/ and the bast.md BAST
JSON data fence
2026-11-03 19:31:53 +00:00

164 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-011: the Inline Vendor: fast-socks5's `T`-Generic Subset, In-Tree
## Status
Superseded by [ADR-013](013-targeted-fork.md) (2026-09-22): the
maintenance posture (the vendor framing, re-base/re-vendor
discipline, the verbatim/lint-allow apparatus) is retired; the
extracted region is a targeted fork, module `rfc1928`. **The
extraction mechanics here remain authoritative**; the cut table,
the two carried changes (BIND, `new_reply`), the §Amendment
(extraction, not wholesale copy; the net gate dissolves) describe
what was copied and what was not; only the post-copy maintenance
posture changed. Originally Accepted 2026-09-20 (Phase 1;
OQ-SK-04's resolution), Amended 2026-09-21 (pre-decomposition).
## Context
fast-socks5 is **third-party upstream** (v1.0.0, MIT;
`/workspace/fast-socks5` is a read-only reference checkout; we do
not own it, and it has not been forked). The wrapper's needs go
beyond what it publishes:
1. **The wasm gate**: `tokio::net` + `socket2` (via `mio`) hard-error
on `wasm32-unknown-unknown`; the default crate must compile there
(AGENTS.md convention 4). POC #4 measured the gate: ~120 changed
lines across 5 files, all `#[cfg]` insertions; the wasm-clean
subset is functionally complete (a full RFC 1928 CONNECT
conversation passes `--no-default-features`).
2. **BIND is absent**: `Socks5Command::TCPBind` is parsed but every
handler refuses it; POC #5 proved the wrapper must drive the
typestate itself for the BIND shape (ADR-007).
3. **The `run_udp_proxy_custom` bind seam**: the "custom" seam is
narrower than it looks: it calls `udp_bind_random_port`
unconditionally and derives the reply port from that kernel
socket; the channels path cannot use it (verified 2026-09-13).
The channels path drives the typestate directly; making the bind
optional / accepting a caller-supplied socket is an in-tree fix.
4. **`new_reply` is private**: the reply encoder is private; the
BIND reply#2 writer otherwise hand-rolls the 10-byte reply
(POC #5's courtesy item, now a vendor change).
Two facts settled the posture: the ownership premise was wrong
(no cheap "file asks / PR" path), and the carried needs (2–4) exist
regardless of the wasm question, so the vendor pays for itself
without leaning on the adapter story. The change set is exactly the
shape POC #4 measured as minimal. (Need 1 later dissolves,
§Amendment; needs 3's handler is not extracted with it.)
## Decision
- **Extract the `T`-generic subset into this crate** (see §Amendment
for the mechanical reading of "extract"): the codecs, the typestate
machinery, the client `use_stream` path, the parts already generic
over `T: AsyncRead + AsyncWrite + Unpin`. The extraction contains
zero kernel-net code and is wasm-clean by construction.
- **Carry the four needs in the same vendor**: the net gate, BIND
(the typestate-driven shape, ADR-007), the `run_udp_proxy_custom`
seam fix, and the `new_reply` publication. (Post-Amendment: the
net gate dissolves; see §Amendment; BIND and `new_reply` are the
two that touch extracted code.)
- **Note the vendor point (v1.0.0) in the vendored code**; keep the
MIT license obligations (upstream is MIT; preserve the license
header per file or a bundled notice).
- **`/workspace/fast-socks5` stays the differential-testing
reference** for RFC edge cases (reply-code mapping, domain
addressing, fragmentation); keep `/workspace/fast-socks5` for
differential testing; never wait on an upstream merge.
- **Offering the net-gate upstream as a courtesy PR stays optional**
(POC #4: it is small enough to offer as-is), never a wait, never
a dependency.
## Consequences
- The crate owns its protocol code in-tree: no upstream release
cadence, no feature waits, and the vendored code co-evolves with
the wrapper (the carried changes are first-class, not patches).
- The cost is maintenance of the extracted subset (the `T`-generic
region + codecs, a fraction of upstream's ~4k LoC; POC #4's
ungated inventory is the shopping list). The diff vs upstream is
deliberate and bounded: the extraction cut + the carried changes +
the license/provenance notes.
- **Nothing in the crate routes through the kernel-net handlers**;
if a future need surfaces (e.g. a standalone vanilla-server
example), the fix is re-vendor that piece then, as an additive
decision.
- Differential testing against `/workspace/fast-socks5` guards the
RFC edge cases the extracted code must keep passing.
## Amendment (2026-09-21, pre-decomposition: "extract" pinned, the `net` gate dissolves)
The decision's wording ("vendor the `T`-generic subset") supported
two mechanical readings; this amendment pins the intent so the
decomposition task is unambiguous:
- **Extraction, not wholesale-copy-plus-gate.** Copy *only* the
`T`-generic inventory into `src/vendor/`; the kernel-net handlers
(`run_tcp_proxy`, `run_udp_proxy*`, `udp_bind_random_port`, the
legacy `Socks5Server`/`Incoming` API, `Socks5Datagram`'s kernel
socket path, the DNS-resolve helper, the net-only tests), the
`socks4` module (upstream's separate default-off `socks4` cargo
feature, which POC #4 never compiled; out of this crate's RFC 1928
scope), and the legacy `Config<A>`/`Authentication` auth layer
(ungated upstream, but consumed only by the cut legacy/net region)
are **not copied into the crate**; they remain available in
`/workspace/fast-socks5`, the differential-testing reference.
The kernel-net region is carried only as the cfg *map* (POC #4's
five-file inventory), recorded in `vendor.md` so a future
re-vendor knows the cut.
- **Why**: convention 17's letter ("vendor the `T`-generic subset");
no crate consumer routes through the kernel-net region (the
producer/consumer/doors drive the typestate and the pure codecs;
the local backends are first-party tokio code, ADR-004/ADR-008);
and the BIND + `new_reply` needs touch only the extracted region
(BIND's driving is wrapper-side code per POC #5, "zero upstream
ask for BIND itself").
- **The `net` feature gate dissolves.** With no kernel-net code
in-tree, the extraction is wasm-clean by construction; no
`#[cfg]` gate needed inside the vendored files. Features collapse
to two: `local` (the crate's first-party socket providers and
front doors, ADR-008) and `composed` (the alktunnels providers).
POC #4's gate map survives as the *extraction recipe* (its ungated
inventory is exactly what to copy), not as in-tree cfg. tokio
stays `default-features = false` with the wasm-clean subset
(AGENTS.md convention 4); `tokio/net` rides `local` alone.
- **The carried-changes list re-reads under extraction**: (1) the
net gate, dissolves (no net code); (2) BIND, stays (the typestate
driving + reply#2 writer are in-tree; ADR-007); (3) the
`run_udp_proxy_custom` bind seam, dissolves (the handler isn't
extracted; the typestate-driving shape POC #2 validated is the
crate's ASSOCIATE path); (4) `new_reply` publication, stays
(it is inside the extracted codec region). Net: **two carried
changes** touch extracted code.
- **Extraction completeness is compiler-checked**: a missed
transitive dependency is a build error, and the wasm check
(`cargo check --target wasm32-unknown-unknown`, plus
`--no-default-features` native) is the structural guard. The
extracted crate must compile clean with no `net`-shaped feature.
- **The extraction's dependency surface is adjusted, not absorbed**:
upstream's `log` calls in the extracted region are dropped or
re-pointed at the crate's `tracing` (the alk* convention); the
extracted region's `anyhow::Context` error wrapping is dropped
(error surface is the crate's `thiserror` types; the vendored
`SocksError`/`SocksServerError` stay the wire-level vocabulary);
`async-trait` is dropped (its four upstream uses are all on the
legacy `Authentication` layer, which is cut; the typestate's
`AuthMethod<T>` traits are native async-fn-in-trait); the
`socks4` module and the legacy `Config`/`Authentication` layer are
out (above). These are extraction-time mechanical adjustments to
the copied code, not new carried changes.
The verification commands in AGENTS.md that mention the wasm check
failing on the dependency graph are stale for this crate (that
caveat predated OQ-SK-04's resolution); the extraction makes the
wasm check a hard gate as in alktty/alktunnels.
## References
- OQ-SK-04 (the resolved posture; the reasoning trail),
OQ-SK-08 (the BIND + `new_reply` needs), OQ-SK-03 (the bind seam)
- POC #4 + POC #1 (`poc-connect-wasm-findings.md`: the measured
gate), POC #5 (`poc-bind-findings.md`: the BIND typestate
driving + the `new_reply` finding)
- ADR-002 (the protocol base), ADR-007 (BIND), ADR-009 (the feature
gates); `/workspace/fast-socks5` (the reference checkout, v1.0.0)