middleware

package
v0.35.22 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

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

View Source
const AdminSessionCookieName = adminSessionCookieName

AdminSessionCookieName is exported for server integration tests that verify setup/auth transitions without duplicating the cookie contract.

Variables

This section is empty.

Functions

func AdminPasswordMatches added in v0.33.0

func AdminPasswordMatches(username, passwordHash, presentedUser, presentedPassword string) bool

func Chain

func Chain(mws ...Middleware) func(http.Handler) http.Handler

Chain composes middlewares left-to-right: the first argument is the outermost wrapper (runs first on request, last on response).

func NewAdminSessionCookie added in v0.33.0

func NewAdminSessionCookie(username, passwordHash string, secure bool, now time.Time) (*http.Cookie, error)

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
	BearerRole        string
	AllowPublicPaths  []string // exact path matches that skip auth entirely (e.g. /healthz)
	AllowPublicRoutes []PublicRoute
	// HTMLUnauthorizedPaths/Routes keep browser-facing admin UI failures out
	// of the JSON API envelope while still requiring normal credentials.
	HTMLUnauthorizedPaths  []string
	HTMLUnauthorizedRoutes []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
	BearerRoleProvider        func() string
	AdminUsernameProvider     func() string
	AdminPasswordHashProvider func() string
	// SmokeTokenProvider returns the optional public demo token used by the
	// smoke UI on `/`. When non-empty and matching the presented Bearer,
	// the middleware attaches a Source="smoke", Plan="demo" identity so
	// handlers and the rate-limiter can treat demo traffic accordingly.
	SmokeTokenProvider 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 NewAuthState(mode, bearerTokenEnv, edgeSecretEnv, adminUsername, adminPasswordHash string) *AuthState

func (*AuthState) AdminPasswordHash added in v0.31.0

func (s *AuthState) AdminPasswordHash() string

func (*AuthState) AdminUsername added in v0.31.0

func (s *AuthState) AdminUsername() string

func (*AuthState) BearerToken added in v0.28.2

func (s *AuthState) BearerToken() string

func (*AuthState) BearerTokenEnv added in v0.28.2

func (s *AuthState) BearerTokenEnv() string

func (*AuthState) EdgeSecret added in v0.28.2

func (s *AuthState) EdgeSecret() string

func (*AuthState) Mode added in v0.28.2

func (s *AuthState) Mode() string

func (*AuthState) Set added in v0.28.2

func (s *AuthState) Set(mode, bearerTokenEnv, edgeSecretEnv string)

func (*AuthState) SetAdmin added in v0.31.0

func (s *AuthState) SetAdmin(username, passwordHash string)

func (*AuthState) SetSmokeTokenEnv added in v0.35.3

func (s *AuthState) SetSmokeTokenEnv(envName string)

SetSmokeTokenEnv records the env var name holding the optional public demo token. Empty string disables smoke-from-page authentication.

func (*AuthState) SmokeToken added in v0.35.3

func (s *AuthState) SmokeToken() string

SmokeToken resolves the current value of the demo bearer token, or "" if the env var is unset / unconfigured.

func (*AuthState) SmokeTokenEnv added in v0.35.3

func (s *AuthState) SmokeTokenEnv() string

SmokeTokenEnv returns the configured env var name for the demo token.

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" | "basic" | "admin_session"
}

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

func IdentityFromContext(ctx context.Context) Identity

IdentityFromContext returns the Identity attached by Auth, or the zero Identity if none is present.

type Middleware

type Middleware func(http.Handler) http.Handler

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 PublicRoute struct {
	Path       string
	PathPrefix string
	PathSuffix string
	Methods    []string
}

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.

Jump to

Keyboard shortcuts

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