CONCEPTS

The Event-Sourcing Model

ChainMemory follows an event-sourcing architecture. Understanding this pattern is key to understanding the entire system.

In traditional systems, you store the current state and overwrite it on each change. In event-sourcing, you store every change as an immutable event, and the current state is derived by replaying those events.

In ChainMemory:

  • Memories are events — Each memory is an immutable record of something that happened: a decision was made, a risk was identified, a technology was chosen, a milestone was reached.
  • Project State is a view built from those events — A client reads the memories and proposes operations that cite them; the server validates and applies them, and stores each resulting version. The state is not recomputed automatically from the log: it changes only when a client consolidates.
  • The anchor is the proof of existence — When a Project State is anchored on-chain, the blockchain certifies that this specific materialized view existed at that exact block height.
Pattern
Memory #1 (event) ─┐
Memory #2 (event) ─┤
Memory #3 (event) ─┼──→ ops from a client ──→ Project State v1 ──→ Anchor (block 120000)
Memory #4 (event) ─┤
Memory #5 (event) ─┘

Memory #6 (event) ─┐
Memory #7 (event) ─┼──→ ops from a client ──→ Project State v2 ──→ Anchor (block 123539)
Memory #8 (event) ─┘

The memories are the source of truth and never change. Each state version records which memories it relied on, so any item can be traced back to its evidence. The anchor proves which state existed at which block.

Why this matters Event-sourcing guarantees full traceability. Every decision in your Project State can be traced back to the specific conversation where it originated. Nothing is lost, nothing is overwritten, and the history is always available.

What is a Memory

A memory is the fundamental unit of ChainMemory. It is a fragment of information extracted from an AI conversation that is considered valuable for the project's future.

