/** * Ingress Protection * * Multiple layers of defense between the internet and your services. * Each layer catches different failure modes: * * ┌─────────────────────────────────────────────────────────────┐ * │ INTERNET │ * └───────────────────────────┬─────────────────────────────────┘ * ▼ * ┌─────────────────────────────────────────────────────────────┐ * │ Layer 1: CONNECTION LIMITER │ * │ Total open connections capped at N (e.g. 10,000). │ * │ New connections get TCP RST when full. │ * │ Prevents file descriptor exhaustion. │ * └───────────────────────────┬─────────────────────────────────┘ * ▼ * ┌─────────────────────────────────────────────────────────────┐ * │ Layer 2: RATE LIMITER (per client) │ * │ Token bucket per IP / API key. Burst-friendly. │ * │ Returns 429 Too Many Requests when exceeded. │ * │ Prevents single client from consuming all capacity. │ * └───────────────────────────┬─────────────────────────────────┘ * ▼ * ┌─────────────────────────────────────────────────────────────┐ * │ Layer 3: GLOBAL LOAD SHEDDER │ * │ Monitors gateway CPU + event loop lag. │ * │ When overloaded, sheds low-priority requests (503). │ * │ High-priority requests (health checks, auth) pass through. │ * │ Prevents cascade failure in the gateway itself. │ * └───────────────────────────┬─────────────────────────────────┘ * ▼ * ┌─────────────────────────────────────────────────────────────┐ * │ Layer 4: ADAPTIVE CONCURRENCY (per service) │ * │ Limits in-flight requests to each downstream service. │ * │ Dynamically adjusts limit based on observed latency. │ * │ When a service slows down, we send FEWER requests to it. │ * │ Returns 503 when the service's concurrency window is full. │ * │ Prevents a slow service from consuming all gateway threads.│ * └───────────────────────────┬─────────────────────────────────┘ * ▼ * ┌─────────────────────────────────────────────────────────────┐ * │ SERVICE (users, billing, etc.) │ * └─────────────────────────────────────────────────────────────┘ * * Usage in a gateway service: * * import { IngressProtection } from 'threadforge/ingress'; * * export default class GatewayService extends Service { * async onStart(ctx) { * this.ingress = new IngressProtection({ * maxConnections: 10000, * rateLimit: { windowMs: 60000, maxRequests: 100 }, * loadShedding: { eventLoopThresholdMs: 100 }, * services: { * users: { maxConcurrent: 200 }, * billing: { maxConcurrent: 50 }, * notifications: { maxConcurrent: 100 }, * }, * }); * * // Apply as middleware * ctx.router.use(this.ingress.middleware()); * } * } */ import { EventEmitter } from "node:events"; import type { IncomingMessage, ServerResponse } from "node:http"; interface TokenBucket { tokens: number; lastRefill: number; } interface RateLimiterOptions { maxTokens?: number; refillRate?: number; maxRequests?: number; windowMs?: number; keyExtractor?: (req: IncomingMessage) => string; } interface RateCheckResult { allowed: boolean; remaining: number; retryAfter?: number; } interface RateLimiterStats { activeClients: number; maxTokens: number; refillRate: number; } type Priority = "critical" | "high" | "normal" | "low"; interface PriorityRule { pattern: RegExp; priority: Priority; } interface ShedThreshold { lagMs: number; loadRatio: number; } interface LoadShedderOptions { eventLoopThresholdMs?: number; maxActiveRequests?: number; checkIntervalMs?: number; priorityRules?: PriorityRule[]; } interface ShedCheckResult { accept: boolean; reason?: string; priority: Priority; } interface LoadShedderStats { activeRequests: number; maxActiveRequests: number; eventLoopLagMs: number; loadPercent: string; shedding: boolean; } interface AdaptiveConcurrencyOptions { initialLimit?: number; minLimit?: number; maxLimit?: number; smoothing?: number; tolerance?: number; } interface AcquireResult { acquired: boolean; release?: (success?: boolean) => void; } interface AdaptiveConcurrencyStats { service: string; currentLimit: number; inFlight: number; utilization: string; minLatencyMs: string | null; smoothedLatencyMs: string; p99LatencyMs: string | null; totalRequests: number; totalRejected: number; rejectionRate: string; } interface IngressOptions { maxConnections?: number; rateLimit?: RateLimiterOptions; loadShedding?: LoadShedderOptions; services?: Record; } interface IngressStats { connections: { active: number; max: number; }; rateLimiter: RateLimiterStats; loadShedder: LoadShedderStats; services: Record; } type MiddlewareFn = (req: IncomingMessage, res: ServerResponse, next?: () => void) => Promise; /** * Per-client rate limiter using the token bucket algorithm. * * Token bucket is burst-friendly: a client that hasn't made * requests in a while accumulates tokens and can burst. * A client that's been hammering the API runs out of tokens * and gets 429'd. * * Why token bucket instead of sliding window: * - Allows natural burst patterns (page load = 10 requests at once) * - Smoother than fixed windows (no "thundering herd" at window reset) * - O(1) per request (no sorted sets or sliding counters) */ export declare class RateLimiter { maxTokens: number; refillRate: number; windowMs: number; keyExtractor: (req: IncomingMessage) => string; buckets: Map; private _cleanupTimer; constructor(options?: RateLimiterOptions); /** * Check if a request is allowed. */ check(req: IncomingMessage): RateCheckResult; private _cleanup; get stats(): RateLimiterStats; stop(): void; } /** * Global load shedder. Monitors the gateway process health and * drops requests when overloaded. * * Monitors two signals: * 1. Event loop lag — if the event loop is delayed by > threshold, * the process is CPU-starved. Start shedding. * 2. Active requests — if we have too many in-flight, shed. * * Shedding is priority-based: * - CRITICAL: health checks, auth — never shed * - HIGH: payment webhooks — shed only at extreme load * - NORMAL: regular API calls — shed when overloaded * - LOW: analytics, logging — shed first */ export declare class LoadShedder { eventLoopThresholdMs: number; maxActiveRequests: number; checkIntervalMs: number; priorityRules: PriorityRule[]; activeRequests: number; eventLoopLag: number; shedThresholds: Record; private _lastCheck; private _lagTimerActive; private _lagTimer; constructor(options?: LoadShedderOptions); /** * Should this request be accepted or shed? */ shouldAccept(req: IncomingMessage): ShedCheckResult; private _getPriority; trackRequest(): () => void; get stats(): LoadShedderStats; stop(): void; } /** * Adaptive Concurrency Limiter (per downstream service) * * The core problem: how many concurrent requests should we send * to the users service? Too few = wasted capacity. Too many = * overwhelm the service, latency spikes, cascade failure. * * Fixed limits are fragile — they're either too conservative * (wasting capacity during normal load) or too aggressive * (causing failures during peak). * * Instead, we use the Vegas algorithm (from TCP congestion control): * * 1. Start with a small concurrency window (e.g., 10) * 2. Measure round-trip latency for each request * 3. Track the minimum observed latency (the "no-load" baseline) * 4. If current latency ≈ baseline → increase the window * (service has capacity, give it more) * 5. If current latency >> baseline → decrease the window * (service is queuing, back off) * * This automatically adapts to: * - Fast services (high window, more concurrent requests) * - Slow services (low window, fewer concurrent requests) * - Services that degrade under load (window shrinks as latency rises) * - Services that recover (window grows as latency drops) * * Inspired by Netflix's concurrency-limits library. */ export declare class AdaptiveConcurrencyLimiter { serviceName: string; limit: number; minLimit: number; maxLimit: number; smoothing: number; tolerance: number; inFlight: number; minLatency: number; smoothedLatency: number; totalRequests: number; totalRejected: number; totalSuccesses: number; totalFailures: number; private _latencies; private _latencyIdx; private _latencyCount; private _minLatencyWindow; private _minLatencyWindowIdx; private _minLatencyWindowCount; private _minLatencyObservations; constructor(serviceName: string, options?: AdaptiveConcurrencyOptions); /** * Try to acquire a concurrency slot. */ tryAcquire(): AcquireResult; private _recordLatency; private _adjustLimit; get stats(): AdaptiveConcurrencyStats; private _getPercentile; } /** * Combined ingress protection middleware. * * Applies all layers in order: connection limit → rate limit → * load shedding → route → adaptive concurrency → service. */ export declare class IngressProtection extends EventEmitter { maxConnections: number; activeConnections: number; rateLimiter: RateLimiter; loadShedder: LoadShedder; serviceLimiters: Map; private _metricsInterval; constructor(options?: IngressOptions); /** * Get or create a concurrency limiter for a service. */ getServiceLimiter(serviceName: string): AdaptiveConcurrencyLimiter; /** * Returns a middleware function for the ForgeContext router. * * Usage: * ctx.router.use(this.ingress.middleware()); */ middleware(): MiddlewareFn; /** * Wrap a proxy call with adaptive concurrency limiting. * * Usage in the gateway: * const user = await this.ingress.withConcurrencyLimit('users', () => * this.users.getUser(id) * ); * * Or in the proxy interceptor layer: * The ServiceProxy automatically calls this if ingress is configured. */ withConcurrencyLimit(serviceName: string, fn: () => Promise): Promise; get stats(): IngressStats; /** * HTTP handler for ingress stats endpoint. * Mount on the metrics server: * GET /ingress → full stats */ httpHandler(req: IncomingMessage, res: ServerResponse): boolean; stop(): void; } export {}; //# sourceMappingURL=Ingress.d.ts.map