Lightweight, local-first, replicable embedded database. The quiet foundation — services embed libsql directly, no separate process, no wrapper pattern.
How services embed libsql — no wrapper, no separate process.
Unlike other KiwiStack components, Database is not a standalone service. There is no LXC container, no wrapper binary, no upstream process. Instead, services that need a database — like Kiwi Work and Kiwi Docs — embed libsql directly as a Rust dependency.
libsql is a fork of SQLite that adds native replication, multi-writer support, and a Rust-native API. Each service gets its own database file, fully isolated. Data stays local to the container unless replication is configured.
use libsql::{Builder, Database}; async fn init_db() -> Database { Builder::new_local("/var/lib/kiwi/data.db") .build() .await .expect("failed to open database") } async fn query_tasks(db: &Database) { let conn = db.connect().unwrap(); let rows = conn .query("SELECT id, title FROM tasks WHERE done = ?", [false]) .await .unwrap(); while let Some(row) = rows.next().await.unwrap() { let id: i64 = row.get(0).unwrap(); let title: String = row.get(1).unwrap(); tracing::info!(id, %title, "pending task"); } }
Why libsql over plain SQLite.
tokio support. No FFI wrappers needed for basic operations.MIT — the most permissive option.
libsql is licensed under the MIT License — fully permissive with no copyleft obligations of any kind. You can embed, modify, and redistribute it freely in both open-source and commercial products.
This is the simplest licensing story in KiwiStack: MIT upstream, Apache-2.0 KiwiStack code, no boundaries to manage. Since libsql is compiled directly into each service binary, there is no network boundary or process isolation to consider.