statute

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: MIT Imports: 48 Imported by: 0

README

statute

Config-as-code reverse proxy in Go. The binary is the configuration.

statute is a reverse proxy framework where your routing topology, TLS material, upstream pools, and middleware stack are expressed as Go values — type-checked, IDE-completed, and validated at startup. There is no runtime config file, no hot reload, no module loader. You write a main.go, you go build, you ship a single binary that boots, validates, and serves.

package main

import "github.com/kjanat/statute"

func main() {
    statute.Main(statute.Config{
        Listeners: statute.Listeners{
            statute.HTTP(":80").RedirectTo("https"),
            statute.HTTPS(":443",
                statute.AutoTLS("example.com").Email("ops@example.com").Storage("/var/lib/statute/certs"),
                statute.HTTP2(),
            ),
        },
        Upstreams: statute.Upstreams{
            "api": statute.Pool{
                Backends: []statute.Backend{{Address: "10.0.0.1:8080"}, {Address: "10.0.0.2:8080"}},
                Strategy: statute.LeastConnections,
                HealthCheck: statute.HealthCheck{Path: "/healthz", Interval: "10s"},
            },
        },
        Routes: statute.Routes{
            statute.Match("/*").ProxyTo("api").With(statute.Timeout("30s")),
        },
        Defaults:      statute.Defaults{ReadHeaderTimeout: "5s", WriteTimeout: "30s", IdleTimeout: "120s"},
        Observability: statute.Observability{AccessLog: statute.JSONLog(statute.Stdout), Metrics: statute.Prometheus(":9090", "/metrics")},
        Shutdown:      statute.Shutdown{GracePeriod: "30s", DrainListeners: true},
    })
}

Why this and not nginx, Caddy, or Traefik

Choose statute when you want your reverse proxy configuration to be Go code that compiles. You get: type checking on every field, IDE completion as you write it, refactoring tools that work, the ability to put helper functions and conditional logic in your config without learning a templating language, and a single static binary that can't drift between "the config file on disk" and "what the daemon loaded".

You give up: hot reload, runtime configuration changes, plugin loading, web admin UIs, and a community ecosystem of off-the-shelf middleware. If those are deal-breakers, run Caddy or Traefik instead — they're better at being them.

statute is designed for teams that already build and ship Go binaries, where adding "edit the config file" as an extra deployment path costs more than recompiling and re-rolling.

Status

The framework is implemented and works end-to-end for the documented features. The HTTP-only, AutoTLS+HTTP-01, AutoTLS+DNS-01, and HTTP/3 paths all pass smoke tests. The API is design-stage and may shift in incompatible ways before a 1.0 release.

What's implemented:

  • HTTP/1.1 and HTTP/2 listeners
  • HTTP/3 (QUIC) via quic-go, with Alt-Svc advertisement
  • TLS termination: static certs, AutoTLS via autocert (HTTP-01 + TLS-ALPN-01), AutoTLS via custom DNS-01 manager with Cloudflare API
  • Upstream pools with round-robin, least-connections, IP-hash, and smooth weighted round-robin strategies
  • Backup tier failover when all primary backends are unhealthy
  • Active health checks with configurable thresholds
  • Per-route middleware: timeout, rate limit (token bucket), retry (idempotent-method only, gRPC-aware), cache, gzip + brotli compression, ETag
  • Static file serving
  • WebSocket pass-through (default httputil.ReverseProxy behaviour)
  • Structured JSON access logging with sampling
  • Prometheus-format metrics
  • OpenTelemetry tracing via OTLP/gRPC
  • pprof endpoints on the metrics server
  • Graceful shutdown with listener draining
  • Cloudflare-aware mode: BehindCloudflare() flips ALPN to suppress TLS-ALPN-01 and trusts CF-Connecting-IP
  • statute.Main CLI wrapper with -validate and -export flags

Install

go get github.com/kjanat/statute

Requires Go 1.25 or newer.

Concepts

Config-as-code

The configuration is Go. Every field is a typed Go value. The Config struct is the entire surface of the framework.

statute.Run(statute.Config{
    Listeners:     ...,
    Upstreams:     ...,
    Routes:        ...,
    Defaults:      ...,
    Observability: ...,
    Shutdown:      ...,
})

