# Plan: alkhttp Implementation Working plan guiding task decomposition and implementation. Less specific than the tasks it produces, more specific than the architecture docs. Status fields in task frontmatter are the source of truth for progress; this document explains the *why* of the ordering. Source crate: `/workspace/@alkdev/alknet/crates/alknet-http` (~11k LOC). Target: this crate, on alkcall 0.1.1 (crates.io). ## What the spike established A validation pass against alkcall's actual source resolved the factual unknowns that would have shaped tasks incorrectly: 1. **The WS adapter must parse outgoing chunk boundaries.** The mux emits one mpsc payload per chunk, but a logical write above the mux (channel 0's `write_frame` issues length-prefix and body as two `write_all`s) can surface as multiple chunks. "One write = one chunk" and "one chunk = one WS message" are both unusable as invariants. The adapter treats the WS message stream as a byte stream in both directions and parses the 8-byte header on both read and write sides. 2. **Chunk size cap.** `MAX_CHUNK_LEN` is 16 MiB; browser WS stacks and intermediaries commonly cap messages far lower. The WS path needs its own practical cap (default ~1 MiB) with oversized chunks split across messages — legal, since the receiver's boundary is the chunk header. 3. **The `install_channel_zero` hook is the exact seam** for the server WS path: alkcall's `ChannelsAdapter` runs the in-line demux loop, and the hook receives channel 0's `Connection` + `AuthContext` — alkhttp's job is to construct the `CallConnection`, attach the bearer-resolved identity, and run `Dispatcher::run_loop_single_stream`. `alkcall`'s own tests (`channels/client.rs`) demonstrate this wiring end-to-end over duplex pairs. 4. **EOF/teardown semantics are already specified** by alkcall (REQ-CH-01/02): `AsyncWrite::shutdown` → zero-length sentinel; demux EOF → all channels cleared. The adapter maps WS close to transport EOF and lets alkcall's invariants do the rest. 5. **`Dispatcher::run_loop_single_stream` exists** and is the channel-0 dispatch loop — no dispatch code is needed anywhere in alkhttp. 6. **The alknet-tty crates provide the precedents for the adapter.** The channels protocol was abstracted from TTY's earlier 5-byte demux work, and `alknet-tty`'s adapter embodies the two patterns OQ-01 needed: - **The drainer pattern** (`pump_session`): all producer pumps feed one bounded mpsc; a single drainer writes chunks in arrival order. The transport write side is therefore sequential — a boundary-parsing state machine in the WS adapter's write path is sound (no interleaving to handle). - **Bounded backpressure** (`TestStdinSink::poll_write`): `try_send` → `Full` → `Poll::Pending` — the inbound buffer pattern for the WS→bytes direction. This de-risks the two "high" tasks (WS adapter, WS session) from "unknown design" to "known shape, careful implementation." A targeted POC (research task) still validates the full loop — axum WS ↔ adapter ↔ alkcall ChannelsAdapter + channel-0 dispatcher over duplex — before the production implementation builds on it, since axum's WS API specifics (message sizing, backpressure interplay) are the one part neither the specs nor the TTY precedent exercise. ## Build order (dependency spine) ``` Phase 1 — server foundation (no WS, no adapters) core types → server adapter + auth → gateway dispatch + routes → verified over tokio DuplexStream end-to-end Phase 2 — WS + channels (the novel part) ws byte-stream adapter → ws upgrade handler + channels session → verified browser-session-style over duplex WS Phase 3 — adapters (mostly ports) http client host → from_openapi/from_jsonschema → to_openapi → from_wss (depends on ws adapter) → mcp feature (from_mcp/to_mcp) → /publish endpoint + to_openapi v2 of the gateway doc Phase 4 — hardening integration test suite (full surface over duplex) → docs sync → publish prep (dry-run, semver check) ``` Rationale for key orderings: - **Server core before WS** because the WS upgrade is a route on the `HttpAdapter` — the router, auth middleware, and decoy must exist first, and they're verifiable without WS (gateway over duplex). - **WS adapter before `from_wss`** — same adapter, both directions; building the consumer first would mean validating the adapter without its hardest user (the axum WS type). - **`/publish` in Phase 3** — the gateway spine (`/call`/`/subscribe`) lands in Phase 1; `/publish` adds a dispatch mode to a working gateway and gates OQ-02's version bump. Building it early would couple an unresolved OQ to the critical path. - **MCP last** — feature-gated, rmcp-heavy, and the gateway dispatch spine it consumes is stable by then. ## OQ dispositions | OQ | Disposition | Where it resolves | |----|-------------|-------------------| | OQ-01 (WS adapter semantics) | Partially resolved by the spike: byte-stream treatment both directions, boundary = chunk header, split oversized chunks, shutdown → EOF sentinel + Close frame. Remaining: exact buffer bounds, flush semantics — locked during the WS adapter task. | `tasks/websocket/` | | OQ-02 (/publish framing) | Resolve in the `/publish` task: first line carries `{operation, chunk}`; terminal error = plain HTTP status + JSON body (not an NDJSON line). Then bump the gateway doc version. | `tasks/gateway/` | | OQ-03 (from_wss reconnect) | v1: connection drop → retryable failures; policy deferred. Documented in ADR-070; no task. | — | | OQ-04 (browser client) | Out of scope for this crate. | — | ## Conventions for the tasks - Topic subdirectories: `tasks/server/`, `tasks/websocket/`, `tasks/gateway/`, `tasks/adapters/`, `tasks/client/`, plus `tasks/infra/` for repo-level concerns (CI, publish prep). - Every task carries the full frontmatter set (`scope`, `risk`, `impact`, `level`) — taskgraph's analysis commands rely on them. - Ported-source references: each task cites the alknet-http source file(s) it ports from, so the implementing agent can diff against the original rather than re-derive. - Verification per task: `cargo test -p ` at minimum; the Phase 4 integration task runs the full `cargo test --all-features`. - No task crosses a subsystem boundary except through its declared `depends_on`. ## What the tasks will NOT cover - The alknet-side wiring (endpoint, TLS, ALPN router registration) — alkhttp exposes `HttpAdapter` as a `ProtocolHandler`; the dial/accept composition is the consumer's job (AGENTS.md convention 9). - A browser/JS client for channels-over-WS (OQ-04, deferred). - WebTransport (ADR-069: out of scope entirely).