logo
  • Docs
  • API Reference
    Introduction
    What is Lix?
    Getting Started
    Comparison to Git
    Lix for AI Agents
    Essentials
    How Lix Works
    Querying Changes
    Data Model
    Plugins
    Persistence
    Guides
    Versions (Branching)
    History
    Diffs
    Attribution (Blame)
    Change Proposals
    Validation Rules
    Undo/Redo
    Restore
    Conversations
    Labels
    Key-Value Store
    Environment API
    Testing
    React Integration
    Logging & Debugging
    Deterministic Mode
    Metadata
    Writer Key
    Architecture
    Lix as File Format
    Previous pagePluginsNext pageVersions (Branching)

    #Persistence

    By default, openLix() runs in-memory. For production use, Lix is persisted as a single blob you can drop wherever you already store data: disk, OPFS, object storage, or a database column.

    Why this matters: you adopt Lix without redesigning storage. Keep Postgres, filesystem, browser storage, or object store as-is—persist the blob there and reopen the identical project anywhere the SDK runs.

    Patterns at a glance

    Where you store itHowNote
    Filesystem (Node)writeFile(Buffer.from(await blob.arrayBuffer()))Use .lix extension
    Object storage (S3/GCS)Upload the bytes as application/octet-streamSame blob downloads and reopens unchanged
    Database column (Postgres)Store in bytea; reopen with new Blob([bytes])Fits existing doc storage patterns
    Browser (OPFS)new OpfsSahEnvironment({ key })Persisted, non-blocking in the browser
    In-memory (tests, demos)Default in-memory instanceGreat for unit tests and ephemeral sessions

    #Save a Lix to disk (Node)

    Same blob, different home. Write it to disk and reopen later on any machine.

    import { writeFile, readFile } from "node:fs/promises";
    import { openLix } from "@lix-js/sdk";
    
    // ...work with your lix...
    const blob = await lix.toBlob();
    await writeFile("repo.lix", Buffer.from(await blob.arrayBuffer()));
    
    // Later: reload the same project
    const bytes = await readFile("repo.lix");
    const reloaded = await openLix({ blob: new Blob([bytes]) });

    #Persist to cloud object storage (e.g., S3)

    Upload the blob to object storage; download it later and reopen unchanged.

    lix.toBlob() returns plain bytes, so you can upload the blob to any object store and pull it back down later.

    import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
    import { openLix } from "@lix-js/sdk";
    
    const s3 = new S3Client({ region: "us-east-1" });
    const bucket = "my-lix-snapshots";
    const key = "repo.lix";
    
    // Upload
    const blob = await lix.toBlob();
    await s3.send(
      new PutObjectCommand({
        Bucket: bucket,
        Key: key,
        Body: Buffer.from(await blob.arrayBuffer()),
        ContentType: "application/octet-stream",
      })
    );
    
    // Download and reopen
    const obj = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
    const bytes = Buffer.from(await obj.Body!.transformToByteArray());
    const restored = await openLix({ blob: new Blob([bytes]) });

    Any HTTP upload/download flow works the same way—treat the .lix blob like any binary file.

    #Store in a database column (e.g., Postgres)

    If your app already stores documents (Markdown, TipTap JSON, etc.) in a column, persist the same .lix blob there for built-in change control.

    import { Pool } from "pg";
    import { openLix } from "@lix-js/sdk";
    
    const pool = new Pool({ connectionString: process.env.DATABASE_URL });
    
    // Save a Lix snapshot
    const blob = await lix.toBlob();
    await pool.query(
      "INSERT INTO documents (id, title, lix_blob) VALUES ($1, $2, $3)\n   ON CONFLICT (id) DO UPDATE SET lix_blob = EXCLUDED.lix_blob",
      [docId, title, Buffer.from(await blob.arrayBuffer())]
    );
    
    // Load and reopen later
    const { rows } = await pool.query(
      "SELECT lix_blob FROM documents WHERE id = $1",
      [docId]
    );
    const bytes: Buffer = rows[0].lix_blob;
    const restored = await openLix({ blob: new Blob([bytes]) });

    Use a binary column type (bytea in Postgres) and treat the .lix payload just like any other document payload. When you reopen with openLix({ blob }), you get the full Lix state back without changing your storage model.

    #Browser persistence with OPFS

    For persistent, non-blocking storage in the browser, run Lix in the OpfsSahEnvironment. This environment automatically persists every change to the browser's private file system (Origin Private File System).

    See the Environment API for how environments control where your Lix runs and persists.

    #Resume a session

    To load the existing project from storage (or create a new empty one if none exists), open Lix without a blob:

    import { openLix, OpfsSahEnvironment } from "@lix-js/sdk";
    
    // Opens "my-app" from OPFS, or creates it if missing.
    const lix = await openLix({
      environment: new OpfsSahEnvironment({ key: "my-app" }),
    });

    #Import or Restore

    To overwrite the persisted data with a snapshot (e.g., restoring a backup), pass the blob:

    // Initializes (or overwrites) "my-app" in OPFS with the provided blob
    const lix = await openLix({
      blob: incomingBlob,
      environment: new OpfsSahEnvironment({ key: "my-app" }),
    });

    #Export

    You can create a snapshot at any time without interrupting your session:

    // Snapshot to a .lix file for backup/export
    const blob = await lix.toBlob();
    const url = URL.createObjectURL(blob);
    // download as shown above

    #In-memory (tests, demos)

    The default Lix instance runs in memory—perfect for unit tests, demos, and ephemeral sessions. Export a blob when you want to persist or share the state.