Helper functions (HTTP, HTTPS, Match, RateLimit, …) construct the values; struct literals fill in named fields. Durations are strings ("10s", "90s", "1h") so the configuration reads like a config file rather than a Go program. Rate limits are strings ("100/min"). Sizes are strings (when added). Type checking still applies — invalid fields are caught at build time.

Two layers: surface and resolved

statute has two type packages:

  • github.com/kjanat/statute is the surface API. It is what you write. It optimises for readability and ergonomic chaining.
  • github.com/kjanat/statute/resolved is the resolved schema. It is what the runtime executes against. It optimises for invariants: durations are time.Duration, upstream references are *Pool pointers, optional fields are filled with their canonical defaults, no string-encoded values remain.

Tooling (validators, exporters, dashboards) targets the resolved schema. End users target the surface API. They are connected by a single Resolve(cfg) (*resolved.Config, error) function.

The pipeline: validate → resolve → run

Every config flows through three stages on startup:

  • Validate rejects structural and semantic errors with path-style locations: route[2] "/api/v1/*": unknown upstream "users".
  • Resolve parses durations, dereferences upstream names, fills defaults, normalises addresses, and emits a *resolved.Config.
  • Run opens listeners, builds per-backend reverse proxies, starts health checks, registers signal handlers, and serves.

You can stop after stage 2 with statute.Resolve() (for tooling) or statute.Export() (for snapshot and diff in CI).

Feature reference

Listeners
statute.HTTP(":80").RedirectTo("https")
statute.HTTPS(":443",
    statute.AutoTLS("example.com").Email("ops@example.com").Storage("/var/lib/statute/certs"),
    statute.HTTP2(),
    statute.HTTP3(":443/udp"),
    statute.BehindCloudflare(),
)
statute.HTTPS(":443",
    statute.StaticTLS("/etc/ssl/cert.pem", "/etc/ssl/key.pem"),
    statute.HTTP2(),
)

HTTP and HTTPS declare a listener; RedirectTo turns a listener into a permanent redirect. The HTTPS variant takes options as variadic arguments — TLS material, HTTP/2, HTTP/3, Cloudflare-awareness — composed flat rather than nested.

