TIMETABLE & REGULATIONS

Documentation

Everything needed to run SpeedyDb in the browser: quick start, the full API surface, best practices, pricing, and licensing.

Quick start

SpeedyDb ships as a WebAssembly bundle (pkg/) plus two host modules: durable.js (IndexedDB persistence) and embedder.js (the on-device embedding model). Copy those four pieces into your app and serve them from the same origin — npm packaging is on the roadmap.

import initSpeedyDb from "./pkg/speedydb.js";
import { openDurable } from "./durable.js";
import { embedQuery, embedDocuments, EMBED_MODEL_ID, EMBED_DIM } from "./embedder.js";

// 1 · boot the engine, open (or restore) a persistent database
const mod = await import("./pkg/speedydb.js");
await mod.default();
const handle = await openDurable(mod, { dbName: "my-app" });
const db = handle.db;

// 2 · ingest — keyed sources are idempotent (same key ⇒ same rows back)
const rows = db.putSourceTextKeyed("doc:guide", ["chunk one", "chunk two"]);
handle.scheduleFlush();                      // write-behind to IndexedDB

// 3 · exact search, context expansion
db.findText("chunk", 5);
db.expandText(rows[0], 1n);                  // i64 params are BigInt

// 4 · semantic search (downloads the model once, ~229 MB, cached)
db.ensureEmbeddingModel(EMBED_MODEL_ID, EMBED_DIM);
db.putEmbeddings(Array.from(rows, Number),
                 await embedDocuments(["chunk one", "chunk two"]));
const hits = db.findSemantic(await embedQuery("what is this about?"), 5);

await handle.close();                        // final flush + lock release

Native (Rust) usage opens the same byte-identical .spdb files through the speedydb crate — a browser export imports natively and vice versa.

API reference

The complete JavaScript surface. i64 parameters are BigInt; hit objects carry row ids as plain Numbers.

Database lifecycle

