/** * SQLite backup via VACUUM INTO with snapshot rotation. * * Produces self-contained, WAL-free copies of every CLEO SQLite database * (`DB_INVENTORY` from `@cleocode/contracts`) into: * * - `.cleo/backups/sqlite/` — project-tier (and `derived` rows * that opt into snapshotting) * - `$XDG_DATA_HOME/cleo/backups/sqlite/` — global-tier * * with a configurable rotation limit. Also provides raw-file backup for the * global-salt binary (not SQLite). All errors are swallowed — backup failure * must never interrupt normal operation. * * Snapshot targets are derived from the canonical inventory in * `db-inventory.json` so the snapshot pipeline cannot drift from the charter * (Saga T10281 / Epic T10284 / E3-BACKUP-RECOVERY). Every inventory entry is * classified into one of two strategies: * * - **chokepoint-opener** — role has a registered canonical opener * (tasks, brain, conduit, nexus, * signaldock-global, telemetry, skills). * Snapshot via the opener's native handle. * - **raw-file-vacuum-readonly** — role has NO live opener (llmtxt reserved, * signaldock-project historical, global-brain * / global-tasks orphans, manifest derived * when opted in). Snapshot by opening the * file read-only and issuing `VACUUM INTO`. * * Both strategies live under `packages/core/src/store/**` — the canonical * allowlist root for direct `DatabaseSync` construction (ADR-068). * * @task T4873 * @task T5158 — extended to cover brain.db * @task T306 — extended to cover global-tier nexus.db (epic T299) * @task T369 — extended to cover conduit.db (project), signaldock.db (global), * and global-salt raw-file backup (epic T310) * @task T10316 — eager-open via per-DB chokepoint (brain backup gap) * @task T10317 — every `DB_INVENTORY` row now produces a snapshot * (Saga T10281 / Epic T10284 / E3) * @epic T4867 */ import { type DbInventoryEntry, type DbRole } from '@cleocode/contracts'; /** * Snapshot strategy classification. * * - `chokepoint-opener` — role has a canonical opener (`getDb`, * `getBrainDb`, `ensureConduitDb`, * `getNexusDb`, `ensureGlobalAgentRegistryDb`, * `getTelemetryDb`, `openSkillsDb`). * - `raw-file-vacuum-readonly` — role has NO live opener; the file is * opened read-only via a one-shot * `DatabaseSync` (allowlisted under * `packages/core/src/store/**`) just to * emit `VACUUM INTO`, then closed. * - `skip-derived` — `tier === 'derived'`; the canonical * charter row marks the file as rebuildable * (`backupPath === 'rebuildable-from-blob-store'`). * Excluded from the snapshot pipeline. The * row IS surfaced in `listSqliteBackupsAll` * for completeness but its bucket stays empty. * * @task T10317 */ type SnapshotStrategy = 'chokepoint-opener' | 'raw-file-vacuum-readonly' | 'skip-derived'; /** Options accepted by {@link vacuumIntoBackup} and {@link vacuumIntoBackupAll}. */ export interface VacuumOptions { /** * Working directory used to resolve the project-local `.cleo/backups/sqlite/` * directory. Defaults to `process.cwd()` (delegated to {@link getCleoDir}). */ cwd?: string; /** When true, bypass the {@link DEBOUNCE_MS} debounce window. */ force?: boolean; } /** * Create a VACUUM INTO snapshot of the primary SQLite database (tasks.db). * * Debounced by default (30s). Pass `force: true` to bypass debounce. This * function is retained for backward compatibility with existing call sites * in `data-safety-central.ts` that only snapshot tasks.db. * * Prefer {@link vacuumIntoBackupAll} for new code — it snapshots every * inventory-registered database and shares the same debounce + rotation * guarantees. * * Non-fatal: all errors are swallowed — backup failure must never * interrupt normal operation. */ export declare function vacuumIntoBackup(opts?: VacuumOptions): Promise; /** * Create VACUUM INTO snapshots of every project-tier (and opt-in `derived`) * SQLite database registered in `DB_INVENTORY`. Each database is debounced * independently. * * This is the preferred entry point for session-lifecycle hooks and * pre-destructive-operation snapshots — it guarantees that BRAIN memory is * snapshotted alongside task state, plus every other inventory-registered * project-tier DB. * * Non-fatal: errors are swallowed per database so any single failure cannot * block snapshots of the rest. * * Global-tier rotation is the responsibility of * {@link vacuumIntoGlobalBackupAll}. * * @task T5158 * @task T10317 — extended to every `DB_INVENTORY` project + derived row */ export declare function vacuumIntoBackupAll(opts?: VacuumOptions): Promise; /** * List existing tasks.db snapshots (newest first). * * Retained for backward compatibility. For new code prefer * {@link listSqliteBackupsAll}. */ export declare function listSqliteBackups(cwd?: string): Array<{ name: string; path: string; mtimeMs: number; }>; /** * List existing brain.db snapshots (newest first). */ export declare function listBrainBackups(cwd?: string): Array<{ name: string; path: string; mtimeMs: number; }>; /** * Aggregated listing of all registered SQLite snapshots. * * Returns an object keyed by snapshot prefix (`tasks`, `brain`, `conduit`, * `llmtxt`, `signaldock-project`, `manifest`) where each value is the * per-prefix list sorted newest-first. Missing prefixes are represented as * empty arrays. Derived rows (`manifest`) keep an always-empty bucket so * downstream code can detect "covered by inventory, not snapshotted by * design" vs "unknown prefix". */ export declare function listSqliteBackupsAll(cwd?: string): Record>; /** * Backup scope: project (per-project `.cleo/`) or global (`$XDG_DATA_HOME/cleo/`). * * @task T306 * @epic T299 */ export type BackupScope = 'project' | 'global'; /** * Names accepted by {@link vacuumIntoGlobalBackup}. Mirrors * {@link DbRole} for the global-tier subset PLUS the historical * `'signaldock'` alias kept for backward compatibility with callers that * pre-date the inventory split. * * @task T10317 — extended to cover every global-tier inventory entry */ export type GlobalBackupName = 'nexus' | 'signaldock' | 'signaldock-global' | 'telemetry' | 'skills' | 'global-brain' | 'global-tasks'; /** * Snapshot a global-tier SQLite database via VACUUM INTO. * * Writes to `$XDG_DATA_HOME/cleo/backups/sqlite/-YYYYMMDD-HHmmss.db` * and enforces a per-prefix rotation window (default 10 snapshots). * * Non-fatal: errors from any individual step are surfaced via the return value * but never thrown — a failed snapshot MUST NOT interrupt normal operation. * * @param dbName - Which global-tier DB to snapshot * (see {@link GlobalBackupName}) * @param opts.rotation - Maximum retained snapshots per prefix (default 10) * @param opts.cleoHomeOverride - Override `getCleoHome()` path (use in tests to target a tmp dir) * @returns Object containing the new snapshot path and any rotated (deleted) file paths * * @task T306 * @task T369 — activated signaldock target (epic T310) * @task T10317 — every `DB_INVENTORY` global-tier row covered * @epic T299 * @why ADR-036 §Backup Mechanism requires VACUUM INTO rotation at the global tier; * nexus.db has zero backup coverage prior to v2026.4.11. */ export declare function vacuumIntoGlobalBackup(dbName: GlobalBackupName, opts?: { rotation?: number; cleoHomeOverride?: string; }): Promise<{ snapshotPath: string; rotated: string[]; }>; /** * Snapshot every global-tier database registered in `DB_INVENTORY`. * * Iterates {@link GLOBAL_SNAPSHOT_TARGETS} and invokes * {@link vacuumIntoGlobalBackup} for each entry that resolves to a present * on-disk file. Non-fatal per target — one failure does not block the rest. * * Useful for session-end + `cleo backup add` flows so the global tier * (nexus, signaldock, telemetry, skills, plus any orphan files) gets the * same per-session rotation treatment as the project tier. * * @param opts.cleoHomeOverride - Override `getCleoHome()` path (test isolation) * @param opts.rotation - Maximum retained snapshots per prefix (default 10) * @returns Array of per-target results (matches insertion order of inventory). * Skipped or failed targets surface as `{ snapshotPath: '', rotated: [] }` * so callers can audit coverage without consulting the inventory. * * @task T10317 — fleet snapshot at session-end (Saga T10281 / E3) */ export declare function vacuumIntoGlobalBackupAll(opts?: { cleoHomeOverride?: string; rotation?: number; }): Promise>; /** * A single entry returned by {@link listGlobalSqliteBackups}. * * @task T306 * @epic T299 */ export interface GlobalBackupEntry { /** Snapshot filename, e.g. `nexus-20260408-143022.db`. */ name: string; /** Absolute path to the snapshot file. */ path: string; /** File size in bytes. */ size: number; /** Last-modified timestamp. */ mtime: Date; } /** * List global-tier SQLite backups from `$XDG_DATA_HOME/cleo/backups/sqlite/`, * optionally filtered by prefix (e.g. `'nexus'`). Sorted newest-first by mtime. * * Returns an empty array when the backup directory does not exist. * * @param prefix - Optional prefix filter; when omitted all `.db` snapshot files are listed * @param cleoHomeOverride - Override `getCleoHome()` path (use in tests to target a tmp dir) * * @task T306 * @epic T299 */ export declare function listGlobalSqliteBackups(prefix?: string, cleoHomeOverride?: string): GlobalBackupEntry[]; /** * Aggregated listing of every global-tier snapshot bucket. Returns a map * keyed by canonical snapshot prefix (`nexus`, `signaldock`, `telemetry`, * `skills`, `global-brain`, `global-tasks`) with each value sorted * newest-first by mtime. Empty arrays surface for buckets with no snapshots * yet — callers can distinguish "covered by inventory, nothing on disk" vs * "unknown prefix". * * @task T10317 */ export declare function listGlobalSqliteBackupsAll(cleoHomeOverride?: string): Record; /** * Inventory coverage report. Lists every {@link DbRole} and the snapshot * strategy that covers it. Used by test suites and `cleo doctor` follow-ups * to assert no inventory entry is silently uncovered. * * @task T10317 */ export interface InventoryCoverageRow { readonly role: DbRole; readonly tier: DbInventoryEntry['tier']; readonly prefix: string; readonly strategy: SnapshotStrategy; } /** * Return the strategy + filename prefix that the snapshot pipeline applies * to each `DB_INVENTORY` row. * * @task T10317 */ export declare function describeSnapshotCoverage(): readonly InventoryCoverageRow[]; /** * Creates a raw-file backup of the global-salt binary at * `${getCleoHome()}/backups/global-salt-YYYYMMDD-HHmmss` with `0o600` * permissions. Rotates to {@link MAX_SNAPSHOTS} (10) copies, deleting the * oldest when the limit is reached. * * Non-fatal: errors are swallowed — salt backup failure must never block cleo. * Returns empty strings and no rotated paths on failure. * * @param opts.cleoHomeOverride - Override `getCleoHome()` path (use in tests to target a tmp dir) * @returns Object with the new snapshot path and any rotated (deleted) file paths * * @task T369 * @epic T310 * @why ADR-037 §5 — global-salt is security-critical; losing it invalidates * all API keys. Backup enables recovery from accidental deletion. */ export declare function backupGlobalSalt(opts?: { cleoHomeOverride?: string; }): Promise<{ snapshotPath: string; rotated: string[]; }>; /** * A single entry returned by {@link listGlobalSaltBackups}. * * @task T369 * @epic T310 */ export interface GlobalSaltBackupEntry { /** Backup filename, e.g. `global-salt-20260408-143022`. */ name: string; /** Absolute path to the backup file. */ path: string; /** File size in bytes (should be 32 for a valid global-salt). */ size: number; /** Last-modified timestamp. */ mtime: Date; } /** * List global-salt backup files from `$XDG_DATA_HOME/cleo/backups/`, sorted * newest-first by mtime. * * Returns an empty array when the backup directory does not exist. * * @param cleoHomeOverride - Override `getCleoHome()` path (use in tests to target a tmp dir) * * @task T369 * @epic T310 */ export declare function listGlobalSaltBackups(cleoHomeOverride?: string): GlobalSaltBackupEntry[]; export {}; //# sourceMappingURL=sqlite-backup.d.ts.map