Files
alkhttp/docs/architecture/README.md
glm-5.3-flash 8e8e1f2b14 refactor(gateway): migrate to alkcall 0.2 promoted gateway module
Bump the alkcall dependency to 0.2 (with the gateway feature) and
converge on the promoted shared pieces:

- The local dispatch spine (gateway/dispatch.rs, 721 lines) is deleted;
  GatewayDispatch, schema_disclosure_denial, and DEFAULT_DEADLINE are
  re-exported from alkcall::gateway (alkcall ADR-048). The 30 s default
  deadline preserves the previous behavior exactly.
- gateway/schema_cache.rs (PublishSchemaCache) is deleted: alkcall CF-003
  compiles publish_schema at registration time and exposes
  OperationRegistry::publish_validator; the /publish chunk stream
  resolves against it. Un-compilable schemas are now rejected at
  registration, so the two end-to-end fail-closed tests were reworked
  into a registration-rejection test (a stronger guarantee).
- schema_disclosure_denial consumers (to_mcp, routes) use alkcall's
  promoted implementation; the alkhttp-local copy is gone (ADR-071
  updated: the guard stays as defense-in-depth, the implementation no
  longer forks).
- CF-001: from_wss drop monitor and the WS overlay tests use
  CallError::connection_closed; the review-001-ws-eof-signal race tests
  now assert retryable CONNECTION_CLOSED on both resolution paths (the
  tolerated non-retryable INTERNAL write-failure outcome is gone).
- Added CHANGELOG.md (Keep a Changelog), Unreleased section records the
  bump and convergence.

Verification: cargo test default 453 ok, wss 470 ok, mcp 526 ok,
all-features 575 ok; clippy -D warnings clean (default + all-features,
all-targets); fmt clean; cargo doc warning-free.

Net: -1093 lines.
2026-08-31 10:36:32 +00:00

13 KiB

status, last_updated
status last_updated
draft 2026-08-28

alkhttp

HTTP interface for the alk stack: serves HTTP/1.1 and HTTP/2 on standard ALPNs (with WebSocket upgrade carrying the channels protocol for browser bidirectional access to the call protocol), and hosts the HTTP-backed call-protocol adapters (from_openapi, from_jsonschema, from_mcp, to_openapi, to_mcp, from_wss). HTTP/3 + WebTransport (h3) is out of scope per ADR-069 — it is an alknet-side concern, not an alkhttp one.

alkhttp is the extraction of alknet-http from the alknet mono-repo, re-implemented on the published crates: alkvault (secrets) and alkcall 0.1.1 (call protocol + channels protocol + vendored core types — the former alknet-core and alknet-call merged).

Documents

Document Status Description
overview.md draft Crate purpose, two roles (server + client host), dependency edges, adapter location map
http-server.md draft HttpAdapter (ProtocolHandler for h2/http/1.1 + WS upgrade route), axum over a BiStream, Bearer auth, stealth, /healthz; WS hands off to the channels session spec
websocket.md draft WebSocket browser bidirectional path — the WS connection carries the channels protocol (8-byte chunk multiplexing, channel 0 pre-negotiated as alk/call); framing via the WS↔byte-stream adapter, dispatch, bidirectionality, connection-local Layer 2 overlay, browsers-are-not-peers rationale, streaming (native, no SSE), from_wss consumer adapter
http-adapters.md draft from_openapi (reqwest client; JSON + YAML input per ADR-051), from_jsonschema (single-endpoint reqwest forwarding handler per ADR-066), to_openapi (OpenAPI projection of the 6-endpoint gateway), from_wss (WSS consumer adapter per ADR-070); no-env-vars invariant point
http-mcp.md draft from_mcp / to_mcp (feature-gated), streamable-HTTP-only, stdio exclusion

Applicable ADRs

Ported from alknet (same numbers)