endpointwhat it does
openDurable(mod, {dbName, segmentLen?, contentCapacity?})Open (or restore) a database persisted in IndexedDB. Returns {db, flush, scheduleFlush, close, reset}. Holds a Web Lock — a second tab fails cleanly (error has .speedydbLocked).
new WasmBrowserDb(backend, segmentLen, contentCapacity)Ephemeral in-memory database ("memory" or "segmented" backend) — resets on reload. Good for scratch work and tests.
db.reopen()Close (persisting state) and reopen over the same backing store. Consumes the handle; use the returned one.
db.flush()Flush pending engine writes (durability to IndexedDB is the host's flush — see Persistence).
handle.reset() / destroyDurable(dbName)Delete the persisted database entirely.

Ingest

endpointwhat it does
db.putSourceTextKeyed(key, chunks)Idempotent ingest under a stable key (e.g. a content hash). Returns the rows' ids — existing rows if the key is already active.
db.putSourceText(chunks)Unkeyed ingest; every call creates a new source.
db.deactivateSource(key) / db.deactivateRow(id)Soft delete — rows flip inactive, are excluded from search, and never physically removed.

Fetch & context

endpointwhat it does
db.getText(id) / db.getBytes(id)One row's payload (undefined if unknown).
db.isActive(id)true/false/undefined (unknown id).
db.expandText(id, n)The row plus up to n neighbours each side within its source, clamped at source edges.
db.rowCount() / db.sourceCount()Totals (row ids are dense, 1-based, never reused).

Search

endpointwhat it does
db.findText(query, limit)Case-insensitive substring scan over active rows (limit ≤ 50). O(rows) — a convenience scan, not an index.
db.findSemantic(queryVec, limit)Cosine top-k over active rows with stored vectors. Same hit shape as findText plus score.

Files & sync

endpointwhat it does
db.listFiles() / db.exportFile(key) / db.importFile(key, bytes)Byte-exact .spdb snapshot surface — exports open natively, native files import here. Call reopen() after imports.
db.isDurable() · db.takeDirty() · db.exportSegment(path, i) · db.exportLen(path)The write-behind persistence protocol (drained by durable.js; you only need these to build a custom persistence host).
db.linkFromRedeem(json, deviceId, at) · db.linkStatus() · db.unlinkDevice()Master-link handoff: persist/read/remove this database's link descriptor (no secrets stored).

Semantic search

SpeedyDb ships with the approved on-device embedding model — LiquidAI/LFM2.5-Embedding-350M (1024-dim, CLS pooling), running on the vendored llama.cpp WebAssembly runtime. Weights (official GGUF, ~229 MB) download on first use and are cached by the browser.

The prompts are part of the model. Queries must be embedded with the query:  prefix and passages with document:  — omitting them silently degrades retrieval. embedQuery() / embedDocuments() apply them for you; never embed raw strings another way.
endpointwhat it does
loadEmbedder(onProgress?)Load the model (idempotent; reports download progress).
embedQuery(text) / embedDocuments(texts, onEach?)L2-normalized vectors with the mandatory prefixes applied.
db.ensureEmbeddingModel(modelId, dim)Pin the embedding space. Returns false when created fresh or a different space was wiped — mixing spaces corrupts retrieval, so a mismatch always clears. Re-embed after false.
db.putEmbeddings(rowIds, flatF32)Store one vector per row (row-major Float32Array). Vectors persist and restore with the database.
db.rowsMissingEmbeddings(limit)Active rows without vectors — the backfill worklist.
db.embeddingModel()The stored space as {"model_id","dim"} JSON, or undefined.

Persistence model

The engine runs synchronously over in-memory segments; IndexedDB is the durability layer. On open, durable.js hydrates every persisted record; after mutations it drains the engine's dirty record keys and writes only those back in one transaction (write-behind). Everything — content rows, sidecars, semantic vectors — rides the same mechanism and survives reloads.

  • Durability is advisory: a flush scheduled but not committed when the page dies is lost; that ingest simply re-runs next visit.
  • Single tab: a Web Lock makes a second tab's open fail cleanly rather than corrupt via last-flush-wins.
  • Byte compatibility: exported files are byte-identical to native .spdb — a browser export opens on the server and vice versa.

Best practices

  • Key your sources. Use a content hash or stable document id with putSourceTextKeyed so re-ingesting is a no-op instead of a duplicate.
  • Chunk to fit. Keep each chunk within contentCapacity bytes (set at creation). Split on paragraph boundaries; group CSV lines under a repeated header.
  • Flush after writes. Call scheduleFlush() after mutations and flush() on pagehide; call close() when done.
  • One embedding space. Same model, dim, and ideally the same quantization everywhere vectors are compared. Treat ensureEmbeddingModel(...) === false as "re-embed now".
  • Backfill in batches. Drain rowsMissingEmbeddings in bounded batches so the UI stays responsive during large backfills.
  • Prefer findSemantic at scale. findText is an O(rows) scan for small sets and debugging; semantic search is the retrieval path.
  • Soft-delete, don't rewrite. Deactivate sources/rows; ids are never reused, so references stay valid.

Pricing

The in-browser demo on this site is free to try. SpeedyDb itself is commercially licensed software; production pricing (per-seat, embedded/OEM, and master-server tiers) is being finalized.

For evaluation licenses or pricing, contact [email protected].

Licensing

SpeedyDb is proprietary software — © 2026 Griffin Pilz, all rights reserved. It is not open source and not free software: no use, copying, modification, distribution, or reverse engineering is permitted without a separate signed written agreement, and the software is provided "as is" without warranty. The full text ships as LICENSE with the software.

Third-party components

componentlicense · role
wllama / llama.cppMIT — vendored WebAssembly inference runtime for the embedding model.
LiquidAI LFM2.5-Embedding-350MLFM Open License — model weights, fetched from the official repository at runtime (not redistributed here).
wasm-bindgen / js-sys / serdeMIT/Apache-2.0 — compiled into the WebAssembly bundle.