# 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 `-D` semantic). 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's `dns_resolve` config (and its `resolve_dns` step) 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)