Skip to content

API layers

quicz exposes four layers. Most applications only touch the top two; the lower layers exist for custom protocols, benchmarks, and embedding quicz in a bespoke event loop. This mirrors how the reference QUIC stacks split their APIs (comparison table at the end). Mirrors the repo’s api-layers.md.

runtime.Server.serveH3 + runtime.Client + runtime.h3_server / runtime.h3_client. A full HTTP/3 + QPACK service with no transport code.

var server = try Server.init(allocator, io, .{
.port = 4433, .alpn = &.{"h3"},
.cert_der = &certificate_der, .private_key = &server_private_key,
});
defer server.deinit();
try server.serveH3(.{}, handler); // handler: fn (DecodedRequest) Response

Handlers return Response (bodyless / buffered body / streamed ResponseBody), receive request bodies, and the runtime handles QPACK dynamic tables, control streams, and flow control. See the quick start.

Layer 2 — runtime transport (custom stream protocols)

Section titled “Layer 2 — runtime transport (custom stream protocols)”

runtime.Server.serve + runtime.Client for non-HTTP protocols over QUIC streams (std.http per-connection handler model).

fn echoHandler(conn: runtime_server.ServerConnection) std.Io.Cancelable!void {
var c = conn;
while (true) {
var stream = c.acceptStream() catch return;
var buf: [65536]u8 = undefined;
while (true) {
const n = stream.receive(&buf) catch return;
if (n == 0) break;
stream.send(buf[0..n], false) catch return;
}
}
}
try server.serve(&echoHandler);

Client side: connect()send/receive on streams, openUniStream / sendOnStream for server pushes, openStream for bidirectional.

Layer 3 — TLS endpoints (multi-connection handshake)

Section titled “Layer 3 — TLS endpoints (multi-connection handshake)”

Tls13ServerEndpoint / Tls13ClientEndpoint + Tls13ServerTransport / Tls13ClientTransport. The TLS 1.3 handshake bound to a multi-connection endpoint, without the runtime’s socket loop. runtime.Server is built on Tls13ServerEndpoint; you can drive it directly for a custom event loop that still wants the full TLS handshake + transport.

Layer 4 — sans-IO transport (bare metal)

Section titled “Layer 4 — sans-IO transport (bare metal)”

Connection + endpoint_types + EndpointConnectionLifecycle / EndpointConnectionRegistry. Pure QUIC state machines with no TLS and no socket I/O: you feed datagrams and poll output. This is the quiche / zttp style — the base for interop clients, benchmarks, and XDP/GSO experiments.

const action = lifecycle.feedDatagram(&scratch, path, data, &[_]u8{}, &.{ .v1 }) catch ...;
switch (action) {
.accept_initial => |ia| { /* create record, acceptInitialRecord, poll responses */ },
.routed => { /* receiveDatagramStepWithRoutePath, deliver stream data */ },
}

Most examples/*_loopback.zig and the interop tools drive this layer.

Use case Layer
HTTP/3 API server / client 1
Custom stream protocol (echo, chat, file transfer) 2
Custom event loop with full TLS handshake 3
Benchmarks, interop, sans-IO embedding, protocol research 4
Layer s2n-quic quic-go quinn quiche msquic quic-zig quicz
Turnkey H3 Server + tls provider http3.Server/Transport tokio-quiche event_loop + Handler runtime.serveH3
Transport + custom stream Connection + streams quic.Transport/Conn Endpoint + Connection Connection Registration/Listener/Connection Connection + manager runtime.serve/Client
Sans-IO / bare metal s2n-quic-core/dc quinn-proto Config + Connection top-level objects Connection self-driven Connection + endpoint_types + EndpointConnectionLifecycle

quicz’s layering matches the mainstream: a turnkey HTTP/3 layer on top of a runtime transport layer, with a sans-IO core underneath. The main difference from quic-go / quinn / s2n-quic is that quicz’s runtime is a single-threaded event loop (like quiche and quic-zig) rather than a per-connection task spawner; see production tuning “Runtime Deployment”.