# Files and tables share one ACID repository, permissions are still marked planned, and the CLI cannot build from the workspace root

> Lix is an embeddable Rust library that versions files and SQL rows in a single repository with pluggable storage. It is labelled alpha at version 0.18.1, ships JavaScript and Rust SDKs while the Python and Go ones are still open issues, and its own Cargo workspace excludes the CLI and the end to end tests.

**opral/lix** — Embeddable repository that combines files, database, and version control.

- Repository: https://github.com/opral/lix
- Website: https://lix.dev
- Stars: 762 · Forks: 26
- Language: Rust
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/opral-lix

## One ACID database holds the files, the rows and the history

The central claim is that files, tables and history live in one ACID OLTP database rather than in a repository of bytes plus a database beside it. With `FilesystemStorage` the file stays available on disk while its rows are queryable with SQL, and Lix tracks changes to both. Plugins are what make that work per format: they map parts of a file to SQL rows, so a paragraph, a cell or a property becomes a row Lix can version, while a format with no plugin gets whole-file history instead. Markdown and CSV are named as formats with structured diffs and merges, and Word, PowerPoint, CAD and video files are named as things the system will hold. The insert path is plain SQL with positional parameters, `INSERT INTO lix_file (path, content) VALUES ($1, $2)`, and the content is bytes, so the same statement writes a text note, a script or a binary document. Three such writes plus a row update go out as one batch, described as a script, a document and an app table change in one transaction:

```ts
// A script, a document, and an app table change in one transaction.
await lix.executeBatch([
  {
    sql: "INSERT INTO lix_file (path, content) VALUES ($1, $2)",
    params: ["/automations/report.js", source],
  },
  {
    sql: "INSERT INTO lix_file (path, content) VALUES ($1, $2)",
    params: ["/docs/handbook.docx", handbook],
  },
  {
    sql: "UPDATE orders SET status = 'shipped' WHERE id = $1",
    params: [1002],
  },
]);
```

The README makes a performance claim of querying millions of rows with SQL, and that claim is not measured anywhere in the repository.

## History records an author today, authorization is marked planned

The feature list carries one entry that is not a feature yet: permissions, planned, per file and per group, stored in the repository and versioned like any other change. Everything else on that list is present tense. That gap is visible in the SQL. The history query the project shows for a sidebar, a diff view and an undo button selects `created_at`, `account_id`, `schema_key`, `row_pk` and `snapshot_content` from `lix_change`, ordered newest first. So every recorded change already carries an account, and the argument is that history, blame, branching and rollback become queries instead of features you build. The uncomfortable part is the order of the two. Authorship is stored and versioned; the rules about who may read or write are the thing still on the roadmap. A schema you register is versioned the same way, which means an application table and a document can be branched, reviewed and rolled back together, including in a single transaction.

## Alpha, with two SDKs to install and two that are issue links

The one line that governs expectations sits in the middle of the getting started section: Lix is in alpha. Underneath it, the installable surface is one npm command:

```bash
npm install @lix-js/sdk @lix-js/storage-filesystem
```

and the Rust crate, whose API reference lives on docs.rs. The other two languages on that row are not packages. Python is a link to issue 373 titled The Python SDK is planned, with an invitation to upvote, and Go is issue 370 with the same wording. So the repository's primary language is Rust, the workspace contains a js-sdk package, and a Python or Go integration today means building against the HTTP server yourself or waiting. Version numbers tell a similar story: the workspace sits at 0.18.1, v0.18.0 and v0.18.1 both shipped on 2026-09-25 about two and a half hours apart, and a plugin package moved under its own tag the day before at v0.1.0.

## Tracked writes commit on their own, which is the whole difference from Git

The comparison table in the README is short and it is where the pitch lives. Git is CLI-first, Lix is a library running inside your process. Git stores on local disk, Lix stores in memory, on a filesystem, in browser OPFS or on S3. In Git an application's tables need a separate database, in Lix they are versioned SQL rows next to the files. Git records changes through manual commits, Lix commits tracked writes automatically. Git handles any bytes with text diffs, Lix handles any bytes with structured diffs coming from plugins. Git collaboration is push and pull, Lix collaboration is real time. Underneath that comparison sits the specific complaint: committing a SQLite database to Git versions its bytes rather than its rows, which makes changes hard to review or merge and makes every database revision grow the repository. The history the project points to is the inlang case, which used Git for translation files, moved to SQLite as its structured messages grew, and then moved to Lix so application data could have the same pull request workflow.

## The workspace root cannot build the CLI or the end to end tests

The Cargo manifest excludes two packages from the workspace, and the comment above the exclusion explains why. `packages/cli` and `packages/e2e` use Cargo artifact dependencies to build Wasm plugins, and keeping them outside the SDK workspace lets stable Cargo load `packages/lix` as a local path dependency without having to parse nightly-only manifests. Continuous integration reaches them through `tooling/Cargo.toml` instead. The practical consequence is that `cargo build` at the workspace root builds a library, a server, a schema package, four storage crates and every plugin, and does not build the command line tool or the end to end tests. The workspace declares `resolver = "3"`, `edition = "2024"` and `rust-version = "1.94"`, and it holds two collaboration oriented members you would not guess from the name, `packages/collaboration-test-support` and `packages/server`, alongside the js-sdk.

