First speccing pass for alknet-http (HTTP interface crate: h2/http1.1/h3 server + from_openapi/to_openapi/from_mcp/to_mcp adapters). Specs (crates/http/): - README.md, overview.md — crate index, two-roles-in-one-crate framing, adapter location map, feature gates (h3, mcp), no-env-vars invariant - http-server.md — HttpAdapter for h2/http1.1, axum over QUIC stream, Bearer auth, SSE projection for subscriptions, /healthz, stealth decoy - http-adapters.md — from_openapi (reqwest) and to_openapi (projection), error fidelity (HTTP_<status> per ADR-023), type definitions - http-mcp.md — from_mcp/to_mcp (feature-gated), streamable-HTTP-only - webtransport.md — h3/WebTransport handler, browser streaming path, HTTP/3 request vs WebTransport session distinguished at framing layer ADRs: - ADR-036 HTTP-to-Call Operation Mapping (Proposed) — direct path mapping; to_openapi is projection, not router (the load-bearing one-way door from Phase 0 DH-3) - ADR-037 MCP Stdio Transport Exclusion (Proposed) — streamable HTTP only; stdio is not built (RCE-vector security position) - ADR-038 HTTP/3 and WebTransport as First-Class HTTP Transports (Proposed) — corrects the Phase 0 DH-2 deferral framing; h3 is in scope, not deferred, per ADR-009 §'What this framework is NOT' - ADR-039 HTTP Server and Client Host Colocated in alknet-http (Proposed) — one crate for server + client host (shared HTTP deps, shared operation-spec->HTTP mapping) - ADR-003 Amendment 1 — clarifies alknet-call is a protocol-foundation crate (the alknet-http -> alknet-call dependency edge) Open questions (OQ-38, OQ-39, OQ-40 added under 'Theme: alknet-http'): - OQ-38 WebTransport relay-as-proxy scope (genuine scope question, not a deferral — the decision is made when the use case becomes concrete) - OQ-39 to_openapi published-spec versioning (one-way after first publication) - OQ-40 reqwest client config and connection pooling (two-way-door) Architecture README and overview updated with doc table, ADR table (036-039), current-state note, and crate graph (alknet-http -> alknet-call edge). Reviewed by architecture-reviewer subagent: 3 critical, 4 warning, 5 suggestion issues found and fixed (missing ADR-039, WebTransport stream routing conflation, undefined types, stale OQ-37 deferral language, README OQ table completeness, Bearer-only attribution, cross-references, ADR-038 ALPN quote, feature-gate placeholder, MCP temporal language).
13 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-06-29 |
alknet-http — Overview
The HTTP interface crate: serves inbound HTTP on standard ALPNs and hosts the HTTP-backed call-protocol adapters. This document covers the crate's two roles, its dependency edges, and the adapter location map. Component details are in the sibling documents.
What
alknet-http is the HTTP protocol handler for the ALPN-as-service
architecture. It serves two roles in one crate:
- HTTP server — a
ProtocolHandler(HttpAdapter) that accepts HTTP/2, HTTP/1.1, and HTTP/3 (WebTransport) connections on the standard IANA ALPNs (h2,http/1.1,h3). It serves REST APIs, theto_openapi/to_mcpprojections of local call-protocol operations, the/healthzoperational endpoint, and the decoy surface for stealth mode. - HTTP client host — the home of the HTTP-transport-backed call
adapters:
from_openapi(import external HTTP APIs as call operations, usingreqwestfor outbound calls) andfrom_mcp(import remote MCP tools over streamable HTTP, usingreqwest). The reverse projectionsto_openapi(generate an OpenAPI doc from the local registry'sExternaloperations) andto_mcp(expose local ops as MCP tools over streamable HTTP, usingaxum) also live here.
Both directions share the same HTTP dependencies (axum for serving,
reqwest for calling out), which is why they live in one crate rather
than being split into a server crate and a client crate. See
ADR-039
for the full rationale.
Why
The crate's purpose is to be the HTTP interface library for downstream
crates that need to expose an HTTP interface. A downstream consumer (the
CLI binary, a hub deployment, a browser-facing service) wires
HttpAdapter into the HandlerRegistry for the standard HTTP ALPNs and
gets a full HTTP surface: REST projection of the call protocol, OpenAPI
discovery, MCP tool exposure, and WebTransport for browsers.
The key architectural insight that shapes the crate: HTTP is both a
server surface and a client transport for adapters. The server side
serves HTTP to external clients (browsers, curl, axios); the client side
makes outbound HTTP calls to external APIs (OpenAI, Anthropic, vast.ai)
through the from_openapi/from_mcp forwarding handlers. Both
directions share HTTP dependencies and HTTP-specific concerns (TLS,
headers, streaming, SSE), so they belong in one crate. See
ADR-039
for the colocation decision.
Dependencies
alknet-http
├── alknet-core (ProtocolHandler, Connection, AuthContext, IdentityProvider, Capabilities)
├── alknet-call (OperationAdapter, OperationSpec, Handler, HandlerRegistration,
│ OperationRegistry, AdapterError, OperationProvenance)
├── axum (HTTP server — Router, extractors, middleware)
├── reqwest (HTTP client — from_openapi/from_mcp forwarding)
├── hyper (HTTP/1.1 + HTTP/2 framing; axum is built on hyper)
├── wtransport (HTTP/3 + WebTransport — feature-gated behind `h3`)
└── rmcp (MCP streamable HTTP — feature-gated behind `mcp`)
The alknet-call dependency (ADR-003 Amendment 1)
alknet-http depends on alknet-call. ADR-003's rule is "no handler
crate depends on another handler crate," but alknet-call is both a
handler (it implements ProtocolHandler on alknet/call) and the
protocol-foundation crate that alknet-agent, alknet-napi, and now
alknet-http consume. alknet-http depending on alknet-call is
"HTTP uses the call protocol types" (OperationSpec, Handler,
HandlerRegistration, OperationAdapter), not "HTTP depends on SSH."
See ADR-003 Amendment 1.
alknet-call stays lean — it has no reqwest, no axum, no HTTP
dependencies. The from_openapi/from_mcp forwarding handlers are
opaque Arc<dyn Handler> from the registry's perspective: constructed by
alknet_http::from_openapi() at registration time, stored in
HandlerRegistration, dispatched by the CallAdapter which doesn't
know reqwest is involved.
ALPNs
| ALPN | Handler | Transport | Browser? |
|---|---|---|---|
http/1.1 |
HttpAdapter |
HTTP/1.1 over QUIC stream | No |
h2 |
HttpAdapter |
HTTP/2 over QUIC stream | No |
h3 |
HttpAdapter |
HTTP/3 / WebTransport | Yes (X.509 required) |
These are standard IANA ALPN strings, not alknet/-prefixed. Any HTTP
client connects without knowing about alknet — the TLS handshake
negotiates h2 or http/1.1 normally, and the HttpAdapter serves
HTTP. This is the stealth mapping (ADR-010): clients that don't offer
alknet ALPNs get the HTTP handler, just like port scanners in stealth
mode.
The HttpAdapter registers for all three ALPNs (when the corresponding
features are enabled). The endpoint's HandlerRegistry maps each ALPN to
the same HttpAdapter instance; the handler branches on
connection.remote_alpn() to pick the right framing.
Adapter Location Map
The decomposition principle (settled in
client-and-adapters.md): the adapter
trait lives where the types live (alknet-call); the adapter
implementations live where their transport dependencies live.
alknet-call (lean — no HTTP client, no HTTP server)
├── OperationAdapter trait (the contract — async, ADR-017 §5)
├── from_call (QUIC — discovers remote ops via call protocol)
├── from_jsonschema (pure parse — caller fetches the doc, passes it in)
└── CallClient (outbound connection opener)
alknet-http (owns HTTP server + HTTP client)
├── HttpAdapter (axum server — inbound HTTP on h2/http1.1/h3)
├── from_openapi (parse OpenAPI doc + reqwest forwarding handler)
├── to_openapi (generate OpenAPI doc from local registry)
├── from_mcp (feature-gated) (import remote MCP tools over streamable HTTP — reqwest)
└── to_mcp (feature-gated) (expose local ops as MCP tools over streamable HTTP — axum)
alknet-call never sees the HTTP client. The from_openapi/from_mcp
forwarding handlers are opaque Arc<dyn Handler> from the registry's
perspective. alknet-call stays lean; alknet-http owns both HTTP
directions.
Feature Gates
[features]
default = ["h2", "http1"] # the non-browser HTTP surface
h3 = ["dep:wtransport"] # HTTP/3 + WebTransport (browser path; X.509 required)
mcp = ["dep:rmcp"] # from_mcp / to_mcp (streamable HTTP only — ADR-037)
h2+http1(default): theaxum+hyperHTTP/1.1 + HTTP/2 server. This is the surface non-browser clients use.h3: thewtransport(or quinn HTTP/3 extension) dependency. Adds theh3ALPN handler and the WebTransport streaming path. See webtransport.md and ADR-038.mcp: thermcpdependency with streamable HTTP transport features only. Addsfrom_mcp/to_mcp. See http-mcp.md and ADR-037.
A deployment that only needs the REST surface (no browsers, no MCP) uses
the default features. A browser-facing hub enables h3. A deployment
that wants MCP tool import/export enables mcp.
The No-Env-Vars Invariant
The from_openapi/from_mcp forwarding handlers are the credential
injection point for the no-env-vars architecture. The path (from the
gap analysis):
vault → assembly layer → Capabilities → HandlerRegistration.capabilities
→ OperationContext.capabilities → from_openapi handler reads
context.capabilities.get("openai") → injects into HTTP Authorization
header → reqwest request goes out with vault-derived credential
This makes aisdk's std::env::var("OPENAI_API_KEY") reads unreachable —
the assembly layer never calls Default::default() on a provider; it
constructs them with vault-derived credentials, or routes HTTP calls
through from_openapi operations that carry the credential in
Capabilities.
This is a spec-level invariant: no handler reads outbound
credentials from any source other than OperationContext.capabilities.
The from_openapi/from_mcp implementations in alknet-http are
verified against this invariant. See ADR-014 and
client-and-adapters.md.
Architecture (component pointers)
- http-server.md — the
HttpAdapterforh2/http/1.1: how axum is run over a QUIC bidirectional stream, Bearer auth resolution, the/healthzraw route, stealth decoy, and the HTTP-to-call dispatch (ADR-036). - http-adapters.md —
from_openapi(parse OpenAPI, build forwarding handlers withreqwest) andto_openapi(generate an OpenAPI doc from the registry'sExternaloperations). Error fidelity per ADR-023. - http-mcp.md —
from_mcp/to_mcp(feature-gated), streamable HTTP only (ADR-037), the rmcp integration. - webtransport.md — the
h3ALPN handler, WebTransport session/stream handling, the browser streaming path (ADR-038).
Design Decisions
| Decision | ADR | Summary |
|---|---|---|
| HTTP-to-call operation mapping | ADR-036 | Direct path mapping; to_openapi is projection, not router |
| MCP stdio transport exclusion | ADR-037 | Streamable HTTP only; stdio is not built (RCE vector) |
| HTTP/3 + WebTransport first-class | ADR-038 | h3 in scope, not deferred; browser streaming uses QUIC streams |
| HTTP server + client host colocated | ADR-039 | One crate for server + adapters (shared HTTP deps, shared mapping) |
alknet-call is protocol-foundation |
ADR-003 Am. 1 | alknet-http depends on alknet-call (types, not peer handler) |
Bearer auth via resolve_from_token |
ADR-004 | HTTP handler credential source + resolution (settled) |
| Stealth mode = HTTP handler on standard ALPNs | ADR-010 | Decoy for unknown paths (settled) |
Adapter-registered ops are Internal |
ADR-015 | from_openapi/from_mcp produce Internal leaves (settled) |
OperationAdapter trait is async |
ADR-017 | HTTP adapters implement the async trait (settled) |
to_* adapters are projections |
ADR-017 | to_openapi/to_mcp consume the registry, don't produce entries (settled) |
| Error schema fidelity | ADR-023 | from_openapi maps HTTP status → HTTP_<status> codes; to_openapi projects back (settled) |
| Browsers require X.509 | ADR-027 | h3/WebTransport needs X.509 (settled) |
| Browsers are not alknet peers | ADR-034 | Browser over WebTransport/HTTPS = bearer token, no PeerId (settled) |
Open Questions
See open-questions.md for full details.
- OQ-13 (resolved): Operation path format
/{service}/{op}— the HTTP path. - OQ-26 (resolved):
AdapterErrorvariants — reused by HTTP adapters;#[non_exhaustive]allows extension. - OQ-37 (resolved): Browsers are not peers;
h3hub is a mixed-fingerprintPeerEntry. - OQ-38 (open, scope): WebTransport relay-as-proxy — does the proxy
live in
alknet-httpor a separate relay crate? - OQ-39 (open):
to_openapipublished-spec versioning — versioning strategy for generated OpenAPI specs. - OQ-40 (open): reqwest client config and connection pooling — two-way-door config shape.
References
docs/research/alknet-http/phase-0-findings.md— Phase 0 researchdocs/research/alknet-call-completion/gap-analysis.md— adapter location map, no-env-vars invariant/workspace/@alkdev/operations/src/from_openapi.ts,/workspace/@alkdev/operations/src/from_mcp.ts— TypeScript prior art/workspace/rust-sdk/— MCP Rust SDK (rmcp); streamable HTTP examples/workspace/wtransport/— pure-Rust WebTransport reference