- 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
86 lines
4.1 KiB
Markdown
86 lines
4.1 KiB
Markdown
# 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) |