When AutoTLS is configured anywhere in the config, the plain-HTTP listener automatically serves /.well-known/acme-challenge/* so HTTP-01 validation works without separate plumbing.

Upstreams
Upstreams: statute.Upstreams{
    "api": statute.Pool{
        Backends: []statute.Backend{
            {Address: "10.0.0.1:8080", Weight: 2},
            {Address: "10.0.0.2:8080", Weight: 1},
            {Address: "10.0.0.3:8080", Backup: true},
        },
        Strategy: statute.LeastConnections,
        HealthCheck: statute.HealthCheck{
            Path: "/healthz", Interval: "10s", Timeout: "2s",
            Healthy: 2, Unhealthy: 3,
        },
        Transport: statute.Transport{
            MaxIdleConnsPerHost: 32,
            IdleConnTimeout:     "90s",
        },
    },
}

Upstreams are a named map. Routes refer to pools by string key. A single pool can be reused across many routes.

Strategies:

  • RoundRobin — even distribution.
  • LeastConnections — pick the backend with fewest in-flight requests. Best when request durations vary.
  • IPHash — consistent per-client routing for session affinity.
  • Weighted — smooth weighted round-robin (Nginx-style).

The picker filters to healthy primary backends; when no primary is healthy, it falls through to the backup tier; when none of those are healthy either, it goes degraded and tries primaries anyway. Active health checks demote and promote backends in the background based on consecutive success/failure thresholds.

Transport tunes the HTTP transport reused across all backends in the pool. The default MaxIdleConnsPerHost (32) is a much better default for a proxy than Go's stdlib value (2); leave it alone unless you know why you're changing it.

Routes and middleware
Routes: statute.Routes{
    statute.Match("/api/v1/*").Host("api.example.com").ProxyTo("api").
        With(
            statute.RateLimit("100/min").Per(statute.ClientIP),
            statute.Retry(3, statute.OnStatus(502, 503, 504)),
            statute.Timeout("30s"),
        ),
    statute.Match("/static/*").Serve("./public").
        With(statute.Cache("1h"), statute.Compress(statute.Gzip, statute.Brotli), statute.ETag()),
}

Routes are matched in declaration order; the first match wins. Patterns support exact match (/api) and a trailing wildcard (/api/*). Host scopes a route to a specific Host header value. Catch-all /* should be last.

Each route is either a proxy (ProxyTo("pool")) or a static-file serve (Serve("./dir")), not both.

Middleware:

  • Timeout(dur) — wraps the handler in http.TimeoutHandler. Returns 503 when exceeded.
  • RateLimit(rate).Per(key) — token bucket per key. Rate format is "N/unit" where unit is s, min, h. Keys: ClientIP (default), HostHeader.
  • Retry(max, OnStatus(...)) — retries upstream calls up to max attempts when the response status matches one of the listed codes. Skips for non-idempotent methods (POST, PATCH), gRPC, SSE, WebSocket upgrades, and bodies > 1 MiB. Buffers smaller bodies to replay on retry.
  • Cache(ttl) — in-process cache for 2xx GET/HEAD responses. Replace with a real LRU for high-cardinality deployments.
  • Compress(Gzip, Brotli) — negotiates content encoding via Accept-Encoding. Brotli preferred when the client advertises both.
  • ETag() — adds an SHA-256-based ETag to 200 responses; answers 304 on If-None-Match match.
Observability
Observability: statute.Observability{
    AccessLog: statute.JSONLog(statute.Stdout).Sample(0.1),
    Metrics:   statute.Prometheus(":9090", "/metrics"),
    Tracing:   statute.OTLP("otel-collector:4317").ServiceName("edge").Insecure().Sample(0.05),
}

Access log — one JSON line per request. Fields: ts, method, host, path, query, remote, user_agent, referer, status, duration_us, proto, forwarded_for. Sample(rate) records a fraction of successful requests; errors (status ≥ 400) are always logged regardless.

Metrics — Prometheus exposition format on a separate listener. Counters for total requests, requests by status, and request duration. pprof is mounted under /debug/pprof/* on the same listener.

Tracing — OTLP/gRPC export to an OpenTelemetry collector. Spans use HTTP semantic conventions. W3C trace context is automatically propagated to upstream backends (the reverse proxy injects traceparent and tracestate headers). Sample(rate) is TraceIDRatioBased with parent-based sampling, so trace continuity is preserved across hops.

TLS
// Static cert from disk
statute.StaticTLS("/etc/ssl/cert.pem", "/etc/ssl/key.pem")

// Auto-provisioned via Let's Encrypt with HTTP-01 (default)
statute.AutoTLS("example.com", "api.example.com").
    Email("ops@example.com").
    Storage("/var/lib/statute/certs")

// Auto-provisioned via Let's Encrypt with DNS-01 + Cloudflare
// (required for wildcards and when port 80 is not reachable)
statute.AutoTLS("*.example.com", "example.com").
    Email("ops@example.com").
    Storage("/var/lib/statute/certs").
    CloudflareDNS01(token).Zone(zoneID)

AutoTLS persistence is mandatory. The Storage directory holds the ACME account key, issued certs, and renewal state; without it, every restart re-registers and re-issues, blowing through Let's Encrypt rate limits in days.

The DNS-01 path is implemented in-tree using golang.org/x/crypto/acme directly + a tiny Cloudflare DNS API client. It does not pull in lego or certmagic. It supports wildcards and works without a publicly-reachable port 80. See docs/cloudflare.md for setup details.

HTTP/3
statute.HTTPS(":443",
    statute.AutoTLS(...),
    statute.HTTP2(),
    statute.HTTP3(":443/udp"),
)

When HTTP3() is on a listener, statute runs a quic-go HTTP/3 server alongside the HTTPS listener and adds an Alt-Svc: h3=":443"; ma=86400 header on every HTTPS response so browsers upgrade subsequent requests. The same TLS material is shared between the HTTPS listener and the HTTP/3 server.

CLI

The statute.Main(cfg) wrapper provides standard flags:

$ ./myproxy -validate          # parse and resolve, exit 0/1
$ ./myproxy -export             # write resolved config as JSON to stdout
$ ./myproxy                     # run the server

Use Run(cfg) directly if you want to handle flags yourself.

Production checklist

The following are framework-enforced or strongly recommended:

  • ReadHeaderTimeout is required. The default scaffold sets 5s. Without it, statute is vulnerable to Slowloris.
  • Graceful shutdown with Shutdown.GracePeriod and DrainListeners: true. Without it, every deploy drops in-flight requests.
  • Observability — at minimum, access log + metrics. A proxy without observability is operationally blind.
  • Persistent AutoTLS storage. Re-issuing on every restart will get the account rate-limited.
  • Health checks on every pool. Without them, statute keeps sending traffic to dead backends.
  • At least two backends per pool. A single-backend pool has no failover.
  • Tracing in production. Set a sample rate (e.g. 0.05) to control collector cost. Errors are still captured because parent-based sampling preserves traced error paths.
  • BehindCloudflare() when fronted by Cloudflare. Without it, client IPs collapse to the CF edge node and rate limiting becomes useless.

Examples

Examples are runnable Go programs in examples/:

  • examples/http-only — HTTP-only proxy on :8080. Smallest runnable config.
  • examples/basic — canonical AutoTLS + HTTP/2 + HTTP/3 setup.
  • examples/cloudflare — fronted by Cloudflare with HTTP-01 (no API key).
  • examples/cloudflare-wildcard — wildcard cert via Cloudflare DNS-01 + OTLP tracing.

Run any of them:

go run ./examples/http-only
go run ./examples/cloudflare-wildcard       # needs CLOUDFLARE_API_TOKEN

Deeper docs

  • docs/cloudflare.md — running behind Cloudflare, HTTP-01 vs DNS-01, settings to enable on the Cloudflare side, failure modes.
  • docs/observability.md — access log fields, metric names, span structure, sampling guidance.
  • docs/production.md — deployment patterns, ports, capabilities, the setcap trick for binding low ports as a non-root user.

Testing

go test ./...           # all unit tests
go vet ./...            # vet
golangci-lint run ./... # lint

The race detector (go test -race) does not work on Raspberry Pi / older 64-bit Arm kernels with VMA range < 48; this is a TSAN limitation, not a code issue.

License

See LICENSE.

Contributing

The API is design-stage. If you want to use statute in production, pin a specific commit, expect breakage on updates, and read the source — the tree is small (~4.3 kLOC across ~30 files) and self-contained.

Documentation

Overview

Package statute is a config-as-code reverse proxy framework.

Configurations are written as Go values, validated and resolved at startup, then executed by the runtime. There is no runtime config file, no hot reload, and no module loader — the binary IS the configuration.

See the examples directory for canonical usage.

Index

Constants

This section is empty.

Variables

View Source
var Stderr = LogWriter{/* contains filtered or unexported fields */}

