docs(http): draft alknet-http architecture specs and ADRs 036-039
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).
This commit is contained in:
@@ -736,17 +736,108 @@ is a feature extension, not an unmade architecture decision.
|
||||
all (no CA fallback) and fails closed — same model as iroh.
|
||||
|
||||
**Downstream, not blocking, recorded so they don't get lost:**
|
||||
WebTransport relay-as-proxy (browser → proxy → P2P hub) is deferred
|
||||
with the rest of h3/WebTransport (alknet-http DH-2); ADR-030 §6's
|
||||
fingerprint normalization already keeps the proxied path clean. On-
|
||||
chain / smart-contract peer discovery (relays syncing git repos via
|
||||
iroh gossip) is a *source* of `PeerEntry` records, fits the OQ-36
|
||||
repo/adapter pattern (`alknet-peer-store-onchain` implementing
|
||||
`IdentityProvider`), and does not change the auth model.
|
||||
WebTransport relay-as-proxy (browser → proxy → P2P hub) is the
|
||||
remaining scope question tracked as OQ-38 (h3/WebTransport itself is
|
||||
now in scope, ADR-038); ADR-030 §6's fingerprint normalization already
|
||||
keeps the proxied path clean. On-chain / smart-contract peer
|
||||
discovery (relays syncing git repos via iroh gossip) is a *source* of
|
||||
`PeerEntry` records, fits the OQ-36 repo/adapter pattern
|
||||
(`alknet-peer-store-onchain` implementing `IdentityProvider`), and
|
||||
does not change the auth model.
|
||||
|
||||
Not blocking the ADR-029 migration — the Ed25519 path is the primary
|
||||
use case and was already resolved; this ADR closes the X.509
|
||||
outgoing-only remainder.
|
||||
- **Cross-references**: ADR-027, ADR-029, ADR-030, ADR-033, ADR-034,
|
||||
OQ-29, OQ-36, [client-and-adapters.md](crates/call/client-and-adapters.md),
|
||||
[endpoint.md](crates/core/endpoint.md), [auth.md](crates/core/auth.md)
|
||||
[endpoint.md](crates/core/endpoint.md), [auth.md](crates/core/auth.md)
|
||||
|
||||
## Theme: alknet-http
|
||||
|
||||
### OQ-38: WebTransport Relay-as-Proxy Scope
|
||||
|
||||
- **Origin**: [ADR-034](decisions/034-outgoing-only-x509-and-three-peer-roles.md)
|
||||
§5, [webtransport.md](crates/http/webtransport.md)
|
||||
- **Status**: open (scope, not deferral)
|
||||
- **Door type**: One-way (crate boundary), two-way (mechanism)
|
||||
- **Priority**: low
|
||||
- **Resolution**: A WebTransport proxy that terminates the browser's
|
||||
WebTransport connection and forwards encrypted traffic to a P2P hub's
|
||||
Ed25519 endpoint (so the hub need not expose its own public X.509
|
||||
cert) is a real feature for the browser-to-P2P-peer case. ADR-034 §5
|
||||
recorded it in the h3/WebTransport bucket; ADR-038 brought h3/
|
||||
WebTransport into scope, so this OQ is now the remaining scope
|
||||
question: does the proxy live in `alknet-http` (as a mode of the `h3`
|
||||
handler) or in a separate relay crate?
|
||||
|
||||
This is a genuine scope question, not a deferral. The proxy use case
|
||||
is not yet concrete enough to decide the crate boundary — no
|
||||
deployment has asked for it yet, and the design (transport-only
|
||||
proxy, no auth-model change per ADR-034 §5) is clear but the home is
|
||||
not. The decision is made when the browser-to-P2P-peer proxy use
|
||||
case becomes concrete; until then it is tracked here, not deferred
|
||||
with "v1/later" language. The proxy does not change the auth model
|
||||
(bearer token + `PeerEntry.auth_token_hash`; proxy is transport-
|
||||
only), so it does not block any other ADR.
|
||||
- **Cross-references**: ADR-027, ADR-030, ADR-034, ADR-038,
|
||||
[webtransport.md](crates/http/webtransport.md)
|
||||
|
||||
### OQ-39: `to_openapi` Published-Spec Versioning
|
||||
|
||||
- **Origin**: [ADR-017](decisions/017-call-protocol-client-and-adapter-contract.md)
|
||||
Consequences, [http-adapters.md](crates/http/http-adapters.md)
|
||||
- **Status**: open
|
||||
- **Door type**: One-way (after first publication), two-way (before)
|
||||
- **Priority**: medium
|
||||
- **Resolution**: ADR-017 Consequences notes that published `to_*`
|
||||
specs are compatibility contracts: once a generated OpenAPI spec is
|
||||
published and external clients build against it, the mapping
|
||||
semantics (e.g., subscriptions → SSE long-poll, error codes → HTTP
|
||||
statuses) become a de facto contract. Changing the mapping later
|
||||
breaks every client. `to_openapi` mapping choices are two-way *before*
|
||||
first publication but one-way *after*.
|
||||
|
||||
The versioning strategy for generated OpenAPI specs needs
|
||||
specifying: version the generated spec (e.g., an OpenAPI `info.version`
|
||||
tied to the registry's `External` operation set version) and emit a
|
||||
spec version marker so consumers can detect mapping changes. The
|
||||
exact versioning scheme (semver tied to operation additions/changes,
|
||||
a content-hash, a monotonically-increasing counter) is a two-way-door
|
||||
implementation detail before first publication; the one-way constraint
|
||||
is that the version marker is emitted and consumers can detect
|
||||
breaking changes. This is the "published artifact is a contract"
|
||||
blind spot in ADR-009's framework (it classifies doors by reversal
|
||||
cost in the codebase, not by compatibility cost for external
|
||||
consumers).
|
||||
- **Cross-references**: ADR-009, ADR-017, ADR-023, ADR-036,
|
||||
[http-adapters.md](crates/http/http-adapters.md)
|
||||
|
||||
### OQ-40: reqwest Client Config and Connection Pooling
|
||||
|
||||
- **Origin**: [http-adapters.md](crates/http/http-adapters.md),
|
||||
[http-mcp.md](crates/http/http-mcp.md), the alknet-http Phase 0
|
||||
findings DH-7
|
||||
- **Status**: open
|
||||
- **Door type**: Two-way
|
||||
- **Priority**: low
|
||||
- **Resolution**: `alknet-http` maintains a shared `reqwest::Client`
|
||||
(constructed once, reused across all `from_openapi`/`from_mcp`
|
||||
forwarding handlers) with connection pooling, keep-alive, and TLS.
|
||||
The aisdk `core/client.rs` reference shows the pattern worth
|
||||
referencing: `OnceLock<reqwest::Client>`, retry logic (exponential
|
||||
backoff, `Retry-After` header), and separate streaming vs
|
||||
non-streaming clients.
|
||||
|
||||
The exact pooling/retry config (pool size, retry policy, timeout
|
||||
defaults, hot-reloadability via `DynamicConfig`) is a two-way-door
|
||||
implementation detail. The one-way constraints are: (1)
|
||||
`alknet-http` owns its `reqwest::Client` (no env-var-based client
|
||||
config, no shared global client), (2) credential injection happens
|
||||
per-request (from `OperationContext.capabilities`), not at client
|
||||
construction (the client is shared across all operations, the
|
||||
credentials are per-call), and (3) TLS for outbound calls uses the
|
||||
system trust store by default (custom CA bundle + client certs are
|
||||
an optional config for self-hosted API gateways). This OQ tracks the
|
||||
two-way-door config shape; the constraints are settled.
|
||||
- **Cross-references**: ADR-014, ADR-017,
|
||||
[http-adapters.md](crates/http/http-adapters.md)
|
||||
Reference in New Issue
Block a user