/** * RuntimeContext — per-tenant container for the framework's mutable * singletons. * * The kernel ships with several module-level slots that production wiring * fills at boot: * * - kill switch state (active/reason/toggledAt + env-seed memo) * - MetricsSink (recordDecision/recordRefusal/etc.) * - LearningSink (recordOutcome) * - ShadowTelemetrySink (BASIS_ONLY/DECISION_KIND/PAYLOAD_REWRITE) * - EnforceConfig (`IBX_KERNEL_SHADOW` / `IBX_KERNEL_ENFORCE` parses) * * Module-level singletons block multi-tenancy: a single Node process * cannot host two tenants with independent kill switches, sink fan-out, * or per-intent enforce configs without cross-talk. This module * introduces a fresh container abstraction: * * - `createRuntimeContext()` mints a new isolated container with its * own kill switch / sinks / enforce snapshot. Tenant code holds the * handle and routes reads/writes through it. * - `getDefaultRuntimeContext()` returns the process-wide default * context. Existing module-level callers (`isKilled()`, * `recordDecision()`, etc.) operate on it. Back-compat is total. * * Existing call sites do not move to the context API automatically; * production migration is opt-in. New code paths that accept a * `context` parameter (e.g. `adjudicateAndAudit({ context })`) route * through it; otherwise everything continues to use the default. * * Env-seed reseed (#16): `KillSwitchControl.reseedFromEnv()` re-reads * `IBX_KILL_SWITCH` even after manual toggles. The default context * preserves the existing one-shot behaviour for back-compat; tenant * contexts can opt in to per-tenant env vars (e.g., * `IBX_KILL_SWITCH_TENANT_FOO`). */ import type { Decision } from "../decision.js"; import type { DecisionEvent, LedgerOpEvent, MetricsSink, RefusalEvent, ResourceLimitEvent, ShadowDivergenceEvent, SinkFailureEvent } from "./metrics.js"; import type { LearningSink } from "./learning.js"; import type { KernelIdentity } from "./identity.js"; import type { OutcomeSink } from "./outcomes.js"; import type { LegacyDecisionResult, ShadowTelemetrySink } from "./shadow.js"; export interface KillSwitchState { readonly active: boolean; readonly reason: string; readonly toggledAt: string; } export interface KillSwitchControl { isKilled(): boolean; state(): KillSwitchState; set(active: boolean, reason: string): void; /** * Re-read the env var (`IBX_KILL_SWITCH` by default; tenant contexts * may use a per-tenant variable). Resets the one-shot env-seed memo, * so an operator can flip the env and force a re-read without process * restart. Returns the new state. */ reseedFromEnv(env?: NodeJS.ProcessEnv): KillSwitchState; } export interface EnforceConfig { isShadowed(intentKind: string, env?: NodeJS.ProcessEnv): boolean; isEnforced(intentKind: string, env?: NodeJS.ProcessEnv): boolean; reset(): void; } export interface RuntimeContext { /** Identifier for telemetry / logs. Default context is `"default"`. */ readonly id: string; readonly killSwitch: KillSwitchControl; readonly metrics: MetricsSinkSlot; readonly learning: LearningSinkSlot; readonly shadowTelemetry: ShadowTelemetrySinkSlot; readonly enforceConfig: EnforceConfig; /** * Optional kernel identity (SA6 §3.5 seam). When supplied, audit emissions * carry `{ id, version }` so operators can correlate decisions to the * kernel build that produced them. Attestation bytes are reserved for v0.2. */ readonly kernelIdentity?: KernelIdentity; /** * Optional retrospective-outcome sink. The admin SDK's * `governance.recordOutcome` mutation routes through this when configured. */ readonly outcomeSink?: OutcomeSink; } export interface MetricsSinkSlot { readonly current: () => MetricsSink; readonly set: (sink: MetricsSink) => void; readonly reset: () => void; readonly hasExplicit: () => boolean; recordLedgerOp(event: LedgerOpEvent): void; recordDecision(event: DecisionEvent): void; recordRefusal(event: RefusalEvent): void; recordSinkFailure(event: SinkFailureEvent): void; recordResourceLimit(event: ResourceLimitEvent): void; recordShadowDivergence(event: ShadowDivergenceEvent): void; } export interface LearningSinkSlot { readonly current: () => LearningSink; readonly set: (sink: LearningSink) => void; readonly reset: () => void; readonly hasExplicit: () => boolean; } export interface ShadowTelemetrySinkSlot { readonly current: () => ShadowTelemetrySink; readonly set: (sink: ShadowTelemetrySink) => void; readonly reset: () => void; recordBasisOnly(intentKind: string, decision: Decision): void; alertDecisionKind(intentKind: string, legacy: LegacyDecisionResult, decision: Decision): void; alertPayloadRewrite(intentKind: string, decision: Decision): void; } export interface CreateRuntimeContextOptions { readonly id?: string; readonly metrics?: MetricsSink; readonly learning?: LearningSink; readonly shadowTelemetry?: ShadowTelemetrySink; readonly envSeed?: NodeJS.ProcessEnv; /** * Custom env-var name for the kill switch. * Default: `"IBX_KILL_SWITCH"` (IBX_ prefix is a framework-origin artifact; * override this with your own prefix in production). */ readonly killSwitchEnvVar?: string; /** Custom shadow-list env var. Default: `"IBX_KERNEL_SHADOW"` (see killSwitchEnvVar note). */ readonly shadowEnvVar?: string; /** Custom enforce-list env var. Default: `"IBX_KERNEL_ENFORCE"` (see killSwitchEnvVar note). */ readonly enforceEnvVar?: string; } export declare function createRuntimeContext(options?: CreateRuntimeContextOptions): RuntimeContext; /** * Returns the process-wide default RuntimeContext. Module-level kernel * functions (`isKilled`, `recordDecision`, etc.) read and write this * context. Adopters that want isolation use `createRuntimeContext()` and * route through their tenant context instead. * * Lazily initialised on first read so test harnesses that mutate * `process.env` after import still see fresh seeds. */ export declare function getDefaultRuntimeContext(): RuntimeContext; /** @internal — for tests. Drops and re-creates the default context. */ export declare function _resetDefaultRuntimeContext(): void; //# sourceMappingURL=runtime-context.d.ts.map