Complete the ADR-011 §Amendment (extraction, not wholesale-copy-plus- gate) across every doc that stated the pre-amendment posture: - ADR-011: add the two extraction-recipe exclusions the amendment missed (the socks4 module — upstream's separate default-off cargo feature, out of RFC 1928 scope; the legacy Config/Authentication auth layer — ungated but consumed only by the cut region) and the dependency-surface adjustments (log -> tracing, anyhow dropped, async-trait dropped with its legacy-layer uses) - ADR-009: amend — the vendor's net gate dissolves; features collapse to local + composed; local carries tokio/net + socket2 itself - vendor.md: replace the contradictory old "four carried changes" section with the two-change reading + extraction-time adjustments; extend the cut table (socks4 row, legacy auth layer) - overview.md: dependency diagram + feature table drop the net feature; module map reflects two carried changes - README.md: principles 7/8 + doc/ADR tables updated - ADR-002/012, open-questions.md (OQ-SK-04), phase-0.md current-state sections, AGENTS.md conventions 4/17 + stale wasm-caveat sentence, POC findings posture notes: aligned with the extraction Verification: repo-wide grep for stale "four carried"/default-on net/ implies-net statements returns only amendment-context matches; extraction recipe re-verified against POC #4's gate map and the fast-socks5 v1.0.0 source (async-trait usage confined to the cut legacy layer; AuthMethod<T> traits are native async-fn-in-trait).
4.1 KiB
ADR-012: Target Policy and the Virtual-Subnet Translation Layer
Status
Accepted (2026-09-20, Phase 1; the dialer-refuses shape is OQ-SK-05's evidence-backed resolution — see ADR-006 — and the 2026-09-14 virtual-subnet proposal is resolved as assembly-layer policy)
Context
OQ-SK-05's residual Phase 1 questions: (a) does the base crate ship a per-target allowlist hook, or is target policy entirely the dialer's concern; (b) domain-form targets (RFC 1928 ATYP 0x03) — resolve producer-side, refuse, or pass through unresolved; (c) the 2026-09-14 virtual-subnet proposal (identity-scoped subnets so proxied resources become IP-addressable — "BND.ADDR ↔ channel id" translation, a hub exposing proxied resources per identity under ACL).
The proposal's key property: the mechanism needs no wire change
— DST.ADDR and BND.ADDR are arbitrary RFC fields, so virtual
addresses ride ordinary CONNECT/BIND requests; the translation layer
is the dial callback (dial(target) matches a virtual subnet
against a per-identity resource table).
Decision
- No allowlist hook in v1 (ADR-006's resolution, restated as the target-policy half): the dial callback is the policy point; per-target refusal is an RFC reply code. The declarative-allowlist variant remains an additive future extension if a deployment needs declarative policy; the evidence-backed default is dialer-refuses.
- Domain-form targets: pass through unresolved to the dialer.
Resolution is the egress's/dialer's concern (POC #2 resolved the
local-egress half: "the egress owns resolution" — the ssh
-Dsemantic). The protocol layer never resolves: a wasm producer may have no resolver at all, and the composed variant's targets ride the alktunnels direct op (producer-side resolution there, per alktunnels ADR-007's own posture). fast-socks5'sdns_resolveconfig (and itsresolve_dnsstep) is not part of the extracted vendor region at all — the crate's producer never resolves (ADR-006/ADR-012), so the extraction drops both. - The virtual-subnet translation layer is assembly-layer policy,
not a crate feature. The crate ships no translation-table
helper in v1: the dial callback is the seam, and a deployment
wanting identity-scoped virtual subnets implements the
subnet→resource table there (per-identity scoping = resources +
ACL + target policy; hub exposure = the terminate-and-re-produce
story — no new wire vocabulary). The full-tunnel composition (the
host routes the subnet into the proxy via tun2proxy) is where a
virtual subnet becomes routable from vanilla apps:
connect(10.x.y.z:p)is an ordinary CONNECT the dialer translates. tun2proxy's virtual-network/virtual-DNS support is the prior art to check if and when a deployment builds this — the crate's obligation is only that the seams support it, which ADR-004's shape already does.
Consequences
- The security boundary stays exactly two mechanisms: the open
gate (
socks5:open/socks5:bind) and the dial/accept callbacks. No third policy layer. - The virtual-subnet endgame is unblocked without crate work: the translation layer rides the dial callback; if a deployment materializes with concrete needs, a translation-table helper can be added additively (assembly-layer code, possibly later graduated into the crate behind a feature) — the seams don't change.
- DNS posture is uniform: producer-side resolution everywhere (local egress resolves; composed egress hands the name to the direct op which resolves at the tunnel producer).
References
- OQ-SK-05 (the full question set + the proposal's record), OQ-SK2-07 (the deferred helper question this ADR's resolution feeds), OQ-SK-01 (the dial callback), OQ-SK-09 (the multiplexing rejection — the virtual-subnet idea that survives is recorded in the tracker)
- POC #2 (
poc-udp-associate-findings.md— the egress-owns-DNS resolution), ADR-004 (the seams), ADR-006 (the ACL posture), ADR-008 (the provider defaults) - alktunnels phase-0-findings OQ-TN-08 (the target-policy
dissolution precedent); tun2proxy (
/workspace/tun2proxy— the virtual-network prior art + the endgame composition partner)