Stderr writes logs to process stderr.

View Source
var Stdout = LogWriter{/* contains filtered or unexported fields */}

Stdout writes logs to process stdout.

Functions

func Cache

func Cache(ttl string) *cacheMW

Cache returns a response-cache middleware with the given TTL.

func Compress

func Compress(algos ...CompressAlgo) *compressMW

Compress returns a response-compression middleware that negotiates one of the listed algorithms based on the request's Accept-Encoding header.

func ETag

func ETag() *etagMW

ETag returns a middleware that adds ETag headers to static file responses and serves 304 Not Modified for matching If-None-Match requests.

func Export

func Export(cfg Config, w io.Writer) error

Export validates and resolves the surface configuration, then writes the canonical resolved schema as JSON. Useful for diffing deployments and snapshotting in CI without starting a server.

func JSONLog

func JSONLog(dest LogWriter) *jsonLog

JSONLog returns a structured (JSON) access log writing to the given destination. By default every request is logged; use Sample to record only a fraction of requests at high traffic volumes.

func Main

func Main(cfg Config)

Main is a thin CLI wrapper around Run, Export, and a validate-only mode. It parses the standard process arguments and dispatches:

-export    Write the resolved configuration as JSON to stdout and exit.
-validate  Validate the configuration and exit. Prints "ok" on success.
(no flag)  Equivalent to Run.

Programs that want a clean entry point without flag handling can call Run directly.

