/** * portable-bundle-scan.ts — tree walking, SQLite snapshotting and row * counting shared by the portable-bundle export and import sides. * * Kept free of CLI and registry concerns: it answers "what is in this root, * what may leave the machine, and how many rows does each table hold". * * @task T12318 * @epic T12317 * @module store/portable-bundle-scan */ import type { PortableExclusion, PortableSymlinkEntry } from '@cleocode/contracts'; /** * Basename of the consolidated (post-E6) store, derived from the dual-scope * resolver so a future rename cannot silently strand the export again. */ export declare const PRIMARY_STORE_BASENAME: string; /** * Pre-E6 per-domain database basenames that sit directly in a root, by tier. * Derived from `DB_INVENTORY`: each role's pre-consolidation file * (`legacyFilePathTemplate`, else `filePathTemplate`) at the top of `.cleo/` * or `/`. The live store (`cleo.db`) is never one of them (T13245). */ export declare const LEGACY_STORE_BASENAMES: Readonly>>; /** Rules controlling what a section walk captures. */ export interface SectionRules { /** * Relative directory paths excluded with a reason. A key matches the * directory itself; a key ending in `*` matches any directory whose * relative path starts with the prefix. */ excludedDirs: Readonly>; /** Directory basenames excluded at ANY depth, with a reason. */ excludedDirNamesAnywhere: Readonly>; /** Files (relative paths) never exported, not even in encrypted bundles, with a reason. */ excludedFiles?: Readonly>; /** Returns the remedy when `relPath` is a secret, else null. */ secretRemedy: (relPath: string) => string | null; } /** Walk rules for a project `.cleo/` directory. */ export declare const PROJECT_SECTION_RULES: SectionRules; /** Walk rules for the global CLEO home. */ export declare const GLOBAL_HOME_RULES: SectionRules; /** Walk rules for the config home (`$XDG_CONFIG_HOME/cleo`). */ export declare const CONFIG_HOME_RULES: SectionRules; /** Classified contents of one root. */ export interface SectionScan { /** SQLite files (relative paths). */ sqlite: string[]; /** Regular non-secret files (relative paths). */ files: string[]; /** Secret files with their remedies. */ secrets: Array<{ relPath: string; remedy: string; }>; /** Symlinks whose targets stay inside the root. */ symlinks: PortableSymlinkEntry[]; /** Exclusions with sizes. */ excluded: PortableExclusion[]; } /** * True when a file starts with the SQLite header. * * @param absPath - File to test. * @returns Whether the file is a SQLite database. */ export declare function isSqliteFile(absPath: string): boolean; /** * Walk one root and classify every entry. Symlinks are never followed; an * in-root relative symlink is recorded for recreation, any other symlink is * reported as an exclusion so nothing leaves the root silently. * * @param root - Absolute root directory. * @param rules - Section rules. * @param skipAbsolute - Absolute paths to ignore entirely (e.g. the export's own staging dir). * @returns The classified scan. */ export declare function scanSection(root: string, rules: SectionRules, skipAbsolute?: ReadonlySet): SectionScan; /** * Snapshot a SQLite database with `VACUUM INTO` over a READ-ONLY connection. * * VACUUM INTO runs inside one read transaction, so the snapshot is * consistent even while other processes write through WAL. The source is * never checkpointed or otherwise modified. * * @param srcPath - Source database. * @param destPath - Snapshot path (must not exist). * @throws {Error} When the source cannot be opened or read. */ export declare function vacuumSnapshot(srcPath: string, destPath: string): void; /** * Count rows in every ordinary table of a database. * * Virtual tables are reported as uncounted (their rows live in shadow tables, * which are counted); any other failure is reported with its message rather * than swallowed. * * @param dbPath - Database to count. * @returns Counts and uncountable tables. */ export declare function countRows(dbPath: string): { rowCounts: Record; uncountedTables: Record; }; /** * Run `PRAGMA integrity_check` on a database. * * @param dbPath - Database to check. * @returns `ok` or the first reported problem. */ export declare function integrityCheck(dbPath: string): string; /** Tables surfaced as "key counts" in results when present. */ export declare const KEY_COUNT_TABLES: readonly string[]; /** * Legacy (pre-E6) table → consolidated table in `cleo.db`, for detecting data * that exists only in legacy files. */ export declare const LEGACY_TABLE_MAP: Readonly>; /** * Select the key-table subset of a row-count map. * * @param rowCounts - Full counts. * @returns Counts for the key tables that exist. */ export declare function pickKeyCounts(rowCounts: Record): Record; /** * Credential-bearing columns, by table, that an UNENCRYPTED bundle clears. * Found by auditing the live project and global stores for secret-, token-, * password- and key-bearing columns (usage counters such as `tokens_used` * are not credentials). The rows are kept; only these values are cleared. * * Interim list: the table classification registry (T12332) is the intended * long-term source for the `portable-secret` class. */ export declare const CREDENTIAL_COLUMNS: Readonly>; /** * What losing one credential table's secrets costs, as re-entry advice. * * @param table - Table name. * @returns The remedy text (a generic one for a table without its own). * @task T12336 */ export declare function credentialRemedy(table: string): string; /** Memory (ADR-093 `portable`) tables whose counts are disclosed on export. */ export declare const MEMORY_TABLES: readonly string[]; /** One table's cleared credential columns. */ export interface CredentialRedaction { /** Table. */ table: string; /** Columns cleared. */ columns: string[]; /** Rows that held a non-empty credential value. */ rows: number; /** Remedy text. */ remedy: string; } /** * Clear credential columns in a SNAPSHOT (never a live store), then rebuild * the file with `secure_delete` + `VACUUM` so the old values are not left in * free pages. Rows are kept, so row counts are unchanged. * * @param snapshotPath - A VACUUM snapshot owned by the export staging area. * @returns Per-table redactions (tables with no credential values are omitted). * @throws {Error} When a credential column cannot be cleared; the caller must * not ship the snapshot. */ export declare function redactCredentials(snapshotPath: string): CredentialRedaction[]; /** * Remove the given secret columns from the change-journal images of a * SNAPSHOT (never a live store), as {@link redactCredentials} clears them in * the rows: an unencrypted bundle that leaves the machine must not carry a * deleted session or approval token in an undo image (T12535). The journal * rows stay, so a store moved through the bundle still reconciles its unsent * changes; reconcile never reads a secret column from an image (secret * columns are left out of every field comparison). An image of such a table * that is not valid JSON is replaced whole (NULL, or `{}` in a NOT NULL * column), never shipped as written (T13817). Then `secure_delete` + * `VACUUM`. * * @param snapshotPath - A VACUUM snapshot owned by the export staging area. * @param columns - Table to secret column names. * @returns Image values rewritten. * @throws {Error} When an image cannot be rewritten; the caller must not ship the snapshot. * @task T12535 */ export declare function redactJournalSecrets(snapshotPath: string, columns?: Readonly>): number; /** * Clear the given columns in a SNAPSHOT (never a live store), as * {@link redactCredentials} does for the credential columns: NULL (or `''` * when NOT NULL, or a per-row `redacted:` when NOT NULL and UNIQUE), * then `secure_delete` + `VACUUM`. Rows are kept. * * @param snapshotPath - A VACUUM snapshot owned by the export staging area. * @param columns - Table to column names; absent tables and columns are skipped. * @returns Per-table clearings (tables with no values are omitted). * @throws {Error} When a column cannot be cleared; the caller must not ship the snapshot. * @task T13007 */ export declare function clearSnapshotColumns(snapshotPath: string, columns: Readonly>): CredentialRedaction[]; /** Prefix of the per-row placeholder a cleared NOT NULL UNIQUE column takes (`redacted:`). */ export declare const REDACTED_PREFIX = "redacted:"; /** One table {@link clearSnapshotTables} emptied. */ export interface ClearedSnapshotTable { /** Table name. */ table: string; /** Rows deleted. */ rows: number; } /** * Empty the given tables in a SNAPSHOT (never a live store), with the * snapshot's capture and side-effect triggers suspended, then rebuild the file * with `secure_delete` + `VACUUM` so the deleted rows are not left in free * pages. The cloud vault empties this device's local-only change journal * (`_sync_capture`, `_sync_undo`, `_sync_frame`, `_sync_quarantine`), whose images hold values a * snapshot strips; a restore takes those tables from the live store (T13042). * * @param snapshotPath - A VACUUM snapshot owned by the export staging area. * @param tables - Tables to empty; absent tables are skipped. * @returns The tables that held rows, with the count deleted. * @throws {Error} When a table cannot be emptied; the caller must not ship the snapshot. * @task T13042 */ export declare function clearSnapshotTables(snapshotPath: string, tables: readonly string[]): ClearedSnapshotTable[]; /** The rows of a snapshot table to keep: those whose `column` holds one of `values`. */ export interface SnapshotRowFilter { /** The column compared. */ readonly column: string; /** The values kept. */ readonly values: readonly string[]; } /** * Delete from each table of a SNAPSHOT (never a live store) every row its * filter does not keep, the way {@link clearSnapshotTables} empties tables: * triggers suspended, then `secure_delete` + `VACUUM`. The cloud vault keeps * only a journal checkpoint's unresolved inbox rows (T12999). * * @param snapshotPath - A VACUUM snapshot owned by the export staging area. * @param keep - Per table, the rows kept; absent tables are skipped. * @returns The tables that lost rows, with the count deleted. * @task T12999 */ export declare function keepSnapshotRows(snapshotPath: string, keep: Readonly>): ClearedSnapshotTable[]; /** * Stream a file through SHA-256. * * @param filePath - File to hash. * @returns Lowercase hex digest. */ export declare function sha256File(filePath: string): Promise; //# sourceMappingURL=portable-bundle-scan.d.ts.map