No server, no socket, no network. The engine is a struct you call, a 481 KB WebAssembly module, or a no_std library on a chip with no operating system — and it is the same engine, with the same commands, in all three.
Every application that has to work offline ends up writing a storage layer. A desktop app gets SQLite and a schema nobody wanted. A web app gets localStorage, discovers the 5 MB ceiling and the fact that it is synchronous and string-only, and then gets IndexedDB and an abstraction over it. A device gets a hand-rolled ring buffer in flash.
All three are the same problem, and they can be the same solution. kevy embeds with no process boundary, ships to a browser with real TTLs and pub/sub, and boots on a Cortex-M with a fixed arena and no allocator at all. CI proves the last one on every push.
The store is a struct you call — no socket, no serialisation, no second process. Durable, and it replays its log on open.
# Cargo.toml
kevy-embedded = "4.0"let db = Db::open("data/")?;
db.set(b"session:7f3a", b"{\"user\":\"ada\"}", Some(Duration::from_secs(3600)))?;
assert_eq!(db.get(b"session:7f3a")?.is_some(), true);db.listen("127.0.0.1:6379")?;Other processes reach the same store over RESP, without changing any of the above.
Cost & limits An embedded store is not shared. One process owns the data directory; if a second process needs the data, that is what the listener above — or the full server — is for. The store can also take a RAM budget in-process (with_tier_budget): cold values spill to disk and page back inside your process — with the honest note that an in-process cold read holds the store's lock for the read's duration, which is why the largest spillable value is capped at 256 KiB by default. The tiering guide has the details.
481 KB gzipped. Persists to the browser's own filesystem, survives a reload, and speaks pub/sub across tabs.
import { open } from "@goliapkg/kevy";
const db = await open({ persist: { name: "app" } });db.set("cart:u881", JSON.stringify(items), { ttlMs: 86_400_000 });
db.get("cart:u881"); // still there after a reload
db.pttl("cart:u881"); // the engine expires it, not your codedb.subscribe("sync", (payload) => merge(payload));Cost & limits localStorage is faster for a small synchronous read — it is a map in the page's own address space, and nothing built on OPFS will beat it at that. kevy wins on everything that makes localStorage a bad idea anyway: real TTLs, no 5 MB ceiling, byte values rather than strings, and writes that do not block the main thread.
no_std, no allocator, no operating system: the store lives in a fixed arena you size yourself, and CI boots it on every push.
# Cargo.toml
kevy-store = { version = "4.0", default-features = false }let mut arena = [0u8; 64 * 1024];
let mut store = Store::new_in(&mut arena);
store.set(b"temp", b"21.4")?;Cost & limits The arena is fixed. There is no growing it at runtime — that is what "no allocator" means, and sizing it is your design decision, not the engine's. The feature tiers and what each one costs in bytes are in the IoT guide.