func OTLP

func OTLP(endpoint string) *otlpTracing

OTLP configures distributed tracing via OTLP/gRPC to the given collector endpoint (for example "otel-collector:4317"). Spans are produced for every incoming request with HTTP semantic conventions, and W3C trace context is propagated to upstream backends.

func RateLimit

func RateLimit(rate string) *rateLimitMW

RateLimit returns a rate-limit middleware. The rate string is of the form "N/unit" where unit is one of s, min, h. For example "100/min".

func Resolve

func Resolve(cfg Config) (*resolved.Config, error)

Resolve validates the surface configuration, fills defaults, and produces the canonical resolved schema. Resolve is pure: it does not touch the network, the filesystem, or process state.

func Retry

func Retry(max int, opts ...RetryOption) *retryMW

Retry returns a retry middleware with the given maximum attempts and options.

func Run

func Run(cfg Config)

Run validates, resolves, and runs the configuration. It blocks until the process receives SIGINT or SIGTERM, then performs a graceful shutdown.

Any validation or startup error is fatal: Run logs and exits non-zero.

func Timeout

func Timeout(dur string) *timeoutMW

Timeout returns a per-request timeout middleware.

Types

type AccessLog

type AccessLog interface {
	// contains filtered or unexported methods
}

AccessLog is a marker for an access log destination.

type AutoTLSConfig

type AutoTLSConfig struct {
	Domains []string
	// contains filtered or unexported fields
}

AutoTLSConfig declares ACME-managed TLS material.

func AutoTLS

func AutoTLS(domains ...string) *AutoTLSConfig

AutoTLS configures ACME auto-provisioning for the given domains.

func (*AutoTLSConfig) CloudflareDNS01

func (a *AutoTLSConfig) CloudflareDNS01(apiToken string) *AutoTLSConfig

CloudflareDNS01 switches the ACME challenge from HTTP-01 to DNS-01 using Cloudflare's DNS API. Required for wildcard certificates and useful when :80 is not reachable from the public internet (private networks, Cloudflare-only origins, etc.).

The token must be a Cloudflare API Token (not the legacy Global API Key) with the Zone.DNS:Edit permission for the zone(s) covering the listener's domains. Generate one at https://dash.cloudflare.com/profile/api-tokens.

The zone is auto-discovered from each domain by walking the DNS labels against the account's zone list. Use Zone() to pin a specific zone ID and skip discovery.

Returns the parent AutoTLSConfig so the call chain remains a single ListenerOption value usable as an argument to HTTPS.

func (*AutoTLSConfig) Email

func (a *AutoTLSConfig) Email(email string) *AutoTLSConfig

Email sets the contact email registered with the ACME directory.

func (*AutoTLSConfig) Storage

func (a *AutoTLSConfig) Storage(path string) *AutoTLSConfig

Storage sets the on-disk path where issued certificates and ACME state are persisted. Required for production use.

func (*AutoTLSConfig) Zone

func (a *AutoTLSConfig) Zone(id string) *AutoTLSConfig

Zone pins the Cloudflare zone ID for DNS-01 challenges. Must be called after CloudflareDNS01. When unset the zone is discovered by querying Cloudflare for the zone whose name is a suffix of each domain.

type Backend

type Backend struct {
	// Address is the host:port of the backend.
	Address string
	// Weight is the relative weight for weighted strategies. Defaults to 1.
	Weight int
	// Backup is true for failover-only backends; they receive traffic only
	// when all primary backends are unhealthy.
	Backup bool
}

Backend is a single upstream target.

type CompressAlgo

type CompressAlgo int

CompressAlgo identifies a content-encoding algorithm.

const (
	// Gzip compression.
	Gzip CompressAlgo = iota
	// Brotli compression.
	Brotli
)

func (CompressAlgo) String

func (a CompressAlgo) String() string

String returns the canonical name of the algorithm.

type Config

type Config struct {
	Listeners     Listeners
	Upstreams     Upstreams
	Routes        Routes
	Defaults      Defaults
	Observability Observability
	Shutdown      Shutdown
}

Config is the top-level surface configuration.

type Defaults

