Files
alknet/docs/architecture/crates/http/README.md
glm-5.2 0a78306686 docs(http): add ADR-043 WebTransport bidirectional ALPN substrate; fix spec drift from mid-spec pivot
A consistency review of the alknet-http specs found two classes of
issues: internal contradictions from the mid-spec pivot (the to_openapi
gateway pattern landed in prose but not in cross-references), and a
systematic client→server assumption that only holds for the OpenAPI/MCP
case leaking into the WebTransport architecture.

Class 1 (internal contradictions):
- C1: to_openapi was half-refactored — body described the ADR-042
  gateway pattern but the decisions table and ADR-036 still said
  'paths mirror /{service}/{op}'. ADR-036's to_openapi clause is now
  amended as superseded by ADR-042; the stale decisions row and README
  Principle 2 are fixed.
- C2: the axum Router route list didn't include the 5 gateway endpoints
  (/search, /schema, /call, /batch, /subscribe). Added them; clarified
  /openapi.json as the gateway description doc; added gateway paths to
  the decoy exclusion list.
- C3: ADR-034 §5 still talked about the 'h3/WebTransport deferral
  bucket' that ADR-038 eliminated. Amended §5/Consequences/References
  to drop the deferral framing (the auth-model decision stands; only
  the 'when' wording was stale).

Class 2 (one-way direction assumption):
- C4/C5/C6: the WebTransport specs framed the session as browser→hub
  one-way, when the call protocol is bidirectional and WebTransport is
  a general ALPN transport substrate. New ADR-043 reframes WebTransport
  as a bidirectional ALPN transport substrate (call protocol is the
  first/canonical target; needs no WASM parser), names the call
  protocol's bidirectionality over WebTransport sessions, and states
  the inbound no-PeerId connection-local overlay as the mirror of
  ADR-034 §2. webtransport.md is updated to reflect this framing;
  ADR-040 is repositioned (not superseded) as the substrate's non-call-
  ALPN mechanism.
- C7: the HTTP/1.1+HTTP/2 surface's one-directionality is now named as
  a lossy consequence of HTTP request/response; WebTransport is named
  as the surface that restores the bidirectional call model.
- C8: overview.md acknowledges the from/to direction model is
  OpenAPI/MCP-specific, not a call-protocol property.

A review subagent pass on ADR-043 + webtransport.md found no critical
issues; warnings W1-W3 (residual browser-as-subject framing, ADR-009
rationale in spec, opening abstract tone) and suggestions S2/S4/S5
were addressed.
2026-06-29 10:43:18 +00:00

131 lines
10 KiB
Markdown

