@govplane/runtime-sdk 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/RuntimeClient.ts","../src/engine/context.ts","../src/engine/when.ts","../src/engine/traceDispatcher.ts","../src/engine/toStructuredTrace.ts","../src/engine/emitTrace.ts","../src/engine/createPolicyEngine.ts","../src/errors/Errors.ts","../src/engine/formatTrace.ts"],"sourcesContent":["import { request } from \"undici\";\nimport { createPolicyEngine } from \"../engine/createPolicyEngine\";\nimport { promises as fsp } from \"node:fs\";\nimport type {\n RuntimeBundleV1,\n Decision,\n DecisionWithOptionalTrace,\n Target,\n TraceOptions,\n StructuredTraceEvent\n} from \"../engine/types\";\nimport type { ContextPolicy } from \"../engine/context\";\nimport type { PolicyEngine } from \"../engine/types\";\n\nexport type RuntimeClientConfig = {\n baseUrl: string;\n runtimeKey: string;\n orgId: string;\n projectId: string;\n env: string;\n\n // polling\n pollMs?: number; // default 5000\n burstPollMs?: number; // default 500\n burstDurationMs?: number; // default 30000\n\n timeoutMs?: number; // default 5000\n userAgent?: string;\n\n // backoff (Phase 0+)\n backoffBaseMs?: number; // default 500\n backoffMaxMs?: number; // default 30000\n backoffJitter?: number; // default 0.2 (±20%)\n degradeAfterFailures?: number; // default 3\n\n /**\n * Engine config\n */\n engine?: {\n validateContext?: boolean; // default true\n contextPolicy?: ContextPolicy;\n };\n\n /**\n * Decision trace (SDK-only, zero PII)\n */\n trace?: {\n defaults?: TraceOptions; // level/sampling/budget/force\n onDecisionTrace?: (evt: StructuredTraceEvent) => void; // sync\n onDecisionTraceAsync?: (evt: StructuredTraceEvent) => Promise<void>; // async\n queueMax?: number; // default 1000\n dropPolicy?: \"drop_new\" | \"drop_old\"; // default drop_new\n onTraceError?: (err: unknown) => void;\n };\n\n // Incident controls (no endpoints)\n incidentEnvFlag?: string; // default \"GP_RUNTIME_INCIDENT\"\n incidentFilePath?: string; // e.g. \"/etc/govplane/incident.json\"\n incidentFilePollMs?: number; // default 1000\n incidentSignal?: \"SIGUSR1\" | false; // default \"SIGUSR1\"\n};\n\nexport type BundleMeta = {\n etag: string;\n bundleVersion?: number;\n updatedAt?: string;\n};\n\nexport type RuntimeCache<TBundle = unknown> = {\n meta?: BundleMeta;\n bundle?: TBundle;\n};\n\nexport type RefreshResult<TBundle = unknown> =\n | { changed: false; meta?: BundleMeta }\n | { changed: true; meta: BundleMeta; bundle: TBundle };\n\nexport type RuntimeStatus =\n | { state: \"warming_up\" }\n | { state: \"ok\" }\n | {\n state: \"degraded\";\n consecutiveFailures: number;\n lastError: { message: string; at: string };\n nextRetryAt?: string;\n };\n\ntype RequiredCfg =\n Required<\n Pick<\n RuntimeClientConfig,\n | \"pollMs\"\n | \"burstPollMs\"\n | \"burstDurationMs\"\n | \"timeoutMs\"\n | \"backoffBaseMs\"\n | \"backoffMaxMs\"\n | \"backoffJitter\"\n | \"degradeAfterFailures\"\n >\n > &\n Omit<\n RuntimeClientConfig,\n | \"pollMs\"\n | \"burstPollMs\"\n | \"burstDurationMs\"\n | \"timeoutMs\"\n | \"backoffBaseMs\"\n | \"backoffMaxMs\"\n | \"backoffJitter\"\n | \"degradeAfterFailures\"\n >;\n\n type IncidentFilePayload = {\n burst?: boolean;\n burstDurationMs?: number;\n burstPollMs?: number;\n refreshNow?: boolean;\n };\n\n/**\n * RuntimeClient = polling/backoff/degraded + cached runtime bundle + policy evaluation.\n *\n * - No endpoints expuestos por el cliente\n * - No PII en trace: el engine emite StructuredTraceEvent sin context ni reglas completas\n */\nexport class RuntimeClient<TBundle = RuntimeBundleV1> {\n private readonly cfg: RequiredCfg;\n\n private cache: RuntimeCache<TBundle> = {};\n private timer: NodeJS.Timeout | null = null;\n private isRunning = false;\n private hasValidBundle = false;\n\n private inFlightRefresh: Promise<RefreshResult<TBundle>> | null = null;\n private burstUntil = 0;\n\n // health / degraded state\n private consecutiveFailures = 0;\n private lastError: { message: string; at: string } | null = null;\n private nextRetryAtMs: number | null = null;\n\n private updateListeners: Array<(res: RefreshResult<TBundle>) => void> = [];\n private statusListeners: Array<(s: RuntimeStatus) => void> = [];\n\n // Policy engine\n private readonly engine: PolicyEngine;\n\n // Incident controls\n private incidentPollTimer: NodeJS.Timeout | null = null;\n private incidentFileLastMtimeMs: number | null = null;\n private installedSignalHandler = false;\n private sigHandler?: () => void;\n\n constructor(config: RuntimeClientConfig) {\n this.cfg = {\n ...config,\n pollMs: config.pollMs ?? 5000,\n burstPollMs: config.burstPollMs ?? 500,\n burstDurationMs: config.burstDurationMs ?? 30_000,\n timeoutMs: config.timeoutMs ?? 5000,\n\n backoffBaseMs: config.backoffBaseMs ?? 500,\n backoffMaxMs: config.backoffMaxMs ?? 30_000,\n backoffJitter: config.backoffJitter ?? 0.2,\n degradeAfterFailures: config.degradeAfterFailures ?? 3,\n\n incidentEnvFlag: config.incidentEnvFlag ?? \"GP_RUNTIME_INCIDENT\",\n incidentFilePollMs: config.incidentFilePollMs ?? 1000,\n incidentSignal: config.incidentSignal ?? \"SIGUSR1\"\n \n };\n\n // Wiring: el client conecta bundle cacheado + trace sinks + context policy en el engine\n this.engine = createPolicyEngine({\n getBundle: () => this.cache.bundle as any,\n\n validateContext: config.engine?.validateContext !== false,\n contextPolicy: config.engine?.contextPolicy,\n\n traceDefaults: config.trace?.defaults,\n traceSink: config.trace?.onDecisionTrace,\n traceSinkAsync: config.trace?.onDecisionTraceAsync,\n traceQueueMax: config.trace?.queueMax,\n traceQueueDropPolicy: config.trace?.dropPolicy,\n onTraceSinkError: config.trace?.onTraceError\n });\n }\n\n /** Subscribe to bundle updates (only when changed). */\n onUpdate(fn: (res: Extract<RefreshResult<TBundle>, { changed: true }>) => void) {\n const wrapped = (res: RefreshResult<TBundle>) => {\n if (res.changed) fn(res as any);\n };\n this.updateListeners.push(wrapped);\n return () => {\n this.updateListeners = this.updateListeners.filter((x) => x !== wrapped);\n };\n }\n\n /** Subscribe to status changes (ok/degraded). */\n onStatus(fn: (s: RuntimeStatus) => void) {\n this.statusListeners.push(fn);\n fn(this.getStatus());\n return () => {\n this.statusListeners = this.statusListeners.filter((x) => x !== fn);\n };\n }\n\n /** Evaluate a decision using the cached bundle (deny-by-default if bundle missing/invalid). */\n evaluate(input: { target: Target; context?: Record<string, unknown> }): Decision {\n return this.engine.evaluate(input);\n }\n\n /** Evaluate and (optionally) attach trace based on trace defaults/overrides. */\n evaluateWithTrace(\n input: { target: Target; context?: Record<string, unknown> },\n options?: TraceOptions\n ): DecisionWithOptionalTrace {\n return this.engine.evaluateWithTrace(input, options);\n }\n\n /** Flush async trace queue (only does something if traceSinkAsync is configured). */\n flushTraces(): Promise<void> {\n return this.engine.flushTraces();\n }\n\n getCached(): RuntimeCache<TBundle> {\n return { ...this.cache };\n }\n\n getStatus(): RuntimeStatus {\n // si aún no hemos cacheado un bundle válido, es warming_up\n const b: any = this.cache.bundle;\n const valid = !!(b && typeof b === \"object\" && b.schemaVersion === 1 && Array.isArray(b.policies));\n if (!this.hasValidBundle && !valid) return { state: \"warming_up\" };\n\n if (this.consecutiveFailures >= this.cfg.degradeAfterFailures) {\n return {\n state: \"degraded\",\n consecutiveFailures: this.consecutiveFailures,\n lastError: this.lastError ?? { message: \"unknown\", at: new Date().toISOString() },\n nextRetryAt: this.nextRetryAtMs ? new Date(this.nextRetryAtMs).toISOString() : undefined\n };\n }\n return { state: \"ok\" };\n }\n\n start() {\n if (this.isRunning) return;\n this.isRunning = true;\n\n // Incident controls (primero)\n this.applyIncidentFromEnv();\n this.startIncidentFilePoller();\n this.installIncidentSignal();\n\n // Luego el loop\n this.scheduleNext(0);\n }\n\n async warmStart(opts?: { timeoutMs?: number; burst?: boolean }): Promise<void> {\n // Incident controls (por si warmStart se usa sin start)\n this.applyIncidentFromEnv();\n this.startIncidentFilePoller();\n this.installIncidentSignal();\n\n const timeoutMs = opts?.timeoutMs ?? 10_000;\n const until = Date.now() + timeoutMs;\n\n if (opts?.burst) {\n await this.refreshNow({ burst: true }).catch(() => void 0);\n } else {\n await this.refreshNow().catch(() => void 0);\n }\n\n while (Date.now() < until) {\n const b: any = this.cache.bundle;\n const valid = !!(b && typeof b === \"object\" && b.schemaVersion === 1 && Array.isArray(b.policies));\n if (valid) {\n this.hasValidBundle = true;\n return;\n }\n await new Promise((r) => setTimeout(r, 100));\n }\n\n throw new Error(\"warmStart timeout: no valid runtime bundle received\");\n }\n\n stop() {\n this.isRunning = false;\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n\n if (this.incidentPollTimer) clearInterval(this.incidentPollTimer);\n this.incidentPollTimer = null;\n this.uninstallIncidentSignal();\n }\n\n async refreshNow(opts?: { burst?: boolean }): Promise<RefreshResult<TBundle>> {\n if (opts?.burst) {\n this.burstUntil = Date.now() + this.cfg.burstDurationMs;\n if (this.isRunning) this.scheduleNext(0);\n }\n return this.refresh();\n }\n\n private emitStatusIfNeeded(prev: RuntimeStatus) {\n const next = this.getStatus();\n\n const key = (s: RuntimeStatus) => {\n if (s.state === \"warming_up\") return \"warming_up\";\n if (s.state === \"ok\") return \"ok\";\n return `degraded:${s.consecutiveFailures}:${s.lastError.message}`;\n };\n\n if (key(prev) !== key(next)) {\n for (const l of this.statusListeners) l(next);\n }\n }\n \n\n private async refresh(): Promise<RefreshResult<TBundle>> {\n if (this.inFlightRefresh) return this.inFlightRefresh;\n\n const prevStatus = this.getStatus();\n\n this.inFlightRefresh = (async () => {\n try {\n const head = await this.headBundle();\n if (!head) {\n this.markSuccess();\n this.emitStatusIfNeeded(prevStatus);\n return { changed: false, meta: this.cache.meta };\n }\n\n const prevEtag = this.cache.meta?.etag;\n if (prevEtag && head.etag === prevEtag) {\n this.cache.meta = head;\n this.markSuccess();\n this.emitStatusIfNeeded(prevStatus);\n return { changed: false, meta: head };\n }\n\n const got = await this.getBundle();\n this.markSuccess();\n this.emitStatusIfNeeded(prevStatus);\n return got.changed ? got : { changed: false, meta: got.meta };\n } catch (err: any) {\n this.markFailure(err);\n this.emitStatusIfNeeded(prevStatus);\n throw err;\n } finally {\n this.inFlightRefresh = null;\n }\n })();\n\n return this.inFlightRefresh;\n }\n\n private scheduleNext(delayMs: number) {\n if (!this.isRunning) return;\n if (this.timer) clearTimeout(this.timer);\n\n this.timer = setTimeout(async () => {\n const prevStatus = this.getStatus();\n\n try {\n // incident env flag\n this.applyIncidentFromEnv();\n\n // backoff guard (si tienes nextRetryAtMs)\n if (this.nextRetryAtMs && Date.now() < this.nextRetryAtMs) {\n this.scheduleNext(Math.max(0, this.nextRetryAtMs - Date.now()));\n return;\n }\n\n const res = await this.refresh();\n if (res.changed) {\n for (const l of this.updateListeners) l(res);\n }\n } catch {\n // swallow\n } finally {\n this.emitStatusIfNeeded(prevStatus);\n\n const next =\n this.nextRetryAtMs && Date.now() < this.nextRetryAtMs\n ? Math.max(0, this.nextRetryAtMs - Date.now())\n : this.effectivePollMs();\n\n this.scheduleNext(next);\n }\n }, delayMs);\n }\n\n private effectivePollMs() {\n const now = Date.now();\n if (now < this.burstUntil) return this.cfg.burstPollMs;\n return this.cfg.pollMs;\n }\n\n private markSuccess() {\n this.consecutiveFailures = 0;\n this.lastError = null;\n this.nextRetryAtMs = null;\n }\n\n private markFailure(err: any) {\n this.consecutiveFailures += 1;\n this.lastError = { message: String(err?.message ?? err ?? \"unknown\"), at: new Date().toISOString() };\n\n const delay = this.computeBackoffDelayMs(this.consecutiveFailures);\n this.nextRetryAtMs = Date.now() + delay;\n }\n\n private computeBackoffDelayMs(failures: number) {\n const base = this.cfg.backoffBaseMs;\n const max = this.cfg.backoffMaxMs;\n const exp = base * Math.pow(2, Math.max(0, failures - 1));\n const capped = Math.min(max, exp);\n\n const jitter = Math.max(0, Math.min(1, this.cfg.backoffJitter));\n const delta = capped * jitter;\n const min = Math.max(0, capped - delta);\n const maxJ = capped + delta;\n\n const r = Math.random();\n return Math.floor(min + r * (maxJ - min));\n }\n\n private bundleUrl(): string {\n const u = new URL(this.cfg.baseUrl);\n u.pathname = \"/v1/runtime/bundle\";\n u.searchParams.set(\"projectId\", this.cfg.projectId);\n u.searchParams.set(\"env\", this.cfg.env);\n return u.toString();\n }\n\n private commonHeaders(extra?: Record<string, string>) {\n return {\n Authorization: `Bearer ${this.cfg.runtimeKey}`,\n \"User-Agent\": this.cfg.userAgent ?? \"govplane-runtime-sdk/0.x\",\n ...extra\n };\n }\n\n private async headBundle(): Promise<BundleMeta | undefined> {\n const url = this.bundleUrl();\n\n const { statusCode, headers, body } = await request(url, {\n method: \"HEAD\",\n headers: this.commonHeaders(),\n bodyTimeout: this.cfg.timeoutMs,\n headersTimeout: this.cfg.timeoutMs\n });\n\n await body.text().catch(() => void 0);\n\n if (statusCode === 401 || statusCode === 403) throw new Error(`Unauthorized (${statusCode})`);\n if (statusCode >= 500) throw new Error(`Runtime server error (${statusCode})`);\n if (statusCode !== 200 && statusCode !== 304) return this.cache.meta;\n\n const etag = (headers[\"etag\"] as string | undefined) ?? \"\";\n if (!etag) return this.cache.meta;\n\n const bundleVersionRaw = headers[\"x-gp-bundle-version\"] as string | undefined;\n const updatedAt = headers[\"x-gp-updated-at\"] as string | undefined;\n\n return {\n etag,\n bundleVersion: bundleVersionRaw ? Number(bundleVersionRaw) : undefined,\n updatedAt\n };\n }\n\n private async getBundle(): Promise<RefreshResult<TBundle>> {\n const url = this.bundleUrl();\n const ifNoneMatch = this.cache.meta?.etag;\n\n const { statusCode, headers, body } = await request(url, {\n method: \"GET\",\n headers: this.commonHeaders(ifNoneMatch ? { \"If-None-Match\": ifNoneMatch } : undefined),\n bodyTimeout: this.cfg.timeoutMs,\n headersTimeout: this.cfg.timeoutMs\n });\n\n const txt = await body.text();\n\n if (statusCode === 304) {\n const etag = (headers[\"etag\"] as string | undefined) ?? ifNoneMatch ?? \"\";\n const bundleVersionRaw = headers[\"x-gp-bundle-version\"] as string | undefined;\n const updatedAt = headers[\"x-gp-updated-at\"] as string | undefined;\n\n const meta: BundleMeta | undefined = etag\n ? { etag, bundleVersion: bundleVersionRaw ? Number(bundleVersionRaw) : undefined, updatedAt }\n : this.cache.meta;\n\n if (meta) this.cache.meta = meta;\n return { changed: false, meta };\n }\n\n if (statusCode === 401 || statusCode === 403) throw new Error(`Unauthorized (${statusCode})`);\n if (statusCode >= 400) throw new Error(`Runtime HTTP error (${statusCode}): ${txt.slice(0, 200)}`);\n\n const etag = (headers[\"etag\"] as string | undefined) ?? \"\";\n const bundleVersionRaw = headers[\"x-gp-bundle-version\"] as string | undefined;\n const updatedAt = headers[\"x-gp-updated-at\"] as string | undefined;\n\n const meta: BundleMeta = {\n etag: etag || (ifNoneMatch ?? \"\"),\n bundleVersion: bundleVersionRaw ? Number(bundleVersionRaw) : undefined,\n updatedAt\n };\n\n const parsed = JSON.parse(txt) as any;\n\n // Normaliza: si viene { success, data }, usa data (y si data.body existe, usa data.body)\n const normalized =\n parsed?.schemaVersion === 1\n ? parsed\n : parsed?.data?.schemaVersion === 1\n ? parsed.data\n : parsed?.data?.body?.schemaVersion === 1\n ? parsed.data.body\n : parsed?.body?.schemaVersion === 1\n ? parsed.body\n : parsed;\n\n this.cache = { meta, bundle: normalized as TBundle };\n\n const nb: any = normalized;\n if (nb && typeof nb === \"object\" && nb.schemaVersion === 1 && Array.isArray(nb.policies)) {\n this.hasValidBundle = true;\n }\n\n return { changed: true, meta, bundle: normalized as TBundle };\n }\n\n // Incident controls\n private enableBurst(input: { durationMs: number; pollMs: number }) {\n const now = Date.now();\n const until = now + Math.max(0, input.durationMs);\n if (until > this.burstUntil) this.burstUntil = until;\n\n // allow runtime tuning for incident\n if (Number.isFinite(input.pollMs) && input.pollMs > 0) {\n (this.cfg as any).burstPollMs = input.pollMs;\n }\n\n console.log(`[govplane][incident] burst enabled until ${new Date(this.burstUntil).toISOString()} (pollMs=${this.cfg.burstPollMs})`);\n }\n\n private disableBurst() {\n this.burstUntil = 0;\n console.log(`[govplane][incident] burst disabled`);\n }\n\n private applyIncidentFromEnv() {\n const flag = this.cfg.incidentEnvFlag;\n if (!flag) return;\n\n const v = String(process.env[flag] ?? \"\").trim().toLowerCase();\n const on = v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\";\n\n if (on) {\n this.enableBurst({ durationMs: this.cfg.burstDurationMs, pollMs: this.cfg.burstPollMs });\n }\n\n }\n\n private startIncidentFilePoller() {\n const filePath = this.cfg.incidentFilePath;\n if (!filePath) return;\n\n if (this.incidentPollTimer) clearInterval(this.incidentPollTimer);\n\n this.incidentPollTimer = setInterval(() => {\n void this.checkIncidentFileOnce(filePath).catch(() => void 0);\n }, this.cfg.incidentFilePollMs);\n\n // run once immediately\n void this.checkIncidentFileOnce(filePath).catch(() => void 0);\n }\n\n private async checkIncidentFileOnce(filePath: string) {\n // stat: if missing, ignore\n let mtimeMs: number;\n try {\n const st = await fsp.stat(filePath);\n mtimeMs = st.mtimeMs;\n } catch {\n // file missing\n return;\n }\n\n if (this.incidentFileLastMtimeMs !== null && mtimeMs === this.incidentFileLastMtimeMs) return;\n this.incidentFileLastMtimeMs = mtimeMs;\n\n let txt: string;\n try {\n txt = await fsp.readFile(filePath, \"utf8\");\n } catch {\n return;\n }\n\n let payload: IncidentFilePayload;\n try {\n payload = JSON.parse(txt);\n } catch {\n return;\n }\n if (!payload || typeof payload !== \"object\") return;\n\n if (payload.burst === true) {\n this.enableBurst({\n durationMs: payload.burstDurationMs ?? this.cfg.burstDurationMs,\n pollMs: payload.burstPollMs ?? this.cfg.burstPollMs\n });\n\n if (this.isRunning) this.scheduleNext(0);\n } else if (payload.burst === false) {\n this.disableBurst();\n }\n\n if (payload.refreshNow === true) {\n void this.refreshNow({ burst: payload.burst === true }).catch(() => void 0);\n }\n }\n\n private installIncidentSignal() {\n if (this.installedSignalHandler) return;\n if (this.cfg.incidentSignal === false) return;\n\n const sig = this.cfg.incidentSignal;\n\n if(!sig) return;\n\n try {\n this.sigHandler = () => {\n this.enableBurst({ durationMs: this.cfg.burstDurationMs, pollMs: this.cfg.burstPollMs });\n if (this.isRunning) this.scheduleNext(0);\n void this.refreshNow({ burst: true }).catch(() => void 0);\n };\n\n process.on(sig, this.sigHandler);\n this.installedSignalHandler = true;\n } catch {\n // ignore unsupported environments\n }\n }\n\n private uninstallIncidentSignal() {\n if (!this.installedSignalHandler) return;\n if (this.cfg.incidentSignal === false) return;\n\n const sig = this.cfg.incidentSignal;\n\n if(!sig) return;\n\n try {\n if (this.sigHandler) process.off(sig, this.sigHandler);\n } catch {\n // ignore\n } finally {\n this.installedSignalHandler = false;\n this.sigHandler = undefined;\n }\n }\n}","export type ContextPolicy = {\n allowedKeys: string[];\n maxStringLen: number;\n maxArrayLen: number;\n blockLikelyPiiKeys: boolean;\n};\n\nexport const DEFAULT_CONTEXT_POLICY: ContextPolicy = {\n allowedKeys: [\n \"ctx.plan\",\n \"ctx.country\",\n \"ctx.requestTier\",\n \"ctx.feature\",\n \"ctx.amount\",\n \"ctx.isAuthenticated\",\n \"ctx.role\"\n ],\n maxStringLen: 64,\n maxArrayLen: 10,\n blockLikelyPiiKeys: true\n};\n\n// Heurística simple (no perfecta): bloquea nombres de claves típicas de PII\nconst PII_KEY_PATTERNS: RegExp[] = [\n /email/i,\n /phone/i,\n /mobile/i,\n /name/i,\n /firstname/i,\n /lastname/i,\n /address/i,\n /street/i,\n /postcode|postal/i,\n /city/i,\n /ip/i,\n /ssn/i,\n /dni|nie/i,\n /passport/i\n];\n\nconst DEFAULT_POLICY: Required<Omit<ContextPolicy, \"allowedKeys\">> = {\n maxStringLen: 80,\n maxArrayLen: 25,\n blockLikelyPiiKeys: true\n};\n\nexport function validateContext(\n ctx: Record<string, unknown>,\n policy: ContextPolicy\n): void {\n const allowed = new Set(policy.allowedKeys);\n const maxStringLen = policy.maxStringLen ?? DEFAULT_POLICY.maxStringLen;\n const maxArrayLen = policy.maxArrayLen ?? DEFAULT_POLICY.maxArrayLen;\n const blockLikelyPiiKeys = policy.blockLikelyPiiKeys ?? DEFAULT_POLICY.blockLikelyPiiKeys;\n\n for (const [k, v] of Object.entries(ctx)) {\n const path = `ctx.${k}`;\n\n if (!allowed.has(path)) {\n throw new Error(`Context key not allowed: ${path}`);\n }\n\n if (blockLikelyPiiKeys) {\n for (const re of PII_KEY_PATTERNS) {\n if (re.test(k)) {\n throw new Error(`Context key looks like PII and is blocked: ${path}`);\n }\n }\n }\n\n if (v === null || v === undefined) continue;\n\n const t = typeof v;\n if (t === \"boolean\" || t === \"number\") continue;\n\n if (t === \"string\") {\n if ((v as string).length > maxStringLen) throw new Error(`Context value too long: ${path}`);\n continue;\n }\n\n if (Array.isArray(v)) {\n if (v.length > maxArrayLen) throw new Error(`Context array too long: ${path}`);\n for (const it of v) {\n if (typeof it !== \"string\") throw new Error(`Invalid array value type: ${path}`);\n if (it.length > maxStringLen) throw new Error(`Invalid array value length: ${path}`);\n }\n continue;\n }\n\n throw new Error(`Invalid context type for ${path}`);\n }\n}","import type { WhenAstV1 } from \"./types.js\";\n\nfunction getPath(obj: any, path: string): any {\n // path esperado: \"ctx.xxx\"\n const parts = path.split(\".\");\n let cur = obj;\n for (const p of parts) {\n if (!cur || typeof cur !== \"object\") return undefined;\n cur = cur[p];\n }\n return cur;\n}\n\nexport function evalWhen(node: WhenAstV1, ctx: Record<string, unknown>): boolean {\n switch (node.op) {\n case \"and\": return node.args.every(n => evalWhen(n, ctx));\n case \"or\": return node.args.some(n => evalWhen(n, ctx));\n case \"not\": return !evalWhen(node.arg, ctx);\n\n case \"exists\": return getPath({ ctx }, node.path) !== undefined;\n\n case \"in\": {\n const v = getPath({ ctx }, node.path);\n return node.values.some(x => x === v);\n }\n\n case \"eq\": return getPath({ ctx }, node.path) === node.value;\n case \"ne\": return getPath({ ctx }, node.path) !== node.value;\n\n case \"gt\": return Number(getPath({ ctx }, node.path)) > Number(node.value);\n case \"gte\": return Number(getPath({ ctx }, node.path)) >= Number(node.value);\n case \"lt\": return Number(getPath({ ctx }, node.path)) < Number(node.value);\n case \"lte\": return Number(getPath({ ctx }, node.path)) <= Number(node.value);\n\n default:\n return false;\n }\n}","import type { StructuredTraceEvent, TraceSinkAsync, TraceQueueDropPolicy } from \"./types\";\n\ntype Opts = {\n sinkAsync: TraceSinkAsync;\n max: number;\n dropPolicy: TraceQueueDropPolicy;\n onError?: (err: unknown) => void;\n};\n\nexport class TraceDispatcher {\n private q: StructuredTraceEvent[] = [];\n private draining = false;\n\n private flushWaiters: Array<() => void> = [];\n\n constructor(private readonly opts: Opts) {}\n\n enqueue(evt: StructuredTraceEvent): void {\n if (this.q.length >= this.opts.max) {\n if (this.opts.dropPolicy === \"drop_old\") {\n this.q.shift();\n } else {\n return;\n }\n }\n\n this.q.push(evt);\n this.kick();\n }\n\n async flush(): Promise<void> {\n if (!this.draining && this.q.length === 0) return;\n\n return new Promise<void>((resolve) => {\n this.flushWaiters.push(resolve);\n this.kick();\n });\n }\n\n private kick(): void {\n if (this.draining) return;\n if (this.q.length === 0) {\n this.resolveFlushWaitersIfIdle();\n return;\n }\n\n this.draining = true;\n\n // Non-blocking drain\n queueMicrotask(() => void this.drain());\n }\n\n private resolveFlushWaitersIfIdle(): void {\n if (this.draining) return;\n if (this.q.length !== 0) return;\n\n const waiters = this.flushWaiters;\n this.flushWaiters = [];\n for (const w of waiters) w();\n }\n\n private async drain(): Promise<void> {\n try {\n while (this.q.length) {\n const evt = this.q.shift()!;\n try {\n await this.opts.sinkAsync(evt);\n } catch (err) {\n if (this.opts.onError) this.opts.onError(err);\n }\n }\n } finally {\n this.draining = false;\n // resolve flush waiters if idle now\n this.resolveFlushWaitersIfIdle();\n // if items arrived during finishing, restart\n if (this.q.length) this.kick();\n }\n }\n}","import type { Decision } from \"./types\";\nimport type { DecisionTraceCompact, DecisionTraceFull, StructuredTraceEvent, TraceLevel } from \"./types\";\n\nexport function toStructuredTraceEvent(input: {\n level: TraceLevel;\n decision: Decision;\n trace: DecisionTraceCompact | DecisionTraceFull;\n}): StructuredTraceEvent {\n const { decision, trace, level } = input;\n\n const evt: StructuredTraceEvent = {\n v: 1,\n ts: trace.evaluatedAt,\n traceId: trace.traceId,\n sampled: trace.sampled,\n level,\n target: trace.target,\n decision: decision.decision,\n reason: decision.reason,\n winner: trace.winner,\n summary: trace.summary\n };\n\n if (level === \"full\" && \"rules\" in trace) {\n evt.rules = trace.rules.map(r => ({\n policyKey: r.policyKey,\n ruleId: r.ruleId,\n priority: r.priority,\n effectType: r.effectType,\n matched: r.matched,\n discardedReason: r.discardedReason\n }));\n }\n\n return evt;\n}","import type { Decision } from \"./types\";\nimport type {\n DecisionTraceCompact,\n DecisionTraceFull,\n StructuredTraceEvent,\n TraceLevel,\n TraceSink\n} from \"./types\";\nimport { toStructuredTraceEvent } from \"./toStructuredTrace\";\nimport type { TraceDispatcher } from \"./traceDispatcher\";\n\nexport function emitTraceIfAny(input: {\n level: TraceLevel;\n decision: Decision;\n trace?: DecisionTraceCompact | DecisionTraceFull;\n\n sink?: TraceSink;\n dispatcher?: TraceDispatcher;\n\n onSinkError?: (err: unknown) => void;\n}): void {\n const { trace, decision, level } = input;\n if (!trace) return;\n\n let evt: StructuredTraceEvent;\n try {\n evt = toStructuredTraceEvent({ level, decision, trace });\n } catch (err) {\n input.onSinkError?.(err);\n return;\n }\n\n // Sync sink (best-effort)\n if (input.sink) {\n try {\n input.sink(evt);\n } catch (err) {\n input.onSinkError?.(err);\n }\n }\n\n // Async sink via dispatcher (non-blocking)\n if (input.dispatcher) {\n input.dispatcher.enqueue(evt);\n }\n}","import type { Decision, RuntimeBundleV1, Target, PolicyEngine } from \"./types\";\nimport type { ContextPolicy } from \"./context\";\nimport { validateContext, DEFAULT_CONTEXT_POLICY } from \"./context\";\nimport { evalWhen } from \"./when\";\nimport type { TraceOptions, TraceMode, DecisionWithOptionalTrace, TraceSink, TraceSinkAsync, TraceQueueDropPolicy } from \"./types\";\nimport { TraceDispatcher } from \"./traceDispatcher\";\nimport { emitTraceIfAny } from \"./emitTrace\";\n\nfunction clamp01(n: number) {\n if (!Number.isFinite(n)) return 0;\n if (n < 0) return 0;\n if (n > 1) return 1;\n return n;\n}\n\nfunction safeEffectType(effect: any): string | undefined {\n const t = effect?.type;\n return typeof t === \"string\" ? t : undefined;\n}\n\n// UUID v4 (no deps). Node 16+ usually has crypto.randomUUID.\n// Fallback uses randomBytes.\nfunction makeTraceId(): string {\n const g: any = globalThis as any;\n const cryptoAny: any = g.crypto;\n\n if (cryptoAny?.randomUUID) {\n return cryptoAny.randomUUID();\n }\n\n // Node's crypto module fallback (CJS/ESM safe: dynamic require)\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const nodeCrypto = require(\"node:crypto\");\n if (nodeCrypto?.randomUUID) return nodeCrypto.randomUUID();\n const b: Buffer | undefined = nodeCrypto.randomBytes?.(16);\n if (!b || b.length < 16) {\n throw new Error(\"Failed to generate random bytes for UUID\");\n }\n // RFC4122 v4 bits\n if (b) {\n //@ts-ignore\n b[6] = (b[6] & 0x0f) | 0x40;\n //@ts-ignore\n b[8] = (b[8] & 0x3f) | 0x80;\n }\n const hex = b.toString(\"hex\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n } catch {\n // Last resort (not cryptographically strong, but acceptable for trace correlation)\n return `gp_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;\n }\n}\n\ntype EngineOpts = {\n getBundle: () => RuntimeBundleV1 | undefined;\n\n // default: true\n validateContext?: boolean;\n\n // policy configurable (PII-safe)\n contextPolicy?: ContextPolicy;\n\n // optional: default trace options for evaluateWithTrace()\n traceDefaults?: TraceOptions;\n\n // Event emitter for trace sink\n traceSink?: TraceSink;\n traceSinkAsync?: TraceSinkAsync;\n traceQueueMax?: number; // default 1000\n traceQueueDropPolicy?: TraceQueueDropPolicy; // \"drop_new\" | \"drop_old\"\n onTraceSinkError?: (err: unknown) => void;\n};\n\ntype EvaluateInput = {\n target: Target;\n context?: Record<string, unknown>;\n};\n\ntype Match = {\n policyKey: string;\n ruleId: string;\n priority: number;\n effect: any;\n};\n\nfunction targetMatch(a: Target, b: Target) {\n return a.service === b.service && a.resource === b.resource && a.action === b.action;\n}\n\nfunction byRuleOrder(a: Match, b: Match) {\n // priority desc, policyKey asc, ruleId asc\n const pr = (Number(b.priority) || 0) - (Number(a.priority) || 0);\n if (pr !== 0) return pr;\n const pk = String(a.policyKey).localeCompare(String(b.policyKey));\n if (pk !== 0) return pk;\n return String(a.ruleId).localeCompare(String(b.ruleId));\n}\n\nfunction throttleStrictness(th: { limit: number; windowSeconds: number }) {\n const limit = Number(th?.limit);\n const windowSeconds = Number(th?.windowSeconds);\n const rate = windowSeconds > 0 ? limit / windowSeconds : Number.POSITIVE_INFINITY; // lower => stricter\n return { rate, limit, windowSeconds };\n}\n\n// Simple in-memory budget: N traces per windowMs (default 60/min)\nfunction makeTraceBudget(maxTraces: number, windowMs: number) {\n let windowStart = Date.now();\n let used = 0;\n\n return {\n allow(): boolean {\n const now = Date.now();\n if (now - windowStart >= windowMs) {\n windowStart = now;\n used = 0;\n }\n if (used >= maxTraces) return false;\n used += 1;\n return true;\n }\n };\n}\n\nexport function createPolicyEngine(opts: EngineOpts): PolicyEngine {\n const budget = makeTraceBudget(\n Number.isFinite(opts.traceDefaults?.budget?.maxTraces) ? (opts.traceDefaults!.budget!.maxTraces as number) : 60,\n Number.isFinite(opts.traceDefaults?.budget?.windowMs) ? (opts.traceDefaults!.budget!.windowMs as number) : 60_000\n );\n\n // Initialize trace dispatcher if async sink provided\n const dispatcher =\n opts.traceSinkAsync\n ? new TraceDispatcher({\n sinkAsync: opts.traceSinkAsync,\n max: Number.isFinite(opts.traceQueueMax) ? (opts.traceQueueMax as number) : 1000,\n dropPolicy: opts.traceQueueDropPolicy ?? \"drop_new\",\n onError: opts.onTraceSinkError\n })\n : undefined;\n\n function coreEvaluate(input: EvaluateInput, wantTrace: boolean) {\n const bundle = opts.getBundle();\n const ctx = input.context ?? {};\n\n if (opts.validateContext !== false) {\n validateContext(ctx, opts.contextPolicy ?? DEFAULT_CONTEXT_POLICY);\n }\n\n const traceBase = wantTrace\n ? {\n traceId: makeTraceId(),\n sampled: \"random\" as const, // overwritten if forced\n evaluatedAt: new Date().toISOString(),\n target: input.target,\n summary: {\n policiesSeen: 0,\n rulesSeen: 0,\n matched: 0,\n considered: { kill_switch: 0, deny: 0, throttle: 0, allow: 0 }\n },\n rules: [] as any[]\n }\n : null;\n\n // deny-by-default if bundle missing/invalid\n if (!bundle || bundle.schemaVersion !== 1) {\n const decision: Decision = { decision: \"deny\", reason: \"default\" };\n if (!wantTrace) return decision;\n\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: undefined\n }\n };\n }\n\n const kills: Match[] = [];\n const denies: Match[] = [];\n const throttles: Match[] = [];\n const allows: Match[] = [];\n\n const policies = bundle.policies ?? [];\n if (wantTrace) traceBase!.summary.policiesSeen = policies.length;\n\n for (const p of policies) {\n const policyKey = String((p as any).policyKey ?? \"\");\n const rules = (p as any).rules ?? [];\n\n for (const r of rules) {\n if (wantTrace) traceBase!.summary.rulesSeen += 1;\n\n const ruleId = String(r?.id ?? \"\");\n const priority = Number(r?.priority ?? 0);\n const effectType = safeEffectType(r?.effect);\n\n // disabled\n if (r?.status !== \"active\") {\n if (wantTrace) {\n traceBase!.rules.push({\n policyKey,\n ruleId,\n priority,\n effectType,\n matched: false,\n discardedReason: \"disabled\"\n });\n }\n continue;\n }\n\n // target mismatch\n if (!r?.target || !targetMatch(r.target, input.target)) {\n if (wantTrace) {\n traceBase!.rules.push({\n policyKey,\n ruleId,\n priority,\n effectType,\n matched: false,\n discardedReason: \"target_mismatch\"\n });\n }\n continue;\n }\n\n // when false\n if (r.when && !evalWhen(r.when, ctx)) {\n if (wantTrace) {\n traceBase!.rules.push({\n policyKey,\n ruleId,\n priority,\n effectType,\n matched: false,\n discardedReason: \"when_false\"\n });\n }\n continue;\n }\n\n // invalid effect\n if (!effectType) {\n if (wantTrace) {\n traceBase!.rules.push({\n policyKey,\n ruleId,\n priority,\n effectType: undefined,\n matched: false,\n discardedReason: \"invalid_effect\"\n });\n }\n continue;\n }\n\n // matched\n if (wantTrace) {\n traceBase!.summary.matched += 1;\n traceBase!.rules.push({\n policyKey,\n ruleId,\n priority,\n effectType,\n matched: true\n });\n }\n\n const m: Match = { policyKey, ruleId, priority, effect: r.effect };\n\n if (effectType === \"kill_switch\") {\n kills.push(m);\n if (wantTrace) traceBase!.summary.considered.kill_switch += 1;\n } else if (effectType === \"deny\") {\n denies.push(m);\n if (wantTrace) traceBase!.summary.considered.deny += 1;\n } else if (effectType === \"throttle\") {\n throttles.push(m);\n if (wantTrace) traceBase!.summary.considered.throttle += 1;\n } else if (effectType === \"allow\") {\n allows.push(m);\n if (wantTrace) traceBase!.summary.considered.allow += 1;\n }\n }\n }\n\n // Precedence: kill_switch > deny > throttle(strictest) > allow > deny-by-default\n if (kills.length) {\n const w = kills.sort(byRuleOrder)[0]!;\n const decision: Decision = {\n decision: \"kill_switch\",\n reason: \"rule\",\n policyKey: w.policyKey,\n ruleId: w.ruleId,\n killSwitch: w.effect.killSwitch\n };\n\n if (!wantTrace) return decision;\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: { policyKey: w.policyKey, ruleId: w.ruleId, effectType: \"kill_switch\", priority: w.priority }\n }\n };\n }\n\n if (denies.length) {\n const w = denies.sort(byRuleOrder)[0]!;\n const decision: Decision = {\n decision: \"deny\",\n reason: \"rule\",\n policyKey: w.policyKey,\n ruleId: w.ruleId\n };\n\n if (!wantTrace) return decision;\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: { policyKey: w.policyKey, ruleId: w.ruleId, effectType: \"deny\", priority: w.priority }\n }\n };\n }\n\n if (throttles.length) {\n const w = throttles\n .sort((a, b) => {\n const A = throttleStrictness(a.effect.throttle);\n const B = throttleStrictness(b.effect.throttle);\n\n // lower rate => stricter\n if (A.rate !== B.rate) return A.rate - B.rate;\n if (A.limit !== B.limit) return A.limit - B.limit;\n if (A.windowSeconds !== B.windowSeconds) return A.windowSeconds - B.windowSeconds;\n return byRuleOrder(a, b);\n })[0]!;\n\n const decision: Decision = {\n decision: \"throttle\",\n reason: \"rule\",\n policyKey: w.policyKey,\n ruleId: w.ruleId,\n throttle: w.effect.throttle\n };\n\n if (!wantTrace) return decision;\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: { policyKey: w.policyKey, ruleId: w.ruleId, effectType: \"throttle\", priority: w.priority }\n }\n };\n }\n\n if (allows.length) {\n const w = allows.sort(byRuleOrder)[0]!;\n const decision: Decision = {\n decision: \"allow\",\n reason: \"rule\",\n policyKey: w.policyKey,\n ruleId: w.ruleId\n };\n\n if (!wantTrace) return decision;\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: { policyKey: w.policyKey, ruleId: w.ruleId, effectType: \"allow\", priority: w.priority }\n }\n };\n }\n\n const decision: Decision = { decision: \"deny\", reason: \"default\" };\n if (!wantTrace) return decision;\n\n return {\n ...decision,\n trace: {\n ...traceBase!,\n winner: undefined\n }\n };\n }\n\n return {\n evaluate(input: EvaluateInput) {\n return coreEvaluate(input, false) as Decision;\n },\n\n evaluateWithTrace(input: EvaluateInput, options?: TraceOptions): DecisionWithOptionalTrace {\n const merged: TraceOptions = {\n ...(opts.traceDefaults ?? {}),\n ...(options ?? {})\n };\n\n const level = (merged.level ?? \"sampled\") as any;\n const sampling = clamp01(merged.sampling ?? 0.01);\n const force = merged.force === true;\n\n const isOff = level === \"off\";\n const isErrorLevel = level === \"errors\";\n const isFull = level === \"full\";\n const isSampled = level === \"sampled\";\n\n if (isOff) {\n return coreEvaluate(input, false) as any;\n }\n\n // Always compute decision first (no trace)\n const baseDecision: any = coreEvaluate(input, false);\n\n // errors: only trace on deny/kill_switch unless force\n if (isErrorLevel) {\n const isErrDecision = baseDecision.decision === \"deny\" || baseDecision.decision === \"kill_switch\";\n if (!isErrDecision && !force) {\n return baseDecision;\n }\n\n if (!force) {\n const ok = budget.allow();\n if (!ok) return baseDecision;\n }\n\n const out: any = coreEvaluate(input, true);\n if (out?.trace) out.trace.sampled = force ? \"forced\" : \"errors\";\n\n // errors => compact by default\n const { rules: _rules, ...compact } = out.trace ?? {};\n const finalOut = { ...out, trace: compact };\n\n emitTraceIfAny({ level: \"errors\", decision: out, trace: out.trace, sink: opts.traceSink, dispatcher, onSinkError: opts.onTraceSinkError });\n\n return finalOut;\n }\n\n // full: always trace (budget unless forced)\n if (isFull) {\n if (!force) {\n const ok = budget.allow();\n if (!ok) return baseDecision;\n }\n\n const out: any = coreEvaluate(input, true);\n if (out?.trace) out.trace.sampled = force ? \"forced\" : \"random\";\n\n emitTraceIfAny({ level: \"full\", decision: out, trace: out.trace, sink: opts.traceSink, dispatcher, onSinkError: opts.onTraceSinkError });\n\n return out;\n }\n\n // sampled: sampling + budget (compact)\n if (isSampled) {\n const include = force ? true : Math.random() < sampling;\n if (!include) return baseDecision;\n\n if (!force) {\n const ok = budget.allow();\n if (!ok) return baseDecision;\n }\n\n const out: any = coreEvaluate(input, true);\n if (out?.trace) out.trace.sampled = force ? \"forced\" : \"random\";\n\n const { rules: _rules, ...compact } = out.trace ?? {};\n const finalOut = { ...out, trace: compact };\n\n emitTraceIfAny({ level: \"sampled\", decision: out, trace: out.trace, sink: opts.traceSink, dispatcher, onSinkError: opts.onTraceSinkError });\n\n return finalOut;\n }\n\n // fallback: no trace\n return baseDecision;\n },\n flushTraces: async () => {\n if (!dispatcher) return;\n await dispatcher.flush();\n }\n };\n}","export class GovplaneError extends Error {\n constructor(message: string, public readonly code = \"GP_ERROR\", public readonly details?: any) {\n super(message);\n this.name = \"GovplaneError\";\n }\n}\n\nexport class HttpError extends GovplaneError {\n constructor(\n message: string,\n public readonly status: number,\n public readonly headers?: Record<string, string>,\n details?: any\n ) {\n super(message, \"HTTP_ERROR\", details);\n this.name = \"HttpError\";\n }\n}","import type {\n DecisionTraceCompact,\n DecisionTraceFull\n} from \"./types\";\n\ntype FormatOpts = {\n multiline?: boolean; // default: false\n includeDiscarded?: boolean; // only for full traces\n};\n\nexport function formatTrace(\n trace: DecisionTraceCompact | DecisionTraceFull,\n opts: FormatOpts = {}\n): string {\n const lines: string[] = [];\n\n const hdr =\n `[govplane] traceId=${trace.traceId} ` +\n `sampled=${trace.sampled} ` +\n `policies=${trace.summary.policiesSeen} ` +\n `rules=${trace.summary.rulesSeen} ` +\n `matched=${trace.summary.matched}`;\n\n lines.push(hdr);\n\n if (trace.winner) {\n lines.push(\n `winner → policy=${trace.winner.policyKey} ` +\n `rule=${trace.winner.ruleId} ` +\n `effect=${trace.winner.effectType} ` +\n `priority=${trace.winner.priority}`\n );\n } else {\n lines.push(`winner → none (default decision)`);\n }\n\n if (\"rules\" in trace && opts.includeDiscarded) {\n const discarded = trace.rules.filter(r => !r.matched);\n\n if (discarded.length) {\n lines.push(`discarded rules:`);\n\n for (const r of discarded) {\n lines.push(\n `- policy=${r.policyKey} ` +\n `rule=${r.ruleId} ` +\n `effect=${r.effectType ?? \"?\"} ` +\n `reason=${r.discardedReason}`\n );\n }\n }\n }\n\n if (opts.multiline) {\n return lines.join(\"\\n\");\n }\n\n return lines.join(\" | \");\n}"],"mappings":"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","names":["decision","etag","bundleVersionRaw","updatedAt","meta"]}
@@ -0,0 +1,318 @@
1
+ # Govplane Runtime SDK – Incident Playbooks
2
+
3
+ This document describes **how to react to real production incidents** using the Govplane Runtime SDK, without redeploying code and without exposing additional attack surface.
4
+
5
+ All playbooks assume:
6
+ - The SDK is already deployed
7
+ - Runtime bundles are precompiled
8
+ - Decisions are evaluated locally (in-process)
9
+
10
+ ---
11
+
12
+ ## Operational Principles
13
+
14
+ - **No redeploys**
15
+ - **No exposed endpoints**
16
+ - **Local decisions**
17
+ - **Changes via bundles**
18
+ - **Fail-safe by default**
19
+
20
+ ---
21
+
22
+ ## Incident Mode Activation
23
+
24
+ When an incident occurs, you need to enable incident mode to force immediate bundle refresh and activate burst polling for rapid policy enforcement.
25
+
26
+ ### Primary Response: File-based Incident Trigger
27
+
28
+ ```bash
29
+ echo '{"burst":true,"refreshNow":true}' > /etc/govplane/incident.json
30
+ ```
31
+
32
+ Effect:
33
+ - Immediate bundle refresh
34
+ - Burst polling enabled
35
+ - Kill switches / throttles enforced within seconds
36
+
37
+ ---
38
+
39
+ ### ECS Procedure
40
+
41
+ ```bash
42
+ aws ecs execute-command \
43
+ --cluster <cluster> \
44
+ --task <task> \
45
+ --container <container> \
46
+ --command "echo '{"burst":true,"refreshNow":true}' > /etc/govplane/incident.json"
47
+ ```
48
+
49
+ ---
50
+
51
+ ### Kubernetes Procedure
52
+
53
+ ```bash
54
+ kubectl patch configmap govplane-incident \
55
+ --type merge \
56
+ -p '{"data":{"incident.json":"{"burst":true,"refreshNow":true}"}}'
57
+ ```
58
+
59
+ ---
60
+
61
+ ### Alternative: Environment Variable
62
+
63
+ ```bash
64
+ GP_RUNTIME_INCIDENT=1
65
+ ```
66
+
67
+ Requires restart.
68
+
69
+ ---
70
+
71
+ ### Optional: POSIX Signal
72
+
73
+ ```bash
74
+ kill -USR1 <pid>
75
+ ```
76
+
77
+ ---
78
+
79
+ ### Ending the Incident
80
+
81
+ ```bash
82
+ echo '{"burst":false}' > /etc/govplane/incident.json
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 1. DDoS / Traffic Spike
88
+
89
+ ### Goal
90
+ Reduce impact **immediately** without shutting down the service.
91
+
92
+ ### Control Plane Action
93
+ Create or activate a `throttle` policy.
94
+
95
+ ```json
96
+ {
97
+ "effect": {
98
+ "type": "throttle",
99
+ "throttle": {
100
+ "limit": 10,
101
+ "windowSeconds": 60,
102
+ "key": "ctx.plan"
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Runtime SDK Action
109
+ Force fast refresh:
110
+
111
+ ```ts
112
+ await client.refreshNow({ burst: true });
113
+ ```
114
+
115
+ ### Result
116
+ - Local evaluation
117
+ - No remote calls per request
118
+ - Throttling effective within seconds
119
+
120
+ ---
121
+
122
+ ## 2. Kill Switch (Critical Incident)
123
+
124
+ ### Goal
125
+ Immediately block a specific capability.
126
+
127
+ Examples:
128
+ - checkout
129
+ - payments
130
+ - exports
131
+ - sensitive endpoints
132
+
133
+ ### Control Plane Action
134
+ Activate a `kill_switch` policy.
135
+
136
+ ```json
137
+ {
138
+ "effect": {
139
+ "type": "kill_switch",
140
+ "killSwitch": {
141
+ "service": "payments",
142
+ "reason": "incident_2026_01"
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Runtime SDK Action
149
+ ```ts
150
+ await client.refreshNow({ burst: true });
151
+ ```
152
+
153
+ ### Evaluation Example
154
+ ```ts
155
+ if (decision.decision === "kill_switch") {
156
+ return res.status(503).send("Service temporarily unavailable");
157
+ }
158
+ ```
159
+
160
+ ### Guarantee
161
+ - Kill switch **always wins**
162
+ - Independent of priority or rule order
163
+
164
+ ---
165
+
166
+ ## 3. Runtime API Down / Degraded
167
+
168
+ ### Scenario
169
+ - Runtime API unreachable
170
+ - Timeouts
171
+ - 5xx errors
172
+
173
+ ### Automatic SDK Behavior
174
+ - Enters exponential backoff
175
+ - Uses cached bundle
176
+ - Switches to `degraded` state
177
+
178
+ ```ts
179
+ client.onStatus((s) => {
180
+ if (s.state === "degraded") {
181
+ alertOps(s);
182
+ }
183
+ });
184
+ ```
185
+
186
+ ### Guarantee
187
+ - Service keeps running
188
+ - Deterministic decisions
189
+ - No request hammering
190
+
191
+ ---
192
+
193
+ ## 4. Cold Start / Safe Startup
194
+
195
+ ### Scenario
196
+ - Service just started
197
+ - No bundle yet
198
+
199
+ ### State
200
+ ```ts
201
+ { state: "warming_up" }
202
+ ```
203
+
204
+ ### Decision Behavior
205
+ - Deny-by-default
206
+ - No implicit allow
207
+
208
+ ### Recommended Pattern
209
+ ```ts
210
+ await client.warmStart({ burst: true, timeoutMs: 8000 });
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 5. Rollout / Canary / Debugging
216
+
217
+ ### Goal
218
+ Understand **why** a decision was taken.
219
+
220
+ ### Action
221
+ Enable sampled decision trace.
222
+
223
+ ```ts
224
+ engine.evaluateWithTrace(
225
+ { target, context },
226
+ { sampling: 0.01, mode: "compact" }
227
+ );
228
+ ```
229
+
230
+ ### What You Get
231
+ - Winning rule
232
+ - Decision reason
233
+ - No PII
234
+ - No sensitive payloads
235
+
236
+ ---
237
+
238
+ ## 6. Incident Investigation
239
+
240
+ ### Action
241
+ Force trace temporarily.
242
+
243
+ ```ts
244
+ engine.evaluateWithTrace(
245
+ { target, context },
246
+ { force: true, mode: "full" }
247
+ );
248
+ ```
249
+
250
+ ### Recommendation
251
+ - Use only in controlled environments
252
+ - Avoid hot paths without sampling
253
+
254
+ ---
255
+
256
+ ## 7. Protection Against Context Abuse
257
+
258
+ ### Scenario
259
+ Someone attempts to inject sensitive data:
260
+
261
+ ```json
262
+ {
263
+ "email": "user@example.com",
264
+ "token": "..."
265
+ }
266
+ ```
267
+
268
+ ### Result
269
+ - Context validation fails
270
+ - Evaluation aborted
271
+ - Engine never executes
272
+
273
+ ### Result
274
+ **PII never enters the system**
275
+
276
+ ---
277
+
278
+ ## 8. Safe Fallback
279
+
280
+ ### Worst-Case Scenario
281
+ - Runtime unavailable
282
+ - Bundle invalid
283
+ - SDK restarted
284
+
285
+ ### Result
286
+ ```ts
287
+ decision === "deny"
288
+ ```
289
+
290
+ ### Why This Is Correct
291
+ - No implicit allow
292
+ - Security over availability
293
+ - Explicit behavior
294
+
295
+ ---
296
+
297
+ ## 9. Operation Without Govplane
298
+
299
+ Even if Govplane becomes completely unavailable:
300
+
301
+ - SDK continues running
302
+ - Cached bundle remains active
303
+ - Decisions are local
304
+ - No per-request dependency
305
+
306
+ ---
307
+
308
+ ## 10. Incident Checklist
309
+
310
+ During an incident:
311
+
312
+ - [ ] Enable burst polling
313
+ - [ ] Activate throttle or kill switch
314
+ - [ ] Monitor client status (`ok / degraded`)
315
+ - [ ] Use trace sampling if needed
316
+ - [ ] Avoid redeploys
317
+ - [ ] Do not expose new endpoints
318
+
@@ -0,0 +1,101 @@
1
+ # Govplane Runtime SDK – Runtime Incident Controls
2
+
3
+ Govplane Runtime SDK provides **passive incident controls** that allow operators to react to incidents **without exposing endpoints**, **without handling PII**, and **without recompiling application code**.
4
+
5
+ These controls are designed to be:
6
+ - Local-only
7
+ - Zero-network-surface
8
+ - Safe under active attack
9
+ - Compatible with containerized and orchestrated environments
10
+
11
+ ---
12
+
13
+ ## Overview
14
+
15
+ During an incident (DDoS, fraud spike, abuse, misconfiguration), it may be necessary to:
16
+ - Force faster policy propagation
17
+ - Trigger immediate policy refresh
18
+ - Temporarily increase polling frequency (“burst mode”)
19
+
20
+ The Runtime SDK supports this via **out-of-band controls**.
21
+
22
+ ---
23
+
24
+ ## Supported Incident Control Mechanisms
25
+
26
+ | Mechanism | Restart Required | Network Surface | Hot | Recommended |
27
+ |---------|------------------|-----------------|-----|-------------|
28
+ | Environment Variable | Usually yes | None | ❌ | ✅ |
29
+ | File-based Hot Reload | No | None | ✅ | ⭐ **Primary** |
30
+ | POSIX Signal (SIGUSR1) | No | None | ✅ | Optional |
31
+
32
+ ---
33
+
34
+ ## 1️⃣ Environment Variable Incident Mode
35
+
36
+ ```bash
37
+ GP_RUNTIME_INCIDENT=1
38
+ ```
39
+
40
+ When detected:
41
+ - Burst polling is enabled
42
+ - Refresh cadence increases
43
+ - Degraded recovery is accelerated
44
+
45
+ > In ECS/Kubernetes this usually requires a restart. For zero‑restart response, use File-based Hot Reload.
46
+
47
+ ---
48
+
49
+ ## 2️⃣ File-based Incident Hot Reload (Recommended)
50
+
51
+ ### Incident File Example
52
+
53
+ ```json
54
+ {
55
+ "burst": true,
56
+ "burstPollMs": 200,
57
+ "burstDurationMs": 60000,
58
+ "refreshNow": true
59
+ }
60
+ ```
61
+
62
+ ### Trigger
63
+
64
+ ```bash
65
+ echo '{"burst":true,"refreshNow":true}' > /etc/govplane/incident.json
66
+ ```
67
+
68
+ ---
69
+
70
+ ## ECS Example
71
+
72
+ ```bash
73
+ aws ecs execute-command --cluster my-cluster --task my-task --container app --command "echo '{"burst":true,"refreshNow":true}' > /etc/govplane/incident.json"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Kubernetes Example
79
+
80
+ ```bash
81
+ kubectl patch configmap govplane-incident --type merge -p '{"data":{"incident.json":"{"burst":true,"refreshNow":true}"}}'
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 3️⃣ POSIX Signal Trigger (Optional)
87
+
88
+ ```bash
89
+ kill -USR1 <pid>
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Security Model
95
+
96
+ - No inbound endpoints
97
+ - No remote triggers
98
+ - Zero PII
99
+ - Local-only controls
100
+
101
+ ---