/** * Machine distress ledger — the CLI's flight recorder. * * Any component (credential resolution, bundle polling, self-update, hooks, * daemon internals) reports a STABLE coded signal via `reportDistress()`. * The ledger is: * - persisted on disk (survives process churn — hooks are short-lived), * - deduped (a failing subsystem repeats every few minutes; the ledger * stores one entry per code+component with a count, not thousands), * - shipped upstream on every daemon heartbeat (`readDistressSnapshot()`), * - included in the collect_diagnostics support bundle, * - echoed into daemon.log so the console's live log panel shows it. * * The code set is OPEN by design: known scenarios get named codes below, * and failures nobody predicted are captured via `reportUnexpected()` — * so even unknown bugs produce structured, fleet-visible telemetry instead * of silence. Adding coverage for a new failure class is ONE call site. * * Everything here is best-effort file I/O — distress reporting must never * break the component doing the reporting (same discipline as * daemonForensics.ts). */ /** Known distress codes. Open set — free-form codes are allowed. */ export declare const DISTRESS: { /** DPAPI-protected shield key would not decrypt (EDR / Constrained Language Mode). */ readonly DPAPI_DECRYPT_FAILED: "dpapi_decrypt_failed"; /** powershell.exe / pwsh.exe could not even start (EDR/AppLocker block). */ readonly POWERSHELL_BLOCKED: "powershell_blocked"; /** PowerShell runs in ConstrainedLanguage/RestrictedLanguage mode. */ readonly POWERSHELL_CONSTRAINED: "powershell_constrained"; /** Control plane rejected our credentials (HTTP 401/403) — key wrong or unavailable. */ readonly AUTH_BROKEN: "auth_broken"; /** Control plane unreachable (network / DNS / proxy / timeout). */ readonly NETWORK_DOWN: "network_down"; /** Self-update triggered repeatedly for the same target without the version changing. */ readonly UPDATE_LOOP: "update_loop"; /** MSI updater scheduled task missing and could not be re-registered. */ readonly UPDATER_TASK_MISSING: "updater_task_missing"; /** Config file missing/unreadable — machine likely not enrolled or wiped. */ readonly CONFIG_MISSING: "config_missing"; /** Native Credential Manager binding did not load (missing/blocked .node) — PowerShell fallback in use. */ readonly NATIVE_STORE_UNAVAILABLE: "native_store_unavailable"; /** Native Credential Manager write/verify failed. */ readonly NATIVE_STORE_FAILED: "native_store_failed"; /** S4U-safe machine key file write/verify failed (daemon may stay credential-less). */ readonly MACHINE_KEYFILE_FAILED: "machine_keyfile_failed"; /** Machine key file kept, but the owner-only ACL could not be applied. */ readonly MACHINE_KEYFILE_ACL_FAILED: "machine_keyfile_acl_failed"; /** Fail-closed engaged: hook is BLOCKING user actions because the policy gate is persistently unreachable. */ readonly HOOK_FAIL_CLOSED: "hook_fail_closed"; /** * A daemon poll never settled and was abandoned at its deadline. Usually a * socket that died without a FIN (sleep / VPN / proxy). Self-heals on the * next tick — but while it lasted, remote actions were undeliverable. */ readonly POLL_STALLED: "poll_stalled"; /** * The watchdog found the daemon process alive but WEDGED — alive-marker * stale past the bound and the verdict pipe silent — and recycled it. */ readonly DAEMON_HUNG: "daemon_hung"; /** * The daemon exited itself after repeated uncaught exceptions in a short * window (process state suspect); the supervisor restarts it clean. */ readonly UNCAUGHT_BURST: "uncaught_burst"; /** * The telemetry spool could not be written (disk full, ACL, AV lock) — the * machine is still enforcing, but its decisions are being LOST locally and * will never reach the console. Deserves an admin's attention because a * quiet machine and a machine that cannot record look identical otherwise. */ readonly SPOOL_WRITE_FAILED: "spool_write_failed"; /** Anything nobody predicted — reported via reportUnexpected(). */ readonly UNEXPECTED: "unexpected_error"; }; export interface DistressEntry { /** Stable machine-readable code (see DISTRESS, open set). */ code: string; /** Component that reported (e.g. 'credentials', 'bundle', 'self-update', 'hook'). */ component: string; /** Bounded human detail — never secrets, never file contents. */ detail?: string; /** First occurrence in the current dedupe window (ISO). */ firstAt: string; /** Most recent occurrence (ISO). */ lastAt: string; /** Occurrences folded into this entry. */ count: number; } export declare function distressFile(): string; /** * Record a distress signal. Deduped: the same code+component within the * dedupe window updates count/lastAt (echoed to daemon.log only on the first * occurrence and then every 10th, to keep the log readable). */ export declare function reportDistress(component: string, code: string, detail?: string): void; /** Structured capture for failures nobody predicted. */ export declare function reportUnexpected(component: string, error: unknown): void; /** * Remove a code from the ledger once its condition is PROVEN resolved (e.g. * update_loop after the installed version reaches the looping target). Keeps * the shipped ledger truthful: resolved incidents must stop riding along in * heartbeats, where they read as live problems in the console and the fleet * alerter. Returns true when something was actually cleared. */ export declare function clearDistress(code: string, component?: string): boolean; /** * Recent distress for the heartbeat: entries seen within `windowMs` * (default 24h), newest last, capped for transport. */ export declare function readDistressSnapshot(options?: { windowMs?: number; limit?: number; }): DistressEntry[]; /** Full ledger for the collect_diagnostics support bundle. */ export declare function readDistressLedger(): DistressEntry[];