TECHNOLOGY LAB · SYSTEMS ENGINEERING

Baz / Design

How Baz fits together

The idea

The good parts of Zap.
A fresh foundation.

Zap made building Zig web applications approachable. Baz carries forward its App and endpoint ergonomics, with a new API and a separate, bounded HTTP engine underneath.

The framework and its HTTP engine are written in Zig. Baz replaces Zap’s facil.io C foundation with bounded/http.

Native Windows support, too. Baz runs on Linux, macOS, and Windows x64—an addition to Zap’s platform support. Read the native evidence →

Baz is a new framework, with its own package and API. The migration guide maps familiar Zap concepts to their Baz equivalents.

Built on bounded/http

Application ergonomics above.
HTTP ownership underneath.

Its foundation, bounded/http, takes inspiration from TigerStyle: set explicit limits, reserve resources up front, and check ownership invariants. Connections, buffers, queues, and workers have fixed capacities; request sizes, response sizes, and I/O deadlines have enforced limits. When capacity runs out, the engine applies backpressure or rejects work instead of growing without bounds. See how limits and backpressure fit together → These guarantees cover the framework’s resources; application code still owns its memory use and must cooperate with cancellation.

Baz imports bounded_http as a pinned external dependency. The engine stays independently usable. Baz supplies the application layer: routing, request views, endpoint composition, and responses.

Baz and bounded/http are separate packagesYour application supplies shared state, endpoint instances, and a caller I/O capability. Baz provides the typed App API. bounded/http supplies parsing, scheduling, response reservations, and Linux io_uring, macOS kqueue, or Windows IOCP transport. Finite blocking services use explicit fixed application workers. Your applicationShared state · endpoint structs · explicit application scratch Caller’s std.IoStartup + application services BazApp(Shared) · router · request views · response drafts Explicit fixed workersFor finite blocking services.Application code and providerown their resource behavior. NO AUTOMATIC CANCELLATION bounded/httpHTTP parser · scheduler · reservations · connection ownership Linux · io_uringmacOS · kqueueWindows · IOCP CURRENT VERIFIED BAZ BASELINE
Two packages, one explicit boundary. Baz uses the engine’s public embedding API; transport and scheduler changes belong upstream.

Use Zig’s std.Io today.

Pass your caller’s std.Io at initialization. Standard memory readers and writers work inline. Finite blocking services require explicit fixed workers; the provider’s resource use remains the application’s responsibility.

Keep the engine’s transports.

Baz supports Linux with io_uring, macOS with kqueue, and native Windows x64 with IOCP. HTTP transport uses the engine directly. A Baz-owned std.Io implementation is a later design step.

A response reservation comes before handler side effectsRetain validated input, reserve the full draft capacity, run the handler once, then finalize and send. Input and output storage remain retained until all application and kernel borrows have returned. 01 / INPUT02 / RESERVATION04 / OUTPUT 03 / APPLICATION Retain the requestSecure capacityRun the handlerFinalize & send Validated, borrowed bytesDrain older output if neededRetain pending borrows One unpublished draft REUSE ONLY WHEN ALL APPLICATION AND KERNEL BORROWS HAVE RETURNED
Output pressure can delay dispatch. It does not replay your one-shot handler. Storage is reusable only after all application and kernel borrows return.

Data stays data

No guessing what
your parameters mean.

Query parameters and URL-encoded forms are ordered lists of raw text slices. Decode when needed, into a buffer you supply. Repeated fields remain repeated fields; files[] is simply a name.

Raw input, ordered views, explicit decodingThe query id=001 and two tag[] fields remain three ordered pairs borrowing the same input. The value a+b stays unchanged in the raw view; percent decoding keeps plus, while form decoding changes it to a space. RETAINED QUERY BUFFERid=001&tag[]=a+b&tag[]=c%2Bd PAIR 01PAIR 02PAIR 03 id → 001tag[] → a+btag[] → c%2Bd Leading zeros stay.Brackets stay in the name.Duplicates stay in order. percentDecodeInto: a+b → a+bformDecodeInto: a+b → a b BOTH DECODERS WRITE INTO CALLER-SUPPLIED STORAGE
A raw query keeps spelling and ordering. The selected decoder determines how + is treated.
InputRaw valueExplicit operationResult
id=001001Read the sliceText, including leading zeros
enabled=falsefalseRead the sliceText; no boolean conversion
q=a+b%2Bca+b%2BcpercentDecodeIntoa+b+c
q=a+b%2Bca+b%2BcformDecodeIntoa b+c

One multipart iterator.

Fields and files share the same flat representation: a raw name, optional filename and content type, and borrowed bytes. One file or several files use the same loop. Baz never saves an upload implicitly.

Read the upload example →

Copies have a place.

Multipart parsing validates the complete supported body before iteration. If HTTP chunks split a body, request a bounded copy into caller storage before parsing a contiguous form. Application access to borrowed views ends with the callback.

Read the ownership contract →

Limits & backpressure

Set the capacity.
Let progress set the pace.

A limit says how much Baz can hold. Backpressure makes a producer wait when the next stage cannot keep up. Even one connection can fill its output buffer if its client reads slowly.

01 — ADMISSION

Room for a connection.

The connection ceiling counts admitted sockets across the cluster. Idle connections count too. When all slots are occupied, an extra accepted socket is closed; a 503 response is not guaranteed.

02 — EXECUTION

Room to run.

Workers and connection slots have separate limits. A busy worker makes its assigned requests wait. Continuations release the executor between steps while keeping bounded request state alive.

03 — OUTPUT

Room to write.

A full streaming buffer flushes before accepting more bytes. The worker waits for that storage to return. The I/O owner keeps running, and the original request deadline still applies.

A fixed buffer connects a fast producer to a slow reader The streaming handler writes into reserved output storage. The transport sends to the client. When output cannot drain, the handler waits. Local transmission completion returns the storage and lets the handler continue. The I/O owner keeps running. 01 / PRODUCE02 / RETAIN03 / DRAIN Streaming handlerFixed output storageTransport → client Write while space is available.Flush keeps these bytes frozen.A slow reader can stall output. Output blocked → handler waits. Storage returned → handler continues. THE WORKER WAITS · THE HTTP I/O OWNER KEEPS RUNNING · THE DEADLINE STILL APPLIES
Backpressure follows the slow reader back to the producer. Waiting reuses fixed storage; it does not increase capacity or reset the deadline.

One connection. Many requests.

Baz supports HTTP/1.1 pipelining: requests run in order, and finished responses can share a send batch. It reserves output before each handler starts. If earlier responses occupy that space, they drain first.

One page. Several downloads.

Browsers normally use several HTTP/1.1 connections for parallel asset downloads. Interleaving responses on one connection is HTTP/2 or HTTP/3 multiplexing. Baz’s current engine serves HTTP/1.1.