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:
1 parent
7b8a3e7558
commit
5f019c02f1
26 files changed
+670
-383
No files matched your search
@@ -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)).
|
||||
Reference in new issue
Block a user