Relationships are first-class typed edges between entities, not hard-coded foreign keys. They are stored as observations and snapshots, so multiple sources can contribute to the same edge, every edge carries full provenance, and the graph stays open-ontology and queryable.
Sits next to entities. Sources produce relationship observations the same way they produce entity observations; the relationship reducer composes them into a relationship snapshot.
Schema#
Relationship pattern
Core relationship types#
The relationship-type vocabulary is a runtime registry, not a fixed list: call list_relationship_types to read what an instance accepts, and register_relationship_type to add to it. PART_OF (hierarchical containment, e.g. invoice_line_item PART_OF invoice) and EMBEDS (container embeds reusable asset, e.g. blog post EMBEDS image) are two of the built-ins.
Open ontology, not hard-coded hierarchies#
Neotoma does not hard-code parent-child columns or a fixed schema of edge types. Hierarchies emerge from edges, which means out-of-order ingestion, multiple parents, overlapping summaries, and corrections all compose without schema changes. New edge types can be introduced without migrating data.
Observation-snapshot pattern#
Each write to a relationship is an immutable observation that carries metadata and provenance. The reducer merges all observations for the same (source_entity, target_entity, type) into a single snapshot. The provenance map records, per metadata field, which observation produced which value, exactly the same shape as entity snapshots.
Queryable in both directions#
Relationships are first-class rows that can be queried by source entity, target entity, type, or metadata. Read paths support traversals (find all PART_OF children of an invoice) and reverse lookups (find every entity that REFERS_TO this contract). Same RLS rules apply, only relationships whose source/target you own are visible.
Invariants#
Every relationship satisfies the following constraints:
MUST
- Be stored as immutable observations + a deterministically computed snapshot
- Carry full provenance, every metadata field traces to the observation that set it
- Be typed (any type registered on the instance — read them via list_relationship_types)
- Respect source-ownership RLS on every read path
MUST NOT
- Be modeled as foreign keys on entity tables
- Be mutated in place, corrections and supersessions create new observations
- Be created without explicit source_entity_id, target_entity_id, and type
- Bypass the relationship reducer for ad-hoc snapshot writes
Related#
- Relationships subsystem doc , Types, graph patterns, query examples
- Observations , Underlying primitive each relationship observation builds on
- Entity merge , DUPLICATE_OF mechanics and merge handling
- Architectural decisions , Why relationships are first-class and the graph is open-ontology
Where to go next#
- All primitive record types , index of sources, interpretations, observations, relationships, and timeline events
- Architecture , how the primitives compose into Neotoma's deterministic state
- Terminology , canonical glossary of terms used across Neotoma docs