Documentation
¶
Overview ¶
Package middleware provides HTTP middleware primitives for the SpeechKit server adapter. Each middleware is a stand-alone decorator so tests can compose them individually.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type AuthMode ¶
type AuthMode string
AuthMode selects which credential format the server accepts.
const ( // AuthModeNone disables built-in server authentication. The request still // receives a stable anonymous identity so mode handlers can apply session // ownership and rate-limit logic without requiring an upstream auth layer. AuthModeNone AuthMode = "none" // AuthModeBearer requires a static bearer token from the configured env // var. Minimum viable auth; suitable for same-network service-to-service // calls (e.g. kombify-AI → speechkit over Render private network). AuthModeBearer AuthMode = "bearer" // AuthModeEdgeHMAC trusts HMAC-signed headers from a known edge // (Cloudflare Worker / reverse proxy). The actual user identity comes // from the edge. Expected header set: // X-Edge-Auth-Hmac, X-Edge-User-Id, X-Edge-Org-Id, X-Edge-Plan, // and optional X-Edge-Role. Role is covered by the HMAC when present. AuthModeEdgeHMAC AuthMode = "edge_hmac" // AuthModeBearerOrEdge accepts either credential format; handy when a // single deployment serves both internal services (bearer) and // browser-originated traffic (edge-signed). AuthModeBearerOrEdge AuthMode = "bearer_or_edge" )
type AuthOptions ¶
type AuthOptions struct {
Mode string
BearerTokenEnv string
EdgeSecretEnv string
AllowPublicPaths []string // exact path matches that skip auth entirely (e.g. /healthz)
AllowPublicRoutes []PublicRoute
// Dynamic providers are evaluated for every request. They let first-run
// setup generate a token without rebuilding the middleware chain.
ModeProvider func() string
BearerTokenProvider func() string
EdgeSecretProvider func() string
// Bootstrap routes are public only while BootstrapAllowed returns true.
// The server uses this for the first settings write when bearer auth is
// configured but no bearer token exists yet.
AllowBootstrapPaths []string
AllowBootstrapRoutes []PublicRoute
BootstrapAllowed func(*http.Request) bool
}
AuthOptions configures the Auth middleware.
type AuthState ¶ added in v0.28.2
type AuthState struct {
// contains filtered or unexported fields
}
AuthState is a concurrency-safe view of the mutable auth config used by the server setup flow. It stores env var names, not secret values.
func NewAuthState ¶ added in v0.28.2
func (*AuthState) BearerToken ¶ added in v0.28.2
func (*AuthState) BearerTokenEnv ¶ added in v0.28.2
func (*AuthState) EdgeSecret ¶ added in v0.28.2
type Identity ¶
type Identity struct {
UserID string `json:"user_id"`
OrgID string `json:"org_id"`
Plan string `json:"plan"`
Role string `json:"role,omitempty"` // "admin" | "" (default)
Source string `json:"source"` // "none" | "bearer" | "edge_hmac"
}
Identity is attached to the request context by Auth and consumed by mode handlers for rate-limit keying, session ownership, and audit logs.
func IdentityFromContext ¶
IdentityFromContext returns the Identity attached by Auth, or the zero Identity if none is present.
type Middleware ¶
Middleware is the standard HTTP decorator signature.
func Auth ¶
func Auth(opts AuthOptions) Middleware
Auth validates credentials according to the configured mode and attaches the resolved Identity to the request context. Unauthenticated requests receive 401 with a JSON error envelope.
func CORS ¶
func CORS(allowedOrigins []string) Middleware
CORS returns a middleware that handles preflight and attaches CORS response headers when the request Origin matches one of the allowed entries.
Empty allowedOrigins disables CORS entirely (safe default; internal service-to-service calls never trigger a browser preflight anyway). Special-case "*" opens CORS to all origins — use with caution and only for OSS dev mode, never when the server is behind auth that trusts cookies.
func Logging ¶
func Logging() Middleware
Logging emits a structured slog record per request including method, path, status, bytes written, and wall-clock duration. Kept dependency-free so the adapter stays transportable to any Go runtime that ships slog.
func RateLimit ¶
func RateLimit(opts RateLimitOptions) Middleware
RateLimit returns a middleware that enforces a per-identity token bucket. Identities come from the Auth middleware; requests without an identity key on the remote address, which is rough but adequate for v1. For production behind a trusted LB, swap to a distributed limiter (Redis, envoy).
Keeping it in-memory is a deliberate v1 choice — it adds zero external dependencies to the OSS Server-Target. The map is bounded by MaxBuckets with LRU eviction so a flood of distinct identities cannot exhaust memory.
func Recover ¶
func Recover() Middleware
Recover turns panics in downstream handlers into 500 responses with a JSON error envelope, logging the stack trace for post-mortem debugging. Without this, a single buggy handler crashes the whole process.
type PublicRoute ¶ added in v0.28.0
type RateLimitOptions ¶
type RateLimitOptions struct {
RequestsPerSecond float64 // sustained rate; zero disables limiting
Burst int // max tokens in bucket; zero disables limiting
// AllowPublicPaths is the list of exact request paths that bypass the
// limiter entirely. Production deployments must always include
// `/healthz` and `/readyz` so external probes (Render, Kubernetes) are
// never rate-limited away from a real outage.
AllowPublicPaths []string
// MaxBuckets caps the in-memory bucket map. Zero falls back to
// defaultRateLimitMaxBuckets. Once the cap is hit, the least-recently-
// used bucket is evicted before a new one is inserted.
MaxBuckets int
// SweepInterval controls how often a background goroutine scans the
// bucket map and evicts entries whose last access is older than
// SweepMaxAge. Zero falls back to 5 minutes; set negative to disable.
SweepInterval time.Duration
// SweepMaxAge is the staleness threshold beyond which buckets are
// evicted by the background sweeper. Zero falls back to a value
// derived from Burst/RequestsPerSecond.
SweepMaxAge time.Duration
// Context, when non-nil, controls the lifetime of the background
// sweeper goroutine. Cancellation stops the sweep loop. Production
// callers should pass the server's shutdown context here.
Context context.Context //nolint:containedctx // intentional middleware-lifetime ctx
}
RateLimitOptions configures the in-memory token-bucket limiter.