diff --git a/.github/benchmark.png b/.github/benchmark.png
new file mode 100644
index 00000000..803a6c68
Binary files /dev/null and b/.github/benchmark.png differ
diff --git a/.github/cover.png b/.github/cover.png
new file mode 100644
index 00000000..1dc4b3f8
Binary files /dev/null and b/.github/cover.png differ
diff --git a/README.md b/README.md
index b8f7e9a3..e551b02f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+
+
+
+
# celeris
[](https://github.com/goceleris/celeris/actions/workflows/ci.yml)
@@ -7,50 +11,57 @@
[](https://goreportcard.com/report/github.com/goceleris/celeris)
[](LICENSE)
-celeris is a high-throughput, load-bearing Go HTTP engine built on a protocol-aware dual-architecture (io_uring & epoll). Its standout strength is **best-in-class throughput under load** — across the [probatorium](https://github.com/goceleris/probatorium) cross-framework matrix it leads the field on driver-backed (PostgreSQL / Redis / memcached) and write-heavy workloads, sustaining the highest request rates while holding tail-latency SLOs. It provides a familiar route-group and middleware API similar to Gin and Echo, so teams can adopt it without learning a new programming model, with zero-allocation hot paths on the H1 / H2 fast paths.
+celeris is a high-throughput, load-bearing HTTP engine for Go, built on a protocol-aware dual architecture of io_uring and epoll. Its standout strength is throughput under load: across the [probatorium](https://github.com/goceleris/probatorium) cross-framework matrix it leads the field on driver-backed (PostgreSQL / Redis / memcached) and write-heavy workloads, sustaining the highest request rates while holding tail-latency SLOs — powered by first-party database drivers that run their socket I/O on the same event loop as your handlers. The API is a familiar route-group and middleware model in the spirit of Gin and Echo, so teams adopt it without learning a new programming model, with zero-allocation hot paths on the HTTP/1.1 and h2c fast paths.
+
+Full documentation lives at [goceleris.dev](https://goceleris.dev).
## Highlights
-- **io_uring and epoll at parity** — both engines deliver equivalent throughput
-- **Zero hot-path allocations** on the H1 / H2 fast paths (pool-based contexts, pre-encoded HPACK responses)
-- **Continuously validated** — a [probatorium](https://github.com/goceleris/probatorium) 48-cell adversarial matrix runs nightly + a deeper weekend soak; see the badges above
+- **io_uring and epoll at parity** — both native engines deliver equivalent throughput; an adaptive meta-engine picks and transplants between them at runtime.
+- **Inline broadcast egress** (v1.5.7) — WebSocket and SSE fan-out now writes inline on the dispatch goroutine on native engines, lifting the single-loop-thread broadcast ceiling.
+- **Zero hot-path allocations** on the HTTP/1.1 and h2c fast paths — pool-based contexts and pre-encoded HPACK responses.
+- **First-party event-loop drivers** — native PostgreSQL, Redis, and memcached clients that colocate socket I/O with your handlers, no CGo, no separate reactor.
+- **Continuously validated** — an adversarial [probatorium](https://github.com/goceleris/probatorium) cluster matrix runs nightly, plus a deeper weekend soak; see the badges above.
+
+## What's new in v1.5.7
+
+Native-engine WebSocket and SSE **broadcast egress now runs inline on the dispatch goroutine** rather than being funneled through a single event-loop thread. This lifts the per-loop fan-out ceiling that previously capped broadcast throughput, parallelizing writes across dispatch goroutines the way the std engine does — while keeping the loop thread free to drive I/O. The change spans both native engines ([`engine/epoll/loop.go`](engine/epoll/loop.go), [`engine/iouring/worker.go`](engine/iouring/worker.go)) and shipped with detach-queue wakeup coalescing (PR #403) and the inline-egress path plus its upgrade/close race fixes (PR #404).
## Features
-- **Tiered io_uring** — auto-selects the best io_uring feature set (multishot accept/recv, provided buffers, SQ poll, fixed files) for your kernel
-- **Edge-triggered epoll** — per-core event loops with CPU pinning
-- **Adaptive meta-engine** — dynamically switches between io_uring and epoll based on runtime telemetry
-- **SIMD HTTP parser** — SSE2 (amd64) and NEON (arm64) with generic SWAR fallback
-- **HTTP/2 cleartext (h2c)** — full stream multiplexing, flow control, HPACK, inline handler execution, zero-alloc HEADERS fast path
-- **Auto-detect** — protocol negotiation from the first bytes on the wire
-- **Error-returning handlers** — `HandlerFunc` returns `error`; structured `HTTPError` for status codes
-- **Pre-routing middleware** — `Server.Pre()` runs middleware before route matching (method override, URL rewrite)
-- **Serialization** — JSON and XML response methods; `Bind` auto-detects request format from Content-Type
-- **net/http compatibility** — wrap existing `http.Handler` via `celeris.Adapt()`
-- **Streaming responses** — `Detach()` + `StreamWriter` for SSE and chunked responses on native engines
-- **Connection hijacking** — `Hijack()` for WebSocket and custom protocol upgrades (H1 only)
-- **Engine-integrated WebSocket** — `UpgradeWebSocket()` hands the conn directly to the worker loop with backpressure, deflate, and a `Hub` for fan-out broadcast
-- **Response buffering** — `BufferResponse`/`FlushResponse` for transform middleware (compress, ETag, cache)
-- **File serving** — `File()` from OS, `FileFromFS()` from `embed.FS` / `fs.FS`
-- **Content negotiation** — `Negotiate`, `Respond`, `AcceptsEncodings`, `AcceptsLanguages`
-- **Configurable body limits** — `MaxRequestBodySize` enforced on H1 and H2 (the net/http bridge has a fixed 100 MB cap)
-- **100-continue control** — `OnExpectContinue` callback for upload validation before body transfer
-- **Accept control** — `PauseAccept()`/`ResumeAccept()` for graceful load shedding
-- **Zero-downtime restart** — `InheritListener` + `StartWithListener` for socket inheritance
-- **Built-in metrics** — atomic counters, CPU utilization sampling, on-by-default `Server.Collector().Snapshot()` (opt out via `Config.DisableMetrics`)
-- **Per-handler async dispatch** — `Route.Async()` / `Route.Sync()` choose inline-on-worker vs. per-conn dispatch goroutine per route; H2 chooses per-stream
-
-**TLS:** the io_uring/epoll engines speak cleartext only (HTTP/1.1 + h2c). Terminate TLS upstream (Caddy/Nginx/Envoy) or use the std engine if you need in-process HTTPS.
-
-## Quick Start
+- **Tiered io_uring** — auto-selects the best io_uring feature set (multishot accept/recv, provided buffers, SQ poll, fixed files) for your kernel.
+- **Edge-triggered epoll** — per-core event loops with CPU pinning.
+- **Adaptive meta-engine** — transplants between io_uring and epoll at runtime based on telemetry.
+- **First-party database drivers** — native [`driver/postgres`](driver/postgres), [`driver/redis`](driver/redis), and [`driver/memcached`](driver/memcached) run on the celeris event loop (see [Database drivers](#database-drivers)).
+- **SIMD HTTP parser** — SSE2 (amd64) and NEON (arm64) with a generic SWAR fallback.
+- **HTTP/2 cleartext (h2c)** — full stream multiplexing, flow control, HPACK, inline handler execution, zero-alloc HEADERS fast path.
+- **Auto-detect** — protocol negotiation from the first bytes on the wire.
+- **Error-returning handlers** — `HandlerFunc` returns `error`; structured `*HTTPError` carries status codes.
+- **Pre-routing middleware** — `Server.Pre()` runs middleware before route matching (method override, URL rewrite).
+- **Serialization** — JSON and XML response methods; `Bind` auto-detects request format from `Content-Type`.
+- **net/http compatibility** — wrap an existing `http.Handler` via `celeris.Adapt()` / `celeris.AdaptFunc()`.
+- **Streaming responses** — `StreamWriter()` for chunked incremental writes on any engine; `Detach()` for keep-alive-after-return streaming on native engines (see [Streaming](#streaming-sse--websocket--chunked)).
+- **Connection hijacking** — `Hijack()` for custom protocol upgrades (HTTP/1.1 only).
+- **Engine-integrated WebSocket** — `UpgradeWebSocket()` hands the conn to the worker loop with backpressure, permessage-deflate, and a `Hub` for fan-out broadcast.
+- **Response buffering** — `BufferResponse` / `FlushResponse` for transform middleware (compress, ETag, cache).
+- **File serving** — `File()` from the OS, `FileFromFS()` from `embed.FS` / `fs.FS`.
+- **Content negotiation** — `Negotiate`, `Respond`, `AcceptsEncodings`, `AcceptsLanguages`.
+- **Configurable body limits** — `MaxRequestBodySize` enforced on HTTP/1.1 and h2c (the net/http bridge has a fixed 100 MB cap).
+- **100-continue control** — `OnExpectContinue` callback validates uploads before the body transfers.
+- **Accept control** — `PauseAccept()` / `ResumeAccept()` for graceful load shedding.
+- **Zero-downtime restart** — `InheritListener` + `StartWithListener` for socket inheritance.
+- **Built-in metrics** — atomic counters, CPU-utilization sampling, on by default via `Server.Collector().Snapshot()` (opt out with `Config.DisableMetrics`).
+- **Per-route async dispatch** — `Route.Async()` / `Route.Sync()` choose inline-on-worker vs. per-conn dispatch goroutine per route; h2 chooses per stream.
+
+**TLS:** the io_uring / epoll engines speak cleartext only (HTTP/1.1 + h2c). Terminate TLS upstream (Caddy, Nginx, Envoy) or use the std engine for in-process HTTPS.
+
+## Quick start
```
go get github.com/goceleris/celeris@latest
```
-Requires **Go 1.26.3+**. Linux for io_uring/epoll engines; any OS for the std engine.
-
-## Hello World
+Requires **Go 1.26.4+**. Linux for the io_uring / epoll / adaptive engines; any OS for the std engine.
```go
package main
@@ -103,7 +114,7 @@ s.Pre(methodOverride, urlRewrite)
## Middleware
-All middleware is in-tree under [`middleware/`](middleware/):
+All middleware is in-tree under [`middleware/`](middleware/) — 36 importable packages:
| Package | Description |
|---------|-------------|
@@ -111,16 +122,16 @@ All middleware is in-tree under [`middleware/`](middleware/):
| [`basicauth`](middleware/basicauth) | HTTP Basic authentication with hashed password support |
| [`bodylimit`](middleware/bodylimit) | Request body size enforcement |
| [`cache`](middleware/cache) | HTTP response cache with singleflight + Cache-Control honoring |
-| [`circuitbreaker`](middleware/circuitbreaker) | Circuit breaker (3-state, sliding window error rate, 503 + Retry-After) |
+| [`circuitbreaker`](middleware/circuitbreaker) | Circuit breaker (3-state, sliding-window error rate, 503 + Retry-After) |
| [`compress`](middleware/compress) | Response compression (zstd, brotli, gzip, deflate; separate go.mod) |
| [`cors`](middleware/cors) | Cross-Origin Resource Sharing (zero-alloc) |
| [`csrf`](middleware/csrf) | CSRF protection (double-submit cookie + origin validation) |
-| [`debug`](middleware/debug) | Debug/introspection endpoints (loopback-only by default) |
+| [`debug`](middleware/debug) | Debug / introspection endpoints (loopback-only by default) |
| [`etag`](middleware/etag) | Automatic ETag generation and conditional 304 responses |
-| [`healthcheck`](middleware/healthcheck) | Kubernetes-style liveness/readiness/startup probes |
-| [`idempotency`](middleware/idempotency) | Idempotency-Key replay protection (state machine: in-flight 409 + cached replay) |
-| [`jwt`](middleware/jwt) | JWT authentication (HMAC/RSA/ECDSA/EdDSA, JWKS auto-refresh) |
-| [`keyauth`](middleware/keyauth) | API key authentication with constant-time comparison |
+| [`healthcheck`](middleware/healthcheck) | Kubernetes-style liveness / readiness / startup probes |
+| [`idempotency`](middleware/idempotency) | Idempotency-Key replay protection (in-flight 409 + cached replay) |
+| [`jwt`](middleware/jwt) | JWT authentication (HMAC / RSA / ECDSA / EdDSA, JWKS auto-refresh) |
+| [`keyauth`](middleware/keyauth) | API-key authentication with constant-time comparison |
| [`logger`](middleware/logger) | Structured request logging (slog, zero-alloc FastHandler) |
| [`methodoverride`](middleware/methodoverride) | HTTP method override via header or form field |
| [`metrics`](middleware/metrics) | Prometheus metrics (separate go.mod) |
@@ -128,21 +139,21 @@ All middleware is in-tree under [`middleware/`](middleware/):
| [`overload`](middleware/overload) | 5-stage CPU + queue-depth + tail-latency-EMA overload control (503 + Retry-After) |
| [`pprof`](middleware/pprof) | Go profiling endpoints (loopback-only by default) |
| [`protobuf`](middleware/protobuf) | Protobuf serialization with content negotiation (separate go.mod) |
-| [`proxy`](middleware/proxy) | Trusted proxy header extraction (X-Forwarded-For, X-Real-IP) |
-| [`ratelimit`](middleware/ratelimit) | Sharded token bucket / sliding window rate limiter (Redis store adapter) |
-| [`recovery`](middleware/recovery) | Panic recovery with broken pipe detection |
-| [`redirect`](middleware/redirect) | URL redirect/rewrite (HTTPS, www, trailing slash) |
-| [`requestid`](middleware/requestid) | Request ID generation (buffered UUID v4) |
-| [`rewrite`](middleware/rewrite) | Regex-based URL rewriting with capture group support |
+| [`proxy`](middleware/proxy) | Trusted-proxy header extraction (X-Forwarded-For, X-Real-IP) |
+| [`ratelimit`](middleware/ratelimit) | Sharded token-bucket / sliding-window rate limiter (Redis / memcached store adapters) |
+| [`recovery`](middleware/recovery) | Panic recovery with broken-pipe detection |
+| [`redirect`](middleware/redirect) | URL redirect / rewrite (HTTPS, www, trailing slash) |
+| [`requestid`](middleware/requestid) | Request-ID generation (buffered UUID v4) |
+| [`rewrite`](middleware/rewrite) | Regex-based URL rewriting with capture-group support |
| [`secure`](middleware/secure) | Security headers (HSTS, CSP, COOP/CORP/COEP, OWASP defaults) |
| [`session`](middleware/session) | Cookie-based sessions on the unified [`store.KV`](middleware/store) (memory / Redis / Postgres / memcached adapters) |
| [`singleflight`](middleware/singleflight) | Request coalescing (collapse identical in-flight requests) |
-| [`sse`](middleware/sse) | Server-Sent Events: heartbeat, Last-Event-ID resumption with pluggable replay store (in-memory ring buffer or KV-backed for cross-restart durability), per-client `MaxQueueDepth` + `OnSlowClient` policy (Drop/Close/Block), and a `Broker` for fan-out to N subscribers with per-subscriber bounded queues + `OnSlowSubscriber` policy (Drop/Remove/Close) |
-| [`static`](middleware/static) | Static file serving with directory browse, ETag/Last-Modified caching |
-| [`store`](middleware/store) | Unified in-memory `KV` (LRU); shared by session / csrf / ratelimit / cache / idempotency / jwt JWKS. Redis / Postgres / memcached adapters live under [`session/redisstore`](middleware/session/redisstore), [`session/postgresstore`](middleware/session/postgresstore), [`session/memcachedstore`](middleware/session/memcachedstore), plus [`ratelimit/redisstore`](middleware/ratelimit/redisstore) / [`ratelimit/memcachedstore`](middleware/ratelimit/memcachedstore) |
+| [`sse`](middleware/sse) | Server-Sent Events: heartbeat, Last-Event-ID replay, per-client slow-client policy, and a `Broker` for fan-out (see [Streaming](#streaming-sse--websocket--chunked)) |
+| [`static`](middleware/static) | Static file serving with directory browse, ETag / Last-Modified caching |
+| [`store`](middleware/store) | Unified in-memory `KV` (sharded, TTL eviction) shared by session / csrf / ratelimit / cache / idempotency; Redis / Postgres / memcached adapters live under the respective `session/*store` and `ratelimit/*store` subpackages |
| [`swagger`](middleware/swagger) | OpenAPI spec + Swagger UI / Scalar / ReDoc (CDN-loaded) |
| [`timeout`](middleware/timeout) | Request timeout with cooperative and preemptive modes |
-| [`websocket`](middleware/websocket) | RFC 6455 WebSocket: permessage-deflate, engine-integrated backpressure, plus a `Hub` for fan-out broadcast to N connections (via cached `PreparedMessage` for O(1) per-message wire-encoding cost) with `OnSlowConn` policy (Drop/Remove/Close) and per-Conn filter via `BroadcastFilter` |
+| [`websocket`](middleware/websocket) | RFC 6455 WebSocket: permessage-deflate, engine-integrated backpressure, and a `Hub` for fan-out (see [Streaming](#streaming-sse--websocket--chunked)) |
```go
import (
@@ -157,18 +168,18 @@ s.Use(logger.New())
s.Use(cors.New())
```
-For middleware with external dependencies, use separate imports:
+Middleware with external dependencies lives in its own module — import it separately:
```go
import "github.com/goceleris/celeris/middleware/metrics" // requires prometheus
import "github.com/goceleris/celeris/middleware/otel" // requires opentelemetry
```
-## Error Handling
+## Error handling
-`HandlerFunc` has the signature `func(*Context) error`. Returning a non-nil error propagates it up through the middleware chain. If no middleware handles the error, the router's safety net converts it to an HTTP response:
+`HandlerFunc` has the signature `func(*Context) error`. Returning a non-nil error propagates it up through the middleware chain. If no middleware handles it, the router's safety net converts it to an HTTP response:
-- `*HTTPError` — responds with `Code` and `Message` from the error.
+- `*HTTPError` — responds with its `Code` and `Message`.
- Any other `error` — responds with `500 Internal Server Error`.
```go
@@ -197,41 +208,95 @@ s.POST("/items", func(c *celeris.Context) error {
```go
s := celeris.New(celeris.Config{
Addr: ":8080",
- Protocol: celeris.Auto, // HTTP1, H2C, or Auto
- Engine: celeris.Adaptive, // IOUring, Epoll, Adaptive, or Std
+ Protocol: celeris.Auto, // HTTP1, H2C, or Auto
+ Engine: celeris.Adaptive, // IOUring, Epoll, Adaptive, or Std
Workers: 8,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
- ShutdownTimeout: 10 * time.Second, // max wait for in-flight requests (default 30s)
- MaxRequestBodySize: 50 << 20, // 50 MB (default 100 MB, -1 for unlimited)
- AsyncHandlers: false, // server-level default (per-route .Async() overrides)
+ ShutdownTimeout: 10 * time.Second, // max wait for in-flight requests (default 30s)
+ MaxRequestBodySize: 50 << 20, // 50 MB (default 100 MB, -1 for unlimited)
+ AsyncHandlers: false, // server-level default (per-route .Async() overrides)
Logger: slog.Default(),
})
```
-## Async Handlers (per-route)
+## Async handlers (per route)
-Celeris runs every handler **inline on the I/O worker** by default — lowest latency, zero handoff. For handlers that block on I/O (database, RPC, file system) you can opt **per-route** into the per-connection dispatch goroutine, so the worker stays free to drive other connections:
+Celeris runs every handler **inline on the I/O worker** by default — lowest latency, zero handoff. For handlers that block on I/O (database, RPC, filesystem), opt **per route** into the per-connection dispatch goroutine so the worker stays free to drive other connections:
```go
// CPU-only / cache-only — runs inline (default).
s.GET("/healthz", healthHandler)
-// Blocking I/O — async, runs on per-conn goroutine.
+// Blocking I/O — async, runs on a per-conn goroutine.
s.GET("/db", dbHandler).Async()
// Or flip the default at the group level:
api := s.Group("/api").Async()
-api.GET("/products", productHandler) // async (inherited)
-api.GET("/cached", cachedHandler).Sync() // opt back to sync
+api.GET("/products", productHandler) // async (inherited)
+api.GET("/cached", cachedHandler).Sync() // opt back to sync
```
-Precedence is **route > group > server default** (`Config.AsyncHandlers`). Works identically across iouring, epoll, and adaptive (both sub-engines honor the per-route flag and async promotions survive sub-engine swaps): the conn is promoted **once** via the `ErrAsyncDispatch` sentinel (sticky — subsequent requests skip the inline check entirely); H2 routes the choice **per stream** between inline-on-event-loop and the shared H2 worker pool (`runtime.GOMAXPROCS*4` goroutines). The `Async`/`Sync` distinction is a no-op on the `std` engine (net/http already does goroutine-per-request).
+Precedence is **route > group > server default** (`Config.AsyncHandlers`). It works identically across io_uring, epoll, and adaptive — both sub-engines honor the per-route flag, and async promotions survive sub-engine swaps: a conn is promoted **once** via the `ErrAsyncDispatch` sentinel (sticky — subsequent requests skip the inline check entirely). h2 routes the choice **per stream** between inline-on-event-loop and the shared h2 worker pool. The `Async` / `Sync` distinction is a no-op on the std engine, where net/http already does goroutine-per-request.
Observe how often the inline → goroutine handoff fires via `Server.EngineInfo().Metrics.AsyncRoutes` (static count of `.Async()` routes) and `.AsyncPromotedConns` (cumulative promotions).
-## net/http Compatibility
+## Streaming (SSE / WebSocket / chunked)
+
+Streaming is a first-class differentiator, and native engines get an inline egress path that avoids funneling every write through one loop thread. Four building blocks compose:
+
+- **`Context.StreamWriter()`** — synchronous incremental writes (chunked / progressive responses). Works on **every** engine, including std.
+- **`Context.Detach()`** — keep the connection alive after the handler returns, so a spawned goroutine can drive a long-lived stream. This is **native-engine only** (io_uring / epoll / adaptive). On std, the connection closes the moment the handler returns.
+- **[`middleware/websocket`](middleware/websocket)** — RFC 6455 WebSocket with an engine-integrated `Hub` for broadcast.
+- **[`middleware/sse`](middleware/sse)** — Server-Sent Events with a `Broker` for publish-to-N fan-out and Last-Event-ID replay.
+
+Gate async streaming on the active engine with `Context.EngineSupportsAsyncDetach()` — it reports whether the engine can keep the conn alive after the handler returns:
+
+```go
+func stream(c *celeris.Context) error {
+ if c.EngineSupportsAsyncDetach() {
+ done := c.Detach()
+ go func() {
+ defer done()
+ driveStream(c) // writes flush through the engine's guarded path
+ }()
+ return nil // handler returns; conn stays alive
+ }
+ driveStream(c) // std: must complete before returning
+ return nil
+}
+```
+
+### WebSocket
+
+[`middleware/websocket`](middleware/websocket) is a full RFC 6455 implementation wired directly into the worker loop:
+
+- **Zero-alloc reads** — `Conn.ReadMessageReuse()` reuses a caller buffer for echo / proxy hot paths.
+- **Streaming large messages** — `Conn.NextReader()` / `Conn.NextWriter()` read and write without whole-message buffering.
+- **Control frames** — `Conn.WriteControl()`, `Conn.WritePing()`, and `SetPingHandler` / `SetPongHandler` / `SetCloseHandler`.
+- **permessage-deflate** — RFC 7692 compression negotiated at handshake (`EnableCompression`).
+- **Fan-out `Hub`** — `NewHub(HubConfig{...})`, then `Broadcast` / `BroadcastFilter` / `BroadcastPrepared`. Encode once with `NewPreparedMessage` and reuse the `*PreparedMessage` across every subscriber for O(1) per-message wire-encoding cost. Slow connections are handled by `HubConfig.OnSlowConn`, which returns a `HubPolicy` (Drop / Remove / Close).
+
+### SSE
+
+[`middleware/sse`](middleware/sse) provides heartbeats, Last-Event-ID replay via a pluggable `ReplayStore`, and a fan-out `Broker`:
+
+- **`NewBroker(BrokerConfig{...})`** — publish to N subscribers, each with a bounded queue.
+- **`BrokerConfig.OnSlowSubscriber`** — returns a `BrokerPolicy` (Drop / Remove / Close) when a subscriber's queue is full; slow-subscriber handling is bounded by `DefaultBrokerSlowConcurrency()` (`GOMAXPROCS*4`) so a misbehaving callback can't fan out unboundedly.
+- **Last-Event-ID replay** — on reconnect the broker reads `Since(lastID)` from the replay store and re-sends missed events.
+
+## Database drivers
+
+Celeris ships first-party, event-loop-native database drivers that run their socket I/O on the **same worker loop** as your HTTP handlers — no CGo, no separate reactor thread. They back the driver-isolation benchmarks and probatorium's driver-backed workloads, where celeris's dominance is widest.
+
+- **[`driver/postgres`](driver/postgres)** — speaks the PostgreSQL v3 wire protocol directly. Register it as a standard `database/sql` driver, or use the lower-level worker-affinity `Pool` to skip `database/sql` overhead.
+- **[`driver/redis`](driver/redis)** — RESP2 / RESP3 client (negotiates `HELLO 3` with automatic RESP2 fallback), per-worker connection pools, pub/sub, and Redis Cluster + Sentinel failover.
+- **[`driver/memcached`](driver/memcached)** — text and binary protocols, pooled single-node clients, and a consistent-hash cluster with failover.
+
+Colocate a driver with the server via its `WithEngine` option so commands issue on the loop instead of dialing out on a blocking goroutine. See [goceleris.dev](https://goceleris.dev) for driver guides.
+
+## net/http compatibility
Wrap existing `net/http` handlers and middleware:
@@ -247,20 +312,20 @@ s.GET("/func", celeris.AdaptFunc(func(w http.ResponseWriter, r *http.Request) {
The bridge buffers the adapted handler's response in memory, capped at a compile-time **100 MB** limit (independent of `Config.MaxRequestBodySize`). Responses exceeding this limit return an error.
-## Engine Selection
+## Engine selection
-| Engine | Platform | Use Case |
+| Engine | Platform | Use case |
|--------|----------|----------|
| `IOUring` | Linux 5.10+ | Lowest latency, highest throughput |
| `Epoll` | Linux | Broad kernel support, proven stability |
-| `Adaptive` | Linux | Auto-switch based on telemetry |
+| `Adaptive` | Linux | Auto-switch between io_uring and epoll on telemetry |
| `Std` | Any OS | Development, compatibility, non-Linux deploys |
-Use Adaptive (the default on Linux) unless you have a specific reason to pin an engine. On non-Linux platforms, only Std is available.
+The default is **Adaptive** on Linux and **Std** elsewhere. Prefer Adaptive unless you have a specific reason to pin an engine; on non-Linux platforms only Std is available (the native engines return an error).
-## Graceful Shutdown
+## Graceful shutdown
-Use `StartWithContext` for production deployments. When the context is canceled, the server drains in-flight requests up to `ShutdownTimeout` (default 30s).
+Use `StartWithContext` for production. When the context is canceled, the server drains in-flight requests up to `ShutdownTimeout` (default 30s).
```go
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
@@ -279,21 +344,21 @@ if err := s.StartWithContext(ctx); err != nil {
## Observability
-The core provides a lightweight metrics collector accessible via `Server.Collector()`:
+The core provides a lightweight metrics collector via `Server.Collector()`:
```go
snap := server.Collector().Snapshot()
fmt.Println(snap.RequestsTotal, snap.ErrorsTotal, snap.ActiveConns, snap.CPUUtilization)
```
-For Prometheus exposition and debug endpoints, use the [`middleware/metrics`](middleware/metrics) and [`middleware/debug`](middleware/debug) packages. For OpenTelemetry, use [`middleware/otel`](middleware/otel).
+For Prometheus exposition and debug endpoints, use [`middleware/metrics`](middleware/metrics) and [`middleware/debug`](middleware/debug). For OpenTelemetry, use [`middleware/otel`](middleware/otel).
-## Feature Matrix
+## Feature matrix
| Feature | io_uring | epoll | std |
|---------|----------|-------|-----|
| HTTP/1.1 | yes | yes | yes |
-| H2C | yes | yes | yes |
+| h2c | yes | yes | yes |
| Auto-detect | yes | yes | yes |
| CPU pinning | yes | yes | no |
| Provided buffers | yes (5.19+) | no | no |
@@ -301,54 +366,81 @@ For Prometheus exposition and debug endpoints, use the [`middleware/metrics`](mi
| Multishot recv | opt-in (5.19+, `CELERIS_IOURING_MULTISHOT_RECV=1`) | no | no |
| Provided-buffer ring size | auto-scaled (`CELERIS_IOURING_PBUF_COUNT=N` to override) | n/a | n/a |
| Zero-alloc HEADERS | yes | yes | no |
-| Inline H2 handlers | yes | yes | no |
-| Detach / StreamWriter | yes | yes | yes |
+| Inline h2 handlers | yes | yes | no |
+| Inline WS / SSE broadcast egress | yes | yes | n/a (net/http) |
+| `StreamWriter` (sync incremental) | yes | yes | yes |
+| `Detach` (async keep-alive) | yes | yes | no |
| Connection hijack | yes | yes | yes |
+`StreamWriter()` performs synchronous incremental writes on every engine. Async `Detach()` — keeping the connection alive after the handler returns — is native-engines-only (`EngineSupportsAsyncDetach()` is false on std, which closes the conn when the handler returns).
+
## Benchmarks
-Cross-framework performance benchmarks — the (scenario × server × protocol) matrix driven by [`goceleris/loadgen`](https://github.com/goceleris/loadgen) — live in [goceleris/probatorium](https://github.com/goceleris/probatorium), the authoritative cross-framework bench harness. Reproducible release-gate numbers are published by probatorium's `publish-results` workflow. Driver-isolation benchmarks remain in-tree at [`test/drivercmp/`](test/drivercmp/) and WebSocket comparisons at [`test/benchcmp_ws/`](test/benchcmp_ws/).
+
+
+
-> **Note:** In-tree middleware benchmarks (e.g., `middleware/compress/bench_test.go`) use `celeristest` which provides pool-based contexts with no HTTP overhead. These numbers measure pure middleware logic and should not be compared directly with `httptest`-based competitor benchmarks. Use [probatorium](https://github.com/goceleris/probatorium) for fair cross-framework comparisons.
+Plaintext GET at 1024 connections, saturation req/s, linux/amd64, celeris v1.5.6 bench data.
-## Continuous Validation
+Across the cross-language field in the chart above (45 servers, 10 languages), celeris tops the plaintext-GET throughput leaderboard — ahead of hand-tuned C, C++, Rust, and Java servers — and the published headline is **fastest Go framework in 25 of 29 scenarios**, sweeping the driver-backed database workloads. Full interactive results are at [goceleris.dev](https://goceleris.dev).
-Correctness is validated by [goceleris/probatorium](https://github.com/goceleris/probatorium) — an adversarial cluster matrix run on real hardware:
+The cross-framework matrix (scenario × server × protocol, driven by [`goceleris/loadgen`](https://github.com/goceleris/loadgen)) lives in [`goceleris/probatorium`](https://github.com/goceleris/probatorium), the authoritative bench harness; its `publish-results` workflow emits the release-gate numbers. In-tree comparison modules run in isolation:
+
+- [`test/drivercmp/`](test/drivercmp/) — PostgreSQL / Redis / memcached driver-isolation benchmarks (one module each).
+- [`test/benchcmp_ws/`](test/benchcmp_ws/) — WebSocket comparisons.
+- [`test/benchcmp_sse/`](test/benchcmp_sse/) — SSE broadcast (publish-to-N) comparisons.
+
+> **Note:** In-tree middleware benchmarks (e.g. `middleware/compress/bench_test.go`) use `celeristest`, which provides pool-based contexts with no HTTP overhead. Those numbers measure pure middleware logic and should not be compared directly with `httptest`-based competitor benchmarks. Use [probatorium](https://github.com/goceleris/probatorium) for fair cross-framework comparisons.
+
+## Continuous validation
+
+Correctness is validated by [`goceleris/probatorium`](https://github.com/goceleris/probatorium), an adversarial cluster matrix run on real hardware:
- **PR tier** (`matrix-pr-tier`) — fast gate that every celeris PR must pass before merge.
-- **Nightly** (`matrix-nightly-tier`) — 48 cells × every engine × every protocol × refapp; ~7h, every night.
-- **Weekend soak** (`matrix-weekend-tier`) — deeper run with the same matrix at higher request counts; multi-hour endurance.
+- **Nightly** (`matrix-nightly-tier`) — 24 cells across every refapp × every engine, exercising every protocol slice per cell; ~1h budget, every night.
+- **Weekend soak** (`matrix-weekend-tier`) — the same matrix at higher request counts for multi-hour endurance.
-Bug oracles include slowloris hang detection, malformed-request acceptance, WebSocket torture-frame acceptance, h2c churn-crash detection, and tier-3 property seeds. Badge status above.
+Bug oracles include slowloris hang detection, malformed-request acceptance, WebSocket torture-frame acceptance, h2c churn-crash detection, and tier-3 property seeds. Badge status is above.
-## Project Structure
+## Project structure
```
adaptive/ Adaptive meta-engine (Linux)
celeristest/ Test helpers (NewContext, NewContextT, ResponseRecorder, With* options)
-cmd/ Celeris CLI binary (validation / diagnostics entrypoint)
+cmd/celeris/ CLI launcher — validation / diagnostics entrypoint (see below)
driver/ First-party event-loop database drivers (postgres, redis, memcached)
engine/ Engine interface + implementations (iouring, epoll, std)
internal/ Shared internals (conn, cpumon, ctxkit, negotiate, platform, sockopts)
-middleware/ In-tree middleware ecosystem (36 packages)
+middleware/ In-tree middleware ecosystem (36 importable packages)
observe/ Collector, CPUMonitor, Snapshot
probe/ System capability detection (kernel version, io_uring feature probe)
protocol/ Protocol parsers (h1, h2, detect)
resource/ Configuration, presets, defaults
-test/ Conformance, spec compliance, integration, benchmarks (drivercmp, benchcmp_ws, …)
+test/ Conformance, spec compliance, integration, benchmarks (drivercmp, benchcmp_ws, benchcmp_sse)
validation/ Runtime invariant assertions + validation hooks (debug builds)
```
+`cmd/celeris` is a minimal launcher around `celeris.New`, intended primarily as the entry point for validation soak runs; under the `validation` build tag it also streams assertion counters over a unix socket. Production deployments should embed `celeris.New` directly rather than depend on this binary.
+
## Requirements
-- **Go 1.26.3+**
-- **Linux** for io_uring and epoll engines (kernel 5.10+ for io_uring; 5.19+ for the multishot / provided-buffers tier)
+- **Go 1.26.4+**
+- **Linux** for the io_uring / epoll / adaptive engines (kernel 5.10+ for io_uring; 5.19+ for the multishot / provided-buffers tier)
- **Any OS** for the std engine
-- Direct runtime dependencies: `golang.org/x/sys`, `golang.org/x/net` (only)
+- Direct runtime dependencies: `golang.org/x/sys` and `golang.org/x/net` only (`golang.org/x/text` is indirect)
+
+## Ecosystem
+
+Celeris is one of the [goceleris](https://github.com/goceleris) family:
+
+- **[celeris](https://github.com/goceleris/celeris)** — this repository: the HTTP engine, middleware, and drivers.
+- **[loadgen](https://github.com/goceleris/loadgen)** — the load generator that drives the cross-framework matrix.
+- **[probatorium](https://github.com/goceleris/probatorium)** — the adversarial correctness + performance bench harness.
+- **[docs](https://github.com/goceleris/docs)** — source for [goceleris.dev](https://goceleris.dev).
## Contributing
-See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and pull request guidelines.
+See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and pull-request guidelines.
## License