The catalog
July 3, 2026 · View on GitHub
Part of the LibreDB guides. See also: key-value · document · relational.
Open a LibreDB file cold and the bytes alone don't say which lens each namespace belongs to. The
catalog records that as you write: a table registers { kind: "relational", schema } when its
handle is built, and a doc collection registers { kind: "document" } on its first write.
catalog(db) reads the whole registry — a Map from namespace name to its entry — so a tool can
render faithful per-kind views without guessing:
import { open, table, doc, catalog } from "@libredb/libredb";
const db = open();
table(db, "users", { primaryKey: "id", columns: { id: "string", age: "number" } });
doc(db, "logs").put("l1", { message: "hello" });
const registry = catalog(db);
registry.get("users");
// { kind: "relational", schema: { primaryKey: "id", columns: { id: "string", age: "number" } } }
registry.get("logs");
// { kind: "document" }
[...registry.keys()].sort();
// ["logs", "users"]
The catalog lives under a reserved key prefix that sorts below all user data, so its entries never
appear in a doc or table scan, and no kv scan over user keys crosses into them — and no user
row leaks into the registry. kv namespaces
are deliberately not cataloged: kv is the raw layer with full keyspace access.
A tool that renders the raw kv layer (so it sees everything, including the catalog) should hide
those engine-internal keys. Rather than hardcode the byte layout, import the contract:
isReservedKey(key) is true for any key in the reserved namespace, and RESERVED_MARKER /
CATALOG_PREFIX are the underlying constants if you need to build a range.
import { open, kv, isReservedKey } from "@libredb/libredb";
const db = open();
// ... user writes through doc/table, which also write catalog entries ...
// range is half-open [start, end). "" encodes to the lowest bytes, and
// "\u{10FFFF}" (the highest Unicode code point) encodes above any key that starts
// with a lower code point, so this interval covers every practical key. (A key
// beginning with U+10FFFF itself falls outside the half-open interval.) (kv.prefix
// cannot scan everything — it rejects an empty prefix.) This is the same
// full-keyspace pattern LibreDB Studio's provider uses.
const visible = kv(db)
.range("", "\u{10FFFF}")
.toArray()
.filter((e) => !isReservedKey(e.key));
// only user keys; catalog entries (under the reserved marker) are filtered out