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 ¶
- func BaseURLFromRequest(c *gin.Context, defaultPort int) string
- func CORS() gin.HandlerFunc
- func CORSWithConfig(config CORSConfig) gin.HandlerFunc
- func ClearServerIOTimeouts() gin.HandlerFunc
- func Gzip() gin.HandlerFunc
- func RateLimitMiddleware(rl *RateLimiter, authPaths ...string) gin.HandlerFunc
- type APITokenStore
- type AuthMiddleware
- type CORSConfig
- type ErrorDetail
- type ErrorResponse
- type MultiModeMemoryLogMiddleware
- func (m *MultiModeMemoryLogMiddleware) Clear()
- func (m *MultiModeMemoryLogMiddleware) GetEntries() []*logrus.Entry
- func (m *MultiModeMemoryLogMiddleware) GetEntriesByLevel(level logrus.Level) []*logrus.Entry
- func (m *MultiModeMemoryLogMiddleware) GetEntriesSince(since time.Time) []*logrus.Entry
- func (m *MultiModeMemoryLogMiddleware) GetLatestEntries(n int) []*logrus.Entry
- func (m *MultiModeMemoryLogMiddleware) Middleware() gin.HandlerFunc
- func (m *MultiModeMemoryLogMiddleware) Size() int
- type RateLimiter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BaseURLFromRequest ¶ added in v0.260625.1
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 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 ¶
func (m *MultiModeMemoryLogMiddleware) Middleware() gin.HandlerFunc
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 ¶
func (m *MultiModeMemoryLogMiddleware) Size() int
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)