# ADR-001: ALPN, Open Op, and Trivial Params ## Status Accepted (2026-09-20, Phase 1) ## Context The crate rides alkcall channels: a produced SOCKS5 resource is an openable ALPN-op, opened by consumers via `ChannelClient` (alkcall ADR-037/043). The ALPN string is wire-stable once published (AGENTS.md conventions 5/16: decide via ADR before the first consumer). Phase 0's POCs (#1, #2, #5) all ran one ALPN (`alk/socks5`), one open op (`channels/socks5/sub`), with trivially empty params, the natural reading of "the RFC conversation is the data plane": the SOCKS5 target is unknown at open time (it arrives in-stream post-negotiation), so there is nothing for params to carry (`poc-connect-wasm-findings.md` §Wire/API notes). ## Decision - **ALPN: `alk/socks5`** (provisional → stable on first publish; the alk/ prefix per the alkcall ADR-004 ALPN-string convention; AGENTS.md convention 16's "ADR-004/ADR-006" shorthand cites the naming family, of which ADR-004 is the operative one). One ALPN per protocol. - **One open op: `channels/socks5/sub`**: the SOCKS5 session op, flavor-form like alktunnels' (`…/sub`; alkcall ADR-047: openable ALPNs are operations). All three RFC 1928 commands (CONNECT, BIND, UDP ASSOCIATE) ride this one op, selected in-stream at command dispatch (the command is an RFC-1928 wire fact, not a wire-format choice of ours). - **Params: the trivial object** `{}` (schema `{type: object}`, additionalProperties false). Nothing else is needed at open time (the establisher is a session gate, not a dial; the target is unknown). Extensibility: if a later need surfaces per-session options (e.g. a requested local bind hint), that is an **additive** params schema change while no params were ever required; consumers that send `{}` stay valid. Params are a self-contained JSON object identifying the produced resource (alkcall ADR-039's shape, following alktunnels ADR-001's template). - **Reply: `{"channel_id": }`**: the Sub-typed operation reply, as with alktunnels. No extra reply fields in v1 (the establishment phase is a session gate and has nothing to project; the alkcall 0.8.0 `with_reply_field` seam remains available additively if a future need appears). ## Consequences - The wire surface is minimal and stable: one ALPN + one op + empty params + one reply field. The one-way-door surface is the ALPN string, the op id, and the (trivial) params shape, all frozen at first publish. - Command-level differentiation (CONNECT vs ASSOCIATE vs BIND) lives in the RFC 1928 stream and in the ACL scope check at command dispatch (see ADR-006), not in the op set. No per-command ops: that would re-encode the RFC's own command byte in our wire format for no benefit. - POC-validated: all three POCs served CONNECT/ASSOCIATE/BIND on the same registration (`connect_still_served_on_the_same_ registration`, POC #2; test 7, POC #5). ## References - OQ-SK-01 (params context), OQ-SK-03 (the handle swap, why no correlation params), OQ-SK-08 (BIND rides the same op) - POC #1 `poc-connect-wasm-findings.md` (the trivial-params finding), POC #2 `poc-udp-associate-findings.md` (one-op regression guard), POC #5 `poc-bind-findings.md` (one-registration-three-commands) - Upstream: alkcall ADR-037 (channel open ops), ADR-039 (params is ALPN-specific), ADR-047 (flavor-form op ids) - Sibling template: alktunnels ADR-001/ADR-002