type Defaults struct {
	// ReadHeaderTimeout caps how long a client may take to send the request
	// headers. The Go standard library has no default; setting this is the
	// primary mitigation for Slowloris denial-of-service.
	ReadHeaderTimeout string

	// ReadTimeout caps the entire request read, including body. Use with care
	// for streaming and long-poll endpoints.
	ReadTimeout string

	// WriteTimeout caps how long the server may take to write the response.
	WriteTimeout string

	// IdleTimeout caps how long an idle keep-alive connection may sit between
	// requests before being closed.
	IdleTimeout string

	// MaxHeaderBytes caps the size of the request header block. Defaults to
	// the Go standard library default of 1MB when unset.
	MaxHeaderBytes int
}

Defaults sets the conservative production baseline for all listeners. Routes may override individual values via middleware.

type HealthCheck

type HealthCheck struct {
	Path      string // HTTP path to probe; empty disables active health checks
	Interval  string // how often to probe; e.g. "10s"
	Timeout   string // probe timeout; e.g. "2s"
	Healthy   int    // consecutive successes to mark healthy; defaults to 2
	Unhealthy int    // consecutive failures to mark unhealthy; defaults to 3
}

HealthCheck configures active health checks against backends.

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener is a surface listener declaration. Construct via HTTP or HTTPS.

func HTTP

func HTTP(addr string) *Listener

HTTP starts an HTTP/1.1 listener declaration on the given address.

func HTTPS

func HTTPS(addr string, opts ...ListenerOption) *Listener

HTTPS starts an HTTPS listener declaration on the given address. Options configure TLS material, HTTP/2, and HTTP/3.

func (*Listener) RedirectTo

func (l *Listener) RedirectTo(scheme string) *Listener

RedirectTo turns this listener into a permanent redirect to the named scheme. The listener will not serve content beyond the redirect.

type ListenerOption

type ListenerOption interface {
	// contains filtered or unexported methods
}

ListenerOption configures an HTTPS listener.

func BehindCloudflare

func BehindCloudflare() ListenerOption

BehindCloudflare marks the listener as sitting behind a Cloudflare proxy. This affects two things:

