/** * Subset of `DkgConfig['store']` used by the wipe step to talk to an * external SPARQL endpoint. Decoupled from the CLI's config types so * this module stays free of upward dependencies. */ export interface ChainResetWipeStoreConfig { backend: string; options?: { url?: string; queryEndpoint?: string; updateEndpoint?: string; auth?: string; /** * True when the namespace was provisioned by the CLI (PR 3 Docker * convenience path). Operator-provided URLs default to false; the * wipe then scopes deletes to the V10 named-graph prefix to avoid * clobbering V6/V8 data sharing the same Blazegraph instance. */ managedByDkg?: boolean; }; } export interface ChainResetWipeResult { /** True when a wipe was performed. */ wiped: boolean; /** * True when a wipe WOULD have run (marker mismatch) but was bypassed * because `skip` was set (`DKG_SKIP_CHAIN_RESET_WIPE=1`). Mutually * exclusive with `wiped`. The marker is deliberately NOT persisted on a * skip, so the wipe re-triggers once the env var is unset. */ skipped: boolean; /** The marker we had persisted before this boot, or null on first boot / no persisted state. */ prevMarker: string | null; /** Files removed during the wipe (relative to dataDir). Empty when `wiped=false`. */ removedFiles: string[]; /** * `store.nq` backup filenames created by renaming it instead of deleting it * (relative to dataDir, `store.nq.pre-wipe--`). Backup is * always-on; this is empty only when the wipe ran with no `store.nq` * present (or the rename failed — then the failure is in `failedFiles`). */ backedUpFiles: string[]; /** * Files we attempted to wipe but could not remove. When non-empty, the * marker is intentionally not persisted so the wipe retries on next boot. */ failedFiles: Array<{ file: string; error: string; }>; } export interface ChainResetWipeOptions { /** Node data directory (e.g. `~/.dkg`). */ dataDir: string; /** * Bundled network config's `chainResetMarker`. `undefined` means the * network has not opted into the auto-wipe protocol — the hook is then * a no-op (no state file written, no wipe). */ currentMarker: string | undefined; /** * Resolved runtime path of the random-sampling WAL. When the operator * sets `randomSampling.walPath` in their config, the prover writes to * that path instead of the default `dataDir/random-sampling.wal`. We * have to wipe whichever path is actually in use; the default-path * wipe alone would leave a stale WAL under operator-supplied paths. * Falsy → fall back to `dataDir/random-sampling.wal` (the default). */ randomSamplingWalPath?: string; /** * Operator's `config.store` block. Required to wipe an external SPARQL * endpoint when the backend is `blazegraph` / `sparql-http`. Local * backends ignore this field. */ storeConfig?: ChainResetWipeStoreConfig; /** * Override for the SPARQL HTTP transport. Tests inject a mock to * assert the issued UPDATE body; defaults to `globalThis.fetch`. * Kept on the options surface (rather than module-scope monkey-patch) * so parallel test cases don't race. */ fetch?: typeof globalThis.fetch; /** * Dev-loop opt-out. Sourced from `process.env.DKG_SKIP_CHAIN_RESET_WIPE === '1'` * in production (tests pass it explicitly). When `true` and a wipe WOULD run * (marker mismatch, including first-boot-with-marker), the wipe is bypassed * entirely AND the marker is NOT persisted — so unsetting the env var later * re-triggers the wipe on the next boot. This keeps the operator guarantee * intact (a real operator node with the flag unset still wipes by default) * while letting monorepo developers switch between marker-pinned worktrees * without losing their local `store.nq`. Mirrors the `acceptStoreReset` / * `acceptNetworkSwitch` env-opt-out pattern used by the sibling detectors. */ skip?: boolean; /** Optional logger. Defaults to no-op so the function is silent in tests by default. */ log?: (msg: string) => void; } /** * Read the dev-loop opt-out switch. Extracted (and exported) so the * documented user-facing env var `DKG_SKIP_CHAIN_RESET_WIPE=1` is unit * testable without mutating the real `process.env`. `'1'` (exactly) enables * the opt-out; anything else (unset, `'0'`, `'true'`) leaves the wipe on. */ export declare function skipChainResetWipe(env?: NodeJS.ProcessEnv): boolean; /** * Pure rotation policy: given every `store.nq.pre-wipe-*` entry (name + mtime), * return the names to EVICT so that only the newest `max` snapshots remain. * * `keepName` is the backup created THIS run: it is dropped from the candidate * set up front and can never be returned for eviction, so a non-monotonic wall * clock (NTP step-back, VM snapshot/restore) can never evict the fresh recovery * snapshot the wipe just made — that would silently defeat the recoverability * this backup exists for. It occupies one of the `max` slots, so we retain the * newest `max - 1` of the OTHER backups (ranked by mtime DESC; an unreadable * stat is passed in as mtimeMs 0 = oldest, so a broken file is evicted first) * and return the remainder. No filesystem, no logging — a plain-input seam * that is unit-testable in isolation. */ export declare function selectBackupsToRotate(entries: { name: string; mtimeMs: number; }[], keepName: string, max: number): string[]; export declare function chainResetWipe(opts: ChainResetWipeOptions): Promise; export interface BackendSwitchDetectOptions { dataDir: string; /** * Backend name from the current config. Pass the effective value * including the default — e.g. when `config.store?.backend` is * undefined, callers should pass `'oxigraph-worker'` so the check * is symmetric across "no store block" ↔ "explicit store block". */ currentBackend: string; /** * Operator opt-in to proceed despite a backend change. Sourced from * `process.env.DKG_ACCEPT_STORE_RESET === '1'` in production; tests * inject explicitly. */ acceptStoreReset: boolean; log?: (msg: string) => void; } export interface BackendSwitchDetectResult { /** True when `lastBackend` was recorded and differs from `currentBackend`. */ changed: boolean; /** Previously-recorded backend, or null if none / legacy state file. */ previous: string | null; /** Effective current backend (passed through for callers). */ current: string; /** * True when the daemon should abort boot. Set on `changed && !acceptStoreReset`. * Caller exits the process so the operator can either flip the env * var or revert their config edit. */ aborted: boolean; } export declare function detectBackendSwitch(opts: BackendSwitchDetectOptions): BackendSwitchDetectResult; export interface NetworkSwitchDetectOptions { dataDir: string; /** * The resolved network this boot is using — the `networkConfig` overlay * name including the fallback (pass `resolveNetworkConfigName(config)`, so * a legacy config with no `networkConfig` is compared as its fallback, * `testnet`). Symmetric across "no networkConfig" ↔ "explicit networkConfig". */ currentNetworkConfig: string; /** * Operator opt-in to proceed despite a network change. Sourced from * `process.env.DKG_ACCEPT_NETWORK_SWITCH === '1'` in production; tests * inject explicitly. */ acceptNetworkSwitch: boolean; log?: (msg: string) => void; } export interface NetworkSwitchDetectResult { /** True when `lastNetworkConfig` was recorded and differs from current. */ changed: boolean; /** Previously-recorded network, or null if none / legacy state file. */ previous: string | null; /** Effective current network (passed through for callers). */ current: string; /** True when the daemon should abort boot (`changed && !acceptNetworkSwitch`). */ aborted: boolean; } /** * Guard against an operator repointing `config.networkConfig` at a different * network on an existing data dir. Mirrors {@link detectBackendSwitch}: the * store still holds the previous network's chain-derived state (KC ids, * merkle roots, publish-journal, RS WAL), which is meaningless on the new * chain — and `chainResetWipe` won't save us (mainnet overlays ship no * `chainResetMarker`, so its hook is inert). So on a recorded mismatch we * abort boot unless `DKG_ACCEPT_NETWORK_SWITCH=1`. First boot / legacy state * silently records and proceeds. */ export declare function detectNetworkSwitch(opts: NetworkSwitchDetectOptions): NetworkSwitchDetectResult; //# sourceMappingURL=chain-reset-wipe.d.ts.map