export { readKernelBootId } from './boot-id.js'; /** The kernel's per-boot identity + a wall-clock fallback estimate. */ export interface BootIdentity { /** `/proc/sys/kernel/random/boot_id` (a fresh UUID per kernel boot), or null * when unreadable (non-Linux). Exact when present. */ bootId: string | null; /** Best-effort wall-clock estimate of when the current boot started. Fallback * discriminator only, used when `bootId` is null on either side. */ bootEpochMs: number; } /** Identity of the CURRENT OS boot. `now` is injectable for tests. */ export declare function currentBootIdentity(now?: number): BootIdentity; /** Pure decision core: does `current` belong to a DIFFERENT boot than the * persisted `known`? `known === null` (no history — a fresh install, or the * first run of a build that added this marker) is never a boot change: there * is nothing to contradict it, and treating "no history" as a change could * clear genuinely live pids that predate this feature. * * When BOTH sides carry a kernel `boot_id`, that exact identity decides it — no * wall-clock window, so a real quick reboot/recreate (a short-lived prior boot * whose start is <60s away) is still detected. Only when a `boot_id` is missing * on either side (non-Linux dev) does it fall back to the epoch-drift window. * Unit-testable without real uptime/fs. */ export declare function bootChanged(current: BootIdentity, known: BootIdentity | null): boolean; /** POSITIVE proof that `current` is the SAME boot the persisted `known` marker * recorded — as distinct from `!bootChanged(...)`, which is merely "no change * detected" and is also true when there is no history, a malformed marker, or * only the coarse wall-clock fallback to go on. Proof requires an exact kernel * `boot_id` present and equal on BOTH sides. This gates the one-time legacy * identity migration: that sweep rewrites a legacy row's baseline as * `#`, permanently blessing the recorded pid as a * current-boot process, so it must run ONLY when we can prove the recorded pid * really is from this boot — never on a mere ticks match under an unproven * boot (the epoch fallback or an absent/malformed marker could otherwise bless * an unrelated stranger process that happens to collide on pid+ticks). */ export declare function bootProvenSame(current: BootIdentity, known: BootIdentity | null): boolean; export interface BootReconcileResult { /** True when a boot change was detected (and, therefore, pids were cleared). */ changed: boolean; /** node_ids whose stale `pi_pid` was cleared. */ cleared: string[]; /** True ONLY when this run positively proved same-boot provenance against the * persisted marker (exact `boot_id` present and equal on both sides). Gates * the legacy identity migration — see `bootProvenSame`. False on a boot * change, a first-ever/absent/malformed marker, or a wall-clock-fallback * match. */ sameBootProven: boolean; } /** Idempotent and safe to call from every revive entry point (daemon startup, * reviveNode): compares the current boot's identity against the last one * recorded, and if they differ, clears `pi_pid` on every node that has one — * regardless of status, since reviveNode's double-launch guard reads `pi_pid` * unconditionally, not just on active/idle rows. The first caller after a * genuine boot change does the sweep and persists the new marker; every call * after that (in this process or another) reads a matching marker and no-ops. * * SINGLE-WRITER: the whole read-marker + sweep + write-marker runs in one * `BEGIN IMMEDIATE` transaction. A concurrent caller (a manual revive racing * daemon startup, in a separate process) blocks on the SQLite write lock until * this commits, then reads the already-updated marker and no-ops — so it can * never `clearPid` a broker pid the first caller's revive just recorded, which * would otherwise hide a live broker behind `pi_pid=NULL` and risk a second * broker on the same session. * * `current` defaults to the real identity; tests inject a synthetic one against * an isolated CRTR_HOME to simulate a boot change without a real reboot. */ export declare function reconcileBootLiveness(current?: BootIdentity): BootReconcileResult; /** The one-time legacy pid-identity migration, injected into the startup * composition. In production this is `migrateLegacyPidIdentities` from * canvas.ts; it is a parameter (not a direct import) because canvas.ts already * depends on pid.ts which depends on this module — injecting it keeps the * dependency one-directional and lets tests exercise this exact composition. */ export type MigrateLegacyPidIdentities = (captureIdentity?: (pid: number) => string | undefined) => { migrated: string[]; }; export interface BootStartupResult { /** The pid-liveness reconciliation result (boot-change sweep + provenance). */ reconcile: BootReconcileResult; /** node_ids whose legacy identity baseline was migrated — empty when the * provenance gate skipped the sweep. */ migrated: string[]; } /** The daemon's one-time startup boot composition, the single source of truth * for its ORDERING and GATE: reconcile pid liveness FIRST (clearing any pids * recorded before this boot), then run the legacy identity migration ONLY when * reconciliation positively PROVED same-boot provenance (`sameBootProven`). * Both `runDaemon` and the migration-gate regression tests call this, so a * revert that inverts the gate, drops it, or reorders migration ahead of * reconciliation is caught here rather than only in a test-side duplicate. * * `migrate` is injected (see `MigrateLegacyPidIdentities`); `current` and * `captureIdentity` pass through to the two steps for test injection. */ export declare function reconcileAndMigrateBoot(migrate: MigrateLegacyPidIdentities, current?: BootIdentity, captureIdentity?: (pid: number) => string | undefined): BootStartupResult;