---
status: draft
last_updated: 2026-06-29
---
# alknet-http
HTTP interface for alknet: serves HTTP/1.1, HTTP/2, and HTTP/3 (WebTransport)
on standard ALPNs, and hosts the HTTP-backed call-protocol adapters
(`from_openapi`, `to_openapi`, `from_mcp`, `to_mcp`).
## Documents
| Document | Status | Description |
|----------|--------|-------------|
| [overview.md](overview.md) | draft | Crate purpose, two roles (server + client host), dependencies, adapter location map |
| [http-server.md](http-server.md) | draft | `HttpAdapter` (`ProtocolHandler` for `h2`/`http/1.1`), axum over QUIC, Bearer auth, stealth, `/healthz` |
| [http-adapters.md](http-adapters.md) | draft | `from_openapi` (reqwest client) and `to_openapi` (OpenAPI projection); no-env-vars invariant point |
| [http-mcp.md](http-mcp.md) | draft | `from_mcp` / `to_mcp` (feature-gated), streamable-HTTP-only, stdio exclusion |
| [webtransport.md](webtransport.md) | draft | `h3`/WebTransport handler — the browser streaming path |
## Applicable ADRs
| ADR | Title | Relevance |
|-----|-------|-----------|
| [001](../../decisions/001-alpn-protocol-dispatch.md) | ALPN-Based Protocol Dispatch | `HttpAdapter` registers on standard HTTP ALPNs |
| [002](../../decisions/002-protocol-handler-trait.md) | ProtocolHandler Trait | `HttpAdapter` implements `ProtocolHandler` |
| [003](../../decisions/003-crate-decomposition.md) | Crate Decomposition | `alknet-http` depends on `alknet-core` + `alknet-call` (protocol-foundation exception, Amendment 1) |
| [004](../../decisions/004-auth-as-shared-core.md) | Auth as Shared Core | Bearer → `resolve_from_token` |
| [007](../../decisions/007-bistream-type-definition.md) | BiStream Type Definition | `HttpAdapter` receives `Connection`, accepts a stream for hyper |
| [010](../../decisions/010-alpn-router-and-endpoint.md) | ALPN Router and Endpoint | Stealth mode = HTTP handler on standard ALPNs |
| [014](../../decisions/014-secret-material-flow-and-capability-injection.md) | Secret Material Flow | `from_openapi`/`from_mcp` are the credential injection point |
| [015](../../decisions/015-privilege-model-and-authority-context.md) | Privilege Model | Adapter-registered ops are `Internal` by default |
| [017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Call Protocol Client and Adapter Contract | `OperationAdapter` trait; `to_*` are projections; published-spec contract |
| [022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | Handler Registration, Provenance, Composition Authority | `from_openapi`/`from_mcp` produce leaf bundles |
| [023](../../decisions/023-operation-error-schemas.md) | Operation Error Schemas | `from_openapi`/`to_openapi` error fidelity; `HTTP_<status>` error codes |
| [027](../../decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | TLS Identity Redesign | Browsers require X.509; WebTransport requires X.509 |
| [034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Outgoing-Only X.509 and Three Peer Roles | Browsers are not alknet peers; WebTransport relay-as-proxy recorded |
| [036](../../decisions/036-http-to-call-operation-mapping.md) | HTTP-to-Call Operation Mapping | Direct path mapping; `to_openapi` is projection, not router |
| [037](../../decisions/037-mcp-stdio-transport-exclusion.md) | MCP Stdio Transport Exclusion | Streamable HTTP only; stdio not built |
| [038](../../decisions/038-http3-and-webtransport-as-first-class.md) | HTTP/3 and WebTransport as First-Class HTTP Transports | `h3` in scope, not deferred |
| [039](../../decisions/039-http-server-and-client-host-colocated.md) | HTTP Server and Client Host Colocated in alknet-http | One crate for server + client host (shared HTTP deps, shared mapping) |
| [040](../../decisions/040-webtransport-alpn-stream-proxy.md) | WebTransport ALPN-Stream-Proxy | The substrate's mechanism for non-call ALPNs (SSH, git, SFTP) — browser → WebTransport stream → target ALPN handler via WASM parser |
| [041](../../decisions/041-mcp-tool-gateway-pattern.md) | MCP Tool-Gateway Pattern for to_mcp | 4 fixed gateway tools (search/schema/call/batch), not one tool per operation; Subscription excluded |
| [042](../../decisions/042-openapi-gateway-pattern.md) | OpenAPI Gateway Pattern for to_openapi | 5 fixed gateway endpoints (search/schema/call/batch/subscribe), not one path per operation; per-caller AccessControl-filtered |
| [043](../../decisions/043-webtransport-bidirectional-alpn-substrate.md) | WebTransport as a Bidirectional ALPN Transport Substrate | WebTransport carries ALPNs as bidirectional streams; call protocol is the first/canonical target (needs no WASM parser); both sides can initiate calls; no-`PeerId` non-peer clients use a connection-local overlay |
## Relevant Open Questions
| OQ | Title | Status | Relevance |
|----|-------|--------|-----------|
| OQ-11 | Handler-level auth resolution observability | resolved | HTTP handler stores resolved identity on `Connection` via `set_identity` |
| OQ-12 | TLS identity provisioning | resolved | Browsers require X.509 (gates the entire `h3` feature) |
| OQ-13 | Operation path format | resolved | `/{service}/{op}` is the HTTP path (ADR-036) |
| OQ-17 | Call protocol client and adapter contract | resolved | `OperationAdapter` trait; `to_*` projections |
| OQ-24 | Operation error schemas | resolved | `from_openapi`/`to_openapi` error fidelity |
| OQ-26 | OperationAdapter error type | resolved | `AdapterError` variants reused by HTTP adapters |
| OQ-37 | X.509 outgoing-only / three peer roles | resolved | Browsers are not peers; hub with mixed fingerprints |
| OQ-38 | WebTransport standalone relay service scope | open (scope, not deferral) | The standalone relay (future `alknet-relay`, fork of iroh-relay) — distinct from the in-process ALPN-stream-proxy (ADR-040) |
| OQ-39 | `to_openapi` published-spec versioning | open | Versioning strategy for generated OpenAPI specs |
| OQ-40 | reqwest client config and connection pooling | open | Two-way-door: pooling/retry config shape |
## Key Design Principles
1. **HTTP is both a server surface and a client transport for adapters.**
Inbound HTTP (`h2`/`http/1.1`/`h3`) is served by `axum` over a QUIC
stream; outbound HTTP (`from_openapi`/`from_mcp` forwarding) uses
`reqwest`. Both directions share the same HTTP dependencies, which is
why they live in one crate rather than being split. See
[overview.md](overview.md).
2. **The HTTP surface is a projection of the call protocol.** An HTTP
request at `POST /fs/readFile` becomes a `call.requested` for
`/fs/readFile`. The HTTP path IS the operation path on the
**direct-call surface**. `to_openapi` *describes* a different surface
— the 5-endpoint gateway (`/search`, `/schema`, `/call`, `/batch`,
`/subscribe`) that gates discovery and invocation behind a fixed
entry set. See [ADR-036](../../decisions/036-http-to-call-operation-mapping.md)
(direct-call surface) and [ADR-042](../../decisions/042-openapi-gateway-pattern.md)
(`to_openapi` gateway, superseding ADR-036's original `to_openapi`
clause).
3. **Standard ALPNs, not alknet ALPNs.** `h2`, `http/1.1`, `h3` are
IANA-registered ALPN strings. Any HTTP client (browser, curl, axios)
connects without knowing about alknet — the TLS handshake negotiates
`h2` or `http/1.1` normally. This is the stealth mapping (ADR-010).
4. **`from_openapi`/`from_mcp` are the no-env-vars injection point.** The
forwarding handlers read `context.capabilities`, not `std::env::var`.
This is the architectural mechanism that makes aisdk's env-var reads
unreachable. See ADR-014,
[client-and-adapters.md](../call/client-and-adapters.md).
5. **MCP streamable HTTP only; stdio is not built.** stdio = spawn
arbitrary executable = RCE. Streamable HTTP is network-isolated,
auth-gatable, and runs under alknet's auth model. See
[ADR-037](../../decisions/037-mcp-stdio-transport-exclusion.md).
6. **HTTP/3 + WebTransport is a first-class transport, not a deferral.**
The browser streaming path uses QUIC streams directly. See
[ADR-038](../../decisions/038-http3-and-webtransport-as-first-class.md).
7. **The `h3` handler is an ALPN-stream-proxy for browsers.** A browser
with a WASM parser can reach any non-call ALPN handler (SSH, git,
SFTP) via WebTransport — no install, no native client, no VPN. The
call protocol needs no proxy (it speaks EventEnvelope directly);
the ALPN-stream-proxy is the substrate's mechanism for the protocols
that need a client-side parser. SSH-over-WebTransport is
HTTPS-shaped at the network layer (anti-censorship). See
[ADR-040](../../decisions/040-webtransport-alpn-stream-proxy.md)
and [ADR-043](../../decisions/043-webtransport-bidirectional-alpn-substrate.md).
8. **`h3` requires X.509.** Browsers don't support RFC 7250 raw keys
(ADR-027). A node serving WebTransport must have an X.509 identity.
This is a browser limitation, not an alknet decision.
9. **WebTransport is a bidirectional ALPN transport substrate.**
WebTransport carries ALPN protocols as bidirectional streams; the
call protocol is the first/canonical target (JSON-RPC over QUIC
streams, needs no WASM parser, runs in Deno/Node/browsers/native
Rust). Both sides of a WebTransport call-protocol session can
initiate calls — the call protocol's bidirectionality applies
unchanged. The HTTP/1.1 + HTTP/2 surface is the one-directional
projection (HTTP is request/response); WebTransport restores the
bidirectional call model. See
[ADR-043](../../decisions/043-webtransport-bidirectional-alpn-substrate.md).
## References
- `docs/research/alknet-http/phase-0-findings.md` — Phase 0 research
(directionally close; DH-2's deferral framing is corrected by ADR-038)
- `docs/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 v1.8.0); streamable HTTP
transport examples
- `/workspace/wtransport/` — pure-Rust WebTransport reference
implementation (the `h3` feature's candidate dependency)