Version control system for every file format
Lix tracks, reviews, branches, merges, and rolls back changes across Markdown, DOCX, XLSX, JSON, PDFs, and custom file formats.
Use Lix standalone or as the change-control backend for editors, knowledge bases, AI workflows, and file-based products. Lix stores files, tracks semantic changes, exposes history through SQL, and brings version control workflows beyond source code.
- ๐ Supports any file format. Create drafts, branches, checkpoints, and releases for Markdown, DOCX, XLSX, JSON, PDFs, and custom formats.
- ๐ Track semantic changes. See the paragraph, cell, property, clause, or custom entity that changed.
- ๐ Review and merge changes. Build change proposals, accept/reject flows, rollback, and merge workflows around files.
- ๐ค Sync in real time. Keep versions and changes in sync across users, agents, devices, and runtimes.
- โ Validate and automate. Run checks, enforce rules, and trigger workflows when files change.
- ๐ง Query everything with SQL. Ask what changed, where, when, and by whom without rereading whole files.
Try a demo app
Flashtype is a Markdown editor for Claude and Codex built on Lix. Open local Markdown files, let agents edit them, review changes as diffs, and restore previous versions from history.
Getting started
JavaScript ยท
Python ยท
Rust ยท
Go
npm install @lix-js/sdk
import { openLix, SqliteBackend } from "@lix-js/sdk";
const lix = await openLix({
backend: new SqliteBackend({ path: "app.lix" }),
});
await lix.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/notes/status.txt", new TextEncoder().encode("draft")],
);
const main = await lix.activeBranchId();
const draft = await lix.createBranch({ name: "Explore" });
await lix.switchBranch({ branchId: draft.id });
await lix.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/notes/status.txt", new TextEncoder().encode("ready for review")],
);
await lix.switchBranch({ branchId: main });
const changes = await lix.execute(
"SELECT schema_key, count(*) AS count FROM lix_change GROUP BY schema_key",
);
Why Lix?
Version control should not stop at source code.
Most version control systems assume source code and text diffs. But many important products edit files where the useful change is a paragraph, spreadsheet cell, JSON property, PDF section, knowledge-base page, or custom entity.
Lix is built for those files. Plugins translate file updates into semantic changes that can be queried, reviewed, branched, merged, and rolled back.
Flashtype, a Markdown editor for Claude and Codex, uses Lix so every local Markdown edit can be checkpointed, reviewed as a diff, and restored.
How does Lix compare to Git? โ
What Lix provides
Import as a library
Import Lix and open it inside your worker, service, CLI, browser, desktop app, or server-side runtime. No daemon, no protocol.
import { openLix, SqliteBackend } from "@lix-js/sdk";
const lix = await openLix({
backend: new SqliteBackend({ path: "app.lix" }),
});
ACID transactions
Write files, blobs, and history in one transaction.
const tx = await lix.beginTransaction();
try {
await tx.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/spec.docx", body],
);
await tx.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/spec.png", image],
);
await tx.commit();
} catch (error) {
await tx.rollback();
throw error;
}
Parallel branches
Give every draft, user, tool, or AI agent its own isolated branch.
const main = await lix.activeBranchId();
const copy = await lix.createBranch({ name: "Copy draft" });
const pricing = await lix.createBranch({ name: "Pricing draft" });
const qa = await lix.createBranch({ name: "QA draft" });
await lix.switchBranch({ branchId: copy.id });
await lix.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/landing.md", copyDraft],
);
await lix.switchBranch({ branchId: pricing.id });
await lix.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/plans.json", priceModel],
);
await lix.switchBranch({ branchId: qa.id });
await lix.execute(
"INSERT INTO lix_file (path, data) VALUES ($1, $2) ON CONFLICT (path) DO UPDATE SET data = excluded.data",
["/checks/report.json", testRun],
);
await lix.switchBranch({ branchId: main });
Semantic changes
Lix can track structured entities: XLSX rows, DOCX clauses, JSON properties, app records, custom entities, and more.
const changes = await lix.execute(`
SELECT created_at, schema_key, entity_pk, snapshot_content
FROM lix_change
ORDER BY created_at DESC
LIMIT 20
`);
For example, a workflow edits an orders spreadsheet:
Before:
| order_id | product | status |
| -------- | -------- | ------- |
| 1001 | Widget A | shipped |
| 1002 | Widget B | pending |
After:
| order_id | product | status |
| -------- | -------- | ------- |
| 1001 | Widget A | shipped |
| 1002 | Widget B | shipped |
A text-based diff can only tell you the file changed:
-Binary files differ
Lix can expose the row field that changed:
order_id 1002 status:
- pending
+ shipped
Read more about semantic changes โ
SQL interface
Answer version-control questions with SQL instead of whole-file rereads.
const rows = await lix.execute(`
SELECT created_at, schema_key, entity_pk, snapshot_content
FROM lix_change
ORDER BY created_at DESC
LIMIT 20
`);
Every change, across every file and every branch, is a row in lix_change. Filter by branch, file, schema, or time without re-reading whole files.
Portable runtimes and storage
Use Lix standalone or plug it into the infrastructure your product already runs.
SQLite ยท
Postgres ยท
S3 ยท
Cloudflare Workers ยท
Supabase
const lix = await openLix({
backend: new SqliteBackend({ path: "app.lix" }),
});
How Lix works
Lix runs in-process inside your runtime.
It owns the version-control model: files, blobs, versions, history, transactions, and semantic changes. Use it standalone or plug it into whatever backend you need: in-memory, SQLite, Postgres, S3, Cloudflare, or your own adapter.
SQL is the query interface on top. Products, scripts, and agents can ask what changed without rereading whole files.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your runtime โ
โ browser ยท desktop ยท server ยท CLI ยท worker โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Lix โ โ
โ โ Filesystem ยท Versions ยท History ยท SQL โ โ
โ โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Backend โ
โ SQLite, Postgres, S3, Cloudflare, custom โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Read more about Lix architecture โ
Learn More
- Getting Started Guide - Build your first app with Lix
- Documentation - Full API reference and guides
- Discord - Get help and join the community
- GitHub - Report issues and contribute