docs: re-key architecture docs off the alknet mono-repo — speak as alkcall

The ported docs still framed alknet siblings (alknet-core/-call/-channels/
-http/-tls/-client) as live load-bearing context of this crate. With the
extraction complete and alknet headed for glue-level replacement, the docs
now speak as alkcall and cite the real sibling crates (alkhttp, alktls,
alktty, alktunnels, alktrader).

- ADR-054 (new): re-own the verifier-selection rule in-crate — three
  remote roles (public X.509 endpoint / transport relay / hub),
  ServerCertVerifier by PeerEntry presence, RemoteIdentity None
  load-bearing. Provenance: alknet ADR-034 (never ported), demoted to
  footnote. Resolves OQ-21's cross-repo authority gap.
- OQ-43 (new, deferred(scope)): composition-layer ownership and shape
  after the alknet replacement — the named receiver for the
  downstream-side deferrals (OQ-33 endpoint layer, OQ-41 QUIC-native
  substrate, OQ-42 hub broker, OQ-19 persistence adapters, ADR-045 dial
  seam). README layering diagram redrawn with the composition layer as
  TBD-ownership instead of "alknet / alknode" in concrete.
- Spec docs reframed: titles, self-references, dial/TLS/from_jsonschema
  homes, malformed legacy cite rows removed; dead relative research
  paths qualified as source-repo paths.
- ADR amendments (append-only): 027 (alknet-http -> alkhttp), 031 (post-
  extraction state), 044 (split landed as modules in alkcall), 045
  (name normalization; citation-space note).
- Citation hygiene: unprefixed/colliding source-repo cites prefixed
  ("alknet ADR-NNN (provenance)") across 009/011/022/034/035/038/040/
  043/044/045; stale OQ cites corrected (session registries OQ-04, batch
  semantics OQ-03, verifier rule ADR-054); stale anchors fixed.
- README provenance note consolidated; citation conventions documented.

Verification: markdown link check (all local links resolve, all in-crate
ADR cites point at existing decisions/ files); table integrity check
(uniform row widths across open-questions.md and edited tables); residual
alknet-mention sweep (only provenance/historical/intentional framing
remains); architecture review subagent pass with zero remaining criticals.
Docs-only change — no code paths touched.
This commit is contained in:
glm-5.3-flash committed 2026-10-13 10:53:47 +00:00
1 parent 7b8a3e7558
commit 5f019c02f1
26 files changed
+670 -383

No files matched your search

