Files
alknet/docs/architecture
glm-5.2 1ce11717d8 docs(arch): draft alknet-docker architecture specs (ADRs 058-063, OQs 048-051)
Greenfield architecture spec set for the alknet-docker crate — a thin,
single-host bollard wrapper exposing docker container/image operations
as call-protocol ops on the shared alknet/call ALPN, plus a
DockerTtyBackend (impl TtyBackend) behind a tty feature for interactive
terminal sessions into containers over alknet/tty.

Six ADRs:
- 058: docker ops on alknet/call (no separate ALPN; raw-carriage handoff
  dissolved by alknet-tty extraction — interactive attach moved to
  alknet/tty via DockerTtyBackend, no carriage field on call.requested)
- 059: bollard 0.21 (verified current on crates.io) + feature selection
  (http+pipe+time; no ssl/ssh/websocket/buildkit)
- 060: container resource model (ADR-050 application) — alknet.managed/
  alknet.owner labels, list owned_only flag, hosted-services operator
  role via static-resource fallback, handler-driven revoke with
  autonomous-death tolerance, resource-action vocabulary
- 061: DockerTtyBackend in alknet-docker behind tty feature (attach vs
  exec mode; POC drive_attach_raw as reference)
- 062: Docker client + OwnershipStore injection via closure capture
  (not Capabilities, not OperationContext — matches from_openapi pattern)
- 063: exit code on terminal call.responded for non-interactive exec
  (call.completed stays empty, ADR-012 unchanged)

Four spec docs (crates/docker/): README, overview, docker-operations,
docker-tty-backend. Four deferred-scope OQs (048-051): network/volume
ops, buildkit, system events subscription, create options surface.

Updates the tty-backend.md Backend implementations table (DockerTtyBackend
row now specced) and the architecture README (doc table, ADR table,
current-state paragraph, OQ count).

Grounded in the alknet-docker POC (docs/research/alknet-docker/poc-summary.md)
which validated the hard parts; the remaining lifecycle ops are mechanical
bollard wrapping. Reviewed by architecture-reviewer subagent; criticals
(the docker_client injection model conflicting with the Capabilities
contract) resolved via ADR-062/063 before commit.
2026-07-08 09:29:22 +00:00
..

status, last_updated
status last_updated
draft 2026-07-08

Alknet Architecture

Current State

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.id becomes the stable peer_id (not the fingerprint); key rotation changes the fingerprint, not the identity. Supersedes ADR-029's v1 UUID source (the one-way door — PeerId is logical, not crypto — is preserved; the source changes from UUID to Identity.id from PeerEntry). Resolves OQ-33 and the storage-boundary half of OQ-34.
  • ADR-031 (CredentialStore repo trait): the second repo trait in core (alongside IdentityProvider), with InMemoryCredentialStore default adapter. Establishes the credential-persistence abstraction.
  • ADR-032 (Forwarded-for identity): forwarded_for field on call.requested and OperationContext; metadata only — AccessControl::check never reads it; the from_call handler 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).

Architecture Documents

Document Status Description
overview.md draft Workspace-level overview, crate graph, 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
crates/core/core-types.md draft ProtocolHandler, HandlerError, Connection, BiStream, StreamError
crates/core/endpoint.md draft ALPN router, HandlerRegistry, accept loop, shutdown
crates/core/auth.md draft AuthContext, 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, EventEnvelope framing, stream model, PendingRequestMap, bidirectional calls, streaming subscribe example
crates/call/operation-registry.md draft OperationSpec, Handler, OperationRegistry, AccessControl, capability injection, service discovery, irpc integration
crates/call/client-and-adapters.md draft CallClient (outbound connection opener), from_call / from_jsonschema, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern
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) 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

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 Accepted
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
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
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
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
028 Peer-Scoped Registry Filtering for CallClient Inbound Dispatch Accepted → Superseded by ADR-029
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 Proposed → Superseded by ADR-044
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

Open Questions

Open questions are tracked in open-questions.md — an index of theme-grouped tables (51 OQs across 16 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/