middleware

package
v0.260801.2 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: MPL-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package middleware provides Gin middleware for the tingly-box server.

Middleware Stack

Middleware is applied globally in server.setupMiddleware() in the order below. Every inbound request passes through all layers before reaching a handler.

Request
  │
  ├─ gin.Recovery          — panic → 500, prevents process crash
  ├─ MultiModeMemoryLog    — structured HTTP log + in-memory ring buffer
  ├─ CORS                  — Access-Control-* headers
  └─ Auth (per-route)      — UserAuth or ModelAuth, applied at route level

Components

MultiModeMemoryLogMiddleware (multi_mode_memory_log.go)

Logs every HTTP request to both a persistent multi-mode logger (text + JSON file via pkg/obs.MultiLogger) and an in-memory circular buffer (500 entries).

For AI-routed requests, the log entry is enriched with routing metadata after the handler returns — these fields are written into the gin context by SetTrackingContext (internal/server/tracking_context.go):

  • request_model — model name the client requested
  • routed_model — model name actually forwarded to the provider
  • routed_provider — provider name selected by the routing pipeline
  • scenario — agent scenario (e.g. "claude_code", "openai")

Non-AI routes (system/management APIs) produce no routing fields.

The access log deliberately records no request/response bodies. Mirroring bodies here (wrapping c.Request.Body / c.Writer) is unstable — it interferes with streaming, Flush/Hijack, and large or Expect-100-continue uploads — for little gain. Bodies that matter for diagnosis are recorded where they are understood: the handler, and the model_request client stage (correlated to this entry by request_id).

AuthMiddleware (auth.go)

Two distinct auth modes are applied at route registration time:

  • UserAuthMiddleware — web-UI routes; validates a static bearer token from config; sets client_id="user_authenticated".

  • ModelAuthMiddleware — AI-endpoint routes; supports three methods in priority order: 1. JWT API tokens (multi-tenant, "tb-share-*" prefix, validated from DB) 2. Global config token ("tingly-box-*" prefix) 3. Enterprise context JWT (X-TBE-Context-JWT header, HS256/RS256)

CORS (cors.go)

Applies permissive Access-Control-Allow-* headers required for the single-page web UI. Preflight OPTIONS requests are handled and short- circuited before auth runs.

RateLimit (ratelimit.go)

Token-bucket rate limiter keyed by client IP. Limits are configurable per scenario and fall back to a global default.

ClearServerIOTimeouts (io_timeout.go)

Applied to the AI protocol route groups (/tingly/:scenario[/v1]) only. Clears the per-connection read/write deadlines armed by http.Server's ReadTimeout/WriteTimeout so long-running SSE streams and large request bodies are bounded by the upstream provider timeout and client disconnect, not by wall-clock from request start (issue #1384).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BaseURLFromRequest added in v0.260625.1

func BaseURLFromRequest(c *gin.Context, defaultPort int) string

BaseURLFromRequest returns the base URL the client used to reach the server, honoring the X-Forwarded-Proto header set by reverse proxies. defaultPort is appended when the request Host carries no explicit port. This is the URL echoed back to clients (e.g. baked into generated agent configs), so it must reflect what the user actually connected to rather than the bind address.

func CORS

func CORS() gin.HandlerFunc

CORS returns a CORS middleware handler

func CORSWithConfig

func CORSWithConfig(config CORSConfig) gin.HandlerFunc

CORSWithConfig returns a CORS middleware handler with custom configuration

func ClearServerIOTimeouts added in v0.260723.1

func ClearServerIOTimeouts() gin.HandlerFunc

ClearServerIOTimeouts removes the per-connection read/write deadlines that http.Server arms from its ReadTimeout/WriteTimeout for the current request.

