Data Model¶
Every memory in uniko is a node or an edge in a single property graph. There is no separate document store, vector store, or relational table sitting beside it — the graph is the memory. When the system asks "what is true now?", "where did this come from?", or "what happened in this session?", it answers by traversing edges, not by joining across systems.
This page walks the node types, the edges that connect them, and the design rule that holds
the whole thing together. The names here are the real ones, registered by uniko-store when a
KnowledgeBase opens; the complete property-and-index catalogue lives in
Reference > Schema.
The shape of the graph¶
uniko's schema registers 24 node types and 53 edge types with the underlying uni-db graph. They are organised into layers that map onto a cognitive-memory model — episodic (what happened), semantic (what we know), procedural (what works), and the meta-memory that tracks how knowledge was derived.
The single most important design rule is this:
Knowledge is derived, never directly stored
Everything traces back to communication and action. A Fact does not appear from
nowhere — it is consolidated from Observations, each Observation is grounded in a
Message or a Chunk, and every Message was sent by a Participant. Provenance is
not a feature bolted on afterwards; it is the spine of the schema.
That spine starts at the Message.
Message anchors everything¶
A Message is the atomic unit of communication — one thing someone said. It is the node
that the rest of the episodic graph hangs off. Its registered edges are deliberately few
and load-bearing:
// crates/uniko-store/src/schema/messages.rs — edge registration
builder
.edge_type(edges::SENT_BY, &[labels::MESSAGE], &[labels::PARTICIPANT])
.property_nullable("role", DataType::String) // "user" | "assistant" | "system" | "tool"
.done()
.edge_type(edges::ADDRESSED_TO, &[labels::MESSAGE], &[labels::PARTICIPANT])
.done()
// IN_SESSION is multi-source: Message, Action and Episode all live in a Session
.edge_type(edges::IN_SESSION,
&[labels::MESSAGE, labels::ACTION, labels::EPISODE],
&[labels::SESSION])
.done()
.edge_type(edges::NEXT, &[labels::MESSAGE], &[labels::MESSAGE])
.property_nullable("gap_ms", DataType::Int64) // wall-clock gap to the next turn
.done()
From a single Message you can reach the Participant who sent it (SENT_BY), the
Session it belongs to (IN_SESSION), the chronological next turn (NEXT), and — once
extraction has run — every Entity it MENTIONS. Those four anchors are enough to
reconstruct a conversation, attribute statements to people, and walk time forward and back.
Participants are uniform
Humans, agents, and services are all the same Participant node, distinguished only by
a kind field. A query for "what did Caroline say" and "what did the assistant do" walk
the exact same edges.
From messages to knowledge¶
Extraction and consolidation lift raw turns into reusable knowledge through three node types, connected by provenance edges:
Observation— a direct claim grounded in a source. "Caroline attended an LGBTQ support group." It carriesOBSERVED_INedges to theMessageorChunkit came from,OBSERVED_DURINGto theEpisode, andABOUTto theEntity(orParticipant) it concerns.Fact— consolidated from multiple observations over time. "Caroline is pursuing adoption." AFactlinks back to its evidence viaSUPPORTED_BY(toObservations) andDERIVED_FROM(to theEpisodes whose observations provided the evidence).Entity— a named thing (person, place, org, concept, tool…) that messages, chunks, actions, artifacts, and episodes allMENTIONS.
A Fact is not eternal. When new evidence contradicts an old belief, consolidation does not
delete the stale fact — it records the supersession explicitly:
// crates/uniko-store/src/schema/facts.rs — the INVALIDATES edge
.edge_type(edges::INVALIDATES, &[labels::FACT], &[labels::FACT])
.property_nullable("reason", DataType::String)
.property_nullable("invalidated_at", DataType::DateTime)
.done()
The invalidated Fact keeps its history; its temporal validity simply closes. Validity
itself is stored on the Fact as a single valid_at property of type Btic — a half-open
interval [lo, hi) with per-bound certainty and granularity — so a question like "what was
true on March 15?" is answered by an interval-containment check rather than juggling separate
valid_from/valid_until columns. See Facts & Drift for
how BTIC drives bitemporal recall.
Multi-source edges are a feature: one relationship, many sources
Several edge types deliberately connect many source labels to a target, so a single
relationship type works everywhere it makes sense. MENTIONS runs from Message,
Chunk, Action, Artifact, and Episode to Entity — one edge for "references a
named thing," wherever that reference appears. DERIVED_FROM runs from Fact,
Procedure, and Artifact to Episode, Action, or another Artifact. When you write
a Cypher pattern, match on the endpoint node, not just the edge type, so you land on
exactly the label you intend.
A representative slice¶
graph LR
P[Participant]
S[Session]
M[Message]
E[Episode]
En[Entity]
O[Observation]
F[Fact]
R[Rule]
M -->|SENT_BY| P
M -->|IN_SESSION| S
M -->|NEXT| M
M -->|MENTIONS| En
P -->|PARTICIPATED_IN| S
E -->|IN_SESSION| S
E -->|MENTIONS| En
O -->|OBSERVED_IN| M
O -->|OBSERVED_DURING| E
O -->|ABOUT| En
F -->|SUPPORTED_BY| O
F -->|DERIVED_FROM| E
F -->|ABOUT| En
F -->|DERIVED_BY| R
F -->|INVALIDATES| F
The diagram shows the load-bearing core. A Message (sent by a Participant, in a
Session) mentions Entitys; an Observation grounded in that message is consolidated into
a Fact, which knows the Rule that derived it and the Fact it replaced.
Node types¶
A representative subset of the 24 registered node types. Each is registered by a module under
crates/uniko-store/src/schema/.
| Node | Layer | What it represents | Key properties |
|---|---|---|---|
Participant |
Participants | A human, agent, or service | participant_id, kind, name |
Goal |
Goals/Tasks/Sessions | A long-running objective | goal_id, title, status |
Task |
Goals/Tasks/Sessions | A unit of work toward a goal | task_id, title, status, priority |
Session |
Goals/Tasks/Sessions | A bounded interaction | session_id, topic, started_at, ended_at |
Message |
Episodic | Atomic unit of communication | message_id, content, timestamp |
Action |
Episodic | Something a participant did beyond talking | action_id, action_type, status |
Episode |
Episodic | A structured learning experience | episode_id, action_type, outcome, importance |
Artifact |
Artifacts | A file, document, URL, or other object | artifact_id, kind, path |
ArtifactContent |
Artifacts | Content-addressed blob metadata, deduped by hash | content_id, mime, size |
Chunk |
Artifacts | A retrievable slice of an artifact or long message | chunk_id, text, index, chunk_type |
Entity |
Semantic | A named thing mentioned anywhere | entity_id, name, entity_type |
Observation |
Semantic | A direct claim grounded in a source | observation_id, content, subject, predicate |
Fact |
Semantic | Knowledge consolidated from observations | fact_id, subject, predicate, object, valid_at |
Topic |
Semantic | An aggregated knowledge cluster | topic_id, name, summary |
Summary |
Semantic | A generated summary at some level | summary_id, text, level |
Procedure |
Procedural | A proven action sequence | procedure_id, name, effectiveness, status |
Rule |
Procedural | A Locy rule for formal reasoning | rule_id, name, source, confidence |
ConsolidationCycle |
Meta | A record of one consolidation run | cycle_id, agent_id, started_at |
Organization / Team |
Organization | Participant grouping | org_id / team_id, name |
KnowledgeBaseStats |
KB metadata | Singleton KB-level metadata (modality presence, blob backend) | stats_id, modality_presence |
Four more nodes round out the 24
Beyond the table, the schema also registers DeadLetter (failed-pipeline-task tracking),
KnowledgeBaseStats, Page (a single PDF page in the document-IR model), and Block
(an atomic content block within a Page). The full
property and index list for every node type is in the
Schema reference.
Edge types¶
A representative subset of the 53 registered edge types. The full list, with source/target labels and edge properties, is in the Schema reference.
| Edge | From → To | Meaning |
|---|---|---|
SENT_BY |
Message → Participant |
Who said it (carries role) |
ADDRESSED_TO |
Message → Participant |
Who it was directed at |
IN_SESSION |
Message, Action, Episode → Session |
Which session it belongs to |
NEXT |
Message → Message |
Chronological next turn (carries gap_ms) |
PARTICIPATED_IN |
Participant → Session |
Who was in the session |
MENTIONS |
Message, Chunk, Action, Artifact, Episode → Entity |
References a named entity |
HAS_CHUNK |
Artifact, Message, Session → Chunk |
Retrievable slices of content |
HAS_CONTENT |
Artifact → ArtifactContent |
Link to the deduped blob |
OBSERVED_IN |
Observation → Message, Chunk |
Source of an observation |
OBSERVED_DURING |
Observation → Episode |
Episode in which it was observed |
ABOUT |
Observation, Fact, Chunk → Entity, Participant |
What it concerns |
SUPPORTED_BY |
Fact → Observation |
Evidence for a fact (carries weight) |
DERIVED_FROM |
Fact, Procedure, Artifact → Episode, Action, Artifact |
Provenance of derived knowledge |
DERIVED_BY |
Fact → Rule |
Which rule produced the fact |
INVALIDATES |
Fact → Fact |
Newer fact supersedes an older one |
FOLLOWED_BY |
Episode → Episode |
Temporal chain of experiences |
SUPERSEDES |
Rule → Rule |
A newer rule replaces an older one |
SUMMARIZES |
Summary → Session/Task/Goal/Artifact/Entity/Topic… |
What a summary covers |
Working memory is a view, not a node¶
There is no WorkingMemory node type. Working memory is a live view computed on demand by
traversing the graph outward from a Goal — its Tasks, the Episodes and Sessions for
those tasks, recent Messages, the Facts derived from those episodes, the Entitys
involved, and any proven Procedures. Because the answer is recomputed from edges each time,
it always reflects the latest state of the graph rather than a cached snapshot.
Where to go next¶
Schema Reference¶
The complete catalogue: every node, every edge, every property and index.
Facts & Drift¶
How valid_at and BTIC intervals drive bitemporal recall.
Architecture¶
How the pipeline turns messages into the graph above.
Knowledge Base¶
The handle that opens the graph and registers this schema.