The earlier draft kept from_stream as an 'escape hatch' for already-split transports. That bakes the split into the constructor API — the same split-leaf shape pushed one step earlier. The cleaner normalization: the split never crosses a crate boundary as part of a constructor. - Connection::from_bidi is the only public stream constructor. - Connection::from_stream(send, recv, ...) is removed. - The channels reassembly path joins MpscSendStream/MpscRecvStream itself via tokio::io::join (one line) and calls from_bidi. - The call crate's 5 test stub sites do tokio::io::split(x) then from_stream — they become from_bidi(x) directly. The split was always gratuitous at the call site. - SendStream::from_stream / RecvStream::from_stream (per-half boxing for into_sub_streams() and the SubStreamHandle leaves) are retained — not constructors that feed Connection.
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-07-17 |
Alknet Architecture
Current State
Client-dial SOCKS5 proxy seam added (ADR-090, 2026-07-16).
AlknetClient (ADR-089) gains an optional SOCKS5 proxy
(with_socks5_proxy) so a native client can hide its real IP from the
hub. dial_quic routes QUIC through SOCKS5 UDP ASSOCIATE (validated by
the /workspace/quinn-proxy-poc PoC and
docs/research/quinn-quic-proxy/findings.md
— quinn's AsyncUdpSocket + new_with_abstract_socket is the
extension point; 5/5 runs clean); dial_tcp_tls routes through SOCKS5
CONNECT. The proxy is invisible above the dial (Connection,
dispatch, credentials, TLS config all proxy-unaware) and the no-proxy
path is the zero-cost default (the socks5 feature and fast-socks5
dep are opt-in). SOCKS5 is the sole proxy protocol (it covers both TCP
and UDP, so no HTTP CONNECT variant needed). The two distinct SOCKS5
concepts — the client-dial proxy (ADR-090, transport-layer privacy)
and the planned alknet-socks5 channels data-channel handler (ADR-085
scope table, a service one side offers the other) — compose without
coupling (a client using the hub's alknet/socks5 service tunnels it
locally and points its Socks5ProxyConfig at the local tunnel end).
iroh was the exception: dial_iroh did not consume
Socks5ProxyConfig — iroh's proxy_url covers the relay-exposure
surface, but the direct-connection peer-exposure case was
OQ-67 (deferred(unclear) — the pieces existed but
the iroh socket-stack composition wasn't clear; did not block the
first hub deployment, which uses QUIC/TCP+TLS). See
ADR-090.
iroh proxy resolved — force relay-only + HTTP-to-SOCKS5 bridge (ADR-090
§5 amended, OQ-67 resolved, 2026-07-16). The iroh-proxy POC
(/workspace/iroh-proxy-poc,
docs/research/iroh-proxy-poc/findings.md,
5/5 runs clean) settled OQ-67: iroh does not expose a
socket-injection hook for the IP/direct transport (the quinn POC's
Socks5UdpSocket does not transfer — noq_endpoint() is pub(crate),
the IP transport binds its own netwatch::UdpSocket, and
CustomTransport operates on a separate CustomAddr address space
iroh's hole-punching doesn't route through). The decision is force
relay-only when a proxy is configured: three stable public iroh
Builder knobs (clear_ip_transports() + addr_filter(relay_only) +
proxy_url) eliminate the direct path and tunnel the relay WebSocket
through the proxy. The peer sees the relay's IP; the relay sees the
proxy's IP; the client's real IP is hidden on both surfaces. No iroh
fork required. Because iroh's proxy_url expects an HTTP CONNECT proxy
(not SOCKS5), the integration runs a tiny local HTTP-to-SOCKS5
bridge (~80 lines) so a single Socks5ProxyConfig covers all three
dials uniformly. The POC also corrected a factual error: iroh's
proxy_url proxies the relay WebSocket only, not pkarr/DoH (those use
pkarr/hickory-resolver directly) — acceptable for the
force-relay-only config (QAD disabled), but the spec text in ADR-090
§5 is corrected. Force relay-only forgoes iroh's direct-path latency
advantage (negligible for the hub deployment, which runs its own
relay) and makes relay availability a hard dependency — the intended
privacy/availability tradeoff; a caller that prefers availability over
privacy for the iroh path simply does not set the proxy. See
ADR-090 §5.
Workspace scope corrected (ADR-085, 2026-07-15). The overview's crate graph had been describing the wrong scope since ADR-003 — a flat ~12-crate workspace including DNS, messaging, and NAPI, while omitting channels, hub, worker, and tls. ADR-085 records the actual scope: the mono-repo is the core networking toolkit (substrate: core, tls, call, channels; deployment shapes: hub, worker; foundational handlers: tty, http, ssh, tunnel, socks5, fs, sftp; vault). Crates that build on top of a hub or worker (docker, agent) are consumer repos — separate repos depending on the published core crates. This corrects the root cause of the "assembly layer" hedging pattern: the overview now reflects the real boundary, so the "assembly layer" has a bounded home (hub/worker), not an escape hatch. The overview.md crate graph and ALPN registry are rewritten to match.
alknet-channels specs drafted. The alknet-channels crate (multiplexing
proxy — ProtocolHandler on alknet/channels, 9-byte chunk format, N
channels over transport stream(s), channel 0 pre-negotiated as
alknet/call) now has architecture specs:
crates/channels/ (overview, channels-wire,
channels-connection, channels-adapter, channel-operations, channel-client)
and eleven ADRs — ADR-071 (9-byte
chunk header; revised for substrate simplification — the header is used in
all substrates including QUIC native, not just in-line; and stream_type
decomposition — every stream_type is unidirectional, grouped in threes:
0/1/2 = data write/read/err, 3/4/5 = control write/read/err, % 3 formula;
resolves the TTY control channel's "not actually bidirectional" flaw),
ADR-072
(channel 0 = alknet/call pre-negotiated, stream_types [0,1] — call frames
bidirectional via 0=in, 1=out),
ADR-073 (channel
lifecycle operations on the call protocol — channel/open/close/
control/resources/subscribe; channel/resources/subscribe is a
Subscription operation using the already-implemented StreamingHandler
machinery, not a polled Query; the direction field pins who is the
ALPN-server; the control-message division is call-ops for orchestration,
stream_type 3/4 for data-ordered control),
ADR-074
(ChannelBidiStreamSource implements BidiStreamSource — ADR-070's
extension point; into_sub_streams() with SubStreamHandle enum (Send/Recv
per unidirectional stream_type); accept_bi() generic path for tunnel/SSH),
ADR-075
(ChannelsAdapter substrate-agnostic demux loop (reads 9-byte headers off
every bidi stream, regardless of substrate) + ChannelManager
reassemble/allocate split; REQ-CH-01..04 wire-level invariants pinned:
shutdown emits zero-length sentinel, transport close drops all senders, mux
dynamic registration, lenient unknown-channel_id),
ADR-076
(bounded-buffer backpressure 1 MiB default, 256-channel cap, monotonic IDs
with wrap-around),
ADR-077 (TTY inside channels uses
sub-streams, not its own 5-byte wire format; 5 sub-streams [0,1,2,3,4]
with control properly bidirectional via 3 (write) + 4 (read); ADR-052's
scope amended to direct-connect TTY only; channels feature on alknet-tty),
ADR-078 (two-pump
handlers MUST shut down the opposite sink on pump completion — the
deadlock contract the POC surfaced; handler-level, not channels-layer;
core helper extraction deferred per OQ-57),
ADR-079 (hub relay
translates channel/open on channel 0 with forwarded_for — ADR-032;
data channels byte-forwarded with channel_id rewrite; the hub never runs
protocol-specific handlers),
ADR-080 (ChannelClient,
transport-agnostic from_connection primary; connect_quic removed
per ADR-089 §5 (dial extracted to AlknetClient),
bidirectionality preserved; AlknetClient dial-seam extracted as
alknet-client per ADR-089, resolving OQ-55),
ADR-081 (sub-crate
decomposition — channels-core (pure multiplexer, depends on alknet-core
only, no call dependency) / channels-call (channel 0 pre-negotiation +
lifecycle op registrations, depends on channels-core + alknet-call) /
channels-hub (relay) / channels-worker (ChannelClient); isolates the
call-protocol coupling from the pure multiplexer). The specs are grounded
in the completed de-risk POC
(docs/research/alknet-channels/poc-summary.md, 28 tests passing, three
validated targets: chunk format + demux/mux, per-channel Connection
presentation, tunnel handler). The core prerequisite — ADR-070
(BidiStreamSource trait + Connection::from_source) — is landed and
implemented. The spec work converted three research hedges into decisions:
channel/resources is subscribe from day one (not poll-for-v1), channel
ID allocation is server-assigned (not "if zero-RTT needed"), and
backpressure is bounded-buffer (not "if HOL blocking becomes a problem").
Two genuine deferrals: OQ-56 (full windowing — blocked on a real HOL-
blocking observation) and OQ-57 (two-pump helper extraction — blocked on a
second two-pump handler). The TTY integration (ADR-077) amends ADR-052's
scope — the 5-byte format is unchanged for direct alknet/tty connections;
inside channels, TTY uses into_sub_streams() and the channels layer's
de-chunking, with control properly bidirectional via stream_types 3/4.
Pre-implementation of the storage/repo pattern. The project has completed a pivot from a three-layer model to an ALPN-as-service model. The greenfield workspace contains alknet-vault (stable — implementation complete and verified, local-only by construction per ADR-025, HD-derivation key model per ADR-026) and research/reference material. Foundational ADRs (001–035) are in place, with the call crate implemented and reviewed.
The storage and auth strategy research (docs/research/alknet-storage-strategy/findings.md) surfaced the repo/adapter pattern as the answer to cross-node state (peer identity, credentials). This has now landed as four ADRs:
- ADR-030 (PeerEntry and Identity.id decoupling):
authorized_fingerprints: HashSet<String>→peers: Vec<PeerEntry>;Identity.idbecomes the stablepeer_id(not the fingerprint); key rotation changes the fingerprint, not the identity. Supersedes ADR-029's v1 UUID source (the one-way door —PeerIdis logical, not crypto — is preserved; the source changes from UUID toIdentity.idfromPeerEntry). Resolves OQ-33 and the storage-boundary half of OQ-34. - ADR-031 (CredentialStore repo trait): the second repo trait in core (alongside
IdentityProvider), withInMemoryCredentialStoredefault adapter. Establishes the credential-persistence abstraction. - ADR-032 (Forwarded-for identity):
forwarded_forfield oncall.requestedandOperationContext; metadata only —AccessControl::checknever reads it; thefrom_callhandler populates it. Wire-format one-way door, included with the ADR-029 migration window. - ADR-033 (Storage boundary and repo/adapter pattern): core defines repo traits + in-memory defaults; persistence adapters are separate crates; the assembly layer wires the adapter. Resolves OQ-34's storage-boundary question. Concrete adapter shapes now committed by ADR-035 (was OQ-36).
The alknet-call crate is implemented and reviewed — both the server-side core and the client/adapter surface (207 lib + 2 integration tests passing). The alknet-core and alknet-call crate specs are in draft; the alknet-vault crate specs are stable.
alknet-http specs drafted and consistency-reviewed. The alknet-http crate (HTTP interface — h2/http/1.1 server + WebSocket browser path + from_openapi/to_openapi/from_mcp/to_mcp adapters) now has architecture specs: crates/http/ (overview, http-server, websocket, http-adapters, http-mcp, webtransport) and thirteen ADRs — ADR-036 (HTTP-to-call mapping; direct-call surface — routing superseded by ADR-047, non-routing clauses survive), ADR-037 (MCP stdio exclusion), ADR-038 (HTTP/3 + WebTransport as first-class — superseded by ADR-044; its correction of the two-way-door-as-deferral anti-pattern stands, its specific decision is reversed by the scope deferral), ADR-039 (HTTP server + client host colocated in one crate), ADR-040 (WebTransport ALPN-stream-proxy — parked per ADR-044; revives unchanged when WebTransport revives), ADR-041 (to_mcp tool-gateway pattern — 4 fixed gateway tools instead of one tool per operation, addressing LLM context tool-bloat), ADR-042 (to_openapi gateway pattern — 5 fixed gateway endpoints instead of one path per operation; per-caller AccessControl-filtered API surface; supersedes ADR-036's original to_openapi clause), ADR-043 (WebTransport as a bidirectional ALPN transport substrate — parked per ADR-044; §2/§3 transfer to WebSocket for v1), ADR-044 (defer h3/WebTransport; browsers use WebSocket for the bidirectional call-protocol path; a scope decision per ADR-009 §"What this framework is NOT"; reversal trigger = a concrete ALPN-stream-proxy use case; states the "browser is not a peer" rationale — addressability vs. bidirectionality — that amends ADR-034 §4), and ADR-045 (to_openapi published-spec versioning — info.version semver tracks the gateway endpoint contract, not the operation set; resolves OQ-39), and ADR-046 (assembly-layer custom HTTP routes on HttpAdapter — extra_routes: Option<Router> for deployment-specific endpoints like an OAI-compatible proxy; default surface unchanged, takes precedence on collision), and ADR-047 (remove the direct-call POST /{service}/{op} surface — the gateway /call is the sole invoke path; the simplified contract is the few-fixed-endpoints model, not a per-operation REST tree; ADR-036's non-routing clauses survive), and ADR-048 (WebSocket carries the native EventEnvelope call-protocol session, not the HTTP gateway shape — the gateway endpoints are HTTP-only; discovery via services/list/services/schema as call-protocol ops; clarifies the WS-path shape ADR-044 committed). ADR-003 Amendment 1 clarifies that alknet-call is a protocol-foundation crate (the alknet-http → alknet-call dependency edge). A consistency review pass corrected drift from the mid-spec pivot (the to_openapi gateway pattern landed in the prose but not in cross-references; the WebTransport specs inherited the OpenAPI/MCP direction assumption that doesn't hold for the call protocol) — ADR-036's to_openapi clause is now amended as superseded by ADR-042, ADR-034 §5's "deferral bucket" wording is corrected (the decision stands), and the http specs now name the one-directional HTTP projection vs. the bidirectional WebSocket (and, when revived, WebTransport) substrate. The WebSocket path is promoted to its own spec (websocket.md) with the native-session-vs-gateway distinction made explicit (ADR-048). The specs are in draft; implementation has not started. Two open questions carried: OQ-38 (WebTransport standalone relay service scope — distinct from the in-process ALPN-stream-proxy resolved by ADR-040) and OQ-40 (reqwest client config — since resolved by the ClientWithMiddleware + middleware stack design). OQ-39 (to_openapi published-spec versioning) is resolved by ADR-045.
Next step: The storage/repo-pattern ADRs (030–033) are accepted and amend the core and call specs. The next implementation phase is the ADR-029 migration (peer-keyed overlays, PeerRef routing, retire remote_safe/trusted_peer) with the ADR-030 PeerEntry change and the ADR-032 forwarded_for field folded in — the OperationContext, from_call handler, and AuthPolicy are all under edit, making this the cheapest window. After that: alknet-http implementation (specs drafted; h3/WebTransport deferred per ADR-044, browser bidirectional path uses WebSocket), which consumes the CredentialStore trait and the OperationAdapter contract. The alknet-ssh crate (the other post-core crate, specced in parallel) proceeds independently — it depends on alknet-core, not alknet-call.
alknet-tty specs drafted. The alknet-tty crate (terminal session protocol handler — ProtocolHandler on alknet/tty, two-carriage wire format with a raw chunk codec + JSON control channel, backend-agnostic via a TtyBackend trait) now has architecture specs: crates/tty/ (overview, tty-wire, tty-backend, tty-adapter, tty-local) and six ADRs — ADR-052 (wire format: alknet/tty ALPN, JSON negotiation frame then raw chunks, fixed channel set 0-3, control as JSON), ADR-053 (TtyBackend trait + TtyHandle; exit_code as a Future; backends need not be natively async — REQ-TTY-01 from the local-PTY POC; TtyControlHandle newtype for Clone-ability — Clone is not object-safe), ADR-054 (alknet-tty-local sibling crate behind a local feature re-export; PTY vs pipe per-session; the runner pattern preserved), ADR-055 (exit code on a stream_type 3 control chunk; "exit chunk is last" invariant; adapter owns the ordering), ADR-056 (backend cleanup contract: dropping the exit_code future on session cancel MUST kill the session target — closes the orphaned-process gap the local-PTY POC surfaced for the waiter thread), ADR-057 (alknet-tty does not depend on alknet-call — the negotiation framing is self-contained; the earlier "reuse FrameFramedReader" claim was unsound because the utility is welded to EventEnvelope deserialization). The specs are grounded in the alknet-docker POC (docs/research/alknet-docker/poc-summary.md) and the alknet-tty POC (/workspace/alknet-tty-poc/, built 2026-07-05), which validated the wire format, the control channel, the local-PTY bridge, and the signal-delivery contract (REQ-TTY-02). The docker and SSH backends are future crates that implement the TtyBackend trait — out of scope for this spec set, but the trait shape is committed so they can be built against it.
alknet-docker specs drafted. The alknet-docker crate (docker operations on the shared alknet/call ALPN + DockerTtyBackend behind a tty feature) now has architecture specs: crates/docker/ (overview, docker-operations, docker-tty-backend) and six ADRs — ADR-058 (docker ops register on alknet/call, not a separate alknet/docker ALPN; the raw-carriage handoff the POC struggled with is dissolved by the alknet-tty extraction — interactive attach moved to alknet/tty via DockerTtyBackend, no carriage field on call.requested), ADR-059 (bollard 0.21, verified current on crates.io; features http+pipe+time, no ssl/ssh/websocket/buildkit — single-host by construction, fleet is a call-protocol concern), ADR-060 (ADR-050 application to bollard: alknet.managed/alknet.owner labels; list owned_only flag; hosted-services operator role via the static-resource fallback; handler-driven revoke on remove with autonomous-death tolerance), ADR-061 (DockerTtyBackend in alknet-docker behind a tty feature, not a sibling crate; attach vs exec mode; the POC's drive_attach_raw as the reference), ADR-062 (the Docker client + OwnershipStore are closure-captured at registration time, not read from OperationContext and not smuggled through Capabilities — Capabilities is for secret material only per ADR-014; matches the from_openapi pattern), ADR-063 (non-interactive exec puts { "exitCode": N, "terminal": true } on a final call.responded before call.completed — call.completed stays empty, ADR-012 unchanged). The specs are grounded in the alknet-docker POC (docs/research/alknet-docker/poc-summary.md, /workspace/alknet-docker-poc/), which validated the hard parts (interactive attach, logs subscription, exec with exit code); the remaining lifecycle operations are mechanical bollard wrapping. The two use cases — disposable dev containers (coordinator-spawned, ownership-recorded) and long-running hosted services (operator-managed, static-resource fallback, per /workspace/system/dev1/docker.md) — both work through one AccessControl model (ADR-050/060). The DockerTtyBackend fills the TtyBackend row the alknet-tty spec left open. Four OQs (048–051) track deferred scope: network/volume ops, buildkit, system events subscription, and the full CreateContainerOptions surface (deferred to v1 implementation).
Transport generalization sweep (2026-07-09). Three commits landed a
clean sweep discovered when building an external app against the crates:
(1) the dead irpc / irpc-derive workspace deps were removed (no .rs
file ever imported irpc — the wire protocol is hand-rolled), recorded by
ADR-064
(supersedes ADR-005, which had accepted "irpc as the call protocol
foundation" based on the previous architecture but was never implemented
as stated); (2) the iroh dep migrated 0.35 → 1.0.2 (6 API surface edits,
no architectural change — unblocks alknet-blobs); (3)
ADR-065
adds Connection::from_stream / from_bidi — Connection now accepts any
AsyncRead + AsyncWrite pair, unblocking TCP+TLS, SSH channel dispatch,
WebTransport streams, and wasm streams through the same HandlerRegistry
as QUIC connections, with zero handler code changes. The
MockConnection / ConnectionKind::Mock test variants are removed (tests
use from_stream with tokio::io::sink/empty). See
docs/research/transport-generalization/findings.md
for the full trace.
from_jsonschema relocation (ADR-066). The from_jsonschema
adapter was originally placed in alknet-call (ADR-017 §5) as a
schema-only adapter with a NOT_FOUND-returning placeholder handler —
broken, because an op in the registry needs a real handler.
ADR-066 moves it to
alknet-http as a real reqwest-backed single-endpoint adapter
(functionally similar to from_openapi, but one endpoint at a time),
for non-standard / non-OpenAPI / basic REST endpoints that don't have a
full OpenAPI document. The FromJsonSchema provenance variant stays in
alknet-call (now a handler-bearing leaf, not a "no handler" entry).
The "schema-only, no handler" concept is removed — schema validation
without a handler is served by consuming OperationSpec directly. The
adapter location map is now consistent: all HTTP-backed adapters
(from_openapi, from_mcp, from_jsonschema) live in alknet-http.
Architecture Documents
| Document | Status | Description |
|---|---|---|
| overview.md | draft | Workspace-level overview, crate graph (core mono-repo scope per ADR-085), hub/worker model, shared types, design principles |
| open-questions.md | draft | OQ index — theme-grouped tables + Deferred/Blocked section; per-OQ files in questions/ |
| crates/core/README.md | draft | alknet-core crate index — shared types + auth + config (endpoint extracted to alknet-endpoint per ADR-083 Am. 2026-07-15; ConnectionCredentials/RemoteIdentity moved here from alknet-call per ADR-091; CallCredentials removed per ADR-091 Am. 2026-07-17) |
| crates/core/core-types.md | draft | ProtocolHandler, HandlerError, Connection (Box<dyn BidiStreamSource> — ADR-070), BidiStreamSource trait, BiStream, StreamError |
| crates/core/endpoint.md | deprecated | Endpoint spec — moved to alknet-endpoint (ADR-083 Am. 2026-07-15); see crates/endpoint/README.md |
| crates/core/auth.md | draft | AuthContext (incl. anonymous constructor), Identity, IdentityProvider, AuthToken, resolution flow |
| crates/core/config.md | draft | StaticConfig, DynamicConfig, ArcSwap, ConfigReloadHandle |
| crates/call/README.md | draft | alknet-call crate index |
| crates/call/call-protocol.md | draft | CallAdapter, hand-rolled EventEnvelope framing (no irpc — ADR-064), stream model, PendingRequestMap, bidirectional calls, streaming subscribe example |
| crates/call/operation-registry.md | draft | OperationSpec, Handler, OperationRegistry, AccessControl, capability injection, service discovery (hand-rolled, no irpc) |
| crates/call/client-and-adapters.md | draft | CallClient (transport-agnostic spawn_dispatch primary; connect removed per ADR-089 §5 — dial extracted to AlknetClient), from_call, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern (from_jsonschema moved to alknet-http per ADR-066) |
| crates/http/README.md | draft | alknet-http crate index |
| crates/http/overview.md | draft | Crate purpose, two roles (server + client host), dependencies, adapter location map |
| crates/http/http-server.md | draft | HttpAdapter for h2/http1.1 + WebSocket upgrade route, axum over QUIC, Bearer auth, stealth, /healthz |
| crates/http/websocket.md | draft | WebSocket browser bidirectional path — native EventEnvelope call-protocol session (not the gateway shape); framing, dispatch, bidirectionality, connection-local overlay, browsers-are-not-peers, deferred from_wss |
| crates/http/http-adapters.md | draft | from_openapi (reqwest; JSON + YAML input per ADR-051), from_jsonschema (single-endpoint reqwest forwarding handler per ADR-066), and to_openapi (projection); no-env-vars injection point |
| crates/http/http-mcp.md | draft | from_mcp / to_mcp (feature-gated), streamable-HTTP-only, stdio exclusion |
| crates/http/webtransport.md | deferred | h3/WebTransport handler — deferred per ADR-044; browser bidirectional path uses WebSocket (see http-server.md). Spec kept intact for revival. |
| crates/tty/README.md | draft | alknet-tty crate index |
| crates/tty/overview.md | draft | Crate purpose, two-carriage model, dependencies, ALPN, backend location map, feature gates |
| crates/tty/tty-wire.md | draft | Wire format: negotiation frame (JSON carriage), raw chunk codec ([stream_type: u8][length: u32 be][payload]), control channel (stream_type 3, JSON control messages), sentinels |
| crates/tty/tty-backend.md | draft | TtyBackend trait, TtyParams, TtyHandle, TtyControl — the backend inversion point. Carries REQ-TTY-01 (backends need not be natively async) |
| crates/tty/tty-adapter.md | draft | TtyAdapter (ProtocolHandler on alknet/tty): session lifecycle, three-pump bidirectional driver, negotiation errors, exit-chunk ordering (ADR-055), access control |
| crates/tty/tty-local.md | draft | alknet-tty-local sibling crate: LocalTtyBackend via portable_pty (PTY) and std::process::Command (pipe/runner). Carries REQ-TTY-02 (signal forwarding to the process group) |
| crates/docker/README.md | draft | alknet-docker crate index |
| crates/docker/overview.md | draft | Crate purpose, two-role design (call ops + DockerTtyBackend), dependencies, ALPN, label namespace, feature gates, assembly-layer wiring |
| crates/docker/docker-operations.md | draft | Operation surface: lifecycle (Query/Mutation), logs/exec/pull (Subscription via StreamingHandler), access control (ADR-050/060), label namespace, teardown coupling |
| crates/docker/docker-tty-backend.md | draft | DockerTtyBackend (impl TtyBackend): attach vs exec mode, TtyHandle field mapping, TtyControl → bollard resize/signal, exit_code Drop-kill (ADR-056) |
| crates/vault/README.md | stable | alknet-vault crate index |
| crates/vault/mnemonic-derivation.md | stable | BIP39, SLIP-0010, BIP-0032, derivation paths, key types |
| crates/vault/encryption.md | stable | AES-256-GCM, EncryptedData, key versioning, salt (Phase B reserved) |
| crates/vault/service.md | stable | VaultServiceHandle lifecycle, direct dispatch, cache, error model |
| crates/vault/protocol.md | stable | DerivedKey redaction, KeyType, serialization behavior |
| crates/hub/README.md | draft | alknet-hub crate — composes a subset of three endpoint types (web/native/iroh — ADR-086), channels substrate (ADR-079 relay), worker registration flow (OQ-58), identity over transports, aggregated peer env, connection lifecycle, service discovery |
| crates/tls/README.md | reviewed | alknet-tls crate — shared TLS config (TlsServerConfig + TlsClientConfig) shared across quinn + TCP+TLS + iroh; one cert, one ACME state machine, N transports; split ALPN lists per endpoint type (ADR-086, resolves OQ-62); FingerprintPinVerifier in alknet-tls (ADR-089 §5); webpki-roots fallback for empty platform stores (ADR-088 §5); isolates cert-reuse from transport wrappers (ADR-082) |
| crates/client/README.md | draft | alknet-client crate — the native client dial seam (AlknetClient), client-side analogue of AlknetEndpoint; three dials (QUIC + TCP+TLS via TlsClientConfig, iroh via key) unified on &ConnectionCredentials (ADR-091); optional SOCKS5 proxy (ADR-090 — UDP ASSOCIATE for QUIC, CONNECT for TCP+TLS, force-relay-only + HTTP-to-SOCKS5 bridge for iroh; OQ-67 resolved); produces Connection for CallClient/ChannelClient take-over; CallClient::connect/ChannelClient::connect_quic removed (dial centralized here); alknet/register named (wire protocol deferred, OQ-66) |
| crates/endpoint/README.md | draft | alknet-endpoint crate — the server-side accept-loop runner (AlknetEndpoint), extracted from alknet-core (ADR-083 Am. 2026-07-15); takes pre-built transports via with_quinn/with_iroh/with_tcp_tls; public dispatch for SSH/WT; EndpointError removed (vestigial); handler crates no longer transitively link quinn/iroh |
| crates/channels/README.md | draft | alknet-channels crate — multiplexing proxy, 9-byte chunk format, N channels over one transport stream |
| crates/channels/overview.md | draft | Crate purpose, the multiplexing collapse, dependencies, transport agnosticism, WASM, relationship to existing crates |
| crates/channels/channels-wire.md | draft | 9-byte chunk format, stream types, sentinels, framing disambiguation, wire-level invariants (REQ-CH-01..05) |
| crates/channels/channels-connection.md | draft | ChannelBidiStreamSource (implements BidiStreamSource), into_sub_streams() typed accessor, recursive composition |
| crates/channels/channels-adapter.md | draft | ChannelsAdapter, ChannelManager, demux/mux contracts (REQ-CH-01..04), two-pump pattern (ADR-078) |
| crates/channels/channel-operations.md | draft | channel/open/close/control/resources/subscribe, ACL flow, direction semantics, hub relay contract (ADR-079) |
| crates/channels/channel-client.md | draft | ChannelClient — client side of a channels connection, transport-agnostic from_connection primary; connect_quic removed per ADR-089 §5 (dial extracted to AlknetClient); bidirectionality preserved |
ADR Table
| ADR | Title | Status |
|---|---|---|
| 001 | ALPN-Based Protocol Dispatch | Accepted |
| 002 | ProtocolHandler Trait | Accepted |
| 003 | Crate Decomposition | Accepted |
| 004 | Auth as Shared Core (IdentityProvider) | Accepted |
| 005 | irpc as Call Protocol Foundation | |
| 006 | ALPN String Convention and Connection Model | Accepted |
| 007 | BiStream Type Definition | Accepted |
| 008 | Vault Integration Point | Accepted |
| 009 | One-Way Door Decision Framework | Accepted |
| 010 | ALPN Router and Endpoint | Accepted (Amendment 1 superseded by ADR-083 — TCP+TLS is a first-class owned transport via with_tcp_tls) |
| 011 | AuthContext Structure and Resolution Flow | Accepted |
| 012 | Call Protocol Stream Model | Accepted |
| 013 | Rust as Canonical Implementation Language | Accepted |
| 014 | Secret Material Flow and Capability Injection | Accepted |
| 015 | Privilege Model and Authority Context | Accepted |
| 016 | Abort Cascade for Nested Calls | Accepted |
| 017 | Call Protocol Client and Adapter Contract | Accepted (from_jsonschema clause superseded by ADR-066) |
| 018 | Vault as Standalone Crate | Accepted |
| 019 | Vault Assembly-Layer-Only Access | Accepted |
| 020 | HD Derivation for Encryption Keys | Accepted |
| 021 | Key Rotation via Version-Indexed Paths | Accepted |
| 022 | Handler Registration, Provenance, and Composition Authority | Accepted (FromJsonSchema row superseded by ADR-066) |
| 023 | Operation Error Schemas | Accepted |
| 024 | Operation Registry Layering | Accepted |
| 025 | Vault Local-Only Dispatch | Accepted |
| 026 | Vault Key Model — HD Derivation | Accepted |
| 027 | TLS Identity Redesign — ACME + RawKey Decoupling | Accepted (§5 amended by ADR-083 — guard moves to shared dispatch) |
| 028 | Peer-Scoped Registry Filtering for CallClient Inbound Dispatch | |
| 029 | Peer-Graph Routing Model for alknet-call Composition | Accepted (Assumption 1's PeerId source superseded by ADR-030) |
| 030 | PeerEntry and Identity.id Decoupling | Accepted (supersedes ADR-029 Assumption 1's UUID source) |
| 031 | CredentialStore Repo Trait | Accepted |
| 032 | Forwarded-For Identity (Metadata, Not Authority) | Accepted |
| 033 | Storage Boundary and Repo/Adapter Pattern | Accepted |
| 034 | Outgoing-Only X.509 and the Three Peer Roles | Accepted |
| 035 | Concrete Persistence Adapter Shapes — Read/Write Split, honker+SQLite | Accepted |
| 036 | HTTP-to-Call Operation Mapping | Proposed — routing decision superseded by ADR-047 (non-routing clauses survive: SSE, auth, /healthz, stealth, error mapping) |
| 037 | MCP Stdio Transport Exclusion | Proposed |
| 038 | HTTP/3 and WebTransport as First-Class HTTP Transports | |
| 039 | HTTP Server and Client Host Colocated in alknet-http | Proposed |
| 040 | WebTransport ALPN-Stream-Proxy | Proposed — parked (implementation deferred per ADR-044) |
| 041 | MCP Tool-Gateway Pattern for to_mcp | Proposed |
| 042 | OpenAPI Gateway Pattern for to_openapi | Proposed |
| 043 | WebTransport as a Bidirectional ALPN Transport Substrate | Proposed — parked (implementation deferred per ADR-044; §2/§3 transfer to WebSocket) |
| 044 | Defer h3/WebTransport; Browsers Use WebSocket | Accepted |
| 045 | to_openapi Gateway-Spec Versioning | Proposed |
| 046 | Assembly-Layer Custom HTTP Routes on HttpAdapter | Proposed |
| 047 | Remove the Direct-Call HTTP Surface; Gateway Is the Sole Invoke Path | Proposed |
| 048 | WebSocket Carries the Native Call-Protocol Session, Not the Gateway Shape | Accepted |
| 049 | Streaming Handler for Subscription Operations | Accepted |
| 050 | Dynamic Resource Ownership for Runtime-Spawned Resources | Accepted |
| 051 | YAML Input Format for from_openapi | Accepted |
| 052 | alknet-tty Wire Format and Two-Carriage Model | Accepted |
| 053 | TtyBackend Trait and TtyHandle — the Backend Inversion Point | Accepted |
| 054 | Local TTY Backend as a Sibling Crate (alknet-tty-local) |
Accepted |
| 055 | Exit Code on a Control Chunk (the Last Chunk Before Stream Close) | Accepted |
| 056 | Backend Cleanup on Session Cancel (Drop of exit_code Kills) |
Accepted |
| 057 | alknet-tty Does Not Depend on alknet-call (Self-Contained Negotiation Framing) | Accepted |
| 058 | alknet-docker Registers on alknet/call (No Separate ALPN) |
Accepted |
| 059 | bollard 0.21 Dependency and Feature Selection | Accepted |
| 060 | Container Resource Model and Label Namespace | Accepted |
| 061 | DockerTtyBackend in alknet-docker | Accepted |
| 062 | Docker Client and OwnershipStore Injection via Closure Capture | Accepted |
| 063 | Exit Code on a Terminal call.responded for Non-Interactive Exec |
Accepted |
| 064 | irpc Was Never Integrated — Hand-Rolled EventEnvelope Framing | Accepted (supersedes ADR-005) |
| 065 | Connection::from_stream — Generic Single-Stream Connections |
Accepted |
| 066 | from_jsonschema as HTTP-Backed Single-Endpoint Adapter in alknet-http |
Accepted (supersedes the from_jsonschema clause of ADR-017 §5 and the FromJsonSchema provenance row of ADR-022) |
| 067 | Aggregated Peer-Environment Wiring for Hub Deployments | Proposed |
| 068 | PeerCompositeEnv::peer_operations Override | Proposed |
| 069 | from_call Is a Manual Free Function, Not Auto-Wired | Proposed |
| 070 | BidiStreamSource Trait — Open Connection for Extension | Accepted |
| 071 | alknet-channels Wire Format — 9-Byte Chunk Header | Accepted |
| 072 | Channel 0 Is Pre-Negotiated alknet/call |
Accepted |
| 073 | Channel Lifecycle Operations on the Call Protocol | Accepted |
| 074 | ChannelConnection — BidiStreamSource over Chunk Reassembly | Accepted |
| 075 | ChannelsAdapter and ChannelManager | Accepted |
| 076 | Backpressure, Channel Limits, and ID Reuse | Accepted |
| 077 | TTY Inside Channels — Sub-Streams, Not Wire Format | Accepted (amends ADR-052 scope — 5-byte format scoped to direct TTY) |
| 078 | Two-Pump Shutdown-on-Completion Pattern | Accepted |
| 079 | Hub Relay — Translate, Not Transparently Forward | Accepted |
| 080 | ChannelClient — the Client Side of a Channels Connection | Accepted |
| 081 | channels Sub-Crate Decomposition | Accepted |
| 082 | alknet-tls Crate Extraction | Accepted (amended — endpoint signature superseded by ADR-083) |
| 083 | Endpoint as Multi-Transport Accept-Loop Runner with Public Dispatch | Accepted (revised — TCP+TLS is an owned transport, not external; amended 2026-07-15 — endpoint extracted from alknet-core into alknet-endpoint; EndpointError removed — both variants vestigial, shutdown() infallible) |
| 084 | aws-lc-rs as the TLS Crypto Provider | Accepted |
| 085 | Workspace Scope — Core vs. Consumer Repos | Accepted |
| 086 | Endpoint Types and Entry Points | Accepted |
| 087 | TlsClientConfig Not Blocked on Dial Seam |
Accepted (§5 amended by ADR-089 — FingerprintPinVerifier in alknet-tls; alknet-call sheds TLS deps; input framing amended by ADR-091 — TlsClientConfig::new takes ConnectionCredentials, not CallCredentials) |
| 088 | TlsError Shape — Single Enum, Owned by alknet-tls |
Accepted (§5 added — webpki-roots fallback when platform store is empty; §7 references ADR-089 for handshake-error surfacing) |
| 089 | AlknetClient — Native Client Dial Seam | Accepted (resolves OQ-55; CallClient::connect / ChannelClient::connect_quic removed; §3/§5 amended by ADR-091 — dial takes ConnectionCredentials, not CallCredentials; CallCredentials removed per ADR-091 Am. 2026-07-17; FingerprintPinVerifier moved to alknet-tls; ClientError removed; alknet-call sheds TLS deps) |
| 090 | Client-Dial SOCKS5 Proxy Seam | Accepted (§5 amended 2026-07-16 — OQ-67 resolved: iroh force-relay-only + HTTP-to-SOCKS5 bridge) |
| 091 | ConnectionCredentials — Decouple Dial Credentials from Call Protocol |
Accepted (amends ADR-089 §3/§5 and ADR-087 input framing; dial takes ConnectionCredentials not CallCredentials; all three dial signatures unified; dial_iroh's node_id derived from remote_identity; auth_token is a per-request payload field; CallCredentials removed per Am. 2026-07-17) |
Open Questions
Open questions are tracked in open-questions.md — an index of theme-grouped tables (67 OQs across 20 themes) with a cross-theme Deferred / Blocked section surfacing the safe-exit deferrals. Each OQ lives in its own file under questions/ (NNN-slug.md, mirroring the ADR convention).
Document Lifecycle
| Status | Meaning | Transitions |
|---|---|---|
draft |
Under active development. May change significantly. | → reviewed when open questions are resolved |
reviewed |
Architecture is final. Implementation may begin. Changes require review. | → stable when implementation is complete and verified |
stable |
Locked. Changes require review and may warrant an ADR. | → deprecated when superseded |
deprecated |
Superseded. Kept for reference. | Removed when no longer referenced |
References
- Pivot proposal:
docs/research/pivot/alpn-service-architecture.md - Cleanup plan:
docs/research/pivot/cleanup-plan.md - SDD process:
docs/sdd_process.md - Reference implementation:
/workspace/@alkdev/alknet-main/