IOBOXX
DOCS · ARCHITECTURE · MEMORY · OBJECT STORE

Object Store (OODB)

EAV-shaped knowledge graph. Schema is data. 4.4× smaller than the 2-table relational form.

The Object Store is the substrate underneath the Memory component. Fundis create, relate, and retrieve typed objects in real time over it. It is not a relational database, not a document store, and not a system of record. It is working memory shaped as a knowledge graph.

Logically, the model is Entity-Attribute-Value (EAV). Every fact is a (subject, predicate, object) triple — which is also a knowledge-graph edge and a retrievable RAG chunk. There is no document chunking step because there are no documents; there are nodes and attributes.

WHY EAVThe logical model

Three properties make EAV the only viable logical model for the knowledge graph.

1. Runtime schema evolution

When a Fundi invents a new object type, it is a data write — not a code deployment. Fixed-schema tables cannot accommodate types that did not exist at deploy time. EAV can, because the "schema" is just two more rows.

2. Sparse storage

The FDA medical-device taxonomy IOBOXX loaded for the CSP procurement workflow has 7,066 device types inheriting nine fields from a root MedicalDevice type. Most leaves populate three or four. A wide table allocates all nine columns per row; EAV stores only the attribute rows that have values.

3. Memory efficiency

The smartphone budget — roughly 33–62 MB total, of which about 6.2 MB is the knowledge graph at phone scale (~5K nodes) — forces sparseness. A wide row of 72 mostly-null columns costs ~1,472 bytes; an EAV attribute row costs ~195 bytes, and only when a value exists.

SCHEMA IS DATATypes are objects

__TypeDef and __FieldDef are not tables — they are object types in the same graph as everything else. A type is a node; its fields are child nodes related by a parent edge. Inheritance is a parent pointer. The no-code UI calls defineObjectType() and addFieldToType() — these are reducers, not DDL. There is no ALTER TABLE, no migration, no deployment.

Inheritance resolves CSS-style. A leaf type inherits every field declared on every ancestor, with the leaf able to override. The Unified Field Model means that when a field is declared once on the root, every descendant gets it for free — no schema ceremony per subtype.

PHYSICAL MODELThe migration

The logical model is stable. The physical model migrated. The original implementation expressed EAV as two SpacetimeDB tables — oodb_object + oodb_attribute — joined by integer FK. SpacetimeDB was designed as a deterministic game-engine compute layer; it did not understand what those two tables were doing. Every knowledge-graph-specific need had to be bolted on as another table or another client-side index.

Five bolt-ons accumulated: a derived index table written through on every attribute upsert; a natural-key registry of composite strings because there were no multi-column uniqueness constraints; a separate embedding table because vectors were not first-class; three relevance tables with hand-tuned decay constants because the engine has no learned scoring; and a client-side materialisation step that joined the two core tables in JavaScript on every subscription update.

ioboxx-core collapses all of that into one native typed Node struct. Attributes live inline in a sparse map. Edges are typed and first-class. The embedding sits inline on the node as a TurboQuant 3-bit compressed vector. Surprise and momentum scalars sit inline as well, replacing the three relevance tables. Indexes and uniqueness are native engine features, not write-through derived state. The query returns pre-materialised nodes — the client no longer joins anything.

AspectSpacetimeDB (then)ioboxx-core (now)
Entity + attributesTwo tables, FK joinOne struct, inline BTreeMap
EdgesInteger columns (parent_ref, ref_object_id)Typed edge list, first-class
Type hierarchyString field, resolved by client hookTypeId with native parent chain
IndexesDerived table, write-throughNative B-tree on attribute keys
UniquenessString registry tableNative constraint on (type, field, value)
VectorSeparate tableInline optional field, TurboQuant compressed
Relevance3 tables + hand-tuned constantsInline surprise + momentum, engine-native
MaterialisationClient-side JSServer-side, query returns whole nodes

MEMORY4.4× smaller at production scale

The collapse is not cosmetic. At production scale — 77,500 nodes, 294,000 attributes, one embedding per node — the SpacetimeDB physical model needed about 420 MB. ioboxx-core needs about 96 MB for the same logical content. The dominant savings come from inline TurboQuant embeddings and from eliminating the five derived tables.

ComponentSpacetimeDBioboxx-core
Objects16.5 MB (77.5K × 213 B)~60 MB (nodes incl. attrs, 77.5K × ~780 B)
Attributes57.3 MB (294K × 195 B)inline (counted in node row)
Indexes26.6 MB (117.6K × 226 B)~5 MB native overhead
Embeddings~318 MB (77.5K × ~4,100 B, f32)~30 MB (77.5K × ~384 B, TurboQuant 3-bit)
Total~420 MB~96 MB

At smartphone scale (5K nodes, before embeddings) the same arithmetic lands at ~6.2 MB for the new physical model versus ~15.5 MB for the old. The phone budget — discussed under tensor as substrate — is what made the migration mandatory.

SURFACEThe MCP contract did not move

Consumers of the Object Store — the observation surface, the no-code UI, every Fundi calling MCP tools — did not change. The MaterializedObject shape is byte-identical. The get_object MCP response is byte-identical. All 24 MCP tools have the same signatures. The __TypeDef / __FieldDef schema-as-data contract is identical. Only the engine internals — what produces those responses — changed.

This is the load-bearing property of the migration. The logical model was always right. The physical model was wrong for the workload, in a way the bolt-ons made progressively worse. Collapsing it into native nodes removes the bolt-ons without breaking anything above the engine line.

Source: ioboxx-platform/knowledge-graph/domains/architecture-memory-object-store-README.md
Last updated: 2026-05-16