The server-wide WriteTimeout is armed once, when the request headers are read, and is never extended by subsequent writes. AI sampling requests are bounded by the upstream provider timeout (provider.Timeout, default 1800s) plus failover attempts — not by wall-clock from request start — so any SSE stream that outlives WriteTimeout gets its TCP connection killed mid-stream and the client sees EOF without a terminal event (Codex: "stream closed before response.completed", issue #1384). ReadTimeout similarly caps reading the request body, which agentic clients fill with the entire conversation (tens of MB) on every turn.

Applied per-group to the AI protocol endpoints only; management/UI routes keep the server-wide protection. Request lifetime on these routes remains bounded by the upstream timeout and by client-disconnect cancellation of the request context.

func Gzip added in v0.260716.1

func Gzip() gin.HandlerFunc

Gzip returns gin middleware that gzip-compresses the response body when the client accepts it. Intended for endpoints that can return large JSON payloads (usage stats, time series, records) — register it per-route via swagger.WithMiddleware(middleware.Gzip()) rather than wrapping the handler directly, so it composes through the normal auth/CORS middleware chain instead of bypassing it. Do not use it on streaming/SSE endpoints.

func RateLimitMiddleware

func RateLimitMiddleware(rl *RateLimiter, authPaths ...string) gin.HandlerFunc

RateLimitMiddleware returns a Gin middleware for rate limiting This is specifically for auth endpoints (handshake, execute)

Types

type APITokenStore added in v0.260418.2200

type APITokenStore interface {
	ValidateToken(tokenID string) (*db.APITokenRecord, error)
	UpdateLastUsed(tokenID string) error
}

APITokenStore interface for token validation (abstracted for testability)

type AuthMiddleware

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

AuthMiddleware provides authentication middleware for different types of authentication

func NewAuthMiddleware

func NewAuthMiddleware(cfg *config.Config, jwtManager *auth.JWTManager, apiTokenManager *auth.APITokenManager, apiTokenStore APITokenStore) *AuthMiddleware

NewAuthMiddleware creates a new authentication middleware

func (*AuthMiddleware) ModelAuthMiddleware

func (am *AuthMiddleware) ModelAuthMiddleware() gin.HandlerFunc

ModelAuthMiddleware middleware for OpenAI and Anthropic API authentication The auth will support both `Authorization` and `X-Api-Key` Supports three authentication methods (in order of precedence): 1. JWT API tokens (when multi-tenant is enabled) 2. Global config model token (backward compatibility) 3. Enterprise context JWT (X-TBE-Context-JWT header)

func (*AuthMiddleware) UserAuthMiddleware

func (am *AuthMiddleware) UserAuthMiddleware() gin.HandlerFunc

UserAuthMiddleware middleware for UI and control API authentication

type CORSConfig

type CORSConfig struct {
	AllowOrigins    string
	AllowMethods    string
	AllowHeaders    string
	ExposeHeaders   string
	MaxAge          int
	HandlePreflight bool
}

CORSConfig defines the configuration for CORS middleware

type ErrorDetail

type ErrorDetail struct {
	Message string `json:"message"`
	Type    string `json:"type"`
	Code    string `json:"code,omitempty"`
}

ErrorDetail represents error details

type ErrorResponse

type ErrorResponse struct {
	Error ErrorDetail `json:"error"`
}

ErrorResponse represents an error response

type MultiModeMemoryLogMiddleware

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

MultiModeMemoryLogMiddleware is the HTTP access log for the whole request chain. It records one structured entry per request — method, path, status, latency, error, and (for AI routes) routing metadata — correlated across stages by a request_id. Entries go to the multi-mode logger (text + JSON files) and an in-memory ring buffer for the logs API.

It deliberately does NOT capture request/response bodies. Opportunistically mirroring bodies here (wrapping c.Request.Body / c.Writer) is unstable — it interferes with streaming, Flush/Hijack, and large/Expect-100-continue uploads — for little gain: the bodies that matter for diagnosis are recorded where they are understood (the handler and the model_request client stage).

func NewMultiModeMemoryLogMiddleware

func NewMultiModeMemoryLogMiddleware(multiLogger *obs.MultiLogger) *MultiModeMemoryLogMiddleware

NewMultiModeMemoryLogMiddleware creates the HTTP access log middleware.

func (*MultiModeMemoryLogMiddleware) Clear

func (m *MultiModeMemoryLogMiddleware) Clear()

Clear removes all log entries from memory

func (*MultiModeMemoryLogMiddleware) GetEntries

func (m *MultiModeMemoryLogMiddleware) GetEntries() []*logrus.Entry

GetEntries returns all log entries from memory in chronological order

func (*MultiModeMemoryLogMiddleware) GetEntriesByLevel

func (m *MultiModeMemoryLogMiddleware) GetEntriesByLevel(level logrus.Level) []*logrus.Entry

GetEntriesByLevel returns log entries from memory matching the specified level

func (*MultiModeMemoryLogMiddleware) GetEntriesSince

func (m *MultiModeMemoryLogMiddleware) GetEntriesSince(since time.Time) []*logrus.Entry

GetEntriesSince returns log entries from memory after the specified time

func (*MultiModeMemoryLogMiddleware) GetLatestEntries

func (m *MultiModeMemoryLogMiddleware) GetLatestEntries(n int) []*logrus.Entry

GetLatestEntries returns the newest N log entries from memory

func (*MultiModeMemoryLogMiddleware) Middleware

Middleware returns a Gin middleware compatible with gin.Logger() It logs all HTTP requests to both the multi-mode logger and memory

func (*MultiModeMemoryLogMiddleware) Size

Size returns the current number of stored log entries in memory

type RateLimiter

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

RateLimiter provides rate limiting functionality

func NewRateLimiter

func NewRateLimiter(maxAttempts int, windowSize, blockDuration time.Duration) *RateLimiter

NewRateLimiter creates a new rate limiter

func (*RateLimiter) Cleanup

func (rl *RateLimiter) Cleanup()

cleanup runs periodically to remove expired entries

func (*RateLimiter) GetStats

func (rl *RateLimiter) GetStats() map[string]interface{}

GetStats returns rate limiting statistics

func (*RateLimiter) ResetIP

func (rl *RateLimiter) ResetIP(ip string)

ResetIP resets the rate limit for a specific IP (admin use only)

Jump to

Keyboard shortcuts

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