Reference

Published artifacts, the facade, the example hub, and the knobs that are not a tutorial. How-to lives in The hub and Domain is data.

Modules

ArtifactDepends onRole
heddleZIO, zio-jsonHTTP types, routes, middleware, Server.install (JVM Loom, Node, Native), Schema, Endpoint, OpenAPI
heddle-mcpheddleMCP 2026-07-28 over Api / BoundOp (HTTP/stdio also answer 2025-11-25 initialize)
heddle-oauthheddleJOSE, resource server, OAuth client, OIDC provider
heddle-brotliheddleRFC 7932 br encoder / decoder (no JNI)

import heddle.* is a curated facade over heddle.http, heddle.route, heddle.endpoint, heddle.server, heddle.client, and heddle.error. SSE, WebSocket, and Datastar stay in their packages. Datastar readSignals is an extension on Request.

Platforms

SurfaceJVMJS (Node)Native
Server.install HTTP/1.1yes (Loom default)yes (node:net)yes (POSIX)
TLSjavax.net.sslnode:tlsOpenSSL
HTTP/2yesnono
Clientpooled socketsfetchone-shot socket
Filesnionode:fs + SafePathnio + SafePath
Digest / RS256java.securitynode:cryptoOpenSSL
MCP HTTP + stdioyesyesyes
heddle-oauthyesyesno
HeddleAppyesnono

docs-js mounts Hub widgets. It does not import BoundOp or Mcp.handle.

Example hub

sbt example/run
  • http://localhost:8080/docs — box office API + Swagger, Authorize against the embedded OP

  • http://localhost:8080/preview — box office UI (same Api)

  • http://localhost:8080/mcp — Streamable HTTP MCP (POST; 2026 native, 2025 initialize)

  • sbt "example/run -- --mcp-stdio" — same Api, stdio JSON-RPC

Seed users: ada / ada. Machine client machine / secret.

Server config

zio.Config keys nest under heddle.server. Sizes are BytesLength (64.K, 10.M). Forever is Duration.Infinity. Why each clock exists: What stays open.

FieldDefaultWhy
host0.0.0.0Bind address
port8080Bind port
maxHeaderBytes64.KCap on the header block
maxBodyBytes10.MCap on the request body
chunkSize8.KRead/write chunk
gracefulShutdownTimeout10sFinish in-flight work, then close
idleTimeout60sClose a quiet keep-alive
headerTimeout30sClose incomplete headers
maxConnections1024Cap on accepted sockets
maxRequestsPerConnection10000Bound keep-alive reuse
soBacklog100Kernel accept queue
reuseAddresstrueSO_REUSEADDR
tcpNoDelaytrueTCP_NODELAY
soKeepAlivetrueDead-peer cleanup
http2trueOffer HTTP/2
http2Config.maxConcurrentStreams100Cap streams per connection
http2Config.initialWindowSize65535Stream flow-control window
http2Config.maxFrameSize16.KFrame cap
http2Config.maxHeaderListSize8.KHPACK list cap
http2Config.maxOutstandingFrames64Bound writer and body queues
Server.serve(app).provide(Server.Config.defaults)
Server.serve(app, Server.Config.default.copy(port = 8080, maxBodyBytes = 1.M, idleTimeout = Duration.Infinity))
Server.serve(app).provide(Server.Config.layer)

Gzip is not a field. Wrap the Routes: app @@ Middleware.compress().

JVM is JDK 21+ (Loom is the default scheduler, not a bind requirement). JS is Node. Native is POSIX plus OpenSSL. HeddleApp is the JVM ZIOAppDefault that serves routes and exits 0 on Ctrl-C under sbt 2.

(
  Server.Config.default.port,
  Server.Config.default.idleTimeout,
  Server.Config.default.maxConnections,
)
(8080,PT1M,1024)

Client config

zio.Config keys nest under heddle.client. Client.get / Client.request / Client.sse take Client.Config (the default is Client.Config.default). Client.live uses that default on the pooled client.

FieldDefaultWhy
maxConnectionsPerHost10In-flight cap per host
maxIdlePerHost10Idle sockets kept
connectTimeout10sGive up connecting
idleTimeout60sGive up reading
poolIdleTimeout60sDrop idle pooled sockets
addUserAgenttrueAdd User-Agent: heddle if missing
maxHeaderBytes64.KCap on response headers
maxBodyBytes10.MCap on response body

Accept-Encoding is not added for you. Set the header if you want gzip. The client inflates only when that request asked for gzip.

heddle.exports$package.Client.Config.default.connectTimeout
PT10S

Middleware

MiddlewareRole
compressResponse Content-Encoding on the wrapped Routes
decompress(maxBytes)Inflate a request body, capped
timeoutSlow handler becomes 504. Does not close the socket
cors / requestId / requireTlsPolicy on the wrapped Routes
true
true