Renames the written-usage rule into practice across the contract surface: docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files, taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the .opencode/agents/ prompts. Every replacement is a sense-preserving rewrite (colons, semicolons, commas, parentheses, sentence splits, varied), never a mechanical swap; no spaced hyphens were substituted. Enum fields, frontmatter, tables, wire data, and cited identifiers survive verbatim; en dashes inside numeric ranges keep their meaning. Verification: taskgraph validate (43 tasks), repo grep clean (single exempt hit: the verification command's own search pattern in AGENTS.md), cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
5.4 KiB
status, last_updated
| status | last_updated |
|---|---|
| reviewed | 2026-10-28 |
ADR-001: The alk/ssh ALPN pin (edge-termination contract for SshAdapter)
Status
Accepted (2026-10-26). One-way door (ledger rule 1: ALPN registry entries are stable; "once published, does not change"). The first consumer is alkforge (family ADR-003 decision 4: the pin lands before alkforge becomes the consumer).
Context
Family ADR-003 (decision 4) requires alkssh to pin exactly one
alk/ssh ALPN via its own ADR. Four alkcall ADRs constrain what that
pin means:
- ADR-004 (ALPN convention + connection model) reserves
alk/ssh→SshAdapteras a custom protocol. - ADR-034 (channels wire format) grants
alk/sshstream_types[0, 1]("SSH multiplexes internally, including its own control"). The channels layer is byte-blind to SSH; it must never parse SSH channel data. - ADR-042 / ADR-051 (hub relay): "The hub never runs a handler for
alk/tty,alk/ssh, oralk/tunnel"; SSH channels are relayed payload-blind between spokes (translate + byte-forward with id rewrite, ACL fired atchannel/openby the hub'sCallAdapter). - ADR-009 (BiStream handler leaf) sketches the browser route: a
browser opens a WebSocket/WebTransport session to
/alk/ssh; the h3/WS handler hands each bidi stream toSshAdapter::handleas aConnection; a WASM SSH parser speaks vanilla SSH over the stream.
Phase 0 left the topology open (mapping-table.md §topology; vision
scope question 1): whether alk/ssh carries raw vanilla-SSH bytes
between alkssh instances (a relay leg), serves the browser route, or
both, and how that coexists with ADR-042's relay ruling. POC-3
settled what could be settled by evidence: the served topology is
edge termination (russh terminates vanilla SSH at the alkssh
endpoint; family seams are consumed in-process or via alk/channels
bridge legs), worked end to end with vanilla clients through published
sibling crates, and no leg of it requires the hub to parse SSH.
The decision that remains is the pin: what contract alk/ssh
commits to on the ledger, so the ALPN string cannot be reinterpreted
later.
Decision
- The pin.
alk/sshis pinned for exactly one contract: a connection opened underalk/sshcarries a raw vanilla-SSH byte stream, unchanged and uninterpreted, and the receiving endpoint isSshAdapter::handle(Connection), alkssh's russh server stack mounted at an alkcallchannel/openboundary. Each bidi stream is one independent SSH transport with its own KEX, auth, and channel multiplexing (hence ADR-034's[0, 1]: SSH owns its own multiplexing, and the channels layer stays byte-blind, per ADR-042's relay ruling). - Payload-blind relay is part of the contract. Any intermediate
(the hub in ADR-042's topology, or a spoke forwarding to a spoke)
relays
alk/sshstreams as opaque bytes; it runs ACL atchannel/openand never an SSH handler. This is ADR-042's ruling, restated as the ALPN's own contract so it cannot drift. - Phase 1 serves edge termination only. The first deployment
shape (russh terminating vanilla SSH at the alkssh edge, family
seams consumed in-process) does not speak
alk/sshitself: it is a door, not a relay party. No pin behavior in this ADR requires the relay leg to exist. - The relay leg and the browser-WASM route remain compatible by
construction. Because the contract is a transport-blind raw byte
stream, either future leg (hub-relayed SSH, browser WS →
SshAdapter::handle) satisfies it without a pin change. Their consumers are a use-case question for assemblies/family work, not alkssh decisions (OQ-01 resolved: this ADR is the entire decision surface; nothing in the crate is gated on a consumer). - What the pin is not. It is not a carrier for alkcall's framed
channels traffic (that has its own protocol); it is not a second
SSH-flavored control channel; and honesty cuts both ways: an
endpoint that declines to serve
alk/sshmust refuse the open, never accept-and-stall (the same honest-advertisement rule that runs through every alkssh decision).
Consequences
- Positive: the ALPN decouples the endpoint-adjacency contract (frozen here) from the served edge topology (which evolves with the family seams); the browser and relay routes are pre-enabled at zero pin cost; the ledger gets a single unambiguous entry.
- Negative: the pin is a one-way door; the raw-vanilla-SSH-stream
contract binds every future consumer (alkforge first). If a consumer ever
needed framed or pre-parsed SSH over
alk/ssh, that would be a new ALPN + new ADR, not a reinterpretation. - Neutral:
SshAdapter::handlemounts the full russh server stack per stream, so endpoints pay per-connection bounded-resource limits (window, auth-attempt, inactivity ceilings); the alkssh limits contract applies peralk/sshstream, not per transport.
References
- Family ADR-003 (
003-alkssh-russh-adapter.md), decisions 1 and 4 - alkcall ADR-002 (ProtocolHandler ALPN claim), ADR-004
(the
alk/ssh→SshAdapterreservation), ADR-009 (browser-WASM sketch), ADR-034 (alk/sshstream_types[0, 1]), ADR-042/051 (relay: payload-blind, hub handler-free) docs/research/mapping-table.md§topology,docs/research/vision.mdscope Q1,docs/research/poc-3-mapping-bakeoff.md(edge termination, proven)- OQ-01 (resolved: no consumer in scope; this ADR is the entire decision surface)