## Every internal crate is pinned to one version, plugins carry their own tag line

All workspace dependencies are pinned with an exact requirement: `lix`, `lix_schema`, `lix_cli` and the three storage crates each carry `version = "=0.18.1"` next to their path. Internal crates therefore move in lockstep, and there is no way to depend on a newer schema package without taking the newer core. Storage is where the workspace and the library story diverge. The workspace builds `packages/storage-filesystem`, `packages/storage-filesystem-native`, `packages/storage-rocksdb` and `packages/storage-slatedb`, four crates, while the library advertises memory, filesystem, browser OPFS and S3. RocksDB and SlateDB are therefore in the build graph while memory, OPFS and S3 are named as options without a matching crate in that list. Plugins are pulled in by the wildcard member `plugins/*`, and a plugin package is released under its own prefixed tag, `plugin_markdown/v0.1.0`, a version line unrelated to the 0.18 series it depends on.

## The server is addressed by a URL, and the hosted option is a different product

There are two ways to open a repository, and the second one is the one you would reach for first if you do not want to run storage. Locally you construct `openLix` with `new FilesystemStorage({ path: "./repository" })`. Against a server you pass a `server` object with a `url`, and the example shape is a path segment that looks like a UUID under a host you control, `https://example.com/lix/01936f4e-7b6c-7c3d-8f9a-123456789abc`. The per-customer pattern repeats that with `customer.repositoryId` interpolated, one hosted repository per customer. Nothing in the interface tells you what runs at the other end of that URL, so the server side is a deployment decision the SDK does not make for you. For trying the idea without writing code there is LixRay, presented as Lix in the cloud at lixray.com, which is a separate service from the library and from this repository.

## Two releases on one day, and a repository full of its own working notes

The root of the repository reads like a working project rather than a published library. Alongside the usual directories there is `.changenotes/`, `HANDOFF-convergence.md`, `observations.md`, an `rfcs/` folder and a `profile-artifacts/` folder, so design arguments and change notes are versioned next to the code. Tooling configuration is committed too: `.cargo/`, `.config/`, `.codex/`, `.vscode/`, `.dockerignore`, and a `.infisical.json` file whose contents are not visible from the tree listing, so what it points at cannot be judged from here. Documentation is split across `docs/`, `website/`, `blog/` and `scripts/`, and the README points at lix.dev pages for JavaScript and Rust quickstarts, for diffs, for persistence and for the getting started guide. The README itself stops inside its License section, where the link text ends mid-word at `./LICENS`, while a LICENSE file is present in the tree.

## Conclusion

Read Lix as a design reference if your product needs version history over application rows as well as documents, because the query shape in lix_change is the part worth copying regardless of which storage you pick. Do not plan around permissions: the feature list marks them planned, so treat any Lix deployment as single tenant until that ships. Verify three things before you commit: that your language has an SDK, since JavaScript and Rust are the two that exist and Python and Go are issue links; that the crate you depend on builds on stable Rust, given that the workspace root deliberately excludes the CLI; and that the storage backend you want is in the workspace, where filesystem, filesystem-native, RocksDB and SlateDB are built and memory, OPFS and S3 are named as options. The latest release is v0.18.1 from 2026-09-25.

## FAQ

### How is Lix different from Git for application data?

Git versions the bytes of a SQLite file rather than its rows, which makes changes hard to review or merge. Lix keeps files and versioned SQL rows in one ACID database, and tracked writes commit without a manual commit step.

### Can I use Lix from Python or Go today?

Not through an SDK. The installable packages are the JavaScript SDK and the Rust crate; Python is tracked as issue 373 and Go as issue 370, both titled as planned SDKs with an invitation to upvote.

### Does Lix support permissions or access control?

Not yet. The feature list marks permissions as planned, per file and per group, stored in the repository and versioned like any other change. What exists today is authorship, since history rows in lix_change carry an account_id.

### Which storage backends can Lix use?

The library names memory, filesystem, browser OPFS and S3. The Cargo workspace builds four storage crates, filesystem, filesystem-native, rocksdb and slatedb.

### Is Lix ready to depend on in production?

The getting started section says Lix is in alpha. The workspace version is 0.18.1, v0.18.1 shipped on 2026-09-25 shortly after v0.18.0 the same day, and the last push to main is dated 2026-10-02.

### What happens to a file format Lix has no plugin for?

It gets whole-file history. Plugins provide structured diffs and merges for supported formats, with Markdown and CSV named as examples, so anything without a plugin is versioned as an opaque blob.

## Sources

- [License: MIT](https://github.com/opral/lix/blob/main/LICENSE)
- [opral/lix on GitHub](https://github.com/opral/lix)
- [Project website](https://lix.dev)
- [README](https://github.com/opral/lix/blob/main/README.md)
- [Releases](https://github.com/opral/lix/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/opral-lix
