/** * The cloud vault: encrypted snapshots of a project's (or this account's * global) CLEO store on Cleo Nexus, with lineage, a single-writer lease and * verified restore (T12336 protocol, T12337 CLI, T12338 lease handoff). * * A snapshot is a checkpoint on the store's journal stream (`project:`, * or the account's `home:`): * * - **Bundle**: the store's portable bundle (`exportPortableBundle`, no * secrets; for the global store without machine-local state or the config * home), sealed with the stream data key and uploaded as a blob. The data * key derives from the account master key, which is escrowed on Cleo Nexus * (released only to your approved devices; see `nexus-vault-keys.ts` for the * threat model), so this is encrypted at rest, not zero-knowledge. * - **Manifest** (plaintext): per-table row counts and keyed hashes of the * store's syncing tables (`buildVaultManifest`). The server checks counts * reconcile with the journal; only key holders can check hashes. * - **Lineage**: each snapshot names its parent; the server refuses one that * does not descend from the stream head (E_LINEAGE). The client refuses * earlier, with `E_NEXUS_VAULT_BEHIND`, when the head is not the snapshot * this store last pushed or restored: another device wrote since. * - **Counts**: a non-genesis snapshot's counts must equal the parent's plus * the per-table deltas of the journal segments between them. A push whose * counts changed appends one signed "vault delta" segment carrying exactly * the difference (the journal sync of Stage C will carry real ops). * - **Lease**: a push takes the stream's `writer` lease. Another device's * live lease refuses the push (`E_NEXUS_VAULT_LEASE_HELD`); `--force` * takes it, and the server labels the take as a fork from the holder. * - **Fork label**: a `--force` push over a head this store had not synced, * or over another device's lease, carries `zz_vault_fork` (0 rows) in its * signed manifest, so every device's verify still sees the fork after the * lease is released (T13007). * - **Stream format** (T13034): the vault writes checkpoint/v2 and stamps * `SYNC_SCHEMA_VERSION` (shared with the change journal) on its segments * and manifests. It reads and verifies the segment/v3 and checkpoint/v3 the * journal writes, and refuses a push onto a stream whose head snapshot is * checkpoint/v3 (the server's `E_STREAM_VERSION` ratchet) or that holds a * newer sync schema before it writes anything * (`E_NEXUS_VAULT_STREAM_UPGRADED`). * * Restore downloads, verifies the author's signature, decrypts, and checks * every table's count and hash against the manifest before anything is * placed; this machine's `local-only` rows are carried into the snapshot so * machine state (replica binding, registry paths) is never overwritten, and * what the snapshot no longer lists is removed afterwards (T13004). * * Nothing here blocks normal commands: the vault runs only when asked. * * @task T12336 * @task T12337 * @task T12338 * @epic T12322 */ import type { CloudLeaseReleaseResult, CloudPushResult, CloudRestoreResult, CloudSyncPushEnableResult, CloudSyncResult, CloudVaultScope, CloudVaultStatusResult, CloudVerifyResult } from '@cleocode/contracts'; import { type PullStreamReport } from '../store/sync/pull.js'; import { type PushStreamReport } from '../store/sync/push.js'; import type { ServerRetirement } from '../store/sync/retire.js'; import { type JournalTallies } from './journal-tallies.js'; import { type NexusVaultConnection, type NexusVaultOptions } from './nexus-vault-keys.js'; /** Default lease length of a push: long enough for a large upload, short enough to hand off. */ export declare const NEXUS_VAULT_LEASE_TTL_SECONDS = 900; /** * Global-home paths a vault snapshot never carries (T12968): this machine's * identity and runtime state, and the content CLEO's installer writes for its * own version (T13022: templates, harness skills, hooks, extensions, the * model catalog, migration markers, the CleoOS hub extensions and identity, * and the CANT starter bundle the CleoOS postinstall rewrites, T13038). * A global pull must leave them as they are, so the bundle does not hold them * at all. The config home is left out too. */ export declare const VAULT_GLOBAL_EXCLUSIONS: { readonly dirs: { readonly state: "machine-local state (on macOS the replica registries live here); never in a vault snapshot"; readonly keys: "machine-local keys (evidence cache key); never in a vault snapshot"; readonly 'rate-limit-state': "machine-local rate-limit counters; never in a vault snapshot"; readonly templates: "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly skills: "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly hooks: "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly extensions: "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly 'pi-extensions': "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly 'llm-catalog': "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly '.migrations': "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly 'cant/starter': "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; }; readonly files: { readonly 'CLEOOS-IDENTITY.md': "installed by this machine's CLEO version (devices on other versions would downgrade it); never in a vault snapshot"; readonly 'device-id': "this machine's stable device id; never in a vault snapshot"; readonly 'web-server.json': "machine-local web server runtime state; never in a vault snapshot"; readonly 'sentient-state.json': "machine-local daemon state; never in a vault snapshot"; readonly 'device-heartbeat.stamp': "machine-local heartbeat; never in a vault snapshot"; readonly 'nexus.db': "machine-local code index (rebuilt per machine); never in a vault snapshot"; readonly 'exodus-complete': "machine-local migration marker; never in a vault snapshot"; readonly 'telemetry-config.json': "this install's telemetry opt-in and anonymous id; never in a vault snapshot"; readonly 'decide/spend.json': "this machine's decision spend ledger (in-flight reservations); never in a vault snapshot"; readonly 'decide/budget.json': "this machine's decision request token bucket; never in a vault snapshot"; }; }; /** Options shared by the vault commands. */ export interface NexusVaultCommandOptions extends NexusVaultOptions { /** `project` (the current project, default) or `global` (this account's global store). */ scope?: CloudVaultScope; } /** * What works on a stream the change journal writes (its head snapshot is * checkpoint/v3), for every message that would otherwise say "push" (T13034). * A build where `sync.push` is unreleased cannot join the journal, since `cleo sync enable push` * refuses there (T13851). While it is unreleased no released CLEO ships it either, so the remedy * never sends the user to `cleo self-update`: a pre-release or allowUnreleased build started the * journal, and pull, restore and verify keep working (T13856). * * @param pushReleased - Whether this build ships `sync.push`; defaults to the release state. * @returns The remedy clause, naming only commands this build can run. */ export declare function journalStreamRemedy(pushReleased?: boolean): string; /** * The fix for E_SYNC_DISABLED: turn the journal on where this build can, and otherwise say that no * released CLEO can yet (T13856), never naming `cleo sync enable push` where it refuses. * * @param pushReleased - Whether this build ships `sync.push`; defaults to the release state. * @returns The fix text. */ export declare function syncDisabledRemedy(pushReleased?: boolean): string; /** * The rows a parent hashed under an older vault format counted that this * format leaves out (T13808): rows local by row-identity spec. They never * travelled, so the journal declares no delete for them; the checkpoint names * them `pruned`, so the server's row rule takes them off the parent's count. * Per table, the shortfall against the tallies, capped by the local rows the * parent itself held ({@link parentLocalRows}); a parent in the current format * prunes nothing. * * @param parentFormat - The parent's {@link vaultManifestFormat}. * @param tallies - The window's tallies. * @param local - The store's rows per manifest key. * @param localRows - Per table, the local rows the parent's own store held. * @returns The tallies less the pruned rows, and the `pruned` map. */ export declare function formatTransitionPruned(parentFormat: number | 'unknown', tallies: JournalTallies, local: Readonly>, localRows: Readonly>): { tallies: JournalTallies; pruned: Record; }; /** * The server's retirements a receiver of `scope`'s stream confirms a `retire` * by (T13366). The home stream: `retiredAt` and `successor` on * `GET /v1/account/home/replicas`. A project stream: the same fields on E15 * `GET /v1/projects/:projectId/replicas`, every page (T13391). A server * without the listing (404), or one older than retirement, confirms none. * * @param conn - The vault connection. * @param scope - The store's scope. * @param projectId - The server project of a project stream. * @returns The retirements, or undefined for a project stream with no server project. */ export declare function serverRetirementsFor(conn: Pick, scope: CloudVaultScope, projectId?: string | null): Promise; /** * {@link serverRetirementsFor}, soft (T13395): a listing that fails (network, * 5xx, a 404) confirms nothing this round instead of failing the pull. The * pull still stages and applies; retires it records stay unconfirmed until a * later pull reads the listing. The failure is a `W_NEXUS_RETIREMENTS_UNAVAILABLE` * warning on the connection. * * @param conn - The vault connection (its warnings collect the failure). * @param scope - The store's scope. * @param projectId - The server project of a project stream. * @returns The retirements, `[]` when the listing failed, or undefined for a project stream with no server project. */ export declare function confirmableRetirements(conn: Pick, scope: CloudVaultScope, projectId?: string | null): Promise; /** How one stream's push and pull legs ended, classified by refusal kind (T13315). */ export interface SyncLegOutcome { /** Refusals that mean "nothing to do here": a flag off, or push before this store's genesis. */ readonly skipped: string[]; /** Every other refusal. */ readonly refused: string[]; /** Both flags off: the stream is disabled. */ readonly disabled: boolean; /** The pull leg was skipped, so its position says nothing. */ readonly pullSkipped: boolean; } /** * Classify a stream's push and pull results (T13315). Classification uses * each report's typed `refusedKind`, never its message, so rewording a * refusal never turns a skip into a refusal. * * @param push - The push leg's `refused` and `refusedKind`. * @param pull - The pull leg's `refused` and `refusedKind`. * @returns Skipped and refused messages, and whether the stream is disabled. */ export declare function classifySyncLegs(push: Pick, pull: Pick): SyncLegOutcome; /** Options of {@link restoreNexusVault}. */ export interface NexusVaultRestoreOptions extends NexusVaultCommandOptions { /** `pull`: bring this store to the head; `restore`: any snapshot (`checkpointId`), or a project onto a new machine. */ mode: 'pull' | 'restore'; /** A specific snapshot (restore only). */ checkpointId?: string; /** Overwrite local changes (a safety backup is taken first). */ force?: boolean; /** Restore a project this machine does not have: its server id. */ projectId?: string; /** Where to place a project restored onto this machine (default: the current directory). */ into?: string; /** Called after a project restore to attach this store (defaults to `linkProjectToNexus`). */ relink?: (projectRoot: string) => Promise; } /** Safety bundles kept per store under `backups/vault`; older ones are removed (T13007). */ export declare const NEXUS_VAULT_SAFETY_BUNDLES_KEPT = 10; /** * `cleo cloud push`: snapshot this store into the vault under the write lease. * * @param opts - Scope, `force` (take the lease, push over a newer head as a labelled fork), * `hold` (keep the writer lease after the push instead of releasing it) and overrides. * @returns What was pushed. * @throws {NexusAccountError} `E_NEXUS_VAULT_LEASE_HELD`, `E_NEXUS_VAULT_BEHIND`, `E_NEXUS_VAULT_REFUSED`, * `E_NEXUS_VAULT_STREAM_UPGRADED`, `E_NEXUS_VAULT_NOT_LINKED`, `E_NEXUS_VAULT_KEY_UNAVAILABLE`, or a * mapped API error. */ export declare function pushNexusVault(opts?: NexusVaultCommandOptions & { force?: boolean; hold?: boolean; /** Push the journal although `sync.push` is unreleased (tests and staging only). Never set from user input. */ allowUnreleased?: boolean; }): Promise; /** * `cleo sync enable push`: record this store's genesis cut on its stream and * push the genesis checkpoint (journal spec §2.11 §10; T12343 S4-1b). The * store is snapshotted under its write lock, so other cleo processes pause * their writes (they wait, then refuse with `E_STORE_GENESIS`) while the * bundle is exported. A push that fails after the cut keeps the bundle beside * the store and resumes on the next run; a cut that crashed before its bundle * was saved is resumed or re-cut (T13301). * * @param opts - Scope and overrides; `allowUnreleased` for tests and staging only. * @returns What was cut and pushed. * @throws {NexusAccountError} `E_NEXUS_SYNC_REFUSED`, `E_NEXUS_SYNC_STREAM_JOURNALED`, * `E_NEXUS_VAULT_BEHIND`, `E_NEXUS_VAULT_LEASE_HELD`, * `E_NEXUS_VAULT_NOT_LINKED`, or a mapped API error. */ export declare function enableSyncPush(opts?: NexusVaultCommandOptions & { allowUnreleased?: boolean; }): Promise; /** * Push this store's change journal on its stream (journal spec §2.8, §2.9; * T12343 S4-2): seal, persist segments, upload the lowest unpushed first. * Pauses while this device's clock runs ahead of the server's, and refuses a * store behind the server (it must rebind). `cleo cloud sync` (T12996) runs * it with pull and apply. * * @param opts - Scope and overrides; `allowUnreleased` for tests and staging only. * @returns What was sealed, persisted and stored, or why nothing was sent. * @throws {NexusAccountError} `E_NEXUS_SYNC_REFUSED` (a segment conflict: the store is behind), * `E_NEXUS_VAULT_NOT_LINKED`, or a mapped API error. */ export declare function pushSyncStream(opts?: NexusVaultCommandOptions & { allowUnreleased?: boolean; }): Promise; /** * Pull this store's stream journal (journal spec §3.1; T12343 S5-1): verified * segments after the store's cursor are decoded, signature-checked, staged * (a re-delivered transaction never twice, a vault delta passed over) and * applied. A store with no pull position starts after the checkpoint it last * synced. `cleo cloud sync` (T12996) runs it with push. * * @param opts - Scope and overrides. * @returns What was received, staged and applied. * @throws {NexusAccountError} `E_NEXUS_SYNC_REFUSED` (replica mismatch, no position), * `E_NEXUS_VAULT_NOT_LINKED`, or a mapped API error. */ export declare function pullSyncStream(opts?: NexusVaultCommandOptions): Promise; /** * `cleo cloud sync`: seal, push, pull and apply the change journal of each * attached stream (the project's `project:`, the account's `home:`; * T12996). One result per stream: sealed, persisted, sent, received, staged, * applied, held and in conflict, and how far the store has staged against * the server's head. An interrupted run resumes on the next: a segment is * resent with the same bytes, and a transaction is never staged twice. * * @param opts - `scope` (one stream; default every attached one), overrides; * `allowUnreleased` for tests and staging only. * @returns Every stream's result. * @throws {NexusAccountError} `E_SYNC_DISABLED` when no attached stream has a journal flag on. */ export declare function cloudSync(opts?: NexusVaultCommandOptions & { allowUnreleased?: boolean; }): Promise; /** * `cleo cloud pull` / `cleo cloud restore`: verify and activate a snapshot. * A restore then turns sync on for the store and the home stream (T13718). * * @param opts - Mode, snapshot, project and target overrides; * `allowUnreleased` for tests and staging only. * @returns What was restored. * @throws {NexusAccountError} `E_NEXUS_VAULT_LOCAL_CHANGES`, `E_NEXUS_VAULT_VERIFY_FAILED`, * `E_NEXUS_VAULT_EMPTY`, `E_NEXUS_VAULT_KEY_UNAVAILABLE`, or a mapped API error. */ export declare function restoreNexusVault(opts: NexusVaultRestoreOptions & { allowUnreleased?: boolean; }): Promise; /** * `cleo cloud vault`: the vault's lineage, last push per device, pending changes and leases. * * @param opts - Scope and overrides. * @returns The vault status. */ export declare function nexusVaultStatus(opts?: NexusVaultCommandOptions): Promise; /** * `cleo cloud verify`: compare this store with the cloud and every device's newest snapshot. * Local backups are checked by `cleo backup verify`. * * @param opts - Scope and overrides; `deep` downloads the snapshots again and * re-checks their bytes and the journal segments after the head (T13291). * @returns The verdict and per-table comparison, with the deep check when asked. */ export declare function verifyNexusVault(opts?: NexusVaultCommandOptions & { deep?: boolean; }): Promise; /** * `cleo cloud lease release`: hand the write lease back. * * @param opts - Scope and overrides. * @returns Whether a lease was released. */ export declare function releaseNexusVaultLease(opts?: NexusVaultCommandOptions): Promise; //# sourceMappingURL=nexus-vault.d.ts.map