Files
alksocks/docs/architecture/decisions/012-target-policy.md
T
glm-5.3-flash b0cfc26065 docs: pin the vendor scope as extraction — the net gate dissolves
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).
2026-09-21 03:15:04 +00:00

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 -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)