ADR Title Relevance
001 ALPN-Based Protocol Dispatch HttpAdapter registers on standard HTTP ALPNs
002 ProtocolHandler Trait HttpAdapter implements ProtocolHandler
003 Crate Decomposition alkhttp depends on alkcall alone (protocol-foundation exception, Amendment 1)
004 Auth as Shared Core Bearer → resolve_from_token
010 ALPN Router and Endpoint Stealth mode = HTTP handler on standard ALPNs
014 Secret Material Flow from_openapi/from_mcp are the credential injection point
015 Privilege Model Adapter-registered ops are Internal by default
017 Call Protocol Client and Adapter Contract OperationAdapter trait; to_* are projections; published-spec contract
022 Handler Registration, Provenance, Composition Authority from_openapi/from_mcp/from_jsonschema produce leaf bundles (FromJsonSchema handler-bearing per ADR-066)
023 Operation Error Schemas from_openapi/from_jsonschema/to_openapi error fidelity; HTTP_<status> error codes
027 TLS Identity Redesign Browsers require X.509; applies to any browser-facing TLS (TLS provisioning itself is an alknet concern)
034 Outgoing-Only X.509 and Three Peer Roles Browsers are not alknet peers (§4)
036 HTTP-to-Call Operation Mapping Direct path mappingrouting superseded by ADR-047; non-routing clauses survive
037 MCP Stdio Transport Exclusion Streamable HTTP only; stdio not built
039 HTTP Server and Client Host Colocated One crate for server + client host (shared HTTP deps, shared mapping)
041 MCP Tool-Gateway Pattern for to_mcp 4 fixed gateway tools (search/schema/call/batch); Sub AND Pub excluded
042 OpenAPI Gateway Pattern for to_openapi Fixed gateway endpoints, not one path per operation; per-caller AccessControl-filtered
044 Defer h3/WebTransport; Browsers Use WebSocket WS as browser path stands; deferral mechanics superseded by ADR-069 (removal)
045 to_openapi Gateway-Spec Versioning info.version (semver) tracks the gateway endpoint contract
046 Assembly-Layer Custom HTTP Routes extra_routes: Option<Router> at construction
047 Remove the Direct-Call HTTP Surface The 6 gateway endpoints are the sole invoke path
048 WebSocket Carries the Native Session, Not the Gateway Shape Amended by ADR-067: WS carries the channels session; channel 0 carries the native call session
049 Streaming Handler for Subscription Operations HandlerKind::Stream for Sub; HandlerKind::Sink for Pub (alkcall ADR-046)
051 YAML Input Format for from_openapi from_json/from_yaml/from_str; JSON-first detection; yaml_serde
066 from_jsonschema as HTTP-Backed Single-Endpoint Adapter Real reqwest-backed single-endpoint adapter; provenance stays in alkcall

New in alkhttp

ADR Title Summary
067 WebSocket Carries the Channels Protocol WS = in-line channels substrate: 8-byte chunk demux, channel 0 pre-negotiated as alk/call, upgrade path /alk/channels; the shared Dispatcher runs on channel 0
068 Gateway /publish Endpoint 6th gateway endpoint for OperationType::Pub (producer→consumer streaming); NDJSON request body → call.published chunks
069 WebTransport Out of Scope h3/WebTransport removed from alkhttp scope entirely (an alknet concern); supersedes the deferral framing of ADR-044
070 from_wss Consumer Adapter Import a remote node's operations over WSS — same-protocol importer, channels-over-WS as transport; wss feature gate
071 Dispatch-Spine services/schema Op-Path Guard GatewayDispatch applies the GET /schema visibility+ACL checks to the meta-op's inner name (review-002 PRJ-16); alkcall CF-004 is the complete fix, this stays as defense-in-depth (now via the promoted alkcall::gateway shared check — spine + guard themselves live in alkcall per ADR-048)

Relevant Open Questions

Open questions are tracked in open-questions.md. Key ones:

OQ Title Status Relevance
OQ-01 WS message ↔ byte-stream adaptation resolved Validated by the production adapter (src/websocket/byte_adapter.rs) in both directions (server upgrade path + from_wss client)
OQ-02 /publish body framing details resolved First line {operation, chunk}; terminal errors are plain HTTP status + JSON body (ADR-068, src/gateway/routes.rs::publish_handler)
OQ-03 from_wss reconnection semantics open v1 = drop → retryable failures (ADR-070, implemented in src/adapters/from_wss.rs); whether a built-in reconnection policy is ever warranted is deferred to the assembly layer