Each memory contains:

  • Content — The text of the conversation or note
  • Hash — SHA-256 of the content, immutable
  • Project — Which project it belongs to
  • Tags — Free-form labels for organization (decision, bug, architecture, idea, etc.). Tags are for your use — the Project State has its own fixed fields (see Project State)
  • Number — Sequential within your account (#1, #2...)
  • Source — Where it was saved from (extension, MCP, API)
  • Timestamp — Exact moment of creation

Projects

A project groups related memories. Each project has its own timeline, consolidated state, and independent on-chain anchor.

Example projects: my-saas, ml-thesis, chainmemory, mobile-app.

Project State

The Project State (also called the Project Brain) is the consolidated, versioned view of a project. It is not generated by a server-side engine: a client — an AI model working through update_project_state (MCP) or POST /v1/project/:name/state/ops (API) — proposes operations, the server validates them against a closed grammar, applies them with a deterministic builder and computes the new state_hash. Each item records the memories that justify it.

What the state contains (schema version 2)

FieldWhat it holdsClosed values
vision, phase, current_focusWhere the project is going, the stage it is in, and what matters right now—
decisionsChoices with a title, a statement and their statusstatus: proposed, confirmed, rejected, superseded
milestonesDeliverablesstatus: planned, in_progress, done
risksIdentified threatsseverity: low, med, high — not medium or critical; status: open, mitigated, closed
prioritiesRanked work with a priority_score, linked to the risks it mitigates (mitigates)status: active, done, dropped
open_questionsQuestions and, once resolved, their answer—
assumptions, constraintsWhat the project takes for granted and the rules it must respect—
vocabulary, metricsShared terms and named values—
environmentHosts, services, repositories and operating rules—
state_metaVersion, consolidated_until_event watermark and previous_state_hash—
Closed value sets A value outside these sets gets the operation rejected. The most common mistake is "severity": "medium": the engine only accepts low, med or high.
Two ways to read it The full state is structured JSON with every id, its evidence and its history: get_project_state (MCP) or GET /v1/project/:name/state, and it is what a client reads before proposing operations. The brief is the same state written as text by relevance and within a character budget, to get oriented or to work on one priority: get_project_brief (MCP), GET /v1/project/:name/inject (API) and the extension's Project Brain button. The brief is public by default: addresses, ports, server paths and security rules are withheld.

State lifecycle

The state is incremental and append-only: each consolidation starts from the previous version and applies only the new operations. Every version carries its state_hash (SHA3-256 of the canonical state, domain-separated with CM_PROJECT_STATE_V<schema_version>) and, in state_meta, the hash of the version before it. After it is written, the version's hash is anchored on-chain in the ProjectStateAnchor contract, normally within ten minutes.

Version chain
v85 ──previous_state_hash──▶ v86 ──previous_state_hash──▶ v87
 │                            │                            │
 └─ anchorId 89               └─ anchorId 90               └─ anchorId 94

Example: what GET /v1/project/:name/state returns (trimmed)

JSON
{
  "project": "payment-system",
  "schema_version": 2,
  "version": 4,
  "state_hash": "0x3f8c2a…e91d",
  "generated_at": "2026-09-13T22:02:35.000Z",
  "anchor": { "status": "anchored", "onchain_anchor_id": 12, "tx_hash": "0x…", "block_number": 681919 },
  "state": {
    "phase": "beta",
    "current_focus": "PCI review before opening to the first tenants",
    "decisions": [
      { "id": "dec_0002", "title": "PostgreSQL with RLS for multi-tenancy",
        "statement": "Row-level security removes tenant filtering from the application layer",
        "status": "confirmed", "superseded_by": null,
        "evidence_root": "0x9a41…c07e", "created_version": 2, "updated_version": 3 }
    ],
    "milestones": [
      { "id": "mil_0001", "title": "Database schema", "status": "done",
        "evidence_root": "0x5b2d…81fa", "created_version": 1, "updated_version": 2 }
    ],
    "risks": [
      { "id": "risk_0001", "title": "RLS performance at 100K tenants", "severity": "med", "status": "open",
        "evidence_root": "0xd7e0…2a19", "created_version": 3, "updated_version": 3 }
    ],
    "state_meta": { "version": 4, "consolidated_until_event": 150, "previous_state_hash": "0x7b2e…f4c0" }
  }
}

GET /v1/project/:name/state requires the owner's API key and always returns the latest version. Past versions are verifiable by hash (see Hash & Verification), not re-readable in full through this endpoint.

Best practices

  • Save the memory first, consolidate after — the state cites memories; a decision that only exists in the operation has no support
  • Wait for anchoring before citing — a plaintext memory gets its event_hash when it syncs to the chain, about 30 seconds after being written
  • Advance the watermark — send consolidated_until_event with the highest memory number you actually reviewed
  • Supersede, don't rewrite — a replaced decision stays in the state with its status changed, so the history of how the project evolved is kept
Multi-agent state Several agents can consolidate the same project, each through its own client. Their operations land in one state; the memories cited in each item show which agent contributed the evidence. See Multi-Agent Systems.

Evidence Chain

When a client proposes an operation it cites the memories that support it in evidence_memory_ids — an array of memory numbers. The server resolves each one to its event_hash and stores, on the resulting item, the Merkle root of those hashes as evidence_root.

Operation sent by the client
{
  "op": "add_decision",
  "title": "Use Clique PoA consensus",
  "statement": "Proof of Authority gives fast, low-cost anchoring for a sovereign chain",
  "evidence_memory_ids": [12, 45, 67]
}
Item stored in the state
{
  "id": "dec_0001",
  "title": "Use Clique PoA consensus",
  "statement": "Proof of Authority gives fast, low-cost anchoring for a sovereign chain",
  "status": "proposed",
  "evidence_root": "0x028218d0…f082"
}

This creates a provenance chain:

  • Decision dec_0001 exists because memories 12, 45 and 67 support it; the consolidation response lists their event hashes
  • Each memory's event_hash is written on-chain when the memory syncs
  • The evidence_root commits to exactly that set of hashes
  • The state that contains the item has a state_hash anchored in ProjectStateAnchor
Unresolvable evidence is rejected, not ignored If any cited memory does not exist, is not yours, or has not been anchored yet, the whole call is rejected with 422 evidence_unresolved, and nothing is written or charged. An operation that cites nothing is accepted but stored with evidence_root 0x000…0: state with no provenance. The chain seals it just the same, because it verifies hashes, not correctness.

Conflict Resolution

Nothing resolves contradictions automatically. There is no engine reading memories and deciding which one wins: the client that consolidates decides, and the grammar makes that decision explicit and auditable.

Replacing a decision

If a newer memory changes direction, the client adds the new decision and marks the old one with supersede_decision (id of the old one, by_id of the new one). Both remain in the state.

Decision status

A decision is created as proposed. The client moves it to confirmed or rejected with set_decision_status, and to superseded with supersede_decision.

Several agents on the same project

Operations are applied on top of whatever version is current when the call arrives; /state/ops does not check an expected version. Nothing is lost — every call creates a new version — but an agent working from a stale read can re-add or contradict what another agent just wrote. Read the state immediately before consolidating. Each call accepts up to 100 operations.

No data loss Superseded and rejected decisions are never deleted. They stay in the Project State with their status changed, preserving how the project's direction evolved.

State Governance

The Project State lifecycle is governed by clear rules:

Who can consolidate?

Only the project owner (the account that created the project) can trigger a consolidation. This ensures that the structured knowledge extraction is always controlled by the data owner.

When does consolidation happen?

Consolidation can be triggered in two ways:

  • Client-driven — an AI model proposes operations through update_project_state (MCP) or POST /v1/project/:name/state/ops (API). The server validates them and only then do they become state
  • No automatic trigger — consolidation never happens on its own. Nothing is ever written to your state unless a client proposes it

Can it be reverted?

Each consolidation creates a new version (v1, v2, v3...). Previous versions remain accessible. You cannot delete a version, but you can always consolidate again to produce a corrected state. The version chain is append-only.

Snapshots

Every Project State version is a snapshot. The combination of version number + state_hash + on-chain anchor creates a verifiable checkpoint. The owner reads the latest version with GET /v1/project/:name/state; the hash and on-chain anchor of any past version are public at GET /v1/project/:name/state/anchor?version=2 and GET /v1/verify/:name.

ActionWhoWhenReversible
ConsolidateProject ownerOnly when a client proposes operationsNew version created (append-only)
Anchor on-chainAutomatic (operator wallet)Within ~10 minutes of each consolidationImmutable once anchored
Archive memoryProject ownerAnytimeCan unarchive
Read the latest stateProject owner (API key)AnytimeN/A (read-only)
Verify any version's hashAnyone (public endpoint)AnytimeN/A (read-only)

Hash & Verification

Each memory generates a SHA-256 hash of its content. This hash is the unique, immutable fingerprint of that memory. Content cannot be modified without changing the hash.

The state hash is a hash of the complete consolidated state (all decisions, milestones, risks, etc.). This state hash is anchored on the blockchain via a transaction on the ProjectStateAnchor contract.

Public verification

Verification in ChainMemory is public and permissionless. Anyone can:

  1. Call GET /v1/project/:name/state/anchor (no API key required)
  2. Get the state_hash and the tx_hash of the transaction
  3. Verify on the explorer that the transaction exists
  4. Read the contract directly on the blockchain to confirm the hash matches

This proves the project state existed exactly as described at the time of anchoring. It cannot be falsified retroactively.

Privacy Model

A common concern with blockchain-based systems is data exposure. ChainMemory addresses this with a strict separation:

Critical principle The plaintext of a memory never goes on-chain. What is written on-chain is its AES-256-GCM ciphertext together with the state hashes — and without the owner key none of it can be read.

What is stored where

DataLocationAccess
Memory contentEncrypted database (off-chain) and AES-256-GCM ciphertext on-chainPlaintext: owner only. Ciphertext: public but unreadable without the key
Memory hash (SHA-256)Database + on-chain once the memory syncs (~30 s)Hash is public but reveals nothing about content
Project State (structured)Database (off-chain)Owner only
State hashBlockchain (on-chain)Public — this is the verifiable proof
Anchor metadata (tx, block)Blockchain (on-chain)Public

A state anchor contains only the hashed project identifier, the version number, the state hash and a timestamp. A memory transaction additionally carries the encrypted content plus its category, importance and plaintext length. From either one it is impossible to reconstruct the content without the owner key.

This design means ChainMemory can provide cryptographic verification without compromising privacy. The blockchain proves that a state existed, not what it contained.