Skip to main content
Version: v0.9.0a2

Why Immutable Typed Facts

4 min readProtocol implementer · SDK authorSpec-01-Fact-Model

What this page is

Why Stigmem stores knowledge as immutable, typed atomic facts — (entity, relation, value, source, timestamp, hlc, confidence, scope) — and why mutable key-value stores fail at federation.

The problem

An AI agent needs to remember things — a user's role, a project's status, a preference. The simplest model is a key-value store: set("alice.role", "CEO"). But the moment a second agent, a second node, or a second point in time enters the picture, key-value breaks down.

Which write wins? Who wrote it? When? Can you prove the value wasn't tampered with?

Key-value stores answer none of these questions without bolting on layers of metadata — at which point you've reinvented a fact model, just poorly.

Naive approaches and why they fail

Approach
Failure mode
Why it doesn't work
Mutable key-value store
silent loss
Two agents write different values for the same key. Last-write-wins silently discards one. No record of the conflict, no provenance, no way to know the discarded value ever existed.
Append-only log
no structure
You keep history, but a flat log has no structure. Querying "what does Alice prefer?" requires scanning the entire log. No way to express typed values, confidence levels, or scope boundaries without ad-hoc conventions.
Document store with versioning
document-level granularity
Git-style versioning preserves history but treats the document as the unit of change. A single-field update creates a full document revision. Merging divergent revisions requires a schema-aware merge algorithm; schema changes break the merger.

Our model

Stigmem stores knowledge as atomic facts. Each fact is a single assertion:

(entity, relation, value, source, timestamp, hlc, confidence, scope)

A fact is immutable once written. Updates are new facts. Retractions are facts with confidence = 0.0. This gives you:

Property
How
Notes
Audit trail
append-only
Every assertion ever made is preserved. Nothing is overwritten or deleted.
Typed values
seven types
string, text, number, boolean, datetime, ref, null (Spec-01-Fact-Model typed values).
Provenance
source + timestamp
Every fact carries who asserted it and when.
Confidence
float [0.0, 1.0]
1.0 = certain, 0.5 = uncertain, 0.0 = retracted.
Scope
visibility boundary
local, team, company, or public — governs visibility and federation.
Causal ordering
HLC
Every fact gets a causally consistent position in time (Spec-12-HLC-Bounded-Skew).

Worked example · asserting and retracting a fact

# Assert: Alice is CEO
curl -X POST $STIGMEM_URL/v1/facts \
-H "Authorization: Bearer $STIGMEM_API_KEY" \
-d '{
"entity": "stigmem://company.example/user/alice",
"relation": "memory:role",
"value": {"type": "string", "v": "CEO"},
"source": "stigmem://company.example/agent/assistant",
"confidence": 1.0,
"scope": "company"
}'
# → 201 { "id": "fact_01J...", "hlc": "1746230400000.001", ... }

# Retract: Alice is no longer CEO
curl -X POST $STIGMEM_URL/v1/facts \
-d '{
"entity": "stigmem://company.example/user/alice",
"relation": "memory:role",
"value": {"type": "string", "v": "CEO"},
"source": "stigmem://company.example/agent/assistant",
"confidence": 0.0,
"scope": "company"
}'
# → 201 { "id": "fact_02J...", "confidence": 0.0, ... }

Both facts exist in the store. The original assertion is never deleted. Querying memory:role for Alice now returns the retraction (confidence 0.0), and any query with min_confidence > 0 filters it out naturally.

Entity URI structure

Entities use a formal URI scheme that binds identity to the owning node:

stigmem://{authority}/{type}/{id}

For example: stigmem://company.example/user/alice, stigmem://company.example/agent/cto. This prevents identity collisions across federated nodes — two peers can each have a user/alice without ambiguity. Entity URIs are normalized to lowercase on ingest (Spec-01-Fact-Model entity normalization) to prevent silent fragmentation.

Why this is non-obvious

Immutability seems wasteful

Storing every version sounds expensive. But mutable state with conflict resolution or distributed locking is more expensive in a federated system. Immutability eliminates an entire class of distributed systems problems: you never need distributed locks, two-phase commits, or merge algorithms. Contradiction detection becomes a simple comparison.

Retracting via confidence = 0.0 seems indirect

But it preserves the retraction as a first-class event with its own provenance. You can answer "who retracted this, and when?" — which matters for compliance (GDPR Art. 17) and debugging. A DELETE operation would destroy that information.

Seven value types seem arbitrary

They're the minimum set that covers practical agent-memory needs without requiring schema registration. string for labels, text for narrative, number/boolean/datetime for structured data, ref for entity-to-entity links (powers the graph index), null for explicit "unknown."

What it costs

Storage

Every update creates a new row. For high-churn relations, the facts table grows proportionally. The decay sweeper (Spec-X9) mitigates this by automatically retracting stale facts based on configurable TTL and half-life policies.

Query complexity

Finding the "current" value for an entity-relation pair requires ordering by HLC and filtering by confidence — not a simple key lookup. The node handles this internally, but implementers should index on (entity, relation, scope).

No partial updates

You can't change one field of a fact. You assert a complete new fact. For the atomic assertions Stigmem models, this is acceptable; for document-shaped data, use ref-type values to point to external storage.

References