- 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
6.1 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-20 (Phase 1 initial draft) |
alksocks — BAST Document for the alk/socks5 Wire Format
This document is the BAST (Binary Abstract Syntax Tree)
specification for the binary portions of the alk/socks5 wire
format — per the alk* family BAST convention (alktunnels AGENTS.md
convention 12, carried from alktty; alktunnels bast.md is the
precedent), the mandatory ASSOCIATE
datagram framing (ADR-003) is
binary framing beyond pass-through, so the BAST document exists even
though the format is small. It is a normative JSON document
conforming to the BAST meta-schema at https://alk.dev/bast/v1/schema
(JSON Schema Draft 2020-12); any Draft 2020-12 validator can check
well-formedness.
alksocks does not depend on alktype. The runtime codec is the
vendored fast-socks5 address encoding + the length-framing codec
(shaped on alktunnels' frame_datagram/DatagramReader, ADR-003);
this BAST document is the human-readable contract describing what
those round-trip. If runtime validation against the BAST becomes
desirable later, alktype becomes an optional dep and this document
feeds it.
Scope
The alk/socks5 wire format is: a trivial JSON open op on the
channels call plane (a UTF-8 JSON object — {}; specified in
wire.md §The Open Op and
ADR-001), and a data plane
inside the channel BiStream that is mostly RFC 1928 wire rules
plus raw pass-through.
Per the BAST format spec, a BAST document describes binary data layouts — not JSON payloads. The RFC 1928 conversation itself (negotiation, command, replies, the RFC UDP datagram header at front doors) is specified by RFC 1928 and implemented by the vendored fast-socks5 codecs (vendor.md) — it is not re-specified here. This document covers exactly the alksocks-specific binary framing:
- The ASSOCIATE datagram frame (
[len: u16 BE][addr + data]) — the one framed stage (ADR-003). CONNECT's pass-through and BIND's pass-through have no framing for BAST to describe.
What this BAST deliberately does not cover:
- The RFC 1928 wire rules — method negotiation, command/reply
encoding, the
addrsub-layout (ATYP + addr + port), the RFC UDP datagram header (RSV/FRAG/ATYP/addr/port, front doors only). The RFC is normative; the vendored codecs implement it. - The open-op params JSON shape (
{}) — UTF-8 JSON on the call plane; ADR-001. - The channels 8-byte chunk header — the channels layer's own framing (alknet ADR-071/093); stripped transparently before the alksocks data plane sees bytes. Specified upstream, not here.
- The EOF sentinel — the channels-level zero-length chunk (upstream); the alksocks codecs never emit it (the F-2 invariant).
The BAST document
{
"$schema": "https://alk.dev/bast/v1/schema",
"$defs": {
"AssociateFrame": {
"kind": "struct",
"endian": "big",
"fields": [
{ "name": "len", "kind": "uint16" },
{ "name": "addr", "kind": "bytes", "encoding": "length-prefixed",
"maxLength": 259, "description": "RFC 1928 address encoding: ATYP + addr + port (TargetAddr::to_be_bytes); v4 = 1 + 4 + 2 = 7, v6 = 1 + 16 + 2 = 19, domain = 1 + 1 + 255 + 2 = 259 (the length byte of the domain form counts)" },
{ "name": "data", "kind": "bytes", "encoding": "length-prefixed",
"maxLength": 65535, "description": "the datagram payload; len == 0 is a legal empty datagram, NOT EOF" }
]
}
}
}
(Note: addr + data together are exactly len bytes — the two
encoding: length-prefixed fields are the decoder's internal
split of one frame's payload; on the wire len covers both, and
data's length is len − len(addr), decodable because the ATYP
determines the address length. The two-field split documents the
decoder's contract, not extra wire bytes. Joint constraint: a
frame is legal only when len(addr) + len(data) ≤ 65535 — the
per-field maxima are not independent (a 65507-byte datagram to a
255-char domain would need 65766 frame bytes and must be rejected
Oversize at frame time; wire.md §The Data Plane carries the
arithmetic).)
Annotations
AssociateFrame — binary, exact
Maps 1:1 to the on-wire bytes: len as uint16 big-endian (2
bytes), then exactly len bytes = addr + data. The addr
sub-layout is the RFC 1928 address encoding as vendored
(TargetAddr::to_be_bytes(): ATYP 0x01 IPv4 / 0x03 domain /
0x04 IPv6, then the address, then the u16 BE port). RSV/FRAG do
not ride the tunnel wire (ADR-003); FRAG ≠ 0 is refused at front
doors.
One frame per datagram, in both directions of the channel
BiStream, for the whole datagram stage (association lifetime =
channel lifetime). Frames are self-delimiting on the byte stream; a
decoder is an incremental state machine across chunk boundaries
(boundary preservation is mandatory — an empty datagram is a frame
with len == 0, never EOF; alktunnels F-2's invariant, met
structurally).
Layering note
The frame codec sits in the wire layer, applied exactly once:
Egress::next_reply returns inner bytes (addr + data); the
producer's frame writer adds the single length prefix (ADR-003's
framing-once invariant — the double-framing bug class POC #2
caught). The consumer side mirrors it: the session's send frames
once, its reader state persists across receives. The BAST describes
the wire inside the BiStream only; pump_bidi (ADR-050) is
upstream's, not re-specified here.
References
- ADR-003 (the handle swap + the frame), ADR-001 (the JSON open-op shape — outside BAST scope), ADR-011 (the vendored codecs)
- wire.md (the normative prose; §The Data Plane)
- alktunnels
bast.md(the BAST precedent — same meta-schema, same "binary framing only" scope rule; the length codec's original document) and alktunnels ADR-003 (the codec alksocks reuses) - POC #2
poc-udp-associate-findings.md(the frame's validation matrix: empty datagrams, splits/batches, garbage-ATYP refusal)