Documentation / build
Session serving and live events
Expose session capabilities, durable reads, control routes, and a cancellable SSE stream through the released harness serving package.
This stage pins harness module v0.24.2 keeps HTTP composition at a narrow boundary. serve.Handler receives a Rig[S,O] for live creation/restoration and a Reader for durable projections. It does not import a session implementation, an LLM client, or a storage backend. serve.ReadHandler builds only the read plane when the process has no live rig.
Narrow serving seams
LiveSession supplies SessionID, Submit, SubscribeEvents, RespondGate, and Interrupt. Reader supplies ListSessions, ReadStatus, and ReadJournal. The rig owns NewSession and RestoreSession. This split matters operationally: durable status and history can be read on any pod, while submit, interrupt, gate response, and live SSE require a live session in that process.
The event stream carries two classes. Enduring events have a journal sequence and are replayable; ephemeral events describe live progress and are not part of the durable history. A heartbeat is an SSE comment. The server subscribes to all loops, closes the subscription on return, and stops on client cancellation or a broken writer.
Routes and planes
The discovery route is GET /v1/capabilities. Lifecycle and control routes are POST /v1/sessions, POST /v1/sessions/{sid}/restore, POST /v1/sessions/{sid}/input, POST /v1/sessions/{sid}/interrupt, and POST /v1/sessions/{sid}/gates/{gid}. The live route is GET /v1/sessions/{sid}/events. Read routes are GET /v1/sessions, GET /v1/sessions/{sid}/status, and GET /v1/sessions/{sid}/journal.
The list, status, and journal routes read durable projections. They do not consult the live registry and therefore work for a session that is not resident on this process. Journal pages contain Enduring events only; GatePrepared is not exposed as history. Control routes return small JSON envelopes, including a command ID for input and an interrupted boolean for interrupt.
Limits and errors
Request bodies are capped at 1 MiB by default; WithMaxBodyBytes can tighten the positive limit but cannot disable it. List and journal paging defaults to 100 and rejects values above 1,000. The SSE stream sends a : ping comment every 20 seconds by default. Server uses bounded header/read/idle timeouts, leaves write timeout open for the long-lived stream, and rejects public non-loopback binding without WithAuth or an explicit WithInsecurePublicBind opt-in.
Malformed IDs and paging are 400 responses. Missing sessions are 404. Invalid JSON, empty input, unknown blocks, and oversized bodies are 400. Backend and subscription failures are generic 500 responses; causes remain in server logs rather than leaking through the wire. serve.Server returns typed InvalidAddrError and PublicBindWithoutAuthError before it returns an http.Server.
Runnable proof
The reviewed serving fixture prints the capabilities document, creates one session, and exercises a protected control route. Its source and route tests are pinned to the harness release commit.