Key Design Principles

  1. HTTP is both a server surface and a client transport for adapters. Inbound HTTP (h2/http/1.1 + WebSocket upgrade) is served by axum over a BiStream; outbound HTTP (from_openapi/from_mcp forwarding) uses reqwest. Both directions share the same HTTP dependencies, which is why they live in one crate. See ADR-039.
  2. The HTTP surface is a fixed-endpoint gateway — 6 endpoints, not a per-operation REST tree. An HTTP client invokes an operation via POST /call with { "operation": "/fs/readFile", "input": {...} }, discovers what it can call via AccessControl-filtered GET /search, learns an operation's shape via GET /schema, streams a Sub operation via POST /subscribe (SSE), and feeds a Pub operation via POST /publish (NDJSON body). There is no per-operation POST /{service}/{op} direct-call surface (removed by ADR-047; the per-caller API surface is the default). to_openapi describes this gateway surface. A deployment that wants a REST-like per-operation surface builds it as a custom route projection (ADR-046). See ADR-042 and ADR-047.
  3. Standard ALPNs, not alk ALPNs. h2, http/1.1 are IANA-registered ALPN strings. Any HTTP client (browser, curl, axios) connects without knowing about the alk stack — the TLS handshake negotiates h2 or http/1.1 normally. This is the stealth mapping (ADR-010).
  4. from_openapi/from_mcp/from_jsonschema are the no-env-vars injection point. The forwarding handlers read context.capabilities, not std::env::var. See ADR-014.
  5. MCP streamable HTTP only; stdio is not built. stdio = spawn arbitrary executable = RCE. See ADR-037.
  6. WebSocket is the browser bidirectional path, and it carries the channels protocol. A browser upgrades an HTTP/1.1 or HTTP/2 request to WebSocket and speaks the channels protocol over binary WS messages: the 8-byte chunk header multiplexes N channels; channel 0 is pre-negotiated as alk/call and carries the native call-protocol session (ADR-067). Both sides can initiate calls on channel 0; the browser may open further channels via the per-ALPN open ops, exactly as a Rust peer would over an in-line transport. The 6 gateway endpoints are the HTTP one-directional projection and do not appear on the WS path — discovery is via services/list/services/schema as call-protocol ops (ADR-048). This supersedes the alknet design where WS carried bare EventEnvelope messages (one envelope per WS message).
  7. Browsers are not alk peers. A browser over WebSocket authenticates by bearer token, gets no PeerId, and its registered ops land in a connection-local Layer 2 overlay. See ADR-034 §4.
  8. Producer/consumer, not server/client. Both sides of a call or channels connection can initiate. A producer exposes operations (call) or opens data channels; a consumer calls operations or opens channels; both sides can be both simultaneously. Connection direction (who opened it) is independent of call/channel direction (who calls/opens). The from_*/to_* adapter names remain directional because OpenAPI and MCP are client/server protocols — that directionality is a property of those protocols, not of the call protocol.

References

  • The alkcall crate (/workspace/@alkdev/alkcall, crates.io alkcall 0.1.1) — call protocol, channels protocol, vendored core types. Its docs/architecture/ owns the call-protocol and channels decisions cited throughout: wire format (ADR-014), stream model (ADR-015), registry layering (ADR-019), abort cascade (ADR-020), streaming handler (ADR-021), adapter contract (ADR-022), channels wire format (ADR-034/035), channel 0 pre-negotiation (ADR-036), ChannelClient (ADR-043), Pub/Sink (ADR-046), openable ALPNs as operations (ADR-047).
  • The alkvault crate (/workspace/@alkdev/alkvault, crates.io) — secrets; feeds Capabilities at the assembly layer (no direct dependency from alkhttp).
  • The alknet mono-repo (/workspace/@alkdev/alknet) — the source of this extraction; retains the endpoint/transport/peer-graph concerns (dial, TLS, QUIC, WebTransport) and the ADRs for them.