/** * Operator-owned limits for the shared Play Runtime traffic path. * * The compiled defaults are intentionally permissive and safe. A live * override is a short-lived incident control, stored in Convex and read by the * worker before it can create a sandbox. It can only reduce new work; it can * never make retries, side effects, or provider limits less safe. */ export const RUNTIME_TRAFFIC_POLICY_SCOPE = 'play_runtime' as const; export const RUNTIME_TRAFFIC_POLICY_LIMITS = { /** An operator must renew a mitigation rather than accidentally leave it on. */ maxOverrideDurationMs: 2 * 60 * 60_000, /** A global start throttle may only shed load, never increase it. */ maxSandboxStartsPerMinute: 600, } as const; export type RuntimeTrafficAdmissionMode = 'normal' | 'hold_new_sandbox_starts'; export type RuntimeTrafficPolicy = Readonly<{ scope: typeof RUNTIME_TRAFFIC_POLICY_SCOPE; version: number; admissionMode: RuntimeTrafficAdmissionMode; /** Null means no extra global pacing beyond the compiled runtime policy. */ sandboxStartsPerMinute: number | null; expiresAt: number | null; reason: string | null; updatedAt: number | null; }>; export const DEFAULT_RUNTIME_TRAFFIC_POLICY: RuntimeTrafficPolicy = { scope: RUNTIME_TRAFFIC_POLICY_SCOPE, version: 0, admissionMode: 'normal', sandboxStartsPerMinute: null, expiresAt: null, reason: null, updatedAt: null, }; /** * The worker reads this value over an app-runtime boundary. Treat a malformed * successful response as an unavailable control plane, rather than allowing * JavaScript's `undefined` coercions to accidentally turn it into a throttle. */ export function isRuntimeTrafficPolicy( value: unknown, ): value is RuntimeTrafficPolicy { if (!value || typeof value !== 'object') return false; const candidate = value as Record; const sandboxStartsPerMinute = candidate.sandboxStartsPerMinute; return ( candidate.scope === RUNTIME_TRAFFIC_POLICY_SCOPE && typeof candidate.version === 'number' && Number.isSafeInteger(candidate.version) && candidate.version >= 0 && (candidate.admissionMode === 'normal' || candidate.admissionMode === 'hold_new_sandbox_starts') && (sandboxStartsPerMinute === null || (typeof sandboxStartsPerMinute === 'number' && Number.isSafeInteger(sandboxStartsPerMinute) && sandboxStartsPerMinute >= 1 && sandboxStartsPerMinute <= RUNTIME_TRAFFIC_POLICY_LIMITS.maxSandboxStartsPerMinute)) && (candidate.expiresAt === null || (typeof candidate.expiresAt === 'number' && Number.isSafeInteger(candidate.expiresAt))) && (candidate.reason === null || typeof candidate.reason === 'string') && (candidate.updatedAt === null || (typeof candidate.updatedAt === 'number' && Number.isSafeInteger(candidate.updatedAt))) ); } export function runtimeTrafficPolicyIsActive( policy: Pick, nowMs = Date.now(), ): boolean { return policy.expiresAt === null || policy.expiresAt > nowMs; } /** * Expired rows remain as audit evidence but must behave exactly like no * override. Keeping this pure makes the app, worker, and CLI agree without * relying on a best-effort expiry job. */ export function effectiveRuntimeTrafficPolicy( policy: RuntimeTrafficPolicy | null | undefined, nowMs = Date.now(), ): RuntimeTrafficPolicy { if (!policy || !runtimeTrafficPolicyIsActive(policy, nowMs)) { return DEFAULT_RUNTIME_TRAFFIC_POLICY; } return policy; } export function validateRuntimeTrafficOverride(input: { admissionMode: RuntimeTrafficAdmissionMode; sandboxStartsPerMinute: number | null; expiresAt: number; nowMs?: number; }): void { const nowMs = input.nowMs ?? Date.now(); if (!Number.isSafeInteger(input.expiresAt) || input.expiresAt <= nowMs) { throw new Error('Runtime traffic override expiry must be in the future.'); } if ( input.expiresAt - nowMs > RUNTIME_TRAFFIC_POLICY_LIMITS.maxOverrideDurationMs ) { throw new Error('Runtime traffic overrides may last at most two hours.'); } if ( input.sandboxStartsPerMinute !== null && (!Number.isSafeInteger(input.sandboxStartsPerMinute) || input.sandboxStartsPerMinute < 1 || input.sandboxStartsPerMinute > RUNTIME_TRAFFIC_POLICY_LIMITS.maxSandboxStartsPerMinute) ) { throw new Error( `sandboxStartsPerMinute must be a whole number from 1 to ${RUNTIME_TRAFFIC_POLICY_LIMITS.maxSandboxStartsPerMinute}.`, ); } }