A simple, crash-safe embedded key-value store
SNKV is a lightweight, ACID-compliant embedded key-value store written in C. It talks directly to SQLite's B-tree storage engine and skips the SQL layer: no parser, no query planner, no VM.
SQLite: Application → SQL Parser → Query Planner → VDBE → B-Tree → Disk
SNKV: Application → KV API → B-Tree → Disk
Looking for Python? The Python bindings are no longer maintained on
master. Use thepython_bindingsbranch.
Single-header integration:
#define SNKV_IMPLEMENTATION
#include "snkv.h"
int main(void) {
KVStore *db;
kvstore_open("mydb.db", &db, KVSTORE_JOURNAL_WAL);
kvstore_put(db, "key", 3, "value", 5);
void *val; int len;
kvstore_get(db, "key", 3, &val, &len);
printf("%.*s\n", len, (char*)val);
snkv_free(val);
kvstore_close(db);
}- API reference: hash-anu.github.io/snkv/api.html
- Examples: examples/ · hash-anu.github.io/snkv/examples.html
- ACID transactions and WAL mode (concurrent readers, single writer)
- Native TTL: per-key expiry, lazy eviction, bulk
kvstore_purge_expired(). No background thread. - Encryption: per-value XChaCha20-Poly1305 with Argon2id key derivation, transparent to all APIs
- Column families: logical namespaces within one database
- Iterators: forward and reverse, prefix, and seek
- Vector search: HNSW index via usearch (
make vector), stored in the same.dbfile. See examples/vector.c. - Thread-safe, single-header, Valgrind-clean
- Compatible with SQLite page/WAL-level tools such as LiteFS. Tools that need SQLite's schema layer (the
sqlite3CLI) do not work.
/* TTL */
kvstore_put_ttl(db, "session", 7, "tok123", 6, kvstore_now_ms() + 60000);
/* Encryption */
kvstore_open_encrypted("secure.db", "hunter2", 7, &db, NULL);
/* Custom configuration */
KVStoreConfig cfg = {0}; /* zero fields are NOT defaults: set journalMode */
cfg.journalMode = KVSTORE_JOURNAL_WAL;
cfg.syncLevel = KVSTORE_SYNC_FULL;
cfg.busyTimeout = 5000;
kvstore_open_v2("mydb.db", &db, &cfg);Linux / macOS
make # libsnkv.a
make snkv.h # single-header version
make examples # build examples
make test # run test suite
make vector # libsnkv_vec.a (core + usearch, requires g++)
make test-vector # run vector testsWindows: use the MSYS2 MinGW 64-bit shell (not cmd.exe or PowerShell):
pacman -S --needed mingw-w64-x86_64-gcc make
make && make test1M records on Linux. Both stores use WAL, synchronous=NORMAL, an 8 MB cache and 4 KB pages; SQLite uses a WITHOUT ROWID table.
Source: SNKV · SQLite
| Benchmark | SQLite | SNKV | Speedup |
|---|---|---|---|
| Sequential writes | 142K ops/s | 232K ops/s | 1.64x |
| Random reads | 90K ops/s | 160K ops/s | 1.77x |
| Sequential scan | 1.56M ops/s | 2.89M ops/s | 1.85x |
| Random updates | 16K ops/s | 31K ops/s | 1.9x |
| Random deletes | 16K ops/s | 31K ops/s | ~2x |
| Mixed workload | 34K ops/s | 62K ops/s | 1.79x |
Comparisons against LMDB and RocksDB are available too.
Good fit: read-heavy or mixed workloads, native TTL (sessions, caches, leases), embedded or memory-constrained targets, and simple C integration. Consider alternatives: RocksDB for maximum write throughput, LMDB for maximum read speed when memory is plentiful.
| Library | License | Use |
|---|---|---|
| SQLite | Public Domain | B-tree, pager, WAL, OS layer (bundled) |
| Monocypher | CC0-1.0 | XChaCha20-Poly1305 + Argon2id (bundled) |
| usearch | Apache 2.0 | HNSW vector index (optional, make vector) |
Apache License 2.0 © 2025 Hash Anu
