Architecture

August 18, 2026 ยท View on GitHub

Noema is a local-first memory layer for humans and AI agents. It exposes a CLI, a TUI, and an MCP server over stdio or Streamable HTTP. The core domain terms are:

  • Noema: the project and binary.
  • Cortex: one named memory collection stored in a directory.
  • Trace: one memory entry, represented by a markdown file plus indexed SQLite metadata.

Storage Model

A Cortex is a user-managed directory:

<cortex-dir>/
  cortex.md
  db/noema.db
  traces/
  archive/traces/
  trash/traces/

Trace markdown files are the source of truth. Each file contains YAML frontmatter followed by free-form body content:

---
id: 20260329-why-we-chose-local-storage
title: Why we chose local storage
type: decision
author: research-agent-1
tags: [storage, architecture]
derived_from: [20260328-language-candidates]
origin: research-cortex
created: 2026-03-29T14:23:00Z
updated: 2026-03-29T14:23:00Z
---

Body content here.

Valid trace types are fact, decision, preference, context, skill, intent, observation, note, and divergence.

Database And Migrations

SQLite stores indexes, tags, lineage, event history, federation state, usage signals, and optional embeddings. Migrations live in migrations/, are embedded into the binary, and run in version order. Schema changes must be transparent and non-destructive: add columns, tables, or indexes; do not drop or reshape stored data inside automatic migrations. Destructive or structural migrations need an explicit CLI command with clear operator confirmation.

Event Log, Lineage, And Federation

Mutations create immutable events in the SQLite event log within the same transaction as the content change. Events include a ULID, action, trace ID, cortex ID, origin, timestamp, vector clock snapshot, and trace state where needed. Lineage is tracked separately from derived_from so Noema can query both ancestors and descendants.

Federation is opt-in through the federation: block in cortex.md. Peers sync over HTTP by pulling sync_events, replaying remote events, and merging vector clocks. Concurrent updates that neither vector clock dominates create a divergence trace rather than silently overwriting content.

Events are authenticated with per-cortex Ed25519 signatures. A cortex that has run noema keygen signs every event it emits and advertises its public key through the cortex_identity handshake; peers pin that key per cortex id (trust-on-first-use) and, under federation.verify: enforce, reject events that are not correctly signed by their owning cortex. This is what makes source-lock enforceable on the replay path and not just for local mutations.

Watcher And External Edits

noema serve starts a filesystem watcher unless disabled in cortex.md. It observes active, archived, and trashed trace directories, then reconciles external edits through the same mutation paths used by CLI and MCP tools. Content hashes prevent Noema from reacting to its own writes. The watcher also guards against atomic-save gaps and can heal missing frontmatter from the DB row for locally-owned traces.

MCP And Access Posture

The MCP server exposes Cortex operations such as list, get, create, update, archive, search, lineage, history, federation status, and sync. Stdio mode is for local agent integration. HTTP mode uses the Streamable HTTP /mcp endpoint and requires an explicit host. Keyed HTTP mode requires TLS and authenticates requests with Authorization: Bearer <key> from NOEMA_MCP_KEY or a configured sidecar key file.

Search And Consolidation

Lexical FTS5 search is always available. Semantic and hybrid search are opt-in: embeddings are stored locally as a derived index and are never federated.

Noema supports short, mid, and long memory tiers. Consolidation can promote short-term traces heuristically or through an optional local LLM pipeline; a separate graduation pass promotes durable mid-tier traces to long-term memory. Preference traces are excluded from automatic mid-to-long graduation because startup reads and mutable user defaults are not enough evidence to lock a trace into the immutable long tier. Operators can still promote a preference manually when they intend to curate it as long-term policy.