topodb
Embedded, temporal, graph-native memory for AI agents — Python bindings for the TopoDB engine.
TopoDB is a single-file property graph (redb-backed) with scoped recall, text +
vector search, and bi-temporal edges. These bindings embed the engine in your
process: no server, no daemon, one .redb file on disk.
Install
pip install topodb
Prebuilt wheels (abi3, Python ≥ 3.9):
| Platform | Architecture |
|---|---|
| Linux (manylinux) | x86_64 |
| Linux (manylinux) | aarch64 |
| macOS | universal2 (x86_64 + arm64) |
| Windows | x64 |
Other platforms build from the sdist (requires a Rust toolchain via maturin).
Quickstart
import topodb
from topodb import ops
# Indexing is opt-in per (label, prop): declare equality lookups and what
# full-text search should cover. Plain TopoDB.open() indexes nothing (fine
# for pure graph workloads); TopoDB.open_stored() reopens a file with the
# spec it was created with.
spec = {
"equality": [{"label": "Entity", "prop": "name"}],
"text": [{"label": "Memory", "prop": "content"}],
}
with topodb.TopoDB.open_with("memory.redb", spec) as db:
r = db.submit([
ops.create_entity("ada"),
ops.create_memory("ada wrote the first program"),
ops.link("#1", "#0", "ABOUT"), # "#n" back-references the n-th op's new id
])
ada_id, memory_id, edge_id = r["ids"]
scopes = ["shared"] # reads always name the scopes they may see
hits = db.search_text(scopes, "first program", 5)
print(hits[0]["node"]["props"]["content"], hits[0]["score"])
sg = db.traverse(scopes, seeds=[memory_id], max_hops=2)
print([n["id"] for n in sg["nodes"]], len(sg["edges"]))
TopoDB.open(path) creates or opens a database with the default index spec;
TopoDB.open_with(path, spec) sets an explicit index spec (equality + text
indexes) on create; TopoDB.open_stored(path) reopens with whatever spec the
file already carries. The handle is a context manager — leaving the with
block closes it, and any later call raises ClosedError.
Writes: ops builders + submit
All mutation goes through db.submit(batch), one atomic batch of command
dicts. The topodb.ops module builds those dicts (the same wire shapes the
CLI and MCP server speak):
ops.create_entity(name, scope=None)
ops.create_memory(content, scope=None)
ops.create_node(label, props=None, scope=None)
ops.link(from_, to, type, props=None, scope=None, valid_from=None)
ops.set_node_props(id, props) # a None value inside props deletes that prop
ops.remove_node(id)
ops.close_edge(id, valid_to=None)
ops.set_embedding(id, model, vector)
Within a batch, "#0", "#1", … refer to the ids created by earlier ops in
the same batch. submit returns {"first_seq", "last_seq", "ids"} — one ULID
string per op. Pass now_ms= to pin the write timestamp (defaults to the wall
clock) and default_scope= to stamp the whole batch (see below).
The multi-scope read model
Every read takes a list of scopes as its first argument and only sees data
stamped with one of them. Every write is stamped with exactly one scope —
per-op via the builder's scope= parameter, or batch-wide via
db.submit(batch, default_scope=...). A scope is "shared" or a ULID string.
This asymmetry is the point: an agent can read across ["shared", project_scope]
while writing only into project_scope.
db.node(scopes, id) # dict or None
db.nodes_by_label(scopes, label)
db.nodes_by_label_newest(scopes, label, k)
db.nodes_by_prop(scopes, label, prop, value) # equality-indexed props only
db.nodes_by_prop_normalized(scopes, label, prop, value)
db.nodes_by_float_range(scopes, prop, lo, hi)
db.edges_from(scopes, id, type=None)
db.traverse(scopes, seeds=[...], max_hops=n,
edge_types=None, direction="both", as_of=None)
db.search_text(scopes, query, k)
db.search_vector(scopes, model, vector, k)
db.recall(scopes, query, k, vector=(model, vec), labels=None, now_ms=None)
db.suggest_links(scopes, id, k, model=None)
Nodes come back as plain dicts: {"id", "scope", "label", "props"}. Search
hits are {"node", "score"}.
Bi-temporal edges
Edges carry two independent time axes, both in the wire dict:
- World time —
valid_from/valid_to: when the fact was true in the world. Settable on write (ops.link(..., valid_from=...),ops.close_edge(id, valid_to=...));valid_toisNonewhile the edge is open.traverse(..., as_of=t)answers "what was true at t". - Belief time —
recorded_at/superseded_at: when the database learned and stopped believing the fact. Stamped by the engine, never settable;superseded_atisNonewhile the edge is current.
The two differ whenever a fact is recorded late or corrected after the fact. Full edge shape:
{"id", "scope", "type", "from", "to", "props",
"valid_from", "valid_to", "recorded_at", "superseded_at"}
Errors
All errors derive from topodb.TopoDBError, so one except catches
everything; subclasses carry the detail:
| Exception | Meaning | Extra attributes |
|---|---|---|
StorageError |
I/O or storage-layer failure | |
EncodingError |
corrupt or undecodable stored data | |
RejectedError |
invalid batch, arguments, or unindexed-prop query | |
CompactedError |
requested ops feed range was compacted away | oldest — first seq still available |
BusyError |
database file held by another process; retryable | |
ClosedError |
handle used after close() |
|
UnsupportedFormatError |
file format version mismatch | found, supported |
Change feed
db.subscribe(capacity) returns a Subscription that yields committed ops as
dicts. sub.next(timeout=...) returns the next event or None on timeout
(the GIL is released while waiting), and the object is also a plain iterator
that ends when the database closes. db.ops_since(seq) / db.current_seq() /
db.compact_ops(seq) cover catch-up and log compaction.
Embedded vs MCP
These bindings are the embedded client: in-process, zero-IPC, one Python
process owning the file. If you want TopoDB behind an agent framework instead
— shared across sessions, spoken over the Model Context Protocol — use the
topodb-mcp server. Both speak the same wire shapes and the same batch DSL;
see docs/agent-clients.md
for the trade-offs.
Versioning
0.1.0 is the first published release of these bindings; it wraps the frozen
0.1 engine API. See the repository CHANGELOG.md for history from here on.
Metadata
Release files for topodb 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| topodb-0.1.0.tar.gz | 2.3 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| topodb-0.1.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| topodb-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| topodb-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| topodb-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64, macOS 11.0+ ARM64, macOS 10.12+ universal2 (ARM64, x86-64) | Details |
Total release size: 9.6 MB
Release files / topodb-0.1.0.tar.gz
| Download URL | topodb-0.1.0.tar.gz |
|---|---|
| Size | 2.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d034bfd1c6d73b06c34678299dd31e0a64ba3d66c69c2fdb66540898a5a12f62
|
|
BLAKE2b-256 checksum How to use checksums |
ee03c3cc10d0658a0931fc72bfafab6643d48b7a4ddaeb30289f7b787ba2116a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logRelease files / topodb-0.1.0-cp39-abi3-win_amd64.whl
| Download URL | topodb-0.1.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
8c55f6e10397b99e2fb8b90ecb2fd0bd1f99e4645d7f1e15c6d0ccb74f6a698d
|
|
BLAKE2b-256 checksum How to use checksums |
5bc01b5760c1d59a8fec03d8881dbfb1f47566c23032da4acaf64c54b679c45c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logRelease files / topodb-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | topodb-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.6 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2f5d0533d37fdda8f959803f128e0d323e1ed057b6ec0f7d4e283642c9341de0
|
|
BLAKE2b-256 checksum How to use checksums |
cdee8ac1417ef28afbc4c9a32437e92e7113777cc64f4aadb831e6da42e99429
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logRelease files / topodb-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | topodb-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
6c5b5c9627d7158e29ee998ce71f4b5a37df29a8694461d3989763bc5299d7c6
|
|
BLAKE2b-256 checksum How to use checksums |
92c373ffb03d1b3d973756fbc1f7aa5c4fa02463365abe605c32e87a05ff6e4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency logRelease files / topodb-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | topodb-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 2.9 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
1f8293ea7b8cceb12904ee5bad8165e2eb0c448ad1db1d72f721fb2d559425cc
|
|
BLAKE2b-256 checksum How to use checksums |
6143fab41eadc964ce11a7ed4add70d431841b15a48fef4592efd87f05b97098
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 22, 2026.
Transparency log