fboxrec 0.1.0

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/index.ts","../src/config.ts","../src/clock.ts","../src/ring-buffer.ts","../src/recorder.ts","../src/encoder.ts","../src/context.ts","../src/load-shedding.ts","../src/dump/stage.ts","../src/dump/panic.ts","../src/dump/serializer.ts","../../format/src/types.ts","../../format/src/parse.ts","../src/dump/snapshot.ts","../src/dump/sinks/disk.ts","../src/dump/sinks/s3.ts","../src/dump/sinks/sigv4.ts","../src/dump/sinks/credentials.ts","../src/dump/sinks/http.ts","../src/dump/sinks/index.ts","../src/triggers/engine.ts","../src/triggers/manual.ts","../src/triggers/uncaught.ts","../src/redaction.ts","../src/triggers/slow-request.ts","../src/instrumentations/http-server.ts","../src/instrumentations/http-client.ts","../src/instrumentations/pg.ts","../src/instrumentations/console.ts","../src/instrumentations/registry.ts","../src/instrumentations/vitals.ts"],"sourcesContent":["import { isMainThread } from 'node:worker_threads';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as zlib from 'node:zlib';\nimport type { IncidentMeta } from '@flightbox/format';\nimport { resolveConfig, type UserConfig, type FlightboxConfig } from './config';\nimport { anchorClock } from './clock';\nimport { RingBuffer } from './ring-buffer';\nimport { Recorder } from './recorder';\nimport { EventType, truncate, LIMITS } from './encoder';\nimport { setShedding } from './load-shedding';\nimport type { AgentApi } from './agent';\nimport { Staging, DiskFullError, recoverOnBoot } from './dump/stage';\nimport { PanicWriter, recoverPanicFiles } from './dump/panic';\nimport { buildIncident, DUMP_GZIP_LEVEL } from './dump/serializer';\nimport { takeFrozenSnapshot } from './dump/snapshot';\nimport { createSink, type Sink } from './dump/sinks';\nimport { TriggerEngine, type TriggerFireInfo } from './triggers/engine';\nimport { createManualTrigger } from './triggers/manual';\nimport { installCrashTriggers } from './triggers/uncaught';\nimport { startWatchdog } from './triggers/slow-request';\nimport { applyInstrumentations } from './instrumentations/registry';\nimport { startVitals } from './instrumentations/vitals';\n\nexport type { UserConfig, FlightboxConfig, SinkConfig } from './config';\n\ninterface Instance {\n config: FlightboxConfig;\n recorder: Recorder;\n staging: Staging;\n engine: TriggerEngine;\n panicWriter: PanicWriter;\n sinks: Sink[];\n manualTrigger: (reason?: string) => string | null;\n teardowns: Array<() => void>;\n /** Files currently being delivered — the retry pass must not double-send. */\n deliveryInFlight: Set<string>;\n}\n\n/** Staged-but-undelivered incidents are retried on a timer, not only on\n * restart — a healthy long-running process must self-heal after a\n * transient sink outage. */\nconst SINK_RETRY_INTERVAL_MS = 5 * 60_000;\n\n/** Memory-pressure dumps swap in this small ring instead of a full-size\n * allocation (see Recorder.freezeAndSwap). */\nconst EMERGENCY_RING_BYTES = 4 * 1024 * 1024;\n\nlet instance: Instance | null = null;\n\n/**\n * Arms the flight recorder: allocates the ring buffer, applies\n * instrumentations, installs triggers/vitals, runs the boot-time recovery\n * pass, and starts delivering anything a previous run left behind.\n * Idempotent.\n */\nexport function start(userConfig: UserConfig = {}): void {\n if (instance) return;\n const config = resolveConfig(userConfig);\n if (!isMainThread && !config.allowWorkerThreads) {\n // ADR 014: prevent N-worker x 64MB silent memory multiplication.\n config.log(\n 'worker thread detected — agent not started (each thread would allocate its own ring buffer; see ADR 014). Set allowWorkerThreads: true to record in this thread anyway.'\n );\n return;\n }\n anchorClock();\n\n const recorder = new Recorder(new RingBuffer(config.bufferMb * 1024 * 1024));\n const staging = new Staging(config.dir, config.staging, config.log);\n // Resurrection order matters: adopt dead workers' dirs (ADR 010), convert\n // any raw panic dumps to .fbox (ADR 009), THEN announce unsent incidents.\n staging.claimOrphanedSync();\n recoverPanicFiles(staging, config.service, config.log);\n const unsent = recoverOnBoot(staging, config.log);\n // Preallocate the OOM panic fd while memory is plentiful (ADR 009).\n const panicWriter = new PanicWriter(staging.stagingDir);\n panicWriter.arm();\n\n const sinks = config.sinks.map((cfg) => createSink(cfg, config.viewerOrigin));\n\n const engine = new TriggerEngine(\n config.triggers.cooldownMs,\n (info) => dump(info),\n (info) =>\n recorder.record(EventType.Trigger, {\n suppressed: true,\n triggerType: info.type,\n reason: info.reason\n })\n );\n\n const agent: AgentApi = {\n recorder,\n config,\n fire: (info) => engine.fire(info)\n };\n\n instance = {\n config,\n recorder,\n staging,\n engine,\n panicWriter,\n sinks,\n manualTrigger: createManualTrigger(engine),\n teardowns: [],\n deliveryInFlight: new Set()\n };\n\n applyInstrumentations(agent);\n instance.teardowns.push(startWatchdog(agent));\n instance.teardowns.push(startVitals(agent));\n instance.teardowns.push(\n installCrashTriggers({\n agent,\n // Crash dumps bypass the routine cooldown (a slow-request dump minutes\n // ago must never cost us the final-moments dump); the engine's short\n // crash window still collapses rejection→rethrow→uncaught to one file.\n syncDump: (info) => engine.fire({ ...info, mode: 'sync', exemptFromCooldown: true }),\n panicDump: () => panicWriter.writeSync(recorder.activeRing)\n })\n );\n\n recorder.armed = true;\n config.log(\n `armed — ${config.bufferMb}MB ring buffer, staging at ${staging.stagingDir}` +\n (sinks.length ? `, sinks: ${sinks.map((s) => s.name).join('+')}` : ', no sinks (staging only)')\n );\n\n // Boot-time delivery of what previous runs never managed to send.\n if (sinks.length > 0 && unsent.length > 0) {\n void deliverRecovered(instance, unsent);\n }\n\n // Periodic retry: transient sink failures heal without a restart.\n if (sinks.length > 0) {\n const inst = instance;\n const retry = setInterval(() => void retryStaged(inst), SINK_RETRY_INTERVAL_MS);\n retry.unref();\n instance.teardowns.push(() => clearInterval(retry));\n }\n}\n\nasync function retryStaged(inst: Instance): Promise<void> {\n try {\n for (const file of inst.staging.listStagedSync()) {\n if (inst.deliveryInFlight.has(file)) continue;\n const meta = readMetaSync(file);\n if (!meta) continue;\n inst.config.log(`retrying delivery of ${path.basename(file)}...`);\n await deliverFile(inst, file, meta);\n }\n } catch {\n // The retry pass itself must never take the process down.\n }\n}\n\nfunction readMetaSync(filePath: string): IncidentMeta | null {\n try {\n const parsed = JSON.parse(zlib.gunzipSync(fs.readFileSync(filePath)).toString('utf8'));\n return parsed?.meta ?? null;\n } catch {\n return null;\n }\n}\n\nasync function deliverRecovered(inst: Instance, files: string[]): Promise<void> {\n for (const file of files) {\n const meta = readMetaSync(file);\n if (!meta) continue;\n inst.config.log(`delivering recovered incident ${path.basename(file)}...`);\n await deliverFile(inst, file, meta);\n }\n}\n\nasync function deliverFile(inst: Instance, filePath: string, meta: IncidentMeta): Promise<void> {\n if (inst.sinks.length === 0 || inst.deliveryInFlight.has(filePath)) return;\n inst.deliveryInFlight.add(filePath);\n try {\n await deliverFileInner(inst, filePath, meta);\n } finally {\n inst.deliveryInFlight.delete(filePath);\n }\n}\n\nasync function deliverFileInner(\n inst: Instance,\n filePath: string,\n meta: IncidentMeta\n): Promise<void> {\n let allOk = true;\n let viewerUrl: string | undefined;\n for (const sink of inst.sinks) {\n try {\n const result = await sink.deliver(filePath, meta);\n if (result.ok) {\n if (result.location) inst.config.log(` stored: ${result.location}`);\n viewerUrl ??= result.viewerUrl;\n } else {\n allOk = false;\n inst.config.log(` sink ${sink.name} failed: ${result.detail ?? 'unknown error'}`);\n }\n } catch (err) {\n allOk = false;\n inst.config.log(` sink ${sink.name} threw: ${(err as Error).message}`);\n }\n }\n if (viewerUrl) inst.config.log(` open: ${viewerUrl}`);\n if (allOk) {\n try {\n inst.staging.markDeliveredSync(filePath);\n } catch {\n // Retention move is best-effort; the data is already delivered.\n }\n }\n // Failures leave the file in staging for the next boot's recovery pass.\n}\n\n/**\n * The dump. `mode: 'sync'` (manual + crash paths) compresses on-thread with\n * gzipSync; the default async mode (live triggers under load) runs gzip on\n * the libuv threadpool so compression never blocks the event loop (ADR 016).\n * Either way the buffer was already frozen-and-swapped (ADR 005).\n */\nfunction dump(info: TriggerFireInfo): string | null {\n const inst = instance;\n if (!inst) return null;\n\n // The trigger marker rides inside the incident itself.\n inst.recorder.record(EventType.Trigger, {\n triggerType: info.type,\n reason: info.reason\n });\n\n // Memory-pressure dumps must not allocate another full ring at the worst\n // possible moment — swap in a small emergency ring instead. The next\n // normal dump restores full capture size.\n const replacement = info.type === 'memory' ? EMERGENCY_RING_BYTES : undefined;\n if (replacement) {\n inst.config.log(\n `memory trigger: recording continues in a ${EMERGENCY_RING_BYTES / 1048576}MB emergency ring until pressure clears`\n );\n }\n const events = takeFrozenSnapshot(inst.recorder, replacement);\n const prepared = buildIncident({\n service: inst.config.service,\n trigger: { type: info.type, reason: info.reason },\n events\n });\n\n const finalize = (data: Buffer): string | null => {\n let stagedPath: string;\n try {\n stagedPath = inst.staging.stageSync(prepared.fileName, data);\n } catch (err) {\n if (err instanceof DiskFullError) {\n // ADR 006: a debugging tool must never DoS its host.\n inst.recorder.armed = false;\n inst.config.log(`DISK CRITICAL — ${err.message}. Agent disabled.`);\n return null;\n }\n inst.config.log(`failed to stage incident: ${(err as Error).message}`);\n return null;\n }\n inst.config.log(\n `🔴 incident captured (${(data.length / 1024).toFixed(1)} KB gz, ${prepared.eventCount} events, ${(prepared.windowMs / 1000).toFixed(1)}s window)`\n );\n inst.config.log(` trigger: ${info.type}${info.reason ? ` — ${info.reason}` : ''}`);\n inst.config.log(` staged: ${stagedPath}`);\n void deliverFile(inst, stagedPath, prepared.meta);\n return stagedPath;\n };\n\n if (info.mode === 'sync') {\n return finalize(zlib.gzipSync(prepared.json, { level: DUMP_GZIP_LEVEL }));\n }\n\n // ADR 016: compression on the libuv threadpool, off the event loop.\n const prospectivePath = path.join(inst.staging.stagingDir, prepared.fileName);\n zlib.gzip(prepared.json, { level: DUMP_GZIP_LEVEL }, (err, data) => {\n if (err) {\n inst.config.log(`gzip failed for ${prepared.fileName}: ${err.message}`);\n return;\n }\n finalize(data);\n });\n return prospectivePath;\n}\n\n/** Manually capture an incident. Returns the staged file path, or null (cooldown / not started / disk guard). */\nexport function trigger(reason?: string): string | null {\n return instance ? instance.manualTrigger(reason) : null;\n}\n\n/** Record a custom timeline event (e.g. flightbox.addEvent('cache.miss', { key })). */\nexport function addEvent(name: string, data: Record<string, unknown> = {}): void {\n instance?.recorder.record(EventType.Custom, {\n name: truncate(name, LIMITS.path),\n ...data\n });\n}\n\n/** Disarms recording, stops samplers and watchdogs. Module patches remain but degrade to no-ops. */\nexport function stop(): void {\n if (instance) {\n instance.recorder.armed = false;\n for (const teardown of instance.teardowns) {\n try {\n teardown();\n } catch {\n /* best-effort */\n }\n }\n instance.panicWriter.disarm();\n setShedding(false);\n instance = null;\n }\n}\n\nexport default { start, stop, trigger, addEvent };\n","import * as path from 'node:path';\n\nexport interface TriggersConfig {\n /** Requests slower than this fire the slow-request trigger (incl. watchdog). */\n slowRequestMs: number;\n /** One storm = one file: minimum time between dumps. */\n cooldownMs: number;\n /** Memory trigger: heapUsed fraction of the V8 limit. */\n heapPct: number;\n /** Event-loop stall trigger: max observed lag in one sample window. */\n stallMs: number;\n}\n\nexport interface SheddingConfig {\n /** ADR 012: mean event-loop lag above this sheds per-request instrumentation. */\n shedLagMs: number;\n}\n\nexport interface StagingConfig {\n /** ADR 006 — hard cap on staging/ size; oldest files FIFO-evicted over quota. */\n maxStageMb: number;\n /** ADR 006 — if the disk has less than this % free, the agent disables itself. */\n minDiskFreePct: number;\n}\n\nexport interface S3SinkConfig {\n type: 's3';\n bucket: string;\n prefix?: string;\n /** Omit for AWS; set for R2/B2/MinIO (path-style is used when set). */\n endpoint?: string;\n region?: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n presign?: {\n enabled?: boolean;\n expiresHours?: number;\n /** ADR 008: self-hosted viewer origin for air-gapped environments. */\n viewerOrigin?: string;\n };\n}\n\nexport interface DiskSinkConfig {\n type: 'disk';\n dir: string;\n /** Quota for the sink dir (FIFO eviction of oldest .fbox). Default 500. */\n maxMb?: number;\n}\n\nexport interface HttpSinkConfig {\n type: 'http';\n url: string;\n headers?: Record<string, string>;\n}\n\nexport type SinkConfig = S3SinkConfig | DiskSinkConfig | HttpSinkConfig;\n\nexport interface FlightboxConfig {\n service: string;\n /** Ring buffer size. The time window is a consequence of this and traffic. */\n bufferMb: number;\n /** Base directory holding staging/ and delivered/. */\n dir: string;\n triggers: TriggersConfig;\n staging: StagingConfig;\n shedding: SheddingConfig;\n /** Arms the manual dump endpoint (GET /__flightbox/dump, x-flightbox-token header). */\n token?: string;\n /** Default viewer origin for magic links (ADR 008). */\n viewerOrigin: string;\n sinks: SinkConfig[];\n /**\n * ADR 014: by default the agent refuses to start inside a worker thread —\n * N workers would allocate N rings (8 cores x 64MB = 512MB). Opt-in only.\n */\n allowWorkerThreads: boolean;\n /** Agent's own diagnostics channel. Defaults to stderr (console is captured). */\n log: (msg: string) => void;\n}\n\nexport interface UserConfig {\n service?: string;\n bufferMb?: number;\n dir?: string;\n triggers?: Partial<TriggersConfig>;\n staging?: Partial<StagingConfig>;\n shedding?: Partial<SheddingConfig>;\n token?: string;\n viewerOrigin?: string;\n sinks?: SinkConfig[];\n allowWorkerThreads?: boolean;\n log?: (msg: string) => void;\n}\n\nconst DEFAULTS = {\n bufferMb: 64,\n dir: '.flightbox',\n slowRequestMs: 5000,\n cooldownMs: 60_000,\n heapPct: 0.9,\n stallMs: 1000,\n shedLagMs: 50,\n maxStageMb: 500,\n minDiskFreePct: 5,\n viewerOrigin: 'https://viewer.flightbox.dev'\n} as const;\n\nfunction envNumber(name: string): number | undefined {\n const raw = process.env[name];\n if (raw === undefined || raw === '') return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction envBool(name: string): boolean {\n return process.env[name] === '1' || process.env[name] === 'true';\n}\n\n/** §5: env-var-only S3 sink for platform users (Render/Railway/ECS...). */\nfunction envS3Sink(): S3SinkConfig | null {\n const bucket = process.env.FLIGHTBOX_S3_BUCKET;\n if (!bucket) return null;\n return {\n type: 's3',\n bucket,\n prefix: process.env.FLIGHTBOX_S3_PREFIX ?? '',\n endpoint: process.env.FLIGHTBOX_S3_ENDPOINT,\n region: process.env.FLIGHTBOX_S3_REGION,\n presign: { enabled: true }\n };\n}\n\n/**\n * Fail loudly on a mistyped sink at start() rather than silently doing\n * nothing (or something surprising) when the first incident fires.\n */\nfunction validateSink(sink: SinkConfig): void {\n switch (sink.type) {\n case 's3': {\n if (!sink.bucket || typeof sink.bucket !== 'string') {\n throw new Error('flightbox: s3 sink requires a non-empty \"bucket\"');\n }\n if (sink.endpoint !== undefined && !/^https?:\\/\\//.test(sink.endpoint)) {\n throw new Error(`flightbox: s3 sink \"endpoint\" must be an http(s) URL, got ${sink.endpoint}`);\n }\n if (sink.prefix !== undefined && typeof sink.prefix !== 'string') {\n throw new Error('flightbox: s3 sink \"prefix\" must be a string');\n }\n const hrs = sink.presign?.expiresHours;\n if (hrs !== undefined && (!Number.isFinite(hrs) || hrs <= 0 || hrs > 168)) {\n throw new Error(`flightbox: s3 presign.expiresHours must be in (0, 168], got ${hrs}`);\n }\n break;\n }\n case 'http': {\n if (!/^https?:\\/\\//.test(sink.url ?? '')) {\n throw new Error(`flightbox: http sink \"url\" must be an http(s) URL, got ${sink.url}`);\n }\n break;\n }\n case 'disk': {\n if (!sink.dir || typeof sink.dir !== 'string') {\n throw new Error('flightbox: disk sink requires a non-empty \"dir\"');\n }\n if (sink.maxMb !== undefined && (!Number.isFinite(sink.maxMb) || sink.maxMb < 1)) {\n throw new Error(`flightbox: disk sink \"maxMb\" must be >= 1, got ${sink.maxMb}`);\n }\n break;\n }\n default:\n throw new Error(`flightbox: unknown sink type \"${(sink as { type: string }).type}\"`);\n }\n}\n\n/**\n * Precedence: env vars > code config > defaults. Env-var-only setup is what\n * makes `node -r fboxrec/register server.js` work with zero code changes.\n */\nexport function resolveConfig(user: UserConfig = {}): FlightboxConfig {\n const bufferMb = envNumber('FLIGHTBOX_BUFFER_MB') ?? user.bufferMb ?? DEFAULTS.bufferMb;\n const dir = process.env.FLIGHTBOX_DIR || user.dir || DEFAULTS.dir;\n const service =\n process.env.FLIGHTBOX_SERVICE || user.service || path.basename(process.cwd());\n const slowRequestMs =\n envNumber('FLIGHTBOX_TRIGGER_SLOW_MS') ?? user.triggers?.slowRequestMs ?? DEFAULTS.slowRequestMs;\n const cooldownMs = user.triggers?.cooldownMs ?? DEFAULTS.cooldownMs;\n const heapPct =\n envNumber('FLIGHTBOX_TRIGGER_HEAP_PCT') ?? user.triggers?.heapPct ?? DEFAULTS.heapPct;\n const stallMs =\n envNumber('FLIGHTBOX_TRIGGER_STALL_MS') ?? user.triggers?.stallMs ?? DEFAULTS.stallMs;\n const shedLagMs =\n envNumber('FLIGHTBOX_SHED_LAG_MS') ?? user.shedding?.shedLagMs ?? DEFAULTS.shedLagMs;\n const maxStageMb =\n envNumber('FLIGHTBOX_MAX_STAGE_MB') ?? user.staging?.maxStageMb ?? DEFAULTS.maxStageMb;\n const minDiskFreePct = user.staging?.minDiskFreePct ?? DEFAULTS.minDiskFreePct;\n\n if (!Number.isFinite(bufferMb) || bufferMb < 1 || bufferMb > 4096) {\n throw new RangeError(`flightbox: bufferMb must be between 1 and 4096, got ${bufferMb}`);\n }\n if (!Number.isFinite(slowRequestMs) || slowRequestMs < 50) {\n throw new RangeError(`flightbox: triggers.slowRequestMs must be >= 50, got ${slowRequestMs}`);\n }\n if (!Number.isFinite(cooldownMs) || cooldownMs < 0) {\n throw new RangeError(`flightbox: triggers.cooldownMs must be >= 0, got ${cooldownMs}`);\n }\n if (!Number.isFinite(heapPct) || heapPct <= 0 || heapPct >= 1) {\n throw new RangeError(`flightbox: triggers.heapPct must be in (0,1), got ${heapPct}`);\n }\n if (!Number.isFinite(stallMs) || stallMs < 50) {\n throw new RangeError(`flightbox: triggers.stallMs must be >= 50, got ${stallMs}`);\n }\n if (!Number.isFinite(shedLagMs) || shedLagMs < 1) {\n throw new RangeError(`flightbox: shedding.shedLagMs must be >= 1, got ${shedLagMs}`);\n }\n if (!Number.isFinite(maxStageMb) || maxStageMb < 1) {\n throw new RangeError(`flightbox: staging.maxStageMb must be >= 1, got ${maxStageMb}`);\n }\n if (!Number.isFinite(minDiskFreePct) || minDiskFreePct < 0 || minDiskFreePct > 50) {\n throw new RangeError(`flightbox: staging.minDiskFreePct must be 0-50, got ${minDiskFreePct}`);\n }\n\n const sinks: SinkConfig[] = [...(user.sinks ?? [])];\n const s3FromEnv = envS3Sink();\n if (s3FromEnv && !sinks.some((s) => s.type === 's3')) sinks.push(s3FromEnv);\n sinks.forEach(validateSink);\n\n return {\n service,\n bufferMb,\n dir: path.resolve(dir),\n triggers: { slowRequestMs, cooldownMs, heapPct, stallMs },\n staging: { maxStageMb, minDiskFreePct },\n shedding: { shedLagMs },\n token: process.env.FLIGHTBOX_TOKEN || user.token,\n viewerOrigin:\n process.env.FLIGHTBOX_VIEWER_ORIGIN || user.viewerOrigin || DEFAULTS.viewerOrigin,\n sinks,\n allowWorkerThreads: envBool('FLIGHTBOX_ALLOW_WORKER_THREADS') || (user.allowWorkerThreads ?? false),\n log: user.log ?? ((msg: string) => process.stderr.write(`flightbox: ${msg}\\n`))\n };\n}\n","/**\n * All event timestamps are process.hrtime.bigint() nanoseconds — monotonic,\n * immune to NTP adjustments. Wall-clock time is anchored exactly once when\n * the agent starts; the serializer derives wall times from (mono - anchor).\n */\n\nexport interface ClockAnchor {\n wallMs: number;\n monoNs: bigint;\n}\n\nlet anchor: ClockAnchor | null = null;\n\nexport function anchorClock(): ClockAnchor {\n anchor = { wallMs: Date.now(), monoNs: process.hrtime.bigint() };\n return anchor;\n}\n\nexport function getAnchor(): ClockAnchor {\n if (anchor === null) anchor = anchorClock();\n return anchor;\n}\n\nexport function nowMono(): bigint {\n return process.hrtime.bigint();\n}\n\nexport function monoToWallMs(tMonoNs: bigint): number {\n const a = getAnchor();\n return a.wallMs + Number(tMonoNs - a.monoNs) / 1e6;\n}\n","/**\n * Fixed-size ring buffer over ONE preallocated Buffer — GC-invisible by\n * design. Records are length-prefixed variable-size byte blobs:\n *\n * [u32 LE length][payload bytes]\n *\n * Wrap boundary (ADR 011, strict no-split): a record that doesn't fit in the\n * tail is written ENTIRELY at offset 0 — records never straddle the boundary\n * (this invariant is what makes raw panic dumps parseable, ADR 009). If >= 4\n * bytes remain at the tail, an explicit WRAP marker (u32 0xFFFFFFFF) is\n * written; a tail gap smaller than 4 bytes is an implicit wrap. Worst-case\n * tail waste is one max-record (~2.1KB truncated) per lap — ~0.003% of a\n * 64MB ring. Writes evict the oldest records they would overwrite.\n * snapshot() copies records out oldest-first.\n */\n\nconst LEN_BYTES = 4;\nconst WRAP_MARKER = 0xffffffff;\n\nexport class RingBuffer {\n readonly capacity: number;\n private readonly buf: Buffer;\n private writePos = 0;\n /** Offset of the oldest valid record. Meaningless when count === 0. */\n private oldest = 0;\n private count = 0;\n private droppedCount = 0;\n\n constructor(capacityBytes: number) {\n if (!Number.isInteger(capacityBytes) || capacityBytes < 16) {\n throw new RangeError(`ring buffer capacity must be an integer >= 16 bytes, got ${capacityBytes}`);\n }\n this.capacity = capacityBytes;\n this.buf = Buffer.allocUnsafe(capacityBytes);\n }\n\n /** Number of records currently held. */\n get size(): number {\n return this.count;\n }\n\n /** Records rejected because they exceed total capacity. */\n get dropped(): number {\n return this.droppedCount;\n }\n\n /**\n * Appends one record. The optional second part lets the hot path write\n * header + body as one frame without concatenating them first (ADR 015).\n */\n write(payload: Buffer | Uint8Array, payload2?: Buffer | Uint8Array): boolean {\n const recordLen = payload.length + (payload2 !== undefined ? payload2.length : 0);\n const needed = LEN_BYTES + recordLen;\n if (needed > this.capacity) {\n this.droppedCount++;\n return false;\n }\n\n if (this.writePos + needed > this.capacity) {\n // Reclaim whatever lives in the tail, mark it skipped, wrap to 0.\n this.evictRange(this.writePos, this.capacity);\n if (this.capacity - this.writePos >= LEN_BYTES) {\n this.buf.writeUInt32LE(WRAP_MARKER, this.writePos);\n }\n this.writePos = 0;\n }\n\n this.evictRange(this.writePos, this.writePos + needed);\n if (this.count === 0) this.oldest = this.writePos;\n\n this.buf.writeUInt32LE(recordLen, this.writePos);\n this.buf.set(payload, this.writePos + LEN_BYTES);\n if (payload2 !== undefined) {\n this.buf.set(payload2, this.writePos + LEN_BYTES + payload.length);\n }\n this.writePos += needed;\n this.count++;\n return true;\n }\n\n /**\n * Copies all records out in chronological order. Recording can continue\n * afterwards; the returned buffers are independent copies.\n */\n snapshot(): Buffer[] {\n return RingBuffer.readRecords(this.buf, this.capacity, this.oldest, this.count);\n }\n\n /**\n * ADR 009 panic path ONLY: the live underlying buffer and pointers, for a\n * raw zero-allocation dump. Everything else must go through snapshot().\n */\n get rawState(): { buf: Buffer; oldest: number; count: number } {\n return { buf: this.buf, oldest: this.oldest, count: this.count };\n }\n\n /**\n * Frame-walks a ring image. Shared by live snapshots and panic-file\n * recovery — the latter reads bytes from a crashed process, so lengths are\n * bounds-checked and the walk bails on the first corrupt frame.\n */\n static readRecords(buf: Buffer, capacity: number, oldest: number, count: number): Buffer[] {\n const out: Buffer[] = [];\n let pos = oldest;\n for (let i = 0; i < count; i++) {\n if (pos < 0 || pos > capacity) break;\n if (capacity - pos < LEN_BYTES || buf.readUInt32LE(pos) === WRAP_MARKER) pos = 0;\n const len = buf.readUInt32LE(pos);\n if (len > capacity - pos - LEN_BYTES) break;\n out.push(Buffer.from(buf.subarray(pos + LEN_BYTES, pos + LEN_BYTES + len)));\n pos += LEN_BYTES + len;\n }\n return out;\n }\n\n clear(): void {\n this.writePos = 0;\n this.oldest = 0;\n this.count = 0;\n }\n\n /** Skip implicit (tail gap < 4B) and explicit wrap markers. */\n private normalize(pos: number): number {\n if (this.capacity - pos < LEN_BYTES || this.buf.readUInt32LE(pos) === WRAP_MARKER) {\n return 0;\n }\n return pos;\n }\n\n /** Evict oldest records while they start inside [start, end). */\n private evictRange(start: number, end: number): void {\n while (this.count > 0) {\n this.oldest = this.normalize(this.oldest);\n if (this.oldest < start || this.oldest >= end) return;\n const len = this.buf.readUInt32LE(this.oldest);\n this.oldest += LEN_BYTES + len;\n this.count--;\n }\n }\n}\n","import { pack } from 'msgpackr';\nimport { RingBuffer } from './ring-buffer';\nimport {\n encodeHeaderInto,\n decodeEvent,\n HEADER_SIZE,\n type DecodedEvent,\n type EventType\n} from './encoder';\nimport { nowMono } from './clock';\nimport { currentRequestId } from './context';\n\n// ADR 015: one scratch header for the process lifetime — record() copies it\n// into the ring immediately, so reuse is safe and per-event allocation-free.\nconst scratchHeader = Buffer.allocUnsafe(HEADER_SIZE);\n\n/**\n * Hard per-event payload cap. addEvent() accepts arbitrary user objects;\n * without this, one call could occupy a huge slice of the ring and balloon\n * dump size. Oversized payloads are replaced by a small marker (the event's\n * existence is still forensic signal), never silently dropped.\n */\nexport const MAX_EVENT_PAYLOAD_BYTES = 64 * 1024;\n\nexport interface RecordOptions {\n requestId?: bigint;\n spanId?: bigint;\n tMonoNs?: bigint;\n}\n\n/**\n * The glue between instrumentations and the ring buffer.\n *\n * Iron rule: record() NEVER throws into user code and never does I/O —\n * encode + copy into the preallocated ring, nothing else.\n */\nexport class Recorder {\n armed = false;\n private seq = 0;\n\n constructor(private ring: RingBuffer) {}\n\n record(type: EventType, payload: Record<string, unknown>, opts?: RecordOptions): void {\n if (!this.armed) return;\n try {\n // ADR 015 zero-allocation path: scratch header + msgpackr's reused\n // internal buffer, both copied into the preallocated ring in one\n // vectored write. No intermediate record buffer is ever allocated.\n encodeHeaderInto(\n scratchHeader,\n this.seq++ >>> 0,\n opts?.tMonoNs ?? nowMono(),\n type,\n opts?.requestId ?? currentRequestId(),\n opts?.spanId ?? 0n\n );\n let body = pack(payload);\n if (body.length > MAX_EVENT_PAYLOAD_BYTES) {\n body = pack({ __truncated: true, originalBytes: body.length });\n }\n this.ring.write(scratchHeader, body);\n } catch {\n // Failure degrades to \"this event wasn't recorded\" — never to a crash.\n }\n }\n\n /**\n * ADR 005 — zero-lock freeze: swap in a fresh ring for active writes and\n * return the frozen one. Incoming traffic keeps recording into the new\n * buffer while the frozen segment is serialized; the frozen bytes can\n * never be overwritten mid-dump.\n *\n * `replacementBytes` lets memory-pressure dumps swap in a SMALL emergency\n * ring instead of another full-size allocation — allocating 64MB (or up\n * to 4GB) at the exact moment the heap trigger fired would deepen the\n * incident being recorded. The next normal-path dump restores full size.\n */\n freezeAndSwap(replacementBytes?: number): RingBuffer {\n const frozen = this.ring;\n this.ring = new RingBuffer(\n Math.min(replacementBytes ?? frozen.capacity, frozen.capacity)\n );\n return frozen;\n }\n\n /** Decode a chronological copy of the active buffer. Recording continues. */\n snapshot(): DecodedEvent[] {\n return decodeRing(this.ring);\n }\n\n get eventCount(): number {\n return this.ring.size;\n }\n\n /** ADR 009 panic path ONLY — raw access to the live ring. */\n get activeRing(): RingBuffer {\n return this.ring;\n }\n}\n\nexport function decodeRing(ring: RingBuffer): DecodedEvent[] {\n const out: DecodedEvent[] = [];\n for (const rec of ring.snapshot()) {\n try {\n out.push(decodeEvent(rec));\n } catch {\n // Skip a corrupt record rather than lose the dump.\n }\n }\n return out;\n}\n","import { pack, unpack } from 'msgpackr';\n\n/**\n * Binary event encoding for the ring buffer: a fixed 29-byte header followed\n * by a msgpackr-packed payload.\n *\n * offset size field\n * 0 4 seq (u32 LE, wraps)\n * 4 8 tMonoNs (u64 LE)\n * 12 1 type (u8)\n * 13 8 requestId (u64 LE, 0 = none)\n * 21 8 spanId (u64 LE, 0 = none)\n * 29 ... msgpackr payload\n */\n\nexport const HEADER_SIZE = 29;\n\n/** Numeric wire codes. The .fbox envelope maps these to string names. */\nexport enum EventType {\n HttpServerStart = 1,\n HttpServerEnd = 2,\n HttpClientStart = 3,\n HttpClientEnd = 4,\n PgQueryStart = 5,\n PgQueryEnd = 6,\n PgPoolWait = 7,\n Log = 8,\n Vitals = 9,\n Trigger = 10,\n Custom = 11\n}\n\nexport const EVENT_TYPE_NAMES: Record<number, string> = {\n [EventType.HttpServerStart]: 'http.server.start',\n [EventType.HttpServerEnd]: 'http.server.end',\n [EventType.HttpClientStart]: 'http.client.start',\n [EventType.HttpClientEnd]: 'http.client.end',\n [EventType.PgQueryStart]: 'pg.query.start',\n [EventType.PgQueryEnd]: 'pg.query.end',\n [EventType.PgPoolWait]: 'pg.pool.wait',\n [EventType.Log]: 'log',\n [EventType.Vitals]: 'vitals',\n [EventType.Trigger]: 'trigger',\n [EventType.Custom]: 'custom'\n};\n\n/** Truncation limits applied at capture time — the hot path never carries big blobs. */\nexport const LIMITS = {\n path: 512,\n query: 2048,\n log: 1024\n} as const;\n\nexport function truncate(s: string, max: number): string;\nexport function truncate(s: string | undefined | null, max: number): string | undefined;\nexport function truncate(s: string | undefined | null, max: number): string | undefined {\n if (s === undefined || s === null) return undefined;\n return s.length > max ? s.slice(0, max) : s;\n}\n\nexport interface EventHeader {\n seq: number;\n tMonoNs: bigint;\n type: number;\n requestId: bigint;\n spanId: bigint;\n}\n\nexport interface DecodedEvent {\n header: EventHeader;\n payload: Record<string, unknown>;\n}\n\n/**\n * ADR 015 hot path: writes the header into a caller-owned (reusable) buffer.\n * Scalar args on purpose — no per-event header object allocation.\n */\nexport function encodeHeaderInto(\n target: Buffer,\n seq: number,\n tMonoNs: bigint,\n type: number,\n requestId: bigint,\n spanId: bigint\n): void {\n target.writeUInt32LE(seq >>> 0, 0);\n target.writeBigUInt64LE(tMonoNs, 4);\n target.writeUInt8(type & 0xff, 12);\n target.writeBigUInt64LE(requestId, 13);\n target.writeBigUInt64LE(spanId, 21);\n}\n\nexport function encodeEvent(header: EventHeader, payload: unknown): Buffer {\n const body: Buffer = pack(payload);\n const buf = Buffer.allocUnsafe(HEADER_SIZE + body.length);\n encodeHeaderInto(buf, header.seq, header.tMonoNs, header.type, header.requestId, header.spanId);\n body.copy(buf, HEADER_SIZE);\n return buf;\n}\n\n/**\n * Round-trip is lossless for JSON payloads with two documented exceptions\n * (both fine for a flight recorder, and pinned by the encoder tests):\n * - msgpackr renames `__proto__` keys to `__proto_` on decode (its\n * prototype-pollution guard). We rely on this — .fbox payloads are\n * untrusted input when opened by the viewer/CLI.\n * - `-0` is encoded as integer `0` (same collapse JSON.stringify performs).\n */\nexport function decodeEvent(buf: Buffer): DecodedEvent {\n if (buf.length < HEADER_SIZE) {\n throw new RangeError(`event record too short: ${buf.length} bytes`);\n }\n return {\n header: {\n seq: buf.readUInt32LE(0),\n tMonoNs: buf.readBigUInt64LE(4),\n type: buf.readUInt8(12),\n requestId: buf.readBigUInt64LE(13),\n spanId: buf.readBigUInt64LE(21)\n },\n payload: unpack(buf.subarray(HEADER_SIZE))\n };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Request context. Every event recorded inside a request's async continuation\n * (queries, outbound calls, logs) inherits the requestId — this is what makes\n * swimlane nesting in the viewer possible.\n */\n\nexport interface RequestContext {\n requestId: bigint;\n}\n\nexport const requestStorage = new AsyncLocalStorage<RequestContext>();\n\nlet nextRequestId = 1n;\nlet nextSpanId = 1n;\n\n// The wire header is u64; letting the counter grow past 2^64 would make\n// writeBigUInt64LE throw and events silently vanish (recorder swallows).\n// Wrap explicitly and skip 0 (0 = \"outside any request\" sentinel).\nconst U64_MASK = 0xffffffffffffffffn;\n\nexport function newRequestId(): bigint {\n const id = nextRequestId;\n nextRequestId = (nextRequestId + 1n) & U64_MASK;\n if (nextRequestId === 0n) nextRequestId = 1n;\n return id;\n}\n\nexport function newSpanId(): bigint {\n const id = nextSpanId;\n nextSpanId = (nextSpanId + 1n) & U64_MASK;\n if (nextSpanId === 0n) nextSpanId = 1n;\n return id;\n}\n\n/** 0n means \"outside any request\". */\nexport function currentRequestId(): bigint {\n return requestStorage.getStore()?.requestId ?? 0n;\n}\n\nexport function runWithRequest<T>(requestId: bigint, fn: () => T): T {\n return requestStorage.run({ requestId }, fn);\n}\n","/**\n * ADR 012 — adaptive load-shedding switch. When active, per-request\n * instrumentation passes straight through: no ALS context, no events, no\n * allocation. The flag is flipped by the vitals sampler (Week 2) when\n * event-loop lag crosses the configured threshold, with hysteresis.\n *\n * Kept as a bare module-level boolean: the check sits on the hottest path\n * in the agent and must cost a single property read.\n */\n\nlet active = false;\n\nexport function isShedding(): boolean {\n return active;\n}\n\nexport function setShedding(on: boolean): void {\n active = on;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { StagingConfig } from '../config';\n\n/**\n * Staging directory — every dump lands here FIRST, via synchronous write\n * (the only I/O survivable on the crash path). Sinks deliver from staging;\n * anything undelivered is recovered on next boot.\n *\n * ADR 006: staging is quota-bound (FIFO eviction of oldest .fbox) and the\n * disk is health-checked before every write so Flightbox can never fill a\n * production disk (self-inflicted DoS).\n *\n * ADR 010: each process stages into its own `staging/pid-<pid>/` dir so\n * cluster workers never collide; dead workers' dirs are adopted atomically\n * on boot via renameSync (exactly one claimant can win).\n */\n\nexport class DiskFullError extends Error {\n constructor(freePct: number, minFreePct: number) {\n super(\n `disk has ${freePct.toFixed(1)}% free, below the ${minFreePct}% safety floor`\n );\n this.name = 'DiskFullError';\n }\n}\n\nconst PID_DIR_RE = /^pid-(\\d+)$/;\n/** A dir mid-adoption: claimed-<claimantPid>-<originalName>. */\nconst CLAIMED_DIR_RE = /^claimed-(\\d+)-(.+)$/;\n\nexport class Staging {\n /** Shared root: <dir>/staging/ */\n readonly baseStagingDir: string;\n /** This process's isolated dir: <dir>/staging/pid-<pid>/ (ADR 010). */\n readonly stagingDir: string;\n readonly deliveredDir: string;\n\n constructor(\n baseDir: string,\n private readonly limits: StagingConfig,\n private readonly log: (msg: string) => void = () => {}\n ) {\n this.baseStagingDir = path.join(baseDir, 'staging');\n this.stagingDir = path.join(this.baseStagingDir, `pid-${process.pid}`);\n this.deliveredDir = path.join(baseDir, 'delivered');\n fs.mkdirSync(this.stagingDir, { recursive: true });\n fs.mkdirSync(this.deliveredDir, { recursive: true });\n }\n\n /**\n * Synchronous, crash-safe write: tmp file + fsync + rename (+ best-effort\n * parent-dir fsync on POSIX), so staging never holds a torn .fbox and a\n * power loss right after return cannot un-persist it. Throws\n * DiskFullError when the disk is critically full — the caller must\n * disable the agent (ADR 006) — and rejects any single dump larger than\n * the quota outright: \"evict everything, then write the oversized file\n * anyway\" would make the cap a fiction.\n */\n stageSync(fileName: string, data: Buffer): string {\n const maxBytes = this.limits.maxStageMb * 1024 * 1024;\n if (data.length > maxBytes) {\n throw new Error(\n `dump is ${(data.length / 1048576).toFixed(1)}MB, larger than the ` +\n `${this.limits.maxStageMb}MB staging quota — refusing to stage it`\n );\n }\n const freePct = this.diskFreePct();\n if (freePct !== null && freePct < this.limits.minDiskFreePct) {\n throw new DiskFullError(freePct, this.limits.minDiskFreePct);\n }\n this.evictOverQuota(data.length);\n const finalPath = path.join(this.stagingDir, fileName);\n const tmpPath = finalPath + '.part';\n const fd = fs.openSync(tmpPath, 'w');\n try {\n fs.writeSync(fd, data);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n fs.renameSync(tmpPath, finalPath);\n try {\n // Durability for the rename itself. Windows cannot open directories\n // as fds — there the rename journal has to be enough.\n const dirFd = fs.openSync(this.stagingDir, 'r');\n fs.fsyncSync(dirFd);\n fs.closeSync(dirFd);\n } catch {\n /* best-effort on platforms that support it */\n }\n return finalPath;\n }\n\n /** Own staged .fbox files, oldest first (names embed timestamps). */\n listStagedSync(): string[] {\n return this.listOwn('.fbox');\n }\n\n /** Own raw panic dumps awaiting conversion (ADR 009). */\n listPanicSync(): string[] {\n return this.listOwn('.fboxpanic');\n }\n\n /**\n * Move a staged file to delivered/ after successful sink delivery.\n * delivered/ shares the ADR 006 quota — local retention must not outlive\n * the disk either.\n */\n markDeliveredSync(filePath: string): string {\n let incoming = 0;\n try {\n incoming = fs.statSync(filePath).size;\n } catch {\n // Missing source will fail the rename below; quota pass is best-effort.\n }\n this.evictFilesOverQuota(this.listDir(this.deliveredDir, '.fbox'), incoming);\n const dest = path.join(this.deliveredDir, path.basename(filePath));\n fs.renameSync(filePath, dest);\n return dest;\n }\n\n /**\n * ADR 010 adoption pass: absorb staging dirs left by DEAD sibling\n * processes. renameSync is the atomicity lock — when several new workers\n * race for the same orphan, exactly one rename succeeds; losers get\n * ENOENT and move on. Also re-adopts `claimed-*` dirs whose claimant died\n * mid-adoption (audit M4) — without this, a crash between the claim\n * rename and the file moves would strand incidents invisibly forever.\n * Returns how many files were adopted.\n *\n * Known limitation (audit L2): PID reuse. If a dead worker's pid was\n * recycled by an unrelated live process, its dir looks \"alive\" and waits\n * until that process exits. Solving this needs process identity beyond\n * the pid (start-time comparison), which has no portable API — accepted\n * for v0.1; the files are preserved, only delayed.\n */\n claimOrphanedSync(): number {\n let adopted = 0;\n let entries: string[];\n try {\n entries = fs.readdirSync(this.baseStagingDir);\n } catch {\n return 0;\n }\n for (const entry of entries) {\n let ownerPid: number;\n let originalName = entry;\n const orphan = PID_DIR_RE.exec(entry);\n const halfClaimed = orphan ? null : CLAIMED_DIR_RE.exec(entry);\n if (orphan) {\n ownerPid = Number(orphan[1]);\n } else if (halfClaimed) {\n ownerPid = Number(halfClaimed[1]); // the claimant that died mid-adoption\n originalName = halfClaimed[2]!;\n } else {\n continue;\n }\n if (ownerPid === process.pid || isProcessAlive(ownerPid)) continue;\n\n const orphanDir = path.join(this.baseStagingDir, entry);\n // Re-claims keep the ORIGINAL name segment so the claimant-pid prefix\n // never nests (claimed-B-claimed-A-pid-N would hide A's death).\n const claimedDir = path.join(this.baseStagingDir, `claimed-${process.pid}-${originalName}`);\n try {\n fs.renameSync(orphanDir, claimedDir); // the lock: one winner only\n } catch {\n continue; // another worker won the race, or the dir vanished\n }\n try {\n for (const file of fs.readdirSync(claimedDir)) {\n if (!file.endsWith('.fbox') && !file.endsWith('.fboxpanic')) {\n try {\n fs.unlinkSync(path.join(claimedDir, file)); // stray .part etc.\n } catch {\n /* best-effort */\n }\n continue;\n }\n try {\n fs.renameSync(path.join(claimedDir, file), path.join(this.stagingDir, file));\n adopted++;\n } catch {\n /* best-effort; leave in claimed dir for a later pass */\n }\n }\n fs.rmdirSync(claimedDir);\n } catch {\n /* partial adoption is fine; remainder is picked up next boot */\n }\n }\n if (adopted > 0) {\n this.log(`adopted ${adopted} file(s) from dead worker staging dirs`);\n }\n return adopted;\n }\n\n private listOwn(ext: string): string[] {\n return this.listDir(this.stagingDir, ext);\n }\n\n private listDir(dir: string, ext: string): string[] {\n try {\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(ext))\n .sort()\n .map((f) => path.join(dir, f));\n } catch {\n return [];\n }\n }\n\n /** null = cannot determine (treat as healthy rather than disable the agent). */\n private diskFreePct(): number | null {\n const statfsSync: ((p: string) => { bavail: number | bigint; blocks: number | bigint }) | undefined =\n (fs as unknown as Record<string, any>).statfsSync;\n if (typeof statfsSync !== 'function') return null;\n try {\n const s = statfsSync(this.stagingDir);\n const blocks = Number(s.blocks);\n if (!Number.isFinite(blocks) || blocks <= 0) return null;\n return (Number(s.bavail) / blocks) * 100;\n } catch {\n return null;\n }\n }\n\n /** ADR 006 FIFO eviction: silently drop oldest staged dumps to fit the quota. */\n private evictOverQuota(incomingBytes: number): void {\n this.evictFilesOverQuota(this.listStagedSync(), incomingBytes);\n }\n\n private evictFilesOverQuota(candidates: string[], incomingBytes: number): void {\n const maxBytes = this.limits.maxStageMb * 1024 * 1024;\n const files: Array<{ file: string; size: number }> = [];\n let total = incomingBytes;\n for (const file of candidates) {\n try {\n const size = fs.statSync(file).size;\n files.push({ file, size });\n total += size;\n } catch {\n // File vanished between listing and stat — ignore.\n }\n }\n for (const { file, size } of files) {\n if (total <= maxBytes) break;\n try {\n fs.unlinkSync(file);\n total -= size;\n this.log(`staging quota (${this.limits.maxStageMb}MB) exceeded — evicted oldest: ${path.basename(file)}`);\n } catch {\n // Eviction is best-effort; never let it break the dump path.\n }\n }\n }\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM = exists but not ours; ESRCH = gone.\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\n/**\n * Boot-time resurrection pass: adopt dead siblings' staging (ADR 010), then\n * report (and later, deliver) anything staged but undelivered. Sinks land in\n * a later milestone; until then files are preserved and announced.\n */\nexport function recoverOnBoot(staging: Staging, log: (msg: string) => void): string[] {\n staging.claimOrphanedSync();\n const files = staging.listStagedSync();\n if (files.length > 0) {\n log(\n `found ${files.length} unsent incident(s) from a previous run in ${staging.stagingDir}`\n );\n }\n return files;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { RingBuffer } from '../ring-buffer';\nimport { decodeEvent, type DecodedEvent } from '../encoder';\nimport { getAnchor, type ClockAnchor } from '../clock';\nimport { serializeIncident } from './serializer';\nimport type { Staging } from './stage';\n\n/**\n * ADR 009 — the OOM panic path. When the process is dying BECAUSE the heap\n * is exhausted, gzip/msgpack/envelope building would abort V8 before the\n * crash handler finishes. This module writes the RAW ring bytes through a\n * fd preopened at start() (when memory was plentiful):\n *\n * [44-byte panic header][ring buffer bytes, verbatim]\n *\n * Header (LE): magic \"FBOXPANC\" (8) | version u32 | capacity u32 |\n * oldest u32 | count u32 | anchor wallMs f64 | anchor monoNs u64 | 4 reserved.\n *\n * The next boot converts .fboxpanic files into normal .fbox incidents —\n * memory is plentiful again by then. The no-split framing invariant\n * (ADR 011) is what makes the raw image parseable.\n */\n\nconst MAGIC = Buffer.from('FBOXPANC', 'ascii');\nconst PANIC_VERSION = 1;\nexport const PANIC_HEADER_LEN = 44;\n\nexport class PanicWriter {\n private fd: number | null = null;\n /** Preallocated so panic-time writes allocate (nearly) nothing. */\n private readonly header = Buffer.alloc(PANIC_HEADER_LEN);\n readonly filePath: string;\n\n constructor(stagingDir: string) {\n this.filePath = path.join(stagingDir, `panic-${process.pid}.fboxpanic`);\n }\n\n /** Preopen the fd while memory is plentiful. Failure is non-fatal. */\n arm(): void {\n try {\n this.fd = fs.openSync(this.filePath, 'w');\n } catch {\n this.fd = null;\n }\n }\n\n /**\n * The panic dump: no gzip, no decode, no envelope — raw ring bytes onto\n * the preopened fd. Safe to call with V8 near the heap limit.\n */\n writeSync(ring: RingBuffer): boolean {\n if (this.fd === null) return false;\n try {\n const state = ring.rawState;\n const anchor = getAnchor();\n MAGIC.copy(this.header, 0);\n this.header.writeUInt32LE(PANIC_VERSION, 8);\n this.header.writeUInt32LE(ring.capacity, 12);\n this.header.writeUInt32LE(state.oldest, 16);\n this.header.writeUInt32LE(state.count, 20);\n this.header.writeDoubleLE(anchor.wallMs, 24);\n this.header.writeBigUInt64LE(anchor.monoNs, 32);\n fs.writeSync(this.fd, this.header, 0, PANIC_HEADER_LEN, 0);\n fs.writeSync(this.fd, state.buf, 0, state.buf.length, PANIC_HEADER_LEN);\n fs.fsyncSync(this.fd);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Clean shutdown: close the fd and sweep the empty placeholder. */\n disarm(): void {\n try {\n if (this.fd !== null) fs.closeSync(this.fd);\n this.fd = null;\n if (fs.existsSync(this.filePath) && fs.statSync(this.filePath).size === 0) {\n fs.unlinkSync(this.filePath);\n }\n } catch {\n this.fd = null;\n }\n }\n}\n\ninterface ParsedPanic {\n anchor: ClockAnchor;\n events: DecodedEvent[];\n}\n\nfunction parsePanicFile(raw: Buffer): ParsedPanic | null {\n if (raw.length < PANIC_HEADER_LEN || !raw.subarray(0, 8).equals(MAGIC)) return null;\n if (raw.readUInt32LE(8) !== PANIC_VERSION) return null;\n const capacity = raw.readUInt32LE(12);\n const oldest = raw.readUInt32LE(16);\n const count = raw.readUInt32LE(20);\n const wallMs = raw.readDoubleLE(24);\n const monoNs = raw.readBigUInt64LE(32);\n const body = raw.subarray(PANIC_HEADER_LEN);\n if (body.length !== capacity || oldest >= capacity) return null;\n\n const events: DecodedEvent[] = [];\n for (const rec of RingBuffer.readRecords(body, capacity, oldest, count)) {\n try {\n events.push(decodeEvent(rec));\n } catch {\n // A frame torn by the crash — skip it, keep the rest.\n }\n }\n // The raw image is in buffer order, not time order; restore chronology.\n events.sort((a, b) =>\n a.header.tMonoNs < b.header.tMonoNs ? -1 : a.header.tMonoNs > b.header.tMonoNs ? 1 : 0\n );\n return { anchor: { wallMs, monoNs }, events };\n}\n\n/**\n * Boot-time conversion pass: turn every .fboxpanic in our staging dir\n * (our own crashed run's, or ones adopted from dead workers, ADR 010) into\n * a normal .fbox incident. Empty placeholders are swept; unparseable files\n * are renamed .corrupt, never silently deleted.\n */\nexport function recoverPanicFiles(\n staging: Staging,\n service: string,\n log: (msg: string) => void\n): number {\n let recovered = 0;\n for (const file of staging.listPanicSync()) {\n try {\n if (fs.statSync(file).size < PANIC_HEADER_LEN) {\n fs.unlinkSync(file); // placeholder from a clean or failed-arm run\n continue;\n }\n const parsed = parsePanicFile(fs.readFileSync(file));\n if (parsed === null) {\n fs.renameSync(file, file + '.corrupt');\n log(`panic dump ${path.basename(file)} is unparseable — kept as .corrupt`);\n continue;\n }\n const { fileName, data, eventCount } = serializeIncident({\n service,\n trigger: { type: 'panic', reason: 'recovered from crash-time panic dump' },\n events: parsed.events,\n anchor: parsed.anchor\n });\n staging.stageSync(fileName, data);\n fs.unlinkSync(file);\n recovered++;\n log(`recovered panic dump → ${fileName} (${eventCount} events)`);\n } catch (err) {\n log(`panic recovery failed for ${path.basename(file)}: ${(err as Error).message}`);\n }\n }\n return recovered;\n}\n","import { gzipSync } from 'node:zlib';\nimport { EVENT_TYPE_NAMES, type DecodedEvent } from '../encoder';\nimport { getAnchor, type ClockAnchor } from '../clock';\nimport {\n FORMAT_VERSION,\n type Incident,\n type IncidentEvent,\n type IncidentMeta\n} from '@flightbox/format';\n\n/** Kept in sync with package.json by scripts/sync-version.mjs (runs inside `pnpm run version`). */\nexport const FLIGHTBOX_VERSION = '0.1.0';\n\n/** ADR 016: dumps are emergency artifacts — fastest gzip level, not smallest. */\nexport const DUMP_GZIP_LEVEL = 1;\n\nexport interface PreparedIncident {\n fileName: string;\n json: string;\n meta: IncidentMeta;\n eventCount: number;\n windowMs: number;\n}\n\nexport interface SerializedIncident {\n fileName: string;\n data: Buffer;\n meta: IncidentMeta;\n eventCount: number;\n windowMs: number;\n}\n\nlet dumpCounter = 0;\n\nexport interface SerializeOptions {\n service: string;\n trigger: { type: string; reason?: string };\n events: DecodedEvent[];\n /** ADR 009: panic recovery passes the CRASHED run's anchor, not this boot's. */\n anchor?: ClockAnchor;\n}\n\n/**\n * Decoded ring events -> envelope JSON (formatVersion 1). Compression is the\n * caller's business: gzipSync on the crash path, threadpool zlib.gzip for\n * live triggers (ADR 016).\n */\nexport function buildIncident(opts: SerializeOptions): PreparedIncident {\n const anchor = opts.anchor ?? getAnchor();\n const events: IncidentEvent[] = opts.events.map((e) => ({\n seq: e.header.seq,\n wallMs: anchor.wallMs + Number(e.header.tMonoNs - anchor.monoNs) / 1e6,\n tMonoNs: e.header.tMonoNs.toString(),\n type: EVENT_TYPE_NAMES[e.header.type] ?? `unknown.${e.header.type}`,\n // ADR 004: context-less events are \"orphan\", never an error.\n requestId: e.header.requestId === 0n ? 'orphan' : e.header.requestId.toString(),\n spanId: e.header.spanId.toString(),\n data: e.payload\n }));\n\n const first = events[0];\n const last = events[events.length - 1];\n const windowMs = first && last ? last.wallMs - first.wallMs : 0;\n\n const meta: IncidentMeta = {\n service: opts.service,\n capturedAt: new Date().toISOString(),\n wallAnchor: { wallMs: anchor.wallMs, monoNs: anchor.monoNs.toString() },\n trigger: opts.trigger,\n eventCount: events.length,\n windowMs,\n flightboxVersion: FLIGHTBOX_VERSION\n };\n const incident: Incident = { formatVersion: FORMAT_VERSION, meta, events };\n\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n // pid in the name keeps files unique across cluster workers (ADR 010).\n const fileName = `incident-${stamp}-p${process.pid}-${(dumpCounter++).toString(36)}.fbox`;\n return {\n fileName,\n json: JSON.stringify(incident),\n meta,\n eventCount: events.length,\n windowMs\n };\n}\n\n/** Synchronous variant — crash path and panic recovery only. */\nexport function serializeIncident(opts: SerializeOptions): SerializedIncident {\n const prepared = buildIncident(opts);\n return {\n fileName: prepared.fileName,\n data: gzipSync(prepared.json, { level: DUMP_GZIP_LEVEL }),\n meta: prepared.meta,\n eventCount: prepared.eventCount,\n windowMs: prepared.windowMs\n };\n}\n","/** Version of the .fbox envelope this code reads and writes. */\nexport const FORMAT_VERSION = 1;\n\n/**\n * Well-known event type names. The wire format inside the agent's ring buffer\n * uses numeric codes; the .fbox envelope always carries these string names so\n * incident files remain readable forever, independent of agent internals.\n */\nexport type EventTypeName =\n | 'http.server.start'\n | 'http.server.end'\n | 'http.client.start'\n | 'http.client.end'\n | 'pg.query.start'\n | 'pg.query.end'\n | 'pg.pool.wait'\n | 'log'\n | 'vitals'\n | 'trigger'\n | 'custom';\n\nexport interface IncidentEvent {\n /** Monotonic sequence number assigned at record time. */\n seq: number;\n /** Wall-clock milliseconds (epoch), derived from the monotonic clock + anchor. */\n wallMs: number;\n /** Raw monotonic nanoseconds as a decimal string (bigint-safe). */\n tMonoNs: string;\n /** Event type name; unknown numeric codes serialize as \"unknown.<code>\". */\n type: EventTypeName | string;\n /**\n * Request correlation id as a decimal string. Events recorded outside any\n * active request context are tagged \"orphan\" (ADR 004) — never an error.\n */\n requestId: string;\n /** Span id as a decimal string; \"0\" = not part of a span pair. */\n spanId: string;\n /** Type-specific payload. Already truncated/redacted at capture time. */\n data: Record<string, unknown>;\n}\n\nexport interface WallAnchor {\n /** Date.now() at the moment the agent started. */\n wallMs: number;\n /** process.hrtime.bigint() at the same moment, as a decimal string. */\n monoNs: string;\n}\n\nexport interface TriggerInfo {\n type: string;\n reason?: string;\n}\n\nexport interface IncidentMeta {\n service: string;\n /** ISO timestamp of when the dump was taken. */\n capturedAt: string;\n wallAnchor: WallAnchor;\n trigger: TriggerInfo;\n eventCount: number;\n /** Time span covered by the captured events, in milliseconds. */\n windowMs: number;\n flightboxVersion: string;\n}\n\nexport interface Incident {\n formatVersion: number;\n meta: IncidentMeta;\n events: IncidentEvent[];\n}\n","import { validateIncident } from './validate';\nimport type { Incident } from './types';\n\nconst GZIP_MAGIC_0 = 0x1f;\nconst GZIP_MAGIC_1 = 0x8b;\n\n/**\n * Untrusted-input limits: the viewer opens arbitrary dropped files and\n * remote URLs, so a crafted gzip bomb must fail with a message instead of\n * freezing the tab. Sized generously above any real ring dump.\n */\nexport const MAX_DECOMPRESSED_BYTES = 1024 * 1024 * 1024; // 1 GiB\nexport const MAX_EVENT_COUNT = 2_000_000;\n\n/**\n * Gunzip that works in both browsers and Node >= 18 without importing any\n * Node built-in at module load (this file must stay browser-safe).\n *\n * Support matrix: browsers use DecompressionStream (Chrome/Edge 80+,\n * Firefox 113+, Safari 16.4+); Node (and older Node without the global)\n * falls back to node:zlib. A browser too old for DecompressionStream gets\n * an actionable error instead of a cryptic failed import of node:zlib.\n */\nasync function gunzip(bytes: Uint8Array): Promise<Uint8Array> {\n const g = globalThis as Record<string, any>;\n if (typeof g.DecompressionStream !== 'undefined') {\n // Streamed with a running total, so a decompression bomb aborts at the\n // cap instead of materializing gigabytes first.\n const reader = new g.Blob([bytes])\n .stream()\n .pipeThrough(new g.DecompressionStream('gzip'))\n .getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += (value as Uint8Array).length;\n if (total > MAX_DECOMPRESSED_BYTES) {\n void reader.cancel();\n throw new Error(\n `Refusing to decompress: file expands past ${MAX_DECOMPRESSED_BYTES} bytes — not a plausible incident.`\n );\n }\n chunks.push(value as Uint8Array);\n }\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.length;\n }\n return out;\n }\n try {\n const zlib = await import('node:zlib');\n const out = new Uint8Array(\n zlib.gunzipSync(bytes, { maxOutputLength: MAX_DECOMPRESSED_BYTES })\n );\n return out;\n } catch (err) {\n if ((err as NodeJS.ErrnoException)?.code === 'ERR_BUFFER_TOO_LARGE') {\n throw new Error(\n `Refusing to decompress: file expands past ${MAX_DECOMPRESSED_BYTES} bytes — not a plausible incident.`\n );\n }\n if ((err as Error)?.message?.includes('output length')) {\n throw new Error(\n `Refusing to decompress: file expands past ${MAX_DECOMPRESSED_BYTES} bytes — not a plausible incident.`\n );\n }\n throw new Error(\n 'This environment cannot decompress .fbox files: it has neither ' +\n 'DecompressionStream (Chrome/Edge 80+, Firefox 113+, Safari 16.4+) ' +\n 'nor working node:zlib. Use a newer browser, or `npx fboxrec open <file>`.'\n );\n }\n}\n\n/**\n * Parse .fbox bytes (gzipped JSON envelope), raw JSON bytes, or a JSON string\n * into a validated, typed Incident. Runs entirely in-process: in the browser\n * the file never leaves the machine.\n */\nexport async function parseIncident(\n input: Uint8Array | ArrayBuffer | string\n): Promise<Incident> {\n let text: string;\n if (typeof input === 'string') {\n text = input;\n } else {\n let bytes = input instanceof ArrayBuffer ? new Uint8Array(input) : input;\n if (bytes.length >= 2 && bytes[0] === GZIP_MAGIC_0 && bytes[1] === GZIP_MAGIC_1) {\n bytes = await gunzip(bytes);\n }\n text = new TextDecoder('utf-8').decode(bytes);\n }\n\n let obj: unknown;\n try {\n obj = JSON.parse(text);\n } catch {\n throw new Error(\n 'Not a Flightbox incident: file is neither gzipped JSON nor plain JSON.'\n );\n }\n validateIncident(obj);\n if (obj.events.length > MAX_EVENT_COUNT) {\n throw new Error(\n `Incident has ${obj.events.length} events — above the ${MAX_EVENT_COUNT} viewer limit.`\n );\n }\n return obj;\n}\n","import { decodeRing, type Recorder } from '../recorder';\nimport type { DecodedEvent } from '../encoder';\n\n/**\n * ADR 005 — take a dump without blocking or corrupting live traffic:\n * atomically swap a fresh ring in for active writes, then decode the frozen\n * one at leisure. Recording continues uninterrupted throughout.\n */\nexport function takeFrozenSnapshot(\n recorder: Recorder,\n replacementBytes?: number\n): DecodedEvent[] {\n return decodeRing(recorder.freezeAndSwap(replacementBytes));\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DiskSinkConfig } from '../../config';\nimport type { Sink, DeliveryResult } from './types';\n\n/**\n * Copy into a configured dir. On EC2/VPS this alone is a complete solution.\n * The target dir is quota-bound like staging (ADR 006 / audit L6): oldest\n * .fbox files are FIFO-evicted so the sink can never fill the disk either.\n */\nexport function createDiskSink(cfg: DiskSinkConfig): Sink {\n const maxBytes = (cfg.maxMb ?? 500) * 1024 * 1024;\n return {\n name: 'disk',\n async deliver(filePath): Promise<DeliveryResult> {\n const dir = path.resolve(cfg.dir);\n await fs.promises.mkdir(dir, { recursive: true });\n const dest = path.join(dir, path.basename(filePath));\n await fs.promises.copyFile(filePath, dest);\n evictOverQuota(dir, maxBytes);\n return { ok: true, location: dest };\n }\n };\n}\n\nfunction evictOverQuota(dir: string, maxBytes: number): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith('.fbox'))\n .sort() // names embed timestamps: lexicographic = oldest first\n .map((f) => path.join(dir, f));\n let total = 0;\n const sized = files.map((file) => {\n const size = fs.statSync(file).size;\n total += size;\n return { file, size };\n });\n for (const { file, size } of sized) {\n if (total <= maxBytes) break;\n fs.unlinkSync(file);\n total -= size;\n }\n } catch {\n // Eviction is best-effort; delivery already succeeded.\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst PUT_TIMEOUT_MS = 30_000;\nimport type { S3SinkConfig } from '../../config';\nimport type { Sink, DeliveryResult } from './types';\nimport { sha256Hex, signHeaders, presignUrl } from './sigv4';\nimport { resolveCredentials, invalidateCredentials } from './credentials';\n\nconst MAX_PRESIGN_SEC = 7 * 24 * 3600; // SigV4 hard limit\n\n/**\n * The S3/R2/B2/MinIO sink — Bible §4.3/§6. Uploads via header-signed PUT;\n * mints the presigned magic link (ADR 008: viewer origin configurable for\n * air-gapped/self-hosted viewers). A 403 invalidates the ADR 017 credential\n * cache and retries once with fresh credentials (rotation race).\n */\nexport function createS3Sink(cfg: S3SinkConfig, defaultViewerOrigin: string): Sink {\n const region = cfg.region || (cfg.endpoint ? 'auto' : 'us-east-1');\n\n function objectUrl(key: string): URL {\n if (cfg.endpoint) {\n // Path-style for custom endpoints (R2/B2/MinIO).\n return new URL(`${cfg.endpoint.replace(/\\/$/, '')}/${cfg.bucket}/${key}`);\n }\n return new URL(`https://${cfg.bucket}.s3.${region}.amazonaws.com/${key}`);\n }\n\n return {\n name: 's3',\n async deliver(filePath, meta): Promise<DeliveryResult> {\n // Async read — delivery happens while the server may be unhealthy;\n // don't add synchronous file I/O to the event loop on top of that.\n const body = await fs.promises.readFile(filePath);\n const key = `${cfg.prefix ?? ''}${path.basename(filePath)}`;\n\n let creds = await resolveCredentials(cfg);\n if (!creds) {\n return {\n ok: false,\n detail:\n 'no AWS credentials resolved (chain: static -> AWS_* env -> ECS task role -> IMDSv2)'\n };\n }\n\n const put = async (c: typeof creds & object): Promise<Response> => {\n const url = objectUrl(key);\n const headers = signHeaders({\n method: 'PUT',\n url,\n payloadHash: sha256Hex(body),\n creds: c,\n region,\n headers: {\n 'content-type': 'application/gzip',\n 'x-amz-meta-flightbox-trigger': meta.trigger.type\n }\n });\n return fetch(url, {\n method: 'PUT',\n headers,\n body,\n // A hung connection must not leave the delivery promise pending\n // forever; the file stays staged and the retry pass gets it.\n signal: AbortSignal.timeout(PUT_TIMEOUT_MS)\n });\n };\n\n let res = await put(creds);\n if (res.status === 403) {\n // Rotation race (ADR 017): refresh once and retry.\n invalidateCredentials();\n const fresh = await resolveCredentials(cfg);\n if (fresh) {\n creds = fresh;\n res = await put(fresh);\n }\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n return { ok: false, detail: `S3 PUT ${res.status}: ${text.slice(0, 200)}` };\n }\n\n const location = `s3://${cfg.bucket}/${key}`;\n let viewerUrl: string | undefined;\n if (cfg.presign?.enabled !== false) {\n // 24h default (audit L4): the link is printed to logs, which makes\n // log access a bearer credential for the incident — keep it short.\n const expiresSec = Math.min(\n (cfg.presign?.expiresHours ?? 24) * 3600,\n MAX_PRESIGN_SEC\n );\n const presigned = presignUrl({ url: objectUrl(key), creds, region, expiresSec });\n const origin = (cfg.presign?.viewerOrigin ?? defaultViewerOrigin).replace(/\\/$/, '');\n viewerUrl = `${origin}/?src=${encodeURIComponent(presigned)}`;\n }\n return { ok: true, location, viewerUrl };\n }\n };\n}\n","import { createHash, createHmac } from 'node:crypto';\n\n/**\n * AWS Signature V4 — Bible §6: S3 (and R2/B2/MinIO) is just HTTP + this\n * signing math. ~120 lines of node:crypto + fetch instead of tens of MB of\n * @aws-sdk. Session tokens supported for the ADR 017 credential chain.\n */\n\nexport interface AwsCredentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n expiresAtMs?: number;\n /** Which chain link produced these (static | aws-env | ecs-task-role | ec2-imdsv2). */\n source?: string;\n}\n\nexport function sha256Hex(data: string | Buffer): string {\n return createHash('sha256').update(data).digest('hex');\n}\n\nfunction hmac(key: string | Buffer, data: string): Buffer {\n return createHmac('sha256', key).update(data, 'utf8').digest();\n}\n\n/** AWS uriEncode: RFC3986, unreserved A-Za-z0-9 - . _ ~, optional '/' passthrough. */\nexport function uriEncode(str: string, encodeSlash: boolean): string {\n let out = '';\n for (const ch of str) {\n if (/[A-Za-z0-9\\-._~]/.test(ch) || (ch === '/' && !encodeSlash)) {\n out += ch;\n } else {\n for (const byte of Buffer.from(ch, 'utf8')) {\n out += '%' + byte.toString(16).toUpperCase().padStart(2, '0');\n }\n }\n }\n return out;\n}\n\nfunction timestamps(now: Date): { amzDate: string; dateStamp: string } {\n const iso = now.toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}/, '');\n return { amzDate: iso, dateStamp: iso.slice(0, 8) };\n}\n\nfunction canonicalUri(url: URL): string {\n return (\n url.pathname\n .split('/')\n .map((seg) => uriEncode(safeDecode(seg), true))\n .join('/') || '/'\n );\n}\n\nfunction safeDecode(seg: string): string {\n try {\n return decodeURIComponent(seg);\n } catch {\n return seg;\n }\n}\n\n/** Code-point order, NOT localeCompare — AWS canonicalization is byte order,\n * and after uriEncode every string is pure ASCII, where the two coincide. */\nfunction byCodePoint(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction canonicalQuery(params: Array<[string, string]>): string {\n return params\n .map(([k, v]) => [uriEncode(k, true), uriEncode(v, true)] as const)\n .sort((a, b) => (a[0] === b[0] ? byCodePoint(a[1], b[1]) : byCodePoint(a[0], b[0])))\n .map(([k, v]) => `${k}=${v}`)\n .join('&');\n}\n\nfunction signingKey(secret: string, dateStamp: string, region: string, service: string): Buffer {\n return hmac(hmac(hmac(hmac(`AWS4${secret}`, dateStamp), region), service), 'aws4_request');\n}\n\nexport interface SignHeadersInput {\n method: string;\n url: URL;\n payloadHash: string;\n creds: AwsCredentials;\n region: string;\n service?: string;\n headers?: Record<string, string>;\n now?: Date;\n}\n\n/** Header-signed request (PUT uploads). Returns the complete header set. */\nexport function signHeaders(input: SignHeadersInput): Record<string, string> {\n const service = input.service ?? 's3';\n const { amzDate, dateStamp } = timestamps(input.now ?? new Date());\n\n const headers: Record<string, string> = {\n host: input.url.host,\n 'x-amz-content-sha256': input.payloadHash,\n 'x-amz-date': amzDate,\n ...(input.creds.sessionToken ? { 'x-amz-security-token': input.creds.sessionToken } : {}),\n ...Object.fromEntries(\n Object.entries(input.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v])\n )\n };\n\n const names = Object.keys(headers).sort();\n const canonicalHeaders = names.map((n) => `${n}:${headers[n]!.trim()}\\n`).join('');\n const signedHeaders = names.join(';');\n\n const canonicalRequest = [\n input.method.toUpperCase(),\n canonicalUri(input.url),\n canonicalQuery([...input.url.searchParams.entries()]),\n canonicalHeaders,\n signedHeaders,\n input.payloadHash\n ].join('\\n');\n\n const scope = `${dateStamp}/${input.region}/${service}/aws4_request`;\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n amzDate,\n scope,\n sha256Hex(canonicalRequest)\n ].join('\\n');\n\n const signature = createHmac(\n 'sha256',\n signingKey(input.creds.secretAccessKey, dateStamp, input.region, service)\n )\n .update(stringToSign, 'utf8')\n .digest('hex');\n\n headers.authorization = `AWS4-HMAC-SHA256 Credential=${input.creds.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n return headers;\n}\n\nexport interface PresignInput {\n url: URL;\n creds: AwsCredentials;\n region: string;\n expiresSec: number;\n method?: string;\n service?: string;\n now?: Date;\n}\n\n/** Presigned GET — the same signature math expressed as query params. */\nexport function presignUrl(input: PresignInput): string {\n const service = input.service ?? 's3';\n const method = input.method ?? 'GET';\n const { amzDate, dateStamp } = timestamps(input.now ?? new Date());\n const scope = `${dateStamp}/${input.region}/${service}/aws4_request`;\n\n const params: Array<[string, string]> = [\n ...input.url.searchParams.entries(),\n ['X-Amz-Algorithm', 'AWS4-HMAC-SHA256'],\n ['X-Amz-Credential', `${input.creds.accessKeyId}/${scope}`],\n ['X-Amz-Date', amzDate],\n ['X-Amz-Expires', String(Math.floor(input.expiresSec))],\n ['X-Amz-SignedHeaders', 'host']\n ];\n if (input.creds.sessionToken) {\n params.push(['X-Amz-Security-Token', input.creds.sessionToken]);\n }\n\n const query = canonicalQuery(params);\n const canonicalRequest = [\n method.toUpperCase(),\n canonicalUri(input.url),\n query,\n `host:${input.url.host}\\n`,\n 'host',\n 'UNSIGNED-PAYLOAD'\n ].join('\\n');\n\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n amzDate,\n scope,\n sha256Hex(canonicalRequest)\n ].join('\\n');\n\n const signature = createHmac(\n 'sha256',\n signingKey(input.creds.secretAccessKey, dateStamp, input.region, service)\n )\n .update(stringToSign, 'utf8')\n .digest('hex');\n\n return `${input.url.origin}${canonicalUri(input.url)}?${query}&X-Amz-Signature=${signature}`;\n}\n","import type { AwsCredentials } from './sigv4';\n\n/**\n * ADR 017 — credential provider chain for the zero-dependency S3 client.\n * Resolved in order, cached until ~5 minutes before expiry:\n *\n * 1. explicit config / FLIGHTBOX_S3_KEY + FLIGHTBOX_S3_SECRET (+_TOKEN)\n * 2. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (+AWS_SESSION_TOKEN)\n * 3. ECS/Fargate task role (container credentials endpoint)\n * 4. EC2 instance role via IMDSv2 (token-required; v1 deliberately omitted)\n *\n * Metadata fetches use ~1s timeouts so non-AWS environments fail through\n * the chain instantly. Standard override env vars\n * (AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_EC2_METADATA_SERVICE_ENDPOINT)\n * are honored — which is also what makes this chain testable without AWS.\n */\n\nconst REFRESH_MARGIN_MS = 5 * 60_000;\nconst METADATA_TIMEOUT_MS = 1000;\n\nlet cache: AwsCredentials | null = null;\n\nexport function invalidateCredentials(): void {\n cache = null;\n}\n\ninterface StaticCredsSource {\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n}\n\nfunction fromStatic(cfg: StaticCredsSource): AwsCredentials | null {\n const key = cfg.accessKeyId ?? process.env.FLIGHTBOX_S3_KEY;\n const secret = cfg.secretAccessKey ?? process.env.FLIGHTBOX_S3_SECRET;\n if (!key || !secret) return null;\n return {\n accessKeyId: key,\n secretAccessKey: secret,\n sessionToken: cfg.sessionToken ?? process.env.FLIGHTBOX_S3_TOKEN,\n source: 'static'\n };\n}\n\nfunction fromAwsEnv(): AwsCredentials | null {\n const key = process.env.AWS_ACCESS_KEY_ID;\n const secret = process.env.AWS_SECRET_ACCESS_KEY;\n if (!key || !secret) return null;\n return {\n accessKeyId: key,\n secretAccessKey: secret,\n sessionToken: process.env.AWS_SESSION_TOKEN,\n source: 'aws-env'\n };\n}\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<any | null> {\n try {\n const res = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(METADATA_TIMEOUT_MS)\n });\n if (!res.ok) return null;\n return await res.json();\n } catch {\n return null;\n }\n}\n\nasync function fromEcs(): Promise<AwsCredentials | null> {\n const fullUri = process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI;\n const relativeUri = process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;\n if (!fullUri && !relativeUri) return null;\n const url = fullUri ?? `http://169.254.170.2${relativeUri}`;\n const body = await fetchJson(url);\n if (!body?.AccessKeyId || !body?.SecretAccessKey) return null;\n return {\n accessKeyId: body.AccessKeyId,\n secretAccessKey: body.SecretAccessKey,\n sessionToken: body.Token,\n expiresAtMs: body.Expiration ? Date.parse(body.Expiration) : undefined,\n source: 'ecs-task-role'\n };\n}\n\nasync function fromImdsV2(): Promise<AwsCredentials | null> {\n const base =\n process.env.AWS_EC2_METADATA_SERVICE_ENDPOINT?.replace(/\\/$/, '') ??\n 'http://169.254.169.254';\n let token: string;\n try {\n const res = await fetch(`${base}/latest/api/token`, {\n method: 'PUT',\n headers: { 'x-aws-ec2-metadata-token-ttl-seconds': '21600' },\n signal: AbortSignal.timeout(METADATA_TIMEOUT_MS)\n });\n if (!res.ok) return null;\n token = await res.text();\n } catch {\n return null;\n }\n const tokenHeader = { 'x-aws-ec2-metadata-token': token };\n\n let role: string;\n try {\n const res = await fetch(`${base}/latest/meta-data/iam/security-credentials/`, {\n headers: tokenHeader,\n signal: AbortSignal.timeout(METADATA_TIMEOUT_MS)\n });\n if (!res.ok) return null;\n role = (await res.text()).split('\\n')[0]!.trim();\n if (!role) return null;\n } catch {\n return null;\n }\n\n const body = await fetchJson(\n `${base}/latest/meta-data/iam/security-credentials/${role}`,\n { headers: tokenHeader }\n );\n if (!body?.AccessKeyId || !body?.SecretAccessKey) return null;\n return {\n accessKeyId: body.AccessKeyId,\n secretAccessKey: body.SecretAccessKey,\n sessionToken: body.Token,\n expiresAtMs: body.Expiration ? Date.parse(body.Expiration) : undefined,\n source: 'ec2-imdsv2'\n };\n}\n\nexport async function resolveCredentials(\n cfg: StaticCredsSource = {}\n): Promise<AwsCredentials | null> {\n // Per-sink static credentials are cheap and sink-SPECIFIC — never cached\n // globally, or two sinks with different explicit keys would poison each\n // other (one sink's creds reused for the other's bucket). Only the\n // ambient identity chain (env/ECS/IMDS — same answer for every sink in\n // this process) goes through the shared cache.\n const staticCreds = fromStatic(cfg);\n if (staticCreds) return staticCreds;\n const envCreds = fromAwsEnv();\n if (envCreds) return envCreds;\n\n if (\n cache &&\n (cache.expiresAtMs === undefined || cache.expiresAtMs - Date.now() > REFRESH_MARGIN_MS)\n ) {\n return cache;\n }\n cache = (await fromEcs()) ?? (await fromImdsV2());\n return cache;\n}\n","import * as fs from 'node:fs';\nimport type { HttpSinkConfig } from '../../config';\nimport type { Sink, DeliveryResult } from './types';\n\nconst MAX_ATTEMPTS = 3;\nconst POST_TIMEOUT_MS = 30_000;\n\n/**\n * POST the gzipped incident to any URL — an internal endpoint, a\n * Slack-forwarding lambda, someday a collector. 3 attempts with backoff;\n * on final failure the file simply stays in staging for the next boot's\n * recovery pass. Nothing is ever lost silently.\n */\nexport function createHttpSink(cfg: HttpSinkConfig): Sink {\n return {\n name: 'http',\n async deliver(filePath, meta): Promise<DeliveryResult> {\n const body = await fs.promises.readFile(filePath);\n // Header values must be single-line.\n const metaHeader = JSON.stringify(meta).replace(/[\\r\\n]/g, ' ');\n\n let lastDetail = '';\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n if (attempt > 0) {\n await new Promise((r) => setTimeout(r, 500 * 2 ** (attempt - 1)));\n }\n try {\n const res = await fetch(cfg.url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/gzip',\n 'x-flightbox-meta': metaHeader,\n ...cfg.headers\n },\n body,\n // A stuck connection must never hang delivery forever.\n signal: AbortSignal.timeout(POST_TIMEOUT_MS)\n });\n if (res.ok) return { ok: true, location: cfg.url };\n lastDetail = `HTTP ${res.status}`;\n // 4xx (except 408/429) won't improve with retries.\n if (res.status >= 400 && res.status < 500 && res.status !== 408 && res.status !== 429) {\n break;\n }\n } catch (err) {\n lastDetail = (err as Error).message;\n }\n }\n return { ok: false, detail: `${lastDetail} after retries — file kept in staging` };\n }\n };\n}\n","import type { SinkConfig } from '../../config';\nimport type { Sink } from './types';\nimport { createDiskSink } from './disk';\nimport { createS3Sink } from './s3';\nimport { createHttpSink } from './http';\n\nexport type { Sink, DeliveryResult } from './types';\n\nexport function createSink(cfg: SinkConfig, defaultViewerOrigin: string): Sink {\n switch (cfg.type) {\n case 'disk':\n return createDiskSink(cfg);\n case 's3':\n return createS3Sink(cfg, defaultViewerOrigin);\n case 'http':\n return createHttpSink(cfg);\n }\n}\n","import { nowMono } from '../clock';\n\nexport interface TriggerFireInfo {\n type: string;\n reason?: string;\n /**\n * 'sync' = compress on-thread (manual + crash paths, where a returned\n * real path / dying process demand it); default = ADR 016 async pipeline.\n */\n mode?: 'sync' | 'async';\n /**\n * Crash-path firings: the process is dying, so a dump taken minutes ago\n * must not suppress the final-moments dump. Exempt fires skip the normal\n * cooldown; a short crash-to-crash window still dedupes the\n * rejection→rethrow→uncaught cascade into one file.\n */\n exemptFromCooldown?: boolean;\n}\n\n/** Crash-to-crash dedupe window (rejection→rethrow→uncaught = one file). */\nexport const CRASH_DEDUPE_MS = 2000;\n\n/**\n * Central trigger evaluation with cooldown: one storm = one file.\n * Suppressed firings are still recorded as timeline events so the incident\n * shows how bad the storm was.\n */\nexport class TriggerEngine {\n private lastFireMono: bigint | null = null;\n private lastExemptFireMono: bigint | null = null;\n\n constructor(\n private readonly cooldownMs: number,\n private readonly onFire: (info: TriggerFireInfo) => string | null,\n private readonly onSuppressed: (info: TriggerFireInfo) => void\n ) {}\n\n fire(info: TriggerFireInfo): string | null {\n const now = nowMono();\n const windowMs = info.exemptFromCooldown ? CRASH_DEDUPE_MS : this.cooldownMs;\n const last = info.exemptFromCooldown ? this.lastExemptFireMono : this.lastFireMono;\n if (last !== null && Number(now - last) / 1e6 < windowMs) {\n try {\n this.onSuppressed(info);\n } catch {\n // Suppression bookkeeping must never break anything.\n }\n return null;\n }\n // A crash dump also resets the normal cooldown — it *is* the storm's file.\n this.lastFireMono = now;\n if (info.exemptFromCooldown) this.lastExemptFireMono = now;\n return this.onFire(info);\n }\n}\n","import type { TriggerEngine } from './engine';\n\n/**\n * Manual trigger — powers flightbox.trigger(). The token-gated HTTP endpoint\n * variant arrives with the trigger milestone (Week 2).\n */\nexport function createManualTrigger(engine: TriggerEngine) {\n return (reason = 'manual trigger'): string | null =>\n // Sync mode: dev-invoked, must return a path to a file that exists.\n engine.fire({ type: 'manual', reason, mode: 'sync' });\n}\n","import * as v8 from 'node:v8';\nimport type { AgentApi } from '../agent';\nimport { EventType, truncate } from '../encoder';\nimport { capScrub } from '../redaction';\n\n/**\n * Crash triggers — Bible §7 + ADR 009.\n *\n * uncaughtException uses 'uncaughtExceptionMonitor': it observes without\n * changing crash semantics (the process still dies exactly as it would\n * have). unhandledRejection records + dumps, then RETHROWS the reason to\n * preserve default crash behavior — but ONLY when Flightbox is the sole\n * listener. If the app installed its own unhandledRejection handler (the\n * log-and-survive pattern), rethrowing would force-crash a process that\n * intended to live AND preempt the app's handler; in that case we record\n * only and let the app decide. The count is checked at emit time, not\n * install time. The resulting uncaughtException dump (sole-listener case)\n * is deduped by the engine's crash cooldown.\n *\n * ADR 009 escalation: when the heap is nearly exhausted (the OOM case),\n * gzip/decode would abort V8 — the crash dump degrades to the raw panic\n * write on the preopened fd instead.\n */\n\nexport const OOM_HEAP_FRACTION = 0.94;\n\nexport interface CrashDeps {\n agent: AgentApi;\n /** Normal path: synchronous serialize + stage (fast gzip). */\n syncDump(info: { type: string; reason?: string }): string | null;\n /** ADR 009 path: raw ring bytes on the preopened fd. */\n panicDump(): boolean;\n}\n\nfunction nearHeapLimit(): boolean {\n try {\n const stats = v8.getHeapStatistics();\n return stats.used_heap_size / stats.heap_size_limit > OOM_HEAP_FRACTION;\n } catch {\n return false;\n }\n}\n\nexport function crashDump(deps: CrashDeps, type: string, err: unknown): void {\n const message = capScrub(String((err as Error)?.message ?? err), 512);\n try {\n deps.agent.recorder.record(EventType.Custom, {\n name: type,\n message,\n stack: capScrub(String((err as Error)?.stack ?? ''), 2048)\n });\n } catch {\n // Even the marker is best-effort at crash time.\n }\n\n if (nearHeapLimit()) {\n // ADR 009: no allocation-heavy work near the heap limit.\n const ok = deps.panicDump();\n deps.agent.config.log(\n ok\n ? `OOM panic path: raw ring dumped; will be converted to .fbox on next boot`\n : `OOM panic path unavailable (no preopened fd)`\n );\n return;\n }\n deps.syncDump({ type, reason: message });\n}\n\nexport function installCrashTriggers(deps: CrashDeps): () => void {\n const onUncaught = (err: unknown): void => {\n try {\n crashDump(deps, 'uncaughtException', err);\n } catch {\n // Never interfere with the crash itself.\n }\n };\n const onRejection = (reason: unknown): void => {\n try {\n crashDump(deps, 'unhandledRejection', reason);\n } catch {\n // Fall through regardless.\n }\n // Preserve default semantics (an unhandled rejection kills the process)\n // ONLY when no app handler exists — never override the app's decision.\n if (process.listenerCount('unhandledRejection') === 1) {\n throw reason;\n }\n };\n\n process.on('uncaughtExceptionMonitor', onUncaught);\n process.on('unhandledRejection', onRejection);\n return () => {\n process.removeListener('uncaughtExceptionMonitor', onUncaught);\n process.removeListener('unhandledRejection', onRejection);\n };\n}\n","/**\n * Redaction engine — ADR 007 fast-path design: a cheap case-insensitive\n * marker scan runs first; heavy regexes execute ONLY on a hit. No hit means\n * zero regex cost on the hot path.\n *\n * Scrubbers (v0.1, non-negotiable set from Bible §7):\n * - key/value masking for password|token|secret|otp|pin|auth|cookie|...\n * - JWT pattern\n * - Luhn-checked PANs (credit card numbers)\n * Plus the HTTP header allowlist.\n */\n\nconst SENSITIVE_MARKERS = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'authorization',\n 'auth',\n 'apikey',\n 'api_key',\n 'api-key',\n 'session',\n 'cookie',\n 'otp',\n 'pin',\n 'credential',\n 'private_key',\n 'bearer'\n];\n\nconst JWT_RE = /\\beyJ[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{2,}\\b/g;\nconst KV_RE =\n /\\b(password|passwd|secret|token|authorization|auth|api[_-]?key|apikey|session|cookie|otp|pin|credential|bearer)\\b([\"']?\\s*[=:]\\s*)(\"[^\"]*\"|'[^']*'|[^\\s&,;)}\\]]+)/gi;\nconst PAN_CANDIDATE_RE = /\\b(?:\\d[ -]?){13,19}\\b/g;\n\n/** Cheap fast-path gate: does this string even deserve regex time? */\nexport function needsScrub(s: string): boolean {\n const lower = s.toLowerCase();\n for (const m of SENSITIVE_MARKERS) {\n if (lower.includes(m)) return true;\n }\n if (lower.includes('eyj')) return true;\n return hasLongDigitRun(s);\n}\n\n/** Manual scan for 13+ digits (allowing space/dash separators) — no regex. */\nfunction hasLongDigitRun(s: string): boolean {\n let run = 0;\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n if (c >= 48 && c <= 57) {\n if (++run >= 13) return true;\n } else if (c !== 32 && c !== 45) {\n run = 0;\n }\n }\n return false;\n}\n\nexport function luhnValid(digits: string): boolean {\n let sum = 0;\n let alt = false;\n for (let i = digits.length - 1; i >= 0; i--) {\n let d = digits.charCodeAt(i) - 48;\n if (d < 0 || d > 9) return false;\n if (alt) {\n d *= 2;\n if (d > 9) d -= 9;\n }\n sum += d;\n alt = !alt;\n }\n return sum % 10 === 0;\n}\n\n/** Scrub free-form text (log lines, error messages, query text). */\nexport function scrubText(s: string): string {\n if (!needsScrub(s)) return s;\n let out = s;\n if (out.toLowerCase().includes('eyj')) {\n out = out.replace(JWT_RE, '[REDACTED:jwt]');\n }\n out = out.replace(KV_RE, '$1$2[REDACTED]');\n if (hasLongDigitRun(out)) {\n out = out.replace(PAN_CANDIDATE_RE, (m) => {\n const digits = m.replace(/[ -]/g, '');\n return digits.length >= 13 && digits.length <= 19 && luhnValid(digits)\n ? '[REDACTED:pan]'\n : m;\n });\n }\n return out;\n}\n\n/**\n * Scrub-then-truncate, with the scan itself bounded: a caller-supplied\n * megabyte URL must not buy megabytes of regex work before the size limit\n * applies (hot-path guarantee). The 4x pre-cap keeps redaction effective\n * around the final boundary — a secret straddling the cut is truncated\n * away rather than surviving in recognizable form.\n */\nexport function capScrub(s: string, limit: number): string {\n const preCapped = s.length > limit * 4 ? s.slice(0, limit * 4) : s;\n const scrubbed = scrubText(preCapped);\n return scrubbed.length > limit ? scrubbed.slice(0, limit) : scrubbed;\n}\n\n/** Bible §7: headers are ALLOWLISTED, never blocklisted. */\nexport const HEADER_ALLOWLIST: ReadonlySet<string> = new Set([\n 'content-type',\n 'content-length',\n 'user-agent',\n 'accept',\n 'x-request-id'\n]);\n\nexport function allowlistHeaders(\n headers: Record<string, unknown> | undefined\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n let out: Record<string, string> | undefined;\n for (const key of Object.keys(headers)) {\n if (HEADER_ALLOWLIST.has(key.toLowerCase())) {\n (out ??= {})[key.toLowerCase()] = String(headers[key]);\n }\n }\n return out;\n}\n","import type { AgentApi } from '../agent';\n\n/**\n * Slow-request trigger, both halves per Bible §7:\n * - at req end: http-server instrumentation calls maybeFireSlow() with the\n * measured duration\n * - in-flight watchdog: a 1s unref'd sweep catches HUNG requests that will\n * never reach 'finish' — the case the end-hook can't see.\n */\n\ninterface InFlight {\n method: string;\n path: string;\n startedAtMs: number;\n fired: boolean;\n}\n\nconst inFlight = new Map<bigint, InFlight>();\n\nexport function trackRequestStart(requestId: bigint, method: string, path: string): void {\n inFlight.set(requestId, { method, path, startedAtMs: Date.now(), fired: false });\n}\n\nexport function trackRequestEnd(requestId: bigint): void {\n inFlight.delete(requestId);\n}\n\nexport function maybeFireSlow(agent: AgentApi, method: string, path: string, durMs: number): void {\n if (durMs > agent.config.triggers.slowRequestMs) {\n agent.fire({\n type: 'slowRequest',\n reason: `${method} ${path} took ${durMs.toFixed(0)}ms (threshold ${agent.config.triggers.slowRequestMs}ms)`\n });\n }\n}\n\nexport function startWatchdog(agent: AgentApi): () => void {\n const interval = setInterval(() => {\n try {\n const now = Date.now();\n const threshold = agent.config.triggers.slowRequestMs;\n for (const entry of inFlight.values()) {\n if (!entry.fired && now - entry.startedAtMs > threshold) {\n entry.fired = true; // one firing per hung request\n agent.fire({\n type: 'slowRequest',\n reason: `${entry.method} ${entry.path} still in flight after ${(now - entry.startedAtMs).toFixed(0)}ms (watchdog)`\n });\n }\n }\n } catch {\n // Watchdog must never throw.\n }\n }, 1000);\n interval.unref();\n return () => {\n clearInterval(interval);\n inFlight.clear();\n };\n}\n","import * as http from 'node:http';\nimport { createHash, timingSafeEqual } from 'node:crypto';\nimport type { AgentApi } from '../agent';\nimport { EventType, truncate, LIMITS } from '../encoder';\nimport { newRequestId, requestStorage, type RequestContext } from '../context';\nimport { nowMono } from '../clock';\nimport { isShedding } from '../load-shedding';\nimport { scrubText, capScrub, allowlistHeaders } from '../redaction';\nimport { trackRequestStart, trackRequestEnd, maybeFireSlow } from '../triggers/slow-request';\n\n/**\n * Patches http.Server.prototype.emit to intercept 'request' — one patch\n * covers Express, Fastify, Koa and bare node:http, on servers created before\n * or after start().\n *\n * Also hosts:\n * - the token-gated manual dump endpoint (GET /__flightbox/dump with the\n * x-flightbox-token header — header ONLY: query-string tokens leak into\n * proxy/access logs). Handled BEFORE the load-shedding check: \"grab me a\n * dump right now\" must work precisely when the event loop is melting.\n * - slow-request firing at request end + in-flight watchdog bookkeeping\n *\n * ADR 004 — strict boundary isolation: the context is created ONLY here, and\n * the requestId is nullified when the response finishes; late events fall\n * back to \"orphan\" instead of mis-attributing.\n */\n\nlet patched = false;\n/** Live agent ref — updated on every start() so restart doesn't strand the patch. */\nlet agentRef: AgentApi;\n\n/** Constant-time equality over hashes — token length stays unobservable too. */\nfunction tokenMatches(supplied: string, expected: string): boolean {\n const a = createHash('sha256').update(supplied).digest();\n const b = createHash('sha256').update(expected).digest();\n return timingSafeEqual(a, b);\n}\n\nfunction handleFlightboxEndpoint(\n agent: AgentApi,\n req: http.IncomingMessage,\n res: http.ServerResponse\n): boolean {\n // Cheap prefix gate first (hot path), then EXACT pathname match — a bare\n // startsWith would swallow app routes like /__flightbox/dump-report.\n if (!agent.config.token || !req.url?.startsWith('/__flightbox/dump')) return false;\n try {\n const url = new URL(req.url, 'http://localhost');\n if (url.pathname !== '/__flightbox/dump') return false;\n if (req.method !== 'GET') {\n // The path is reserved; the contract is GET-only.\n res.writeHead(405, { allow: 'GET' }).end();\n return true;\n }\n const supplied = String(req.headers['x-flightbox-token'] ?? '');\n if (!supplied || !tokenMatches(supplied, agent.config.token)) {\n res.writeHead(401, { 'content-type': 'application/json' });\n res.end('{\"error\":\"invalid token\"}');\n return true;\n }\n // The reason lands in artifacts and logs — cap and scrub it like any\n // other untrusted input; a token holder must not get an injection vector.\n const rawReason = url.searchParams.get('reason') ?? 'no reason given';\n const reason = truncate(scrubText(truncate(rawReason, 512)), 120);\n const staged = agent.fire({\n type: 'manual',\n reason: `http endpoint (${reason})`,\n mode: 'sync' // interactive call: the returned path must exist\n });\n res.writeHead(staged ? 200 : 429, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ staged: staged ?? null, suppressed: staged === null }));\n } catch {\n try {\n res.writeHead(500);\n res.end();\n } catch {\n /* socket already gone */\n }\n }\n return true;\n}\n\nexport function instrumentHttpServer(agent: AgentApi): void {\n agentRef = agent;\n if (patched) return;\n patched = true;\n\n const orig = http.Server.prototype.emit;\n\n const flightboxEmit = function (this: http.Server, event: string | symbol): boolean {\n // eslint-disable-next-line prefer-rest-params\n const args = arguments;\n if (event !== 'request') {\n return (orig as Function).apply(this, args) as boolean;\n }\n\n const agent = agentRef;\n\n // The manual dump endpoint outranks load-shedding: it exists FOR the\n // moments when the loop is lagging (audit M7).\n try {\n if (handleFlightboxEndpoint(agent, args[1] as http.IncomingMessage, args[2] as http.ServerResponse)) {\n return true;\n }\n } catch {\n // Endpoint handling must never break request dispatch.\n }\n\n // ADR 012: under load-shedding, zero context creation, zero recording.\n if (isShedding()) {\n return (orig as Function).apply(this, args) as boolean;\n }\n\n let ctx: RequestContext;\n try {\n const req = args[1] as http.IncomingMessage;\n const res = args[2] as http.ServerResponse;\n\n const requestId = newRequestId();\n const tStart = nowMono();\n const method = req.method ?? 'GET';\n // capScrub: bounded scan — a giant attacker URL must not buy\n // unbounded regex time on the hot path.\n const path = capScrub(req.url ?? '', LIMITS.path);\n ctx = { requestId };\n\n agent.recorder.record(\n EventType.HttpServerStart,\n // Bible §7: headers are allowlisted (content-type, user-agent, ...)\n // — never captured wholesale.\n { method, path, headers: allowlistHeaders(req.headers) },\n { requestId, tMonoNs: tStart }\n );\n trackRequestStart(requestId, method, path);\n\n const finish = (aborted: boolean): void => {\n try {\n trackRequestEnd(requestId);\n const durMs = Number(nowMono() - tStart) / 1e6;\n agent.recorder.record(\n EventType.HttpServerEnd,\n aborted\n ? { method, path, aborted: true, durMs }\n : { method, path, status: res.statusCode, durMs },\n { requestId }\n );\n maybeFireSlow(agent, method, path, durMs);\n } catch {\n // Never throw into user code.\n }\n // ADR 004: aggressively sever the context at the response boundary.\n ctx.requestId = 0n;\n };\n res.once('finish', () => finish(false));\n res.once('close', () => {\n // 'close' without 'finish' = client aborted / connection died.\n if (!res.writableFinished) finish(true);\n });\n } catch {\n // Instrumentation failure degrades to \"unrecorded\", never a broken request.\n return (orig as Function).apply(this, args) as boolean;\n }\n\n return requestStorage.run(ctx, () => (orig as Function).apply(this, args) as boolean);\n };\n\n http.Server.prototype.emit = flightboxEmit as typeof http.Server.prototype.emit;\n}\n","import type * as http from 'node:http';\nimport { createRequire } from 'node:module';\nimport type { AgentApi } from '../agent';\nimport { EventType, truncate, LIMITS } from '../encoder';\nimport { newSpanId, currentRequestId } from '../context';\nimport { nowMono } from '../clock';\nimport { isShedding } from '../load-shedding';\nimport { capScrub } from '../redaction';\n\n/**\n * Outbound HTTP: patches http/https.request+get and global fetch. Events\n * carry host/method/path (scrubbed, truncated) and correlate to the parent\n * request via ALS. ADR 013: these are CJS singleton mutations — valid for\n * core modules; pure-ESM client libs get loader hooks later.\n */\n\nlet patched = false;\nlet agentRef: AgentApi;\n\nfunction extractTarget(args: unknown[]): { method: string; host: string; path: string } {\n let method = 'GET';\n let host = '';\n let path = '';\n const a0 = args[0];\n if (typeof a0 === 'string') {\n try {\n const u = new URL(a0);\n host = u.host;\n path = u.pathname + u.search;\n } catch {\n path = a0;\n }\n } else if (a0 instanceof URL) {\n host = a0.host;\n path = a0.pathname + a0.search;\n } else if (a0 && typeof a0 === 'object') {\n const o = a0 as Record<string, any>;\n host = o.host ?? o.hostname ?? '';\n path = o.path ?? '/';\n method = o.method ?? 'GET';\n }\n const a1 = args[1];\n if (a1 && typeof a1 === 'object' && !(a1 instanceof URL) && typeof a1 !== 'function') {\n const o = a1 as Record<string, any>;\n if (o.method) method = o.method;\n if (o.host || o.hostname) host = o.host ?? o.hostname;\n }\n return { method: String(method).toUpperCase(), host: String(host), path: String(path) };\n}\n\n/**\n * ADR 013 in practice: `import * as http` gives a FROZEN ESM namespace —\n * assigning http.request there throws. The CJS module object (via\n * createRequire) is mutable and is what both require() users and the\n * default-import ESM facade read from.\n */\nconst nodeRequire = createRequire(process.cwd() + '/noop.js');\n\nfunction wrapModule(mod: { request: any; get: any }): void {\n const origRequest = mod.request.bind(mod);\n\n const tracedRequest = function (...args: any[]): http.ClientRequest {\n if (isShedding()) return origRequest(...(args as [any]));\n const agent = agentRef;\n\n let spanId = 0n;\n let requestId = 0n;\n let tStart = 0n;\n let recordedStart = false;\n try {\n const target = extractTarget(args);\n spanId = newSpanId();\n requestId = currentRequestId();\n tStart = nowMono();\n agent.recorder.record(\n EventType.HttpClientStart,\n {\n method: target.method,\n host: truncate(target.host, 256),\n path: capScrub(target.path, LIMITS.path)\n },\n { spanId, requestId, tMonoNs: tStart }\n );\n recordedStart = true;\n } catch {\n // The outbound request must always proceed.\n }\n\n const req = origRequest(...(args as [any]));\n\n if (recordedStart) {\n const done = (status?: number, error?: string): void => {\n try {\n const payload: Record<string, unknown> = {\n durMs: Number(nowMono() - tStart) / 1e6\n };\n if (status !== undefined) payload.status = status;\n if (error !== undefined) payload.error = capScrub(error, 256);\n agent.recorder.record(EventType.HttpClientEnd, payload, { spanId, requestId });\n } catch {\n // Best-effort.\n }\n };\n try {\n req.once('response', (res: http.IncomingMessage) => done(res.statusCode));\n // Our listener alone would mark 'error' as handled and silently\n // swallow failures the app never subscribed to. If nobody else is\n // listening once we've recorded, rethrow to restore Node's default\n // crash-loudly semantics (audit M3a).\n req.once('error', (err: Error) => {\n done(undefined, err.message);\n if (req.listenerCount('error') === 0) throw err;\n });\n } catch {\n // Best-effort.\n }\n }\n return req;\n };\n\n (mod as any).request = tracedRequest;\n (mod as any).get = function (...args: any[]): http.ClientRequest {\n const req = tracedRequest(...args);\n req.end();\n return req;\n };\n}\n\nfunction wrapFetch(): void {\n const origFetch = globalThis.fetch;\n if (typeof origFetch !== 'function') return;\n\n globalThis.fetch = async function flightboxFetch(\n input: any,\n init?: any\n ): Promise<Response> {\n if (isShedding()) return origFetch(input, init);\n const agent = agentRef;\n\n let spanId = 0n;\n let requestId = 0n;\n let tStart = 0n;\n let recordedStart = false;\n try {\n const url = typeof input === 'string' ? input : (input?.url ?? String(input));\n let host = '';\n let path = String(url);\n try {\n const u = new URL(url);\n host = u.host;\n path = u.pathname + u.search;\n } catch {\n // Relative or odd URL — keep as-is.\n }\n spanId = newSpanId();\n requestId = currentRequestId();\n tStart = nowMono();\n agent.recorder.record(\n EventType.HttpClientStart,\n {\n method: String(\n init?.method ?? (typeof input === 'object' ? input?.method : undefined) ?? 'GET'\n ).toUpperCase(),\n host: truncate(host, 256),\n path: capScrub(path, LIMITS.path),\n via: 'fetch'\n },\n { spanId, requestId, tMonoNs: tStart }\n );\n recordedStart = true;\n } catch {\n // Proceed regardless.\n }\n\n const done = (status?: number, error?: string): void => {\n if (!recordedStart) return;\n try {\n const payload: Record<string, unknown> = { durMs: Number(nowMono() - tStart) / 1e6 };\n if (status !== undefined) payload.status = status;\n if (error !== undefined) payload.error = capScrub(error, 256);\n agent.recorder.record(EventType.HttpClientEnd, payload, { spanId, requestId });\n } catch {\n // Best-effort.\n }\n };\n\n try {\n const res = await origFetch(input, init);\n done(res.status);\n return res;\n } catch (err) {\n done(undefined, (err as Error)?.message ?? String(err));\n throw err;\n }\n };\n}\n\nexport function instrumentHttpClient(agent: AgentApi): void {\n agentRef = agent;\n if (patched) return;\n patched = true;\n // Each target independently try/caught: one failing must not skip the rest.\n try {\n wrapModule(nodeRequire('node:http'));\n } catch {\n /* degrade: http.request unrecorded */\n }\n try {\n wrapModule(nodeRequire('node:https'));\n } catch {\n /* degrade: https.request unrecorded */\n }\n try {\n wrapFetch();\n } catch {\n /* degrade: fetch unrecorded */\n }\n}\n","import { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport type { AgentApi } from '../agent';\nimport { EventType, truncate, LIMITS } from '../encoder';\nimport { newSpanId, currentRequestId } from '../context';\nimport { nowMono } from '../clock';\nimport { isShedding } from '../load-shedding';\nimport { capScrub } from '../redaction';\n\n/**\n * Instruments the `pg` driver if the host app has it installed:\n * - Client.prototype.query — text (truncated 2KB) + param SHAPES, never values\n * - Pool.prototype.connect — pool wait time, the demo's star witness\n *\n * The requestId is captured once at query start and stamped on both the\n * start and end events explicitly, so span pairing survives the ADR 004\n * context nullification at response finish.\n */\n\n/** Resolve `pg` from the host application's node_modules, not ours. */\nfunction loadPg(): any | null {\n try {\n const req = createRequire(path.join(process.cwd(), 'noop.js'));\n return req('pg');\n } catch {\n return null;\n }\n}\n\n/** ADR 007-aligned: parameter shapes only — values never enter the ring. */\nfunction paramShapes(values: unknown): string[] | undefined {\n if (!Array.isArray(values)) return undefined;\n return values\n .slice(0, 32)\n .map((v) => (v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v));\n}\n\nlet agentRef: AgentApi;\n\nexport function instrumentPg(agent: AgentApi): boolean {\n agentRef = agent;\n const recorder = {\n record: ((...args) => agentRef.recorder.record(...args)) as AgentApi['recorder']['record']\n };\n const pg = loadPg();\n if (!pg?.Client?.prototype?.query) return false;\n\n if (!(pg.Client.prototype.query as any).__flightbox) {\n const origQuery = pg.Client.prototype.query;\n\n const wrappedQuery = function (this: unknown, ...args: any[]): unknown {\n // ADR 012: shed path — run the query untouched.\n if (isShedding()) return origQuery.apply(this, args);\n let spanId = 0n;\n let requestId = 0n;\n let tStart = 0n;\n let recordedStart = false;\n try {\n const first = args[0];\n const text = typeof first === 'string' ? first : first?.text;\n const values = Array.isArray(args[1])\n ? args[1]\n : Array.isArray(first?.values)\n ? first.values\n : undefined;\n spanId = newSpanId();\n requestId = currentRequestId();\n tStart = nowMono();\n recorder.record(\n EventType.PgQueryStart,\n {\n text: capScrub(typeof text === 'string' ? text : '<unknown>', LIMITS.query),\n params: paramShapes(values)\n },\n { spanId, requestId, tMonoNs: tStart }\n );\n recordedStart = true;\n } catch {\n // Fall through — the query itself must always run.\n }\n\n const done = (err?: unknown): void => {\n if (!recordedStart) return;\n try {\n const payload: Record<string, unknown> = {\n durMs: Number(nowMono() - tStart) / 1e6\n };\n if (err) {\n payload.error = truncate(String((err as Error)?.message ?? err), 256);\n }\n recorder.record(EventType.PgQueryEnd, payload, { spanId, requestId });\n } catch {\n // Never throw into user code.\n }\n };\n\n try {\n const last = args[args.length - 1];\n if (typeof last === 'function') {\n args[args.length - 1] = function (this: unknown, err: unknown): unknown {\n done(err);\n // eslint-disable-next-line prefer-rest-params\n return last.apply(this, arguments);\n };\n return origQuery.apply(this, args);\n }\n } catch {\n // Fall through to the promise path.\n }\n\n const out = origQuery.apply(this, args);\n try {\n if (out && typeof out.then === 'function') {\n // Return the DERIVED promise: attaching handlers to `out` directly\n // would mark its rejection \"handled\" and mute unhandledRejection\n // for fire-and-forget queries (audit M3b). The derived chain\n // records the end and re-throws, so the caller's promise keeps\n // native rejection semantics.\n return out.then(\n (v: unknown) => {\n done();\n return v;\n },\n (e: unknown) => {\n done(e);\n throw e;\n }\n );\n }\n if (out && typeof out.on === 'function') {\n // Submittable path (query(new Query())): pair the span via events\n // so it doesn't render as a phantom in-flight query (audit L3).\n out.once('end', () => done());\n out.once('error', (e: unknown) => {\n done(e);\n if (out.listenerCount('error') === 0) throw e;\n });\n }\n } catch {\n // Ignore — recording the end is best-effort.\n }\n return out;\n };\n\n (wrappedQuery as any).__flightbox = true;\n pg.Client.prototype.query = wrappedQuery;\n }\n\n const Pool = pg.Pool;\n if (Pool?.prototype?.connect && !(Pool.prototype.connect as any).__flightbox) {\n const origConnect = Pool.prototype.connect;\n\n const wrappedConnect = function (this: unknown, cb?: unknown): unknown {\n // ADR 012: shed path — acquire untouched.\n if (isShedding()) return origConnect.call(this, cb);\n const tStart = nowMono();\n const requestId = currentRequestId();\n const record = (): void => {\n try {\n recorder.record(\n EventType.PgPoolWait,\n { waitMs: Number(nowMono() - tStart) / 1e6 },\n { requestId }\n );\n } catch {\n // Never throw into user code.\n }\n };\n\n if (typeof cb === 'function') {\n return origConnect.call(this, function (this: unknown, ...cbArgs: unknown[]) {\n record();\n return (cb as Function).apply(this, cbArgs);\n });\n }\n const out = origConnect.call(this);\n try {\n if (out && typeof out.then === 'function') out.then(record, record);\n } catch {\n // Best-effort.\n }\n return out;\n };\n\n (wrappedConnect as any).__flightbox = true;\n Pool.prototype.connect = wrappedConnect;\n }\n\n return true;\n}\n","import { formatWithOptions } from 'node:util';\nimport type { AgentApi } from '../agent';\nimport { EventType, truncate, LIMITS } from '../encoder';\nimport { capScrub } from '../redaction';\nimport { isShedding } from '../load-shedding';\n\n/**\n * Console passthrough capture: every console.* line lands on the timeline\n * (redacted, truncated at 1KB) and still reaches stdout/stderr untouched.\n * The agent's own diagnostics use process.stderr.write directly, so there\n * is no recursion.\n */\n\nconst LEVELS = ['log', 'info', 'warn', 'error', 'debug'] as const;\n\n/** Bound the inspection work, not just the output length (audit L7):\n * console.log(hugeObject) must not pay full serialization on the hot path. */\nconst FORMAT_OPTS = { depth: 2, maxArrayLength: 20, maxStringLength: LIMITS.log } as const;\n\nlet patched = false;\nlet agentRef: AgentApi;\n\nexport function instrumentConsole(agent: AgentApi): void {\n agentRef = agent;\n if (patched) return;\n patched = true;\n\n for (const level of LEVELS) {\n const orig = console[level].bind(console);\n console[level] = function flightboxConsole(...args: unknown[]): void {\n try {\n if (!isShedding()) {\n const msg = capScrub(formatWithOptions(FORMAT_OPTS, ...args), LIMITS.log);\n agentRef.recorder.record(EventType.Log, { level, msg });\n }\n } catch {\n // Never break the host's logging.\n }\n orig(...args);\n };\n }\n}\n","import type { AgentApi } from '../agent';\nimport { instrumentHttpServer } from './http-server';\nimport { instrumentHttpClient } from './http-client';\nimport { instrumentPg } from './pg';\nimport { instrumentConsole } from './console';\n\n/**\n * Applies every instrumentation, each individually try/caught — a failed\n * patch degrades to \"that signal isn't recorded\", never to a broken app.\n * Returning false means the target module isn't installed (not an error).\n */\nconst INSTRUMENTATIONS: ReadonlyArray<\n readonly [string, (agent: AgentApi) => boolean | void]\n> = [\n ['http-server', instrumentHttpServer],\n ['http-client', instrumentHttpClient],\n ['pg', instrumentPg],\n ['console', instrumentConsole]\n];\n\nexport function applyInstrumentations(agent: AgentApi): void {\n for (const [name, apply] of INSTRUMENTATIONS) {\n try {\n if (apply(agent) === false) {\n agent.config.log(`instrumentation ${name}: module not present, skipped`);\n }\n } catch (err) {\n agent.config.log(`instrumentation ${name} failed to apply: ${(err as Error).message}`);\n }\n }\n}\n","import { monitorEventLoopDelay } from 'node:perf_hooks';\nimport * as v8 from 'node:v8';\nimport type { AgentApi } from '../agent';\nimport { EventType } from '../encoder';\nimport { isShedding, setShedding } from '../load-shedding';\n\n/**\n * 100ms vitals sampler (unref'd — never keeps the process alive):\n * - records event-loop lag + memory as timeline events (they keep recording\n * even during shedding: vitals ARE the degraded mode's signal)\n * - is the ADR 012 shedding SENSOR: shed above lagMs, resume below half\n * (hysteresis), transitions recorded as events\n * - hosts the memory trigger (heap vs V8 limit) and event-loop-stall trigger\n */\n\nexport function startVitals(agent: AgentApi): () => void {\n const histogram = monitorEventLoopDelay({ resolution: 20 });\n histogram.enable();\n const { shedLagMs } = agent.config.shedding;\n const { heapPct, stallMs } = agent.config.triggers;\n const heapLimit = v8.getHeapStatistics().heap_size_limit;\n\n // Edge-triggered with hysteresis (audit L1): sustained pressure fires the\n // trigger ONCE per excursion instead of every 100ms sample — otherwise the\n // ring fills with suppressed-trigger markers during the exact window\n // someone will want to inspect.\n let memAbove = false;\n let stallAbove = false;\n\n const interval = setInterval(() => {\n try {\n const lagMs = histogram.mean / 1e6;\n const maxLagMs = histogram.max / 1e6;\n histogram.reset();\n const mem = process.memoryUsage();\n\n agent.recorder.record(EventType.Vitals, {\n lagMs: Math.round(lagMs * 1000) / 1000,\n maxLagMs: Math.round(maxLagMs * 1000) / 1000,\n heapUsed: mem.heapUsed,\n heapTotal: mem.heapTotal,\n rss: mem.rss\n });\n\n // ADR 012 — adaptive load-shedding with hysteresis.\n if (!isShedding() && lagMs > shedLagMs) {\n setShedding(true);\n agent.recorder.record(EventType.Custom, {\n name: 'flightbox.shedding',\n on: true,\n lagMs: Math.round(lagMs)\n });\n agent.config.log(\n `event-loop lag ${lagMs.toFixed(0)}ms > ${shedLagMs}ms — load-shedding ON (ADR 012)`\n );\n } else if (isShedding() && lagMs < shedLagMs / 2) {\n setShedding(false);\n agent.recorder.record(EventType.Custom, {\n name: 'flightbox.shedding',\n on: false,\n lagMs: Math.round(lagMs)\n });\n agent.config.log('event-loop lag recovered — load-shedding OFF');\n }\n\n // Memory trigger (Bible §7) — once per excursion above the threshold.\n const heapFrac = mem.heapUsed / heapLimit;\n if (heapFrac > heapPct && !memAbove) {\n memAbove = true;\n agent.fire({\n type: 'memory',\n reason: `heapUsed at ${(heapFrac * 100).toFixed(0)}% of V8 limit`\n });\n } else if (memAbove && heapFrac < heapPct * 0.9) {\n memAbove = false;\n }\n\n // Event-loop stall trigger (Bible §7) — once per excursion.\n if (maxLagMs > stallMs && !stallAbove) {\n stallAbove = true;\n agent.fire({\n type: 'eventloop-stall',\n reason: `event loop stalled ${maxLagMs.toFixed(0)}ms (threshold ${stallMs}ms)`\n });\n } else if (stallAbove && maxLagMs < stallMs / 2) {\n stallAbove = false;\n }\n } catch {\n // The sampler must never take the process down.\n }\n }, 100);\n interval.unref();\n\n return () => {\n clearInterval(interval);\n histogram.disable();\n };\n}\n"],"mappings":"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","names":["fs","path","start","import_msgpackr","path","statfsSync","fs","path","anchor","anchor","fs","path","fs","path","fs","path","import_node_crypto","agent","path","patched","agentRef","path","import_node_module","path","agentRef","patched","agentRef","v8"]}
@@ -0,0 +1,106 @@
1
+ interface TriggersConfig {
2
+ /** Requests slower than this fire the slow-request trigger (incl. watchdog). */
3
+ slowRequestMs: number;
4
+ /** One storm = one file: minimum time between dumps. */
5
+ cooldownMs: number;
6
+ /** Memory trigger: heapUsed fraction of the V8 limit. */
7
+ heapPct: number;
8
+ /** Event-loop stall trigger: max observed lag in one sample window. */
9
+ stallMs: number;
10
+ }
11
+ interface SheddingConfig {
12
+ /** ADR 012: mean event-loop lag above this sheds per-request instrumentation. */
13
+ shedLagMs: number;
14
+ }
15
+ interface StagingConfig {
16
+ /** ADR 006 — hard cap on staging/ size; oldest files FIFO-evicted over quota. */
17
+ maxStageMb: number;
18
+ /** ADR 006 — if the disk has less than this % free, the agent disables itself. */
19
+ minDiskFreePct: number;
20
+ }
21
+ interface S3SinkConfig {
22
+ type: 's3';
23
+ bucket: string;
24
+ prefix?: string;
25
+ /** Omit for AWS; set for R2/B2/MinIO (path-style is used when set). */
26
+ endpoint?: string;
27
+ region?: string;
28
+ accessKeyId?: string;
29
+ secretAccessKey?: string;
30
+ sessionToken?: string;
31
+ presign?: {
32
+ enabled?: boolean;
33
+ expiresHours?: number;
34
+ /** ADR 008: self-hosted viewer origin for air-gapped environments. */
35
+ viewerOrigin?: string;
36
+ };
37
+ }
38
+ interface DiskSinkConfig {
39
+ type: 'disk';
40
+ dir: string;
41
+ /** Quota for the sink dir (FIFO eviction of oldest .fbox). Default 500. */
42
+ maxMb?: number;
43
+ }
44
+ interface HttpSinkConfig {
45
+ type: 'http';
46
+ url: string;
47
+ headers?: Record<string, string>;
48
+ }
49
+ type SinkConfig = S3SinkConfig | DiskSinkConfig | HttpSinkConfig;
50
+ interface FlightboxConfig {
51
+ service: string;
52
+ /** Ring buffer size. The time window is a consequence of this and traffic. */
53
+ bufferMb: number;
54
+ /** Base directory holding staging/ and delivered/. */
55
+ dir: string;
56
+ triggers: TriggersConfig;
57
+ staging: StagingConfig;
58
+ shedding: SheddingConfig;
59
+ /** Arms the manual dump endpoint (GET /__flightbox/dump, x-flightbox-token header). */
60
+ token?: string;
61
+ /** Default viewer origin for magic links (ADR 008). */
62
+ viewerOrigin: string;
63
+ sinks: SinkConfig[];
64
+ /**
65
+ * ADR 014: by default the agent refuses to start inside a worker thread —
66
+ * N workers would allocate N rings (8 cores x 64MB = 512MB). Opt-in only.
67
+ */
68
+ allowWorkerThreads: boolean;
69
+ /** Agent's own diagnostics channel. Defaults to stderr (console is captured). */
70
+ log: (msg: string) => void;
71
+ }
72
+ interface UserConfig {
73
+ service?: string;
74
+ bufferMb?: number;
75
+ dir?: string;
76
+ triggers?: Partial<TriggersConfig>;
77
+ staging?: Partial<StagingConfig>;
78
+ shedding?: Partial<SheddingConfig>;
79
+ token?: string;
80
+ viewerOrigin?: string;
81
+ sinks?: SinkConfig[];
82
+ allowWorkerThreads?: boolean;
83
+ log?: (msg: string) => void;
84
+ }
85
+
86
+ /**
87
+ * Arms the flight recorder: allocates the ring buffer, applies
88
+ * instrumentations, installs triggers/vitals, runs the boot-time recovery
89
+ * pass, and starts delivering anything a previous run left behind.
90
+ * Idempotent.
91
+ */
92
+ declare function start(userConfig?: UserConfig): void;
93
+ /** Manually capture an incident. Returns the staged file path, or null (cooldown / not started / disk guard). */
94
+ declare function trigger(reason?: string): string | null;
95
+ /** Record a custom timeline event (e.g. flightbox.addEvent('cache.miss', { key })). */
96
+ declare function addEvent(name: string, data?: Record<string, unknown>): void;
97
+ /** Disarms recording, stops samplers and watchdogs. Module patches remain but degrade to no-ops. */
98
+ declare function stop(): void;
99
+ declare const _default: {
100
+ start: typeof start;
101
+ stop: typeof stop;
102
+ trigger: typeof trigger;
103
+ addEvent: typeof addEvent;
104
+ };
105
+
106
+ export { type FlightboxConfig, type SinkConfig, type UserConfig, addEvent, _default as default, start, stop, trigger };
@@ -0,0 +1,106 @@
1
+ interface TriggersConfig {
2
+ /** Requests slower than this fire the slow-request trigger (incl. watchdog). */
3
+ slowRequestMs: number;
4
+ /** One storm = one file: minimum time between dumps. */
5
+ cooldownMs: number;
6
+ /** Memory trigger: heapUsed fraction of the V8 limit. */
7
+ heapPct: number;
8
+ /** Event-loop stall trigger: max observed lag in one sample window. */
9
+ stallMs: number;
10
+ }
11
+ interface SheddingConfig {
12
+ /** ADR 012: mean event-loop lag above this sheds per-request instrumentation. */
13
+ shedLagMs: number;
14
+ }
15
+ interface StagingConfig {
16
+ /** ADR 006 — hard cap on staging/ size; oldest files FIFO-evicted over quota. */
17
+ maxStageMb: number;
18
+ /** ADR 006 — if the disk has less than this % free, the agent disables itself. */
19
+ minDiskFreePct: number;
20
+ }
21
+ interface S3SinkConfig {
22
+ type: 's3';
23
+ bucket: string;
24
+ prefix?: string;
25
+ /** Omit for AWS; set for R2/B2/MinIO (path-style is used when set). */
26
+ endpoint?: string;
27
+ region?: string;
28
+ accessKeyId?: string;
29
+ secretAccessKey?: string;
30
+ sessionToken?: string;
31
+ presign?: {
32
+ enabled?: boolean;
33
+ expiresHours?: number;
34
+ /** ADR 008: self-hosted viewer origin for air-gapped environments. */
35
+ viewerOrigin?: string;
36
+ };
37
+ }
38
+ interface DiskSinkConfig {
39
+ type: 'disk';
40
+ dir: string;
41
+ /** Quota for the sink dir (FIFO eviction of oldest .fbox). Default 500. */
42
+ maxMb?: number;
43
+ }
44
+ interface HttpSinkConfig {
45
+ type: 'http';
46
+ url: string;
47
+ headers?: Record<string, string>;
48
+ }
49
+ type SinkConfig = S3SinkConfig | DiskSinkConfig | HttpSinkConfig;
50
+ interface FlightboxConfig {
51
+ service: string;
52
+ /** Ring buffer size. The time window is a consequence of this and traffic. */
53
+ bufferMb: number;
54
+ /** Base directory holding staging/ and delivered/. */
55
+ dir: string;
56
+ triggers: TriggersConfig;
57
+ staging: StagingConfig;
58
+ shedding: SheddingConfig;
59
+ /** Arms the manual dump endpoint (GET /__flightbox/dump, x-flightbox-token header). */
60
+ token?: string;
61
+ /** Default viewer origin for magic links (ADR 008). */
62
+ viewerOrigin: string;
63
+ sinks: SinkConfig[];
64
+ /**
65
+ * ADR 014: by default the agent refuses to start inside a worker thread —
66
+ * N workers would allocate N rings (8 cores x 64MB = 512MB). Opt-in only.
67
+ */
68
+ allowWorkerThreads: boolean;
69
+ /** Agent's own diagnostics channel. Defaults to stderr (console is captured). */
70
+ log: (msg: string) => void;
71
+ }
72
+ interface UserConfig {
73
+ service?: string;
74
+ bufferMb?: number;
75
+ dir?: string;
76
+ triggers?: Partial<TriggersConfig>;
77
+ staging?: Partial<StagingConfig>;
78
+ shedding?: Partial<SheddingConfig>;
79
+ token?: string;
80
+ viewerOrigin?: string;
81
+ sinks?: SinkConfig[];
82
+ allowWorkerThreads?: boolean;
83
+ log?: (msg: string) => void;
84
+ }
85
+
86
+ /**
87
+ * Arms the flight recorder: allocates the ring buffer, applies
88
+ * instrumentations, installs triggers/vitals, runs the boot-time recovery
89
+ * pass, and starts delivering anything a previous run left behind.
90
+ * Idempotent.
91
+ */
92
+ declare function start(userConfig?: UserConfig): void;
93
+ /** Manually capture an incident. Returns the staged file path, or null (cooldown / not started / disk guard). */
94
+ declare function trigger(reason?: string): string | null;
95
+ /** Record a custom timeline event (e.g. flightbox.addEvent('cache.miss', { key })). */
96
+ declare function addEvent(name: string, data?: Record<string, unknown>): void;
97
+ /** Disarms recording, stops samplers and watchdogs. Module patches remain but degrade to no-ops. */
98
+ declare function stop(): void;
99
+ declare const _default: {
100
+ start: typeof start;
101
+ stop: typeof stop;
102
+ trigger: typeof trigger;
103
+ addEvent: typeof addEvent;
104
+ };
105
+
106
+ export { type FlightboxConfig, type SinkConfig, type UserConfig, addEvent, _default as default, start, stop, trigger };