First, when AutoTLS is configured on the listener, the TLS-ALPN-01 challenge is suppressed (the "acme-tls/1" entry is dropped from ALPN). Cloudflare terminates TLS at its edge and does not forward custom ALPN protocols, so TLS-ALPN-01 cannot succeed. Provisioning falls back to HTTP-01, which is served by the redirect listener on :80 — Cloudflare proxies that path transparently provided "Always Use HTTPS" is disabled for /.well-known/acme-challenge/*.

Second, the request handling path trusts the CF-Connecting-IP and True-Client-IP headers as the originating client address. Other proxy headers (X-Forwarded-For) remain available but Cloudflare's are preferred because they are populated by the proxy and not user-controllable.

func HTTP2

func HTTP2() ListenerOption

HTTP2 enables HTTP/2 on the listener. Required for h2 ALPN negotiation.

func HTTP3

func HTTP3(addr string) ListenerOption

HTTP3 enables HTTP/3 (QUIC) on the listener at the given UDP address. The addr should typically match the HTTPS port suffixed with /udp, for example ":443/udp".

type Listeners

type Listeners []*Listener

Listeners is the list of listener declarations.

type LogWriter

type LogWriter struct {
	// contains filtered or unexported fields
}

LogWriter identifies a destination for structured logs.

func (LogWriter) Name

func (l LogWriter) Name() string

Name returns a human-readable name for the destination.

func (LogWriter) Writer

func (l LogWriter) Writer() io.Writer

Writer returns the underlying io.Writer. Useful when constructing the resolved configuration.

type Metrics

type Metrics interface {
	// contains filtered or unexported methods
}

Metrics is a marker for a metrics endpoint configuration.

func Prometheus

func Prometheus(addr, path string) Metrics

Prometheus exposes process and request metrics on the given address and path, formatted in the Prometheus exposition format.

type Middleware

type Middleware interface {
	// contains filtered or unexported methods
}

Middleware is a marker interface for surface middleware values. Concrete middleware constructors return values that satisfy this interface.

type Observability

type Observability struct {
	AccessLog AccessLog
	Metrics   Metrics
	Tracing   Tracing
}

Observability bundles the logging, metrics, and tracing configuration.

type Pool

type Pool struct {
	Backends    []Backend
	Strategy    Strategy
	HealthCheck HealthCheck
	Transport   Transport
}

Pool is the surface upstream pool definition.

type RateLimitKey

type RateLimitKey int

RateLimitKey selects what attribute of the request a rate limit is keyed on.

const (
	// ClientIP keys the rate limiter on the client's IP address.
	ClientIP RateLimitKey = iota
	// HostHeader keys the rate limiter on the Host header.
	HostHeader
)

func (RateLimitKey) String

func (k RateLimitKey) String() string

String returns the canonical name of the key.

type RetryOption

type RetryOption interface {
	// contains filtered or unexported methods
}

RetryOption configures the Retry middleware.

func OnStatus

func OnStatus(codes ...int) RetryOption

OnStatus retries when the upstream returns any of the given status codes.

type Route

type Route struct {
	// contains filtered or unexported fields
}

Route is a surface route declaration. Construct via Match.

func Match

func Match(pattern string) *Route

Match begins a route declaration matching the given path pattern. Patterns support a trailing /* wildcard.

func (*Route) Host

func (r *Route) Host(host string) *Route

Host scopes this route to the given Host header value. Empty means any host.

func (*Route) ProxyTo

func (r *Route) ProxyTo(upstream string) *Route

ProxyTo proxies matching requests to the named upstream pool.

func (*Route) Serve

func (r *Route) Serve(dir string) *Route

Serve serves matching requests as static files from the given directory.

func (*Route) With

func (r *Route) With(mws ...Middleware) *Route

With attaches middleware to the route. Middleware runs in declaration order before the upstream proxy or static file handler.

type Routes

type Routes []*Route

Routes is the list of route declarations, matched in declaration order.

type Shutdown

type Shutdown struct {
	// GracePeriod is the maximum time the server will wait for in-flight
	// requests to finish before forcibly closing connections. e.g. "30s".
	GracePeriod string

	// DrainListeners closes listeners (stops accepting new connections)
	// before waiting for in-flight requests. Recommended for production.
	DrainListeners bool
}

Shutdown configures graceful shutdown behaviour.

type StaticTLSConfig

type StaticTLSConfig struct {
	CertFile string
	KeyFile  string
}

StaticTLSConfig declares pre-provisioned TLS material.

func StaticTLS

func StaticTLS(certFile, keyFile string) *StaticTLSConfig

StaticTLS configures TLS using a static certificate and key on disk.

type Strategy

type Strategy int

Strategy selects how a request is routed across the backends in a pool.

const (
	// RoundRobin distributes requests evenly across backends in declaration order.
	RoundRobin Strategy = iota
	// LeastConnections sends each request to the backend with the fewest in-flight requests.
	LeastConnections
	// IPHash routes requests from the same client IP to the same backend (consistent hash).
	IPHash
	// Weighted distributes requests proportionally to each backend's Weight.
	Weighted
)

func (Strategy) String

func (s Strategy) String() string

String returns the canonical name of the strategy.

type Tracing

type Tracing interface {
	// contains filtered or unexported methods
}

Tracing is a marker for a distributed-tracing exporter configuration.

type Transport

type Transport struct {
	MaxIdleConnsPerHost int
	IdleConnTimeout     string // e.g. "90s"
	DialTimeout         string // e.g. "5s"
	TLSHandshakeTimeout string // e.g. "5s"
}

Transport tunes the HTTP transport used to reach backends.

type Upstreams

type Upstreams map[string]Pool

Upstreams maps an upstream name to its pool definition. Routes refer to upstreams by name.

Directories

Path Synopsis
examples
basic command
cloudflare command
Example: a statute deployment fronted by Cloudflare with origin AutoTLS.
Example: a statute deployment fronted by Cloudflare with origin AutoTLS.
cloudflare-wildcard command
Example: wildcard certificate via Cloudflare DNS-01.
Example: wildcard certificate via Cloudflare DNS-01.
http-only command
Package resolved is the canonical, fully-validated schema that the statute runtime operates on.
Package resolved is the canonical, fully-validated schema that the statute runtime operates on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL