function clampInteger(value, fallback, min, max) { const number = Number(value); if (!Number.isFinite(number)) return fallback; return Math.min(max, Math.max(min, Math.floor(number))); } function normalizeList(value) { const items = Array.isArray(value) ? value : String(value ?? '').split(','); return [...new Set(items.map((item) => String(item).trim()).filter(Boolean))]; } export function normalizeExecutorAdmissionConfig(input = {}) { return { tenantAllowlist: normalizeList(input.tenantAllowlist), userAllowlist: normalizeList(input.userAllowlist), workspaceKindAllowlist: normalizeList( input.workspaceKindAllowlist ?? ['workspace-alias'], ), maxGlobalActive: clampInteger(input.maxGlobalActive, 20, 1, 10_000), maxTenantActive: clampInteger(input.maxTenantActive, 5, 1, 1_000), maxUserActive: clampInteger(input.maxUserActive, 2, 1, 100), maxTenantPerMinute: clampInteger(input.maxTenantPerMinute, 30, 1, 100_000), maxUserPerMinute: clampInteger(input.maxUserPerMinute, 10, 1, 10_000), }; } export function createExecutorAdmissionPolicy({ store, config = {}, nowMs = Date.now, } = {}) { if (typeof store?.getAdmissionStats !== 'function') { throw new Error('Executor admission policy requires store.getAdmissionStats'); } const normalized = normalizeExecutorAdmissionConfig(config); function denied(reason, details = {}) { return { version: 'executor-admission-v1', allowed: false, reason, details, }; } return { config: normalized, async evaluate(request) { const tenantId = request.subject?.tenantId; const userId = request.subject?.userId; const workspaceKind = request.task?.workspaceRef?.kind; if (normalized.tenantAllowlist.length && !normalized.tenantAllowlist.includes(tenantId)) { return denied('executor_tenant_not_allowed'); } if (normalized.userAllowlist.length && !normalized.userAllowlist.includes(userId)) { return denied('executor_user_not_allowed'); } if (!normalized.workspaceKindAllowlist.includes(workspaceKind)) { return denied('executor_workspace_kind_not_allowed', { workspaceKind }); } const stats = await store.getAdmissionStats({ tenantId, userId, now: nowMs(), windowMs: 60_000, }); const checks = [ ['globalActive', normalized.maxGlobalActive, 'executor_global_concurrency_limited'], ['tenantActive', normalized.maxTenantActive, 'executor_tenant_concurrency_limited'], ['userActive', normalized.maxUserActive, 'executor_user_concurrency_limited'], ['tenantRecent', normalized.maxTenantPerMinute, 'executor_tenant_rate_limited'], ['userRecent', normalized.maxUserPerMinute, 'executor_user_rate_limited'], ]; for (const [field, limit, reason] of checks) { if (stats[field] >= limit) return denied(reason, { current: stats[field], limit }); } return { version: 'executor-admission-v1', allowed: true, reason: 'executor_admitted', details: { globalActive: stats.globalActive, tenantActive: stats.tenantActive, userActive: stats.userActive, }, }; }, status() { return { version: 'executor-admission-policy-v1', ...normalized, }; }, }; } export const executorAdmissionPolicyInternals = { clampInteger, normalizeList, };