Agents
June 4, 2026 ยท View on GitHub
Instructions for AI coding agents working on this project.
Project Overview
Livery is a BEAM-native web framework that serves one handler set
over HTTP/1.1, HTTP/2, and HTTP/3 from a single service runtime
(the spirit of Axum + Tower + Hyper on the BEAM). The wire layer is
delegated to sibling hex libraries; Livery owns the developer
surface. One OTP application; core runtime stays at the src/ top
level, with subsystems grouped into domain subdirectories (rebar3
compiles src/ recursively):
src/ Core runtime, flat: livery, livery_req, livery_resp,
livery_router, livery_service, livery_h1/h2/h3,
livery_ws*, livery_wt, livery_mcp, livery_openapi,
livery_static, livery_metrics, ... (+ livery.app.src)
src/client/ Outbound HTTP client: livery_client + its layers,
stores, adapter, and discovery
src/middleware/ livery_middleware framework + cross-cutting middlewares
(request_id, access_log, cors, etag, timeout, ratelimit
(+ store), concurrency, security_headers, alt_svc,
instrument_*, openapi_validate, body_limit)
src/auth/ Auth middlewares + helpers (bearer, introspect, session,
jwks, oidc, livery_auth)
src/codec/ Content-coding subsystem (livery_compress + codecs)
include/ Shared records (livery.hrl)
test/ EUnit + Common Test (incl. the cross-adapter parity SUITE
and livery_e2e_SUITE, a full journey over H1/H2/H3)
bench/ Load/latency harness (separate `bench` rebar profile)
examples/ Runnable example apps (`examples` rebar profile)
docs/ Diataxis docs (tutorials/guides/concepts) + design notes
Wire dependencies (h1, h2, quic, ws, webtransport,
instrument, barrel_mcp) are git deps in rebar.config. Local
development overrides them with _checkouts/ symlinks to the
sibling projects; _checkouts/ is gitignored. Never reimplement
HPACK/QPACK/frame/QUIC logic here: that lives in the wire libs;
keep the adapters thin and push changes upstream.
Authoritative behaviour is the test suites under test/ (the
parity SUITE diffs externally observable behaviour across all four
adapters) and the module docs.
Required Checks
Every change must be formatted and pass all checks before committing:
rebar3 fmt # Auto-format (always run first)
rebar3 compile # Must compile cleanly (warnings_as_errors)
rebar3 lint # Elvis linter
rebar3 xref # Cross-reference analysis
rebar3 dialyzer # Type checking
rebar3 eunit # Unit + property tests
rebar3 ct # Common Test (adapters, parity, service, ws, wt, mcp)
CI (.github/workflows/ci.yml) runs format, lint, xref, and
dialyzer as fast-fail static checks, then gates build/eunit/
ct on them.
Build & Development Commands
rebar3 compile # Build
rebar3 shell # Boot a dev shell
rebar3 ct --suite=test/livery_parity_SUITE # One suite
rebar3 fmt # Auto-format (erlfmt)
rebar3 fmt --check # Format check, no writes
rebar3 lint # Elvis linter
rebar3 as bench shell # Benchmark harness (livery_bench)
Architecture
Service runtime
livery:start_service/1 brings H3 (UDP), H2 (TLS), and H1 (TCP) up
together under one livery_service gen_server, sharing one router
and middleware stack, and advertising Alt-Svc for H3 on H1/H2
responses. livery:start_listener/2 serves a single protocol.
livery_drain gives graceful shutdown (stop accepting, wait for
in-flight, then stop).
Per-request worker model
Each adapter, on a new request, spawns a livery_req_proc child of
the single global livery_req_sup (simple_one_for_one) and returns
immediately. A per-stream "translator" process forwards wire events
({h1_stream,_} / {h2,_} / {quic_h3,_}) to the worker as
{livery_body, Ref, _} messages; the translator monitors the
worker and exits on its DOWN. Middleware and the handler run in
the worker, which may block/receive (the cowboy_loop analogue).
Middleware (Tower/Axum style)
call(Req, Next, State) -> Resp: a continuation over immutable
values, not the legacy (req, res, next) mutate-and-next model.
Routes may carry a per-route stack under the route Meta's
middleware key.
Dispatch and emit
The handler returns a #livery_resp{}; livery:emit/3 walks the
body variant (full / chunked / sse / file / empty /
upgrade) and drives Adapter:send_headers/send_data/send_trailers.
The taken_over sentinel tells emit/3 to stop because the stream
was handed off (WebSocket, WebTransport, MCP).
Adapter behaviour
livery_adapter defines start/3, stop/1, send_headers/4,
send_data/3, send_trailers/2, reset/2, peer_info/1,
capabilities/1. Implemented by livery_h1, livery_h2,
livery_h3, and livery_test_adapter (in-memory, used by the
parity SUITE).
Conventions
- Run
rebar3 fmtbefore committing; elvis must pass. New per-module elvis ignores belong inrebar.configwith a one-line reason. - Commit messages: one imperative subject line, body only for non-obvious "why". No phase references, no diff restatement, no "generated by" / "co-authored-by" trailers.
- Do not use the em-dash character in code, docs, or messages.
- Keep adapters thin; cross-protocol behaviour is locked by the parity SUITE from Phase 2 forward.
- TLS
verify_noneis test-only; never ship it insrc/. - File responses (
livery_resp:file/2,3) serve the exact path given; confine any handler-built path before serving.