/** * Portable graph artifact — export/import codec (change: add-shareable-graph-artifact). * * The persisted graph index is a deterministic function of the committed source, so for a * given commit every machine computes the *same* index. Re-indexing it on every teammate's * machine and on every CI run is redundant work that scales with team size. This module * makes the index portable: it bundles the persisted `.openlore/analysis/` graph files * together with their integrity attestation (change: add-index-integrity-attestation) into a * single, compact, self-describing artifact, and validates that artifact on import so a * stale / schema-skewed / tampered bundle is never served as current. * * Trust model (validate-or-rebuild — see the CLI `import` command for the executed order): * 1. bundle/schema version — the bundled index schema must match this OpenLore's `SCHEMA_VERSION`. * 2. payload integrity — a SHA-256 over the canonical bundled bytes. Detects ANY corrupt / * hand-edited / line-merged bundle (a generated artifact is regenerate-don't-merge; a * hand-merge changes the bytes and is rejected here). * 3. graph-content digest — recomputed from the materialized store and compared to the * bundled attestation's `digest` (the spec's "content digest matches its attestation"). * Untrusted-artifact safety is enforced at parse: the decompressed size is bounded, every bundled * file name must be a plain basename (no path traversal), and the manifest's file list must match * the payload. Any failure degrades to a local rebuild; the bundle never leaves the consumer worse off * than having no artifact at all. * * Determinism: the artifact is a byte-stable function of the index it serializes (sorted file * order, no wall-clock fields, fixed gzip level) — exporting the same index twice is identical. * No new dependency (Node `zlib`/`crypto`), no network, no LLM. */ import { type IndexAttestation } from './index-attestation.js'; import { EdgeStore } from '../services/edge-store.js'; /** Artifact format version. Bump only on a shape change of the envelope below. */ export declare const BUNDLE_VERSION = 1; /** Default committed artifact path (outside `analysis/` so export never bundles itself). */ export declare const BUNDLE_DEFAULT_FILENAME = "index-bundle.olbundle"; /** Self-describing manifest carried with every bundle. No wall-clock field → deterministic. */ export interface BundleManifest { /** Envelope format version (BUNDLE_VERSION). */ bundleVersion: number; /** OpenLore version that produced the bundle (informational; not a trust gate). */ openloreVersion: string; /** EdgeStore SCHEMA_VERSION the bundled index was built at (== attestation.schemaVersion). */ schemaVersion: number; /** The source commit the index was built from, or null when it could not be determined. */ sourceCommit: string | null; /** The bundled integrity attestation — the trust stamp a consumer validates against. */ attestation: IndexAttestation; /** SHA-256 over the canonical bundled file bytes (tamper / corruption evidence). */ payloadDigest: string; /** Bundled files, sorted by name (the canonical order the digest is computed over). */ files: Array<{ name: string; bytes: number; }>; } /** The artifact envelope: manifest + base64-encoded file payload. */ export interface Bundle { manifest: BundleManifest; /** filename → base64 of the file's bytes. */ payload: Record; } /** A structured, recoverable bundle error with a stable code for the CLI to branch on. */ export declare class BundleError extends Error { readonly code: 'no-index' | 'unreadable'; constructor(code: 'no-index' | 'unreadable', message: string); } export interface BuildBundleResult { buffer: Buffer; manifest: BundleManifest; } /** * Serialize the persisted index under `analysisDir` (plus a fresh integrity attestation) into * a single gzipped, self-describing artifact. Byte-stable: the same index serializes * identically. The caller SHOULD checkpoint the store's WAL into the main db before calling so * the bundled `call-graph.db` is self-contained. */ export declare function buildBundle(analysisDir: string, openloreVersion: string): Promise; /** * A payload file name is safe to materialize iff it is a plain basename — no directory * separator, no `..`, not absolute, not `.`/empty. A legitimate bundle only ever contains * flat basenames (readdir of `.openlore/analysis/`); rejecting anything else closes a * path-traversal arbitrary-write on import of an untrusted/hand-crafted artifact * (mcp-security: Untrusted Artifact Deserialization Safety). `join(targetDir, name)` with a * name like `../../x` or `/etc/x` would otherwise escape the target directory. */ export declare function isSafeBundleFileName(name: string): boolean; /** * Decompress and structurally validate an artifact buffer. Throws `BundleError('unreadable')` * when the input is not an OpenLore bundle at all (bad gzip / JSON / shape) — a distinct * failure from an artifact that parses but fails trust validation (which degrades to rebuild). */ export declare function parseBundle(raw: Buffer): Bundle; /** Recompute the payload digest from a parsed bundle and compare to the manifest (tamper check). */ export declare function verifyPayloadIntegrity(bundle: Bundle): boolean; /** * Recompute the production-graph content digest from a (materialized) store, using the same * canonical projection the build-time attestation used (internal nodes, all edges, all * classes). Equality with the bundled attestation's `digest` proves the materialized graph * IS the one that was attested — the spec's "content digest matches its attestation". */ export declare function recomputeProductionDigest(store: EdgeStore): string; /** Materialize a parsed bundle's files into `targetDir` (created if needed). Overwrites by name. */ export declare function materializeBundle(bundle: Bundle, targetDir: string): Promise; /** * Copy the bundled files from a staging dir into the live analysis dir. Clears stale WAL sidecars, * the excluded vector-index metadata, and any rebuildable search-index subdirectory left over from * a PRIOR index (whose embeddings would now mismatch the imported graph) before promoting. */ export declare function promoteStagedIndex(bundle: Bundle, stagingDir: string, analysisDir: string): Promise; /** Best-effort directory removal (staging cleanup). */ export declare function removeDir(dir: string): Promise; /** True if `path` exists (file or dir). */ export declare function pathExists(path: string): Promise; //# sourceMappingURL=index-bundle.d.ts.map