Files
alksocks/docs/architecture/decisions/005-auth-split-by-door.md
T
glm-5.3-flash 9587670138 docs: Phase 1 architecture — specs, ADRs 001-012, open-questions tracker
- docs/architecture/: README index, overview, wire, producer,
  consumer, vendor, bast specs + open-questions.md (the OQ-SK-01..09
  ledger promoted; new questions as OQ-SK2-06..)
- ADRs 001-012: ALPN/op/params, protocol base, the handle swap,
  the dial-callback shape, auth split by the door, access control
  (open gate + BIND command gate), BIND in base, provider defaults,
  feature gates, channel capacity, the inline vendor, target policy
- deferred OQs (SK2-06, SK2-07) with tracker tasks in
  tasks/architecture/ per the sdd_process deferred-OQ protocol
- review pass applied: frame-length joint bound (65766 corner),
  accept_no_auth vs skip_auth corrected against fast-socks5 source,
  composed-feature wasm posture, cross-reference fixes

Verified: cross-references (all ADR/OQ refs resolve), doc sizes
under split threshold, Phase 0 fidelity spot-checks
2026-09-20 18:41:56 +00:00

4.2 KiB

ADR-005: Auth Mapping — "Both," Split by the Door

Status

Accepted (2026-09-20, Phase 1; the posture settled 2026-09-13 — OQ-SK-02 — and POC #1 validated the identity seam end to end)

Context

Two auth mechanisms are real: alkcall's identity seam (the per-call opener identity arrives on the open-op hooks — alkcall ledger CF-005/CF-006) and RFC 1929's in-band username/password. Phase 0 resolved the posture: both mechanisms are real, each on its own side of the door — the consumer's optional front door (where the vanilla world meets the ACL world). A producer-side password would add nothing the identity seam doesn't already do, and the ACL system is strictly better than a shared secret.

POC #1's wiring finding (the accept-side install hook must call channel0_conn.set_identity(resolved) — the transport identity does not flow to channel-0 dispatch automatically) is a producer-harness requirement worth surfacing in the spec, not a protocol-layer concern.

Decision

  • Producer side: identity only. The channels-native open is authorized by the alkcall ACL/identity seam. The producer's in-band RFC conversation runs NoAuth via the typestate's normal path — accept_no_auth (the greeting and method selection still happen; POC #2/#5's validated shape: accept_no_auth → read_command). The negotiation is real but the auth decision is vacuous in-band because authorization already happened at the open gate. skip_auth_this_is_not_rfc_compliant is excluded from the in-band path — it wraps a stream already marked authenticated, skipping the greeting entirely; feeding it a client that speaks the RFC greeting would misparse the greeting as command bytes. (At most it belongs to a raw-pipe door that never speaks RFC in-band; the crate's doors speak RFC.)
  • Consumer front door (component 2's optional shape): RFC 1929 where vanilla clients need it. The door owns credential presentation: a vanilla client carrying user:pass gets a real RFC 1929 conversation at the door. The door then maps the accepted credential to local configuration — which channel identity/endpoint to open the producer channel with — and that identity reaches the producer via the CF-005/CF-006 seam. The SOCKS password never crosses the channel as a SOCKS password; the channel identity never reaches the vanilla client.
  • No fast-socks5 AuthMethod impl is required. The door checks credentials itself before/around handing the stream to the typestate — the door is this crate's code; the trait was designed for embedders who don't own the accept loop.
  • The door's credential→identity mapping shape: static config. The mapping is a caller-supplied configuration table (username/password → channel identity or endpoint selection) — static, declarative, assembly-layer. A pluggable lookup (callback) remains possible as an additive extension if a deployment needs dynamic resolution; v1 ships the static shape because the front door's use cases (tun2proxy's fixed user:pass@host, curl's --proxy-user) are single-credential or small-table shapes.
  • The NoAuth door remains valid for loopback-only or wrapper-aware use (POC #1's raw-pipe shape — zero protocol translation); the cred-bearing door is the same bridge plus the greeting half. Door variant selection is an assembly-layer choice.

Consequences

  • Vanilla-client compatibility at the front door is real (RFC 1929), without polluting the channels-native path with shared secrets.
  • The producer-side optional local backend (a front door in the other direction — vanilla clients → a locally-served SOCKS5 resource) offers the same door surface: door = vanilla↔ACL boundary, same code shape.
  • The mapping is not wire-visible: no params, no reply fields, no ALPN changes — the door is entirely assembly-layer.

References

  • OQ-SK-02 (the full reasoning: "replace or second-gate" dissolved — the mechanisms occupy different planes)
  • POC #1 poc-connect-wasm-findings.md (the identity-seam wiring finding; the raw-pipe front door)
  • alkcall ledger CF-005/CF-006 (the identity seam); alkcall ADR-049 (the establisher sees the per-call opener identity)