+33 -27
View File
@@ -1,13 +1,13 @@
---
status: draft
last_updated: 2026-07-18
last_updated: 2026-10-13 (alkcall reframing)
---
# alknet-channels — Overview
# alkcall channels — Overview
## What
`alknet-channels` is a multiplexing proxy crate. It implements
The channels subsystem of alkcall is a multiplexing proxy. It implements
`ProtocolHandler` for the `alk/channels` ALPN: it receives one
bidirectional transport stream, reads 8-byte chunk headers, and routes each
chunk's payload to the right logical channel. Each channel is reassembled
@@ -28,7 +28,7 @@ multiplexing on the `BiStream` it receives.
### The problem: three multiplexing models that don't compose
Before channels, alknet had three multiplexing models:
Before channels, the alk stack had three multiplexing models:
| Model | Where | Mechanism |
|-------|-------|-----------|
@@ -89,7 +89,8 @@ on the `BiStream` the channels layer gives them (ADR-035).
inner layer's framing byte, carried transparently in the payload.
- **The control channel is handler-internal.** TTY sub-demuxes control
from its io `BiStream` using its 5-byte format (`STREAM_CTRL_IN` /
`STREAM_CTRL_OUT` — alknet ADR-052 amended by Phase 7). The channels layer
`STREAM_CTRL_OUT` — the TTY wire format, [alknet ADR-052](/workspace/@alkdev/alknet/docs/architecture/decisions/052-alknet-tty-wire-format-and-two-carriage.md)
amended by Phase 7; TTY is now the sibling crate `alktty`). The channels layer
doesn't carry control.
- **Recursive composition is literal.** A channel with ALPN
`alk/channels` runs another channels demux on its `BiStream`. The
@@ -110,8 +111,9 @@ The crate has two internal components (ADR-039):
`channel/open` operation handler closes over.
Each channel is presented to its handler as a `Connection` constructed via
`Connection::from_source(ChannelBidiStreamSource, alpn)` (ADR-008/074, as
amended by ADR-035). The handler calls `accept_bi()` once (yield-once per
`Connection::from_source(ChannelBidiStreamSource, alpn)` (ADR-008 for the
extension point, ADR-007 for the `Connection` constructors, as amended by
ADR-035). The handler calls `accept_bi()` once (yield-once per
channel) and gets a `BiStream` — identical to how it works on a top-level
QUIC connection.
@@ -139,7 +141,8 @@ Downstream crates depend on alkcall as a single dependency:
alktty / alktunnels / alktrader (protocol crates)
└── alkcall (call + channels protocols, no networking)
alknet / alknode (networking + composition)
composition layer (dial/accept + hub — today the alknet glue,
being replaced; future owner and shape → OQ-43)
├── alkcall
├── alktty (optional — whichever protocol crates are wired in)
├── alktunnels (optional)
@@ -147,7 +150,7 @@ alknet / alknode (networking + composition)
```
The hub and worker are **consumers** of alkcall, not sub-crates. The
downstream alknet crate IS the hub — it depends on alkcall and uses
composition layer IS the hub — it depends on alkcall and uses
channels as its substrate, with the relay logic (ADR-042) living
alongside its peer lifecycle and service discovery responsibilities.
A worker is any crate that uses `ChannelClient` to dial. There are no
@@ -168,7 +171,7 @@ stream:
|-----------|-----|
| QUIC bidi stream | `alk/channels` ALPN on a QUIC connection; one bidi stream carries all channels |
| TCP+TLS | `alk/channels` ALPN on a TLS connection; the TCP stream carries all channels |
| WebTransport | `alk/channels` session (deferred per ADR-044; the browser path uses WebSocket carrying `alk/channels`) |
| WebTransport | `alk/channels` session (deferred per alknet ADR-044 — websocket-native session, provenance — the browser path uses WebSocket carrying `alk/channels`) |
| SSH channel | channels connection riding inside an SSH `direct-tcpip` channel (channels-over-SSH) |
| Another channels connection | recursive composition (channel type `alk/channels` inside `alk/channels`) |
@@ -186,14 +189,14 @@ async shell (demux/mux) wraps this core with `read_exact`/`write_all` and
`mpsc` routing.
The `ChannelManager` is ALPN-blind, auth-blind, and transport-blind (ADR-
075) — pure byte routing with no platform or protocol dependencies. A WASM
039) — pure byte routing with no platform or protocol dependencies. A WASM
build can read chunks from a WebTransport `BiStream`, reassemble them, and
present `AsyncRead + AsyncWrite` handles to WASM-compatible handlers. The
handlers themselves may or may not be WASM-compatible (russh's client is;
`portable_pty` is not), but the channels layer is WASM-compatible by
construction.
The async shell and `alknet-core` dep graph are not fully WASM-clean yet
The async shell dep graph is not fully WASM-clean yet
(transitive `getrandom`/`rand` deps) — this is an implementation concern,
not an architecture concern. The sync core's WASM compatibility is validated.
@@ -225,9 +228,9 @@ A protocol crate can support two paths for its wire format:
The protocol crate doesn't know which path it's on — it takes a
`BiStream` (or `AsyncRead + AsyncWrite`) and drives the session. The
two adapters are thin and live either in the protocol crate (behind
feature flags) or in the downstream alknet crate.
feature flags) or in the composition layer.
### alknet-call
### Channel 0 is the call protocol
The call protocol runs on channel 0 exactly as on a top-level
`alk/call` connection. The `Dispatcher` receives a `Connection`
@@ -236,7 +239,7 @@ doesn't know it's inside channels. The call protocol's `EventEnvelope`
framing (ADR-014) is the channels payload; the channels layer carries
it transparently.
### alknet-hub / alknode
### Hub (composition layer)
The hub is the primary consumer. With channels, the hub holds one
channels connection per leg (browser↔hub, hub↔spoke) and relays
@@ -261,7 +264,7 @@ All design decisions are documented as ADRs in [decisions/](decisions/).
| [040](decisions/040-backpressure-channel-limits-id-reuse.md) | Backpressure, Limits, ID Reuse | Bounded-buffer (1 MiB), 256-channel cap, monotonic IDs |
| [041](decisions/041-per-identity-channel-cap.md) | Per-Identity Channel Cap | 256 per `PeerId`, enforced via `ChannelLifecyclePolicy`; per-connection `max_channels` reframed as a memory bound |
| [042](decisions/042-hub-relay-translate-not-forward.md) | Hub Relay | Translate channel 0, byte-forward data channels with ID rewrite |
| [043](decisions/043-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-045, resolves OQ-55) |
| [043](decisions/043-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; dial lives in the composition layer's dial helper (ADR-045) |
| [044](decisions/044-channels-subcrate-decomposition.md) | Sub-Crate Decomposition | `channels-core` (pure multiplexer) / `channels-call` (call coupling + ChannelClient); hub and worker are consumers |
| [047](decisions/047-openable-alpns-are-operations.md) | Openable ALPNs Are Operations | `channel/open` dissolves into per-ALPN ops; `ChannelCore` wrapper; connection-owner allocates `channel_id` |
@@ -270,19 +273,22 @@ All design decisions are documented as ADRs in [decisions/](decisions/).
Open questions are tracked in [open-questions.md](open-questions.md).
Key questions affecting this crate:
- **alknet OQ-55** (resolved by ADR-045): `AlknetClient` core **dial+TLS seam**
— extracted as `alknet-client` with three dial methods.
`ChannelClient`'s API is transport-agnostic (`from_connection`); the
dial is the shared seam, now extracted. See
- **Dial seam** (ADR-045; source: alknet OQ-55): the dial+TLS seam is
the composition layer's dial helper (`AlknetClient` today — future
owner and shape OQ-43). `ChannelClient`'s API is transport-agnostic
(`from_connection`); the dial is the shared seam. See
[ADR-045](decisions/045-alknetclient-native-dial-seam.md).
- **OQ-23** (tracker; alknet OQ-56) (deferred(scope)): Full channel-level flow-control windowing —
- **OQ-23** ([tracker](open-questions.md); source: alknet OQ-56)
(deferred(scope)): Full channel-level flow-control windowing —
bounded-buffer is decided (ADR-040); full windowing is an extension
blocked on a real HOL-blocking deployment observation.
- **alknet OQ-57** (deferred(scope)): Two-pump helper extraction to alknet-core —
the *contract* is decided (the in-tree helper landed as
`channels::pump_bidi`, ADR-050); any extraction to alknet-core is
alknet-scope.
- **OQ-24** (tracker; alknet OQ-68) (resolved): Add/strip API shape — whether the 8-byte header
- **Two-pump helper** (source: alknet OQ-57) (dissolved): the
contract is decided (the in-tree helper landed as
`channels::pump_bidi`, ADR-050); the extraction target (a mono-repo
core crate) no longer exists — alkcall owns its helpers.
- **OQ-24** ([tracker](open-questions.md); source: alknet OQ-68)
(resolved): Add/strip API shape — whether the 8-byte header
add/strip is built into the channels read/write path or exposed as a
standalone utility. The *contract* (channels strips, handler parses
payload) is decided (ADR-035); the *function surface* is not.
payload) is decided (ADR-035); the shipped surface integrates the
strip (see OQ-24 in [open-questions.md](open-questions.md)).