Waking the server…
It naps when nobody’s around. Give it a few seconds.
Waking the server…
It naps when nobody’s around. Give it a few seconds.
Case study
A local-first database you can branch like code. A Rust-powered database engine with durable snapshots, write-ahead logs, Git-like branching, and a purpose-built `.kern` schema language — designed for developers who want type-safe queries, isolated data branches, and crash-safe recovery. • Rust monorepo (8 crates) • `.kern` schema language with lexer, parser, and type checker • In-memory engine with WAL replay and snapshot persistence • Branch isolation with overlay writes and commit support • TypeScript client (`@kernoiq/client`) — zero dependencies, runs anywhere • V1: local-first database · V2: cloud hosting and multi-user · V3: AI layer with SMCP

KernoIQ is a database prototype built around a simple idea: data should be as branchable as code. It combines a small .kern schema language with a working in-memory engine that supports durable snapshots, write-ahead logs, branching, and table-scoped mutation APIs.
The project is structured as a Rust monorepo with 8 crates: lexer (kern-lexer), parser (kern-parser), types (kern-types), AST (kern-ast), core engine (kerno-core), CLI (kerno-cli), and services (kerno-services). A TypeScript client (@kernoiq/client) provides a zero-dependency API for projects, tables, rows, queries, branches, migrations, snapshots, and authentication.
It's designed for developers who work with databases regularly — people who've written migrations by hand, lost data to crashes, or wished they could branch their test database like a Git repo. KernoIQ gives you that branching capability natively, with type-safe queries and crash-safe recovery built in.
Databases are not branchable. You can't git checkout your test database, git merge schema changes, or git stash experimental data. Developers work around this limitation with manual migrations, separate databases for each environment, and fragile seed scripts. When a crash happens, data is lost. When you need to test a schema change, you restore from backup. When you want to branch your test data, you copy the entire database.
How might we make databases as branchable as code — with type-safe queries, isolated branches, and crash-safe recovery?
Next project
AniOrbit

KernoIQ is built as a Rust monorepo with 8 crates, each responsible for a specific layer:
kern-lexer) — Tokenization for the .kern schema language.kern-parser) — Minimal AST and parser for declarations and queries.kern-types) — Shared type definitions used across all crates.kern-ast) — Abstract syntax tree representation of .kern schemas.kerno-core) — Local database engine with persistence, branching, and queries. Supports durable row storage, WAL replay, snapshot persistence, branch isolation with overlay writes, and table-scoped CRUD APIs.kerno-cli) — Local command-line interface for interacting with the database.kerno-services) — Higher-level services built on top of the core engine.The TypeScript client (@kernoiq/client) provides a zero-dependency API for projects, tables, rows, queries, branches, migrations, snapshots, and authentication. It uses global fetch, runs in Node 18+, browsers, Deno, Bun, and edge runtimes, and is ESM with type declarations.
A small, TypeScript-inspired language for defining tables, enums, queries, migrations, seeds, tests, policies, and modules. Tables have named fields with types (UUID, String, Int, Boolean, Enum). Queries are typed — the compiler checks validity.
The engine persists data to disk as snapshots. On restart, it loads the snapshot and replays any WAL entries to restore state. Crash-safe recovery ensures no data is lost.
All writes go through a WAL before being applied to the snapshot. This ensures that if the process crashes, the next restart can replay the WAL to recover state.
Each branch has its own write layer on top of a shared read layer. Changes to one branch don't affect others until committed. Branches support overlay writes, commit, and rollback.
Insert, update, delete, and batched mutation operations are table-scoped. Queries support filters, ordering, limits, and first/count helpers. The API is fluent and type-safe.
Schema migrations are file-based (.kmig) with ordered execution. Migrations can add tables, modify fields, add indexes, and more. The engine validates migration order and rollback behavior.
Seed data is declarative (.kseed). Tests are defined in .ktest files and executed against the database. Seeds and tests can be run on any branch.
Zero-dependency client using global fetch. Provides createClient (for servers, scripts, CI) and createAuthClient (for browsers and apps). Supports projects, tables, rows, queries, branches, migrations, snapshots, and authentication.
Challenge
Overlay writes with shared read layers. Each branch has its own write layer (new rows, updated rows, deleted rows) on top of a shared read layer (the parent branch's data). When you query a branch, the engine merges the overlay with the base layer transparently. This means branches are cheap to create and don't duplicate data until writes occur. Commits merge the overlay into the parent, and rollbacks discard it.
Challenge
The engine writes all changes to the WAL before applying them to the in-memory store. On restart, it loads the last snapshot and replays the WAL entries in order. If a crash occurs mid-WAL-write, the replay stops at the last complete entry. This ensures that no partial writes are applied and all data is recovered. The WAL is appended-only (never modified), which simplifies crash recovery.
Challenge
The client uses only global fetch for HTTP requests and global TextEncoder/TextDecoder for encoding. It's ESM with type declarations, built from source (not published to npm yet). The build process outputs a single dist/ folder with the client code and types. The package is installed via npm pack and local file references (npm install <file>). This means it works anywhere fetch exists — no dependencies required.
Challenge
The lexer tokenizes .kern files into tokens (identifiers, keywords, operators). The parser builds an AST from the tokens. The type checker validates queries against the schema — checking that tables exist, fields exist, types match, and foreign keys are valid. The query planner generates execution plans for typed queries. This pipeline ensures that queries are type-safe before they reach the engine.
Challenge
A layered architecture where each layer depends only on the one below it. V1 provides the core engine (kerno-core), lexer/parser (kern-lexer, kern-parser), and CLI (kerno-cli). V2 adds cloud services (kerno-services) on top of the V1 foundation, using the same branching and migration code. V3 adds the AI layer (SMCP) on top of V2, using the same public API. The rule is simple: anything we build, others can build. The foundation is stable; everything else is built on top.
8 Rust crates
Lexer, parser, types, AST, core engine, CLI, services
.kern language
Tables, enums, queries, migrations, seeds, tests, policies, modules
Local-first database
Durable snapshots, WAL replay, branching and table-scoped CRUD
TypeScript client
Zero-dependency client (`@kernoiq/client`) for projects
fetch exists — Node, browsers, Deno, Bun, edge runtimes. It's more work to build (no external packages, no bundler features), but it pays off in flexibility.