# TrustMem — Zero Trust Memory Design Specification v7.1

> **sator Inc.** | Last updated: 2026-03-21
>
> This document is the single authoritative design specification for TrustMem.
> It reflects the current implementation: ZTM v2 (confidence-based trust model),
> user_id-scoped data model, hybrid search (vector + keyword + RRF), and Always Verify (read-time hash verification).

---

## Table of Contents

1. [Product Overview](#1-product-overview)
2. [System Architecture](#2-system-architecture)
3. [Data Model & Scoping](#3-data-model--scoping)
4. [Zero Trust Memory (ZTM) v2](#4-zero-trust-memory-ztm-v2)
5. [Data Model](#5-data-model)
6. [API Reference](#6-api-reference)
7. [Extraction Pipeline](#7-extraction-pipeline)
8. [Knowledge Graph](#8-knowledge-graph)
9. [Security](#9-security)
10. [Deployment](#10-deployment)
11. [Architecture Decision Records (ADR)](#11-architecture-decision-records-adr)
12. [Appendix](#12-appendix)

---

## 1. Product Overview

### 1.1 What is TrustMem

TrustMem is a **memory infrastructure for AI agents** developed by **sator Inc.**. It provides persistent, semantically searchable memory with built-in trust evaluation.

The core concept is **Zero Trust Memory (ZTM) v2** — a confidence-based trust model where LLM evaluates reliability at write time. Confidence (high/medium/low) is assigned during fact extraction and updated only when contradictions are detected. At read time, content hashes are verified to detect tampering (Always Verify).

### 1.2 Problems Solved

| Problem | TrustMem Solution |
|---|---|
| Agent memory is volatile | SQLite + VectorStore persistence with semantic search |
| Memory trustworthiness is unknown | LLM confidence evaluation with fact verification at write time |
| Conflicting memories | Automatic CONTRADICT detection with winner/loser resolution |
| False positives in contradiction | Coexistence rules prevent false conflicts (e.g., multiple preferences) |
| Unreliable user claims | LLM verifies factual claims against its knowledge |
| No governance or audit trail | Automatic audit trail for all memory operations |
| Infrastructure lock-in | Plugin architecture for vector store, graph store, and AI provider |
| User scoping | user_id scoping with shared knowledge support |
| Data integrity unknown | Always Verify — read-time hash verification detects tampering |

### 1.3 Core Concept: Zero Trust Memory v2

> **"Confidence is evaluated at write time by LLM, verified at read time by hash, and updated only at CONTRADICT time."**

Every extracted fact is assigned a confidence level (high/medium/low) by the LLM based on:
- Factual accuracy (verified against LLM knowledge)
- Specificity and clarity
- Source reliability (user vs. hearsay)

When contradictions are detected, the winner's confidence is promoted to `high` and the loser's confidence is demoted (LLM-judged).

At read time, content hashes are re-computed to verify integrity. Tampered memories have their confidence demoted to `low` in the response (Zero Trust principle: confidence only goes down).

### 1.4 Technology Stack

| Component | Technology |
|---|---|
| Language | Python 3.12 |
| API Framework | FastAPI |
| Graph Computation | NetworkX 3.3 |
| Package Build | Hatch (pyproject.toml) |
| CI/CD | GitHub Actions |
| Embedding (required) | OpenAI / Azure OpenAI / OpenAI-compatible (vLLM, Ollama) |
| LLM (required) | OpenAI / Azure OpenAI / OpenAI-compatible |
| Data Store | SQLite (default) |
| Vector Store | SQLite (default) / Qdrant |
| Graph Store | SQLite (default) / Neo4j |

### 1.5 Phase History

TrustMem v1.0.0 — All phases complete. See CHANGELOG.md for release notes.

---

## 2. System Architecture

### 2.1 Layer Structure

```
┌──────────────────────────────────────────────────────────┐
│  Layer 5: Client Interface                               │
│  TrustMem SDK (embedded, 29 methods)                     │
│  TrustMemClient (HTTP, 5 methods, separate package)      │
├──────────────────────────────────────────────────────────┤
│  Layer 4: HTTP Interface                                 │
│  FastAPI Server (Core CRUD + Admin endpoints)            │
├──────────────────────────────────────────────────────────┤
│  Layer 3: Extraction Pipeline                            │
│  3-stage: Extract → Similarity → Conflict Resolution     │
├──────────────────────────────────────────────────────────┤
│  Layer 2: Business Logic (Handler)                       │
│  PersistentHandler (sole implementation)                 │
├──────────────────────┬───────────────────────────────────┤
│  Layer 1a: Relational│  Layer 1b: Vector / Graph         │
│  SQLite              │  SQLite / Qdrant / Neo4j          │
│  (Source of Truth)   │  (Semantic search / KG)           │
├──────────────────────┴───────────────────────────────────┤
│  Layer 0: AI Client (required)                           │
│  OpenAI / Azure OpenAI / OpenAI-compatible               │
└──────────────────────────────────────────────────────────┘
```

### 2.2 Two-Layer Access Model

```
Embedded (local, in-process):
  TrustMem (29 methods)  →  PersistentHandler  →  SQLite/Qdrant/Neo4j
    ├── Core CRUD: add, search, search_raw, get, get_all, list_all, delete
    ├── ZTM: verify, verify_all, audit_trail, contradictions, provenance, verify_audit_chain
    ├── Access Log: access_log, verify_access_chain, purge_access_log
    ├── Graph: add_relation, search_relations, get_graph
    ├── Raw: add_raw, update_raw
    ├── Stats: stats, entity_stats
    └── Maintenance: backfill_hashes, cleanup_audit_trail, cleanup_expired, forget, reset

Remote (via HTTP server):
  TrustMemClient (5 methods)  ──HTTP──→  FastAPI Server  →  TrustMem
    └── Core CRUD only: add, search, get_all, delete, close
```

| Package | Install | Methods | Description |
|---|---|---|---|
| `trustmem` | `pip install trustmem` | 29 | Embedded SDK, all features, no HTTP needed |
| `trustmem[server]` | `pip install trustmem[server]` | — | Adds FastAPI server |
| `trustmem-client` | `pip install trustmem-client` | 4 + close | HTTP client, no trustmem dependency |

### 2.3 PersistentHandler

The sole handler implementation. LLM (AIClient) is required — `None` raises `TypeError`.

| Component | Backend | Environment Variable |
|---|---|---|
| Data store | SQLite | `DATABASE_URL` |
| Vector search | SQLite (default) / Qdrant | `VECTOR_STORE` |
| Graph store | SQLite (default) / Neo4j | `GRAPH_STORE` |
| AI (required) | OpenAI / Azure / compatible | `EMBED_PROVIDER`, `OPENAI_API_KEY` |

Design:
- Implements `HandlerProtocol`
- `factory.py` selects backends based on environment variables
- VectorStore / AIClient / GraphStore are constructor-injected (DI)
- Thread-safe embedding cache with `threading.Lock` (10K entry limit, LRU-style eviction)

### 2.4 VectorStore Plugin

```python
class VectorStore(ABC):
    def upsert(self, id: str, vector: list[float], payload: dict) -> None: ...
    def search(self, query_vector: list[float], limit: int, filters: dict | None) -> list[dict]: ...
    def delete(self, id: str) -> None: ...
    def update_payload(self, id: str, payload: dict) -> None: ...
```

| Implementation | `VECTOR_STORE` | Dependency | Notes |
|---|---|---|---|
| `SQLiteVectorStore` | `sqlite` | None | Default, stored in same SQLite file |
| `QdrantVectorStore` | `qdrant` | `qdrant-client` | High-performance ANN, large-scale |

### 2.5 GraphStore Plugin

```python
class GraphStore(ABC):
    def add_entity(self, ...) -> dict: ...
    def add_edge(self, ...) -> dict: ...
    def search_edges(self, ...) -> list[dict]: ...
    def get_subgraph(self, ...) -> dict: ...
```

| Implementation | `GRAPH_STORE` | Dependency | Notes |
|---|---|---|---|
| SQLite (default) | `sqlite` | None | Stored in same SQLite file |
| `Neo4jGraphStore` | `neo4j` | `neo4j` | Full graph database |

### 2.6 AI Client

```python
class AIClient:
    def embed(self, text: str) -> list[float]: ...
    def llm_judge(self, prompt: str) -> str: ...
```

| Pattern | EMBED_PROVIDER | LLM_PROVIDER | Use Case |
|---|---|---|---|
| OpenAI SaaS | `openai` | `openai` | Simplest setup |
| Azure OpenAI | `azure` | `azure` | Data residency |
| Self-hosted | `openai_compat` | `openai_compat` | vLLM / Ollama |
| Hybrid | `azure` | `openai_compat` | Embedding=Azure, LLM=self-hosted |

### 2.7 Configuration

All configuration is managed via `.env` file (python-dotenv) or environment variables. `TrustMemConfig.from_env()` loads and validates all settings.

```
.env (user edits) → config.py (load + validate) → SDK / Server (consume)
```

`TrustMemConfig.validate()` returns a list of `ConfigIssue` objects with `level` (error/warning), `field`, and `message`. `str(issue)` returns `"ERROR: ..."` or `"WARNING: ..."` for backward compatibility.

---

## 3. Data Model & Scoping

### 3.1 Data Structure

```
user_id (data owner, set per method)
  ├── Memory (semantic / document)
  ├── Entity
  └── Edge
user_id=NULL
  └── Shared knowledge
```

- `user_id` is passed per method call: `m.add(messages, user_id="user-123")`
- `visibility="organization"` is syntactic sugar for `user_id=NULL` — it is a **method argument only**, not a DB column
- Tenant isolation (if needed) is handled at the application layer via separate DB files or containers

### 3.2 Scope Validation

All scoped methods (`add`, `search`, `get_all`, `search_raw`, `add_raw`, etc.) enforce:

| user_id | visibility | Behavior |
|---|---|---|
| specified | None | User-scoped operation |
| None | `"organization"` | Shared knowledge (user_id=NULL) |
| None | None | **ValueError** — scope not specified |
| specified | `"organization"` | **ValueError** — mutually exclusive |

### 3.3 Search Merge Behavior

| Scope | Returns |
|---|---|
| `user_id="user-123"` | User's memories + shared knowledge (user_id=NULL) |
| `visibility="organization"` | Shared knowledge only (user_id=NULL) |

This enables a pattern where shared knowledge is accessible to all users while user-specific memories remain scoped.

### 3.4 Memory Types

| memory_type | Description | Use Case |
|---|---|---|
| `semantic` (default) | Atomic facts extracted from conversations | Standard memory |
| `document` | Document chunks for RAG integration | Document ingestion, retrieval |

### 3.5 Hybrid Search

TrustMem supports three search modes via the `search_mode` parameter:

| search_mode | Description |
|---|---|
| `hybrid` (default) | Vector + keyword (inverted index with trigram-like tokenization) + Reciprocal Rank Fusion |
| `vector` | Pure vector similarity search |
| `keyword` | Pure keyword search using inverted index |

### 3.6 Entity Scoping

Entity IDs are deterministic hashes: `entity_id = hash(name|type|user_id)`. Entities and edges are scoped by `user_id`. Entity/graph queries with `user_id` merge user-specific and shared (user_id=NULL) results.

### 3.7 Authorization

TrustMem manages `user_id` scoping only. Authentication and authorization are **outside TrustMem's responsibility** — delegated to the calling application or external authorization service.

---

## 4. Zero Trust Memory (ZTM) v2

### 4.1 Confidence-Based Trust Model

ZTM v2 uses a simple confidence-based model:

```
confidence = high | medium | low
  → assigned at write time by LLM, static thereafter
  → updated only at CONTRADICT time via winner/loser logic
  → demoted at read time if hash verification fails (Always Verify)
```

### 4.2 Confidence Levels

| Confidence | Criteria | Examples |
|---|---|---|
| `high` | Specific, plausible, clearly stated, AND factually correct | "User likes sushi", "Paris is the capital of France" |
| `medium` | Vague, uncertain, hedged, hearsay, or beyond LLM knowledge cutoff (default) | "I think I might switch frameworks" |
| `low` | Factually wrong, outdated, or self-contradictory | "Abe is the PM of Japan" (no longer in office) |

### 4.3 Fact Verification

The extraction prompt instructs the LLM to actively verify factual claims:

- **Political leaders**: Check if the person currently holds the position. Former leaders → `low`
- **World knowledge**: Capitals, scientific facts, historical dates — verify against LLM knowledge
- **Personal facts**: Preferences, plans, personal info → typically `high` (cannot be verified)
- **Beyond knowledge cutoff**: If unsure whether a claim is still true → `medium`

### 4.4 User/Assistant Separation

- Only facts from **user messages** are extracted by default
- **Exception**: Assistant's factual responses (e.g., answering questions) may be extracted if the user did not reject them
- Facts stated by the user but **corrected by the assistant** are NOT extracted (or marked `low`)

### 4.5 CONTRADICT + Winner/Loser

When Stage 3 LLM detects a contradiction between a new fact and an existing memory:

1. Both memories are kept (no deletion)
2. A `contradicts` provenance edge is recorded
3. LLM determines `winner`:
   - `"new"` — **DEFAULT**. The new fact supersedes the existing (temporal updates, corrections)
   - `"existing"` — RARE. New fact is clearly less credible
   - `"unknown"` — RARE. Genuinely subjective conflict with no temporal aspect
4. Winner's confidence → `high`
5. Loser's confidence → `loser_confidence` (LLM-judged, typically `low` for temporal updates)

```
Existing: "Works at Microsoft" (high)
New:      "Works at Google"
→ CONTRADICT, winner: "new"
→ "Works at Google": confidence="high"
→ "Works at Microsoft": confidence="low"
```

### 4.6 Coexistence Rules

Multiple values for the same category are NOT contradictions:

| Scenario | Action | Reason |
|---|---|---|
| "Likes pizza" + "Likes sushi" | ADD | Multiple food preferences can coexist |
| "Speaks English" + "Speaks French" | ADD | A person can speak multiple languages |
| "Hobby is reading" + "Hobby is running" | ADD | Multiple hobbies can coexist |
| "Lives in Tokyo" + "Lives in Osaka" | CONTRADICT | Cannot live in two cities simultaneously |

### 4.7 Always Verify (Read-Time Hash Verification)

Based on the Zero Trust principle, every read operation verifies data integrity:

```
search / get_all / get
  → Retrieve results
  → Re-compute content_hash for each memory
  → If stored_hash ≠ computed_hash:
      confidence → "low" (response only, DB unchanged)
      tampered: true flag added
```

Key properties:
- **stored_confidence** — The DB value (assigned by LLM at write time)
- **confidence** — The response value (may be demoted to `low` if hash verification fails)
- Confidence **only goes down** (Zero Trust principle) — hash mismatch never promotes confidence
- Applied to `search`, `get_all`, and `get`
- DB is not modified — demotion is response-level only

### 4.8 Freshness (Informational Only)

Search results include:
- `freshness`: Negative integer (days since `last_evaluated_at`)
- `last_evaluated_at`: ISO timestamp

No penalty is applied — provided as context for the application to use as needed.

---

## 5. Data Model

### 5.1 SQLite Schema

All tables are managed by `SQLiteStore` (`src/trustmem/storage/sqlite_store.py`) in a single SQLite file (default: `~/.trustmem/trustmem.db`).

#### memories (core)

```sql
CREATE TABLE IF NOT EXISTS memories (
    memory_id         TEXT PRIMARY KEY,
    content_hash      TEXT,
    user_id           TEXT DEFAULT NULL,
    content           TEXT NOT NULL,
    tags              TEXT DEFAULT '[]',
    metadata          TEXT DEFAULT '{}',
    confidence        TEXT DEFAULT 'medium'
                      CHECK(confidence IN ('high', 'medium', 'low')),
    source_trust      TEXT DEFAULT 'derived'
                      CHECK(source_trust IN ('verified', 'input', 'derived')),
    memory_type       TEXT DEFAULT 'semantic'
                      CHECK(memory_type IN ('semantic', 'document')),
    created_at        TEXT,
    updated_at        TEXT,
    last_evaluated_at TEXT
);
```

Note: `confidence` defaults to `'medium'` — all memories start with medium trust. `user_id` is NULL for organization-shared knowledge.

#### memory_provenance (DAG)

```sql
CREATE TABLE IF NOT EXISTS memory_provenance (
    provenance_id     TEXT PRIMARY KEY,
    child_memory_id   TEXT NOT NULL REFERENCES memories(memory_id) ON DELETE CASCADE,
    parent_memory_id  TEXT REFERENCES memories(memory_id) ON DELETE CASCADE,
    user_id           TEXT,
    relationship      TEXT NOT NULL DEFAULT 'contradicts',
    created_at        TEXT
);
```

Relationship types: `contradicts` (primary use case in ZTM v2).

#### memory_audit_trail

```sql
CREATE TABLE IF NOT EXISTS memory_audit_trail (
    audit_id             TEXT PRIMARY KEY,
    memory_id            TEXT NOT NULL,
    event_type           TEXT NOT NULL,
    old_confidence       TEXT,
    new_confidence       TEXT,
    triggered_by         TEXT,
    details              TEXT DEFAULT '{}',
    created_at           TEXT,
    sequence_number      INTEGER UNIQUE,
    record_hash          TEXT,
    prev_hash            TEXT
);
```

The audit trail supports hash-chain verification (`record_hash` / `prev_hash`) for tamper detection of the audit log itself.

#### entity_nodes

```sql
CREATE TABLE IF NOT EXISTS entity_nodes (
    entity_id        TEXT PRIMARY KEY,
    name             TEXT NOT NULL,
    entity_type      TEXT DEFAULT 'other',
    user_id          TEXT DEFAULT NULL,
    confidence       TEXT DEFAULT 'medium',
    source_memory_id TEXT REFERENCES memories(memory_id) ON DELETE SET NULL,
    mention_count    INTEGER DEFAULT 1,
    metadata         TEXT DEFAULT '{}',
    is_active        INTEGER DEFAULT 1,
    created_at       TEXT,
    updated_at       TEXT
);
```

Entity confidence: starts at `medium`, promoted to `high` at 3+ mentions. Scoped by `user_id`.

#### entity_edges

```sql
CREATE TABLE IF NOT EXISTS entity_edges (
    edge_id          TEXT PRIMARY KEY,
    subject_id       TEXT NOT NULL REFERENCES entity_nodes(entity_id) ON DELETE CASCADE,
    subject_name     TEXT NOT NULL,
    predicate        TEXT NOT NULL,
    object_id        TEXT NOT NULL REFERENCES entity_nodes(entity_id) ON DELETE CASCADE,
    object_name      TEXT NOT NULL,
    user_id          TEXT DEFAULT NULL,
    confidence       TEXT DEFAULT 'medium',
    source_memory_id TEXT REFERENCES memories(memory_id) ON DELETE SET NULL,
    mention_count    INTEGER DEFAULT 1,
    metadata         TEXT DEFAULT '{}',
    is_active        INTEGER DEFAULT 1,
    created_at       TEXT,
    updated_at       TEXT
);
```

#### memory_vectors

```sql
CREATE TABLE IF NOT EXISTS memory_vectors (
    id       TEXT PRIMARY KEY,
    vector   BLOB NOT NULL,
    payload  TEXT DEFAULT '{}'
);
```

#### memory_access_log

```sql
CREATE TABLE IF NOT EXISTS memory_access_log (
    access_id       TEXT PRIMARY KEY,
    event_type      TEXT NOT NULL,
    user_id         TEXT,
    accessor        TEXT DEFAULT 'system',
    query           TEXT,
    filters         TEXT DEFAULT '{}',
    memory_ids      TEXT DEFAULT '[]',
    result_count    INTEGER DEFAULT 0,
    created_at      TEXT NOT NULL,
    sequence_number INTEGER UNIQUE,
    record_hash     TEXT,
    prev_hash       TEXT
);
```

The access log supports hash-chain verification for tamper detection of read audit records.

### 5.2 Performance Indexes

```sql
CREATE INDEX idx_memories_user ON memories(user_id);
CREATE INDEX idx_memories_confidence ON memories(confidence);
CREATE INDEX idx_memories_user_created ON memories(user_id, created_at DESC);
CREATE UNIQUE INDEX idx_memories_content_hash ON memories(content_hash) WHERE content_hash IS NOT NULL;
CREATE INDEX idx_memories_source_trust ON memories(source_trust);
CREATE INDEX idx_memories_expires ON memories(expires_at);

CREATE INDEX idx_provenance_child ON memory_provenance(child_memory_id);
CREATE INDEX idx_provenance_parent ON memory_provenance(parent_memory_id);
CREATE INDEX idx_provenance_relationship ON memory_provenance(relationship);

CREATE INDEX idx_audit_trail_memory ON memory_audit_trail(memory_id);
CREATE UNIQUE INDEX idx_audit_sequence ON memory_audit_trail(sequence_number)
    WHERE sequence_number IS NOT NULL;

CREATE INDEX idx_entity_nodes_user_name
    ON entity_nodes(user_id, is_active, name COLLATE NOCASE);
CREATE INDEX idx_entity_edges_user_active
    ON entity_edges(user_id, is_active);
CREATE INDEX idx_entity_edges_subject
    ON entity_edges(user_id, is_active, subject_name COLLATE NOCASE);
CREATE INDEX idx_entity_edges_object
    ON entity_edges(user_id, is_active, object_name COLLATE NOCASE);

CREATE INDEX idx_access_log_user ON memory_access_log(user_id);
CREATE INDEX idx_access_log_accessor ON memory_access_log(accessor);
CREATE INDEX idx_access_log_created ON memory_access_log(created_at);
CREATE UNIQUE INDEX idx_access_log_sequence ON memory_access_log(sequence_number)
    WHERE sequence_number IS NOT NULL;
```

### 5.3 VectorStore Data Structure

```python
{
    "id": "<memory_id>",
    "vector": [0.012, -0.034, ...],  # EMBED_DIM dimensions
    "payload": {
        "user_id": "user-123",
    }
}
```

SQLite is the source of truth. VectorStore data is a secondary index.

### 5.4 Table Relationship Diagram

```
memories ──────┬── memory_provenance (contradicts edges)
    │          │
    ├── memory_audit_trail (all confidence changes, hash-chained)
    ├── memory_access_log (read audit, hash-chained)
    └── memory_vectors (embedding vectors, SQLite default)

entity_nodes ──── entity_edges (subject → predicate → object)
    │                  │
    └── source_memory_id → memories (extraction provenance)

Scoping: user_id on memories, entity_nodes, entity_edges
```

### 5.5 Schema Migrations

| Migration | Content |
|---|---|
| 1 | Add `content_hash`, `last_evaluated_at` to memories |
| 2 | Drop `memory_type`, `write_path` from memories |
| 3 | ZTM v2: Drop `trust_score`, `zone`, `is_contaminated`, `corroboration_count` from memories; add `confidence`; drop `corroboration_map`, `contradiction_cache` tables; update audit trail schema |
| 4 | Entity ZTM v2: Drop `trust_score`, `zone` from `entity_nodes`/`entity_edges`; add `confidence` |
| 5-8 | Source trust, access log, expires_at, hash-chain columns |

---

## 6. API Reference

### 6.1 Python SDK (TrustMem — 29 Methods)

The primary interface. Wraps PersistentHandler directly, no HTTP server needed.

#### Initialization

```python
from trustmem import TrustMem

m = TrustMem()  # Quick start (reads .env)
```

#### Method Reference

| Category | Method | Scope Args | Return | Description | Client |
|---|---|---|---|---|---|
| **Core** | `add(data)` | `user_id` or `visibility` | `dict` | Extract and store memories from `list[dict]` messages (LLM pipeline) | Yes |
| **Core** | `search(query)` | `user_id` or `visibility` | `dict` | Semantic search. `{"results": [...], "relations": [...]}` | Yes |
| **Core** | `search_raw(query)` | `user_id` or `visibility` | `dict` | Pure cosine similarity search (no TrustMem ranking) | — |
| **Core** | `get(memory_id)` | — | `dict` | Get single memory | — |
| **Core** | `get_all()` | `user_id` or `visibility` | `dict` | All memories + relations (scope-enforced) | Yes |
| **Core** | `list_all()` | — | `dict` | Admin: all memories (scope-ignored) | — |
| **Core** | `delete(memory_id)` | — | `bool` | Delete memory by ID | Yes |
| **Core** | `close()` | — | `None` | Release resources (context manager support) | — |
| **ZTM** | `verify(memory_id)` | — | `dict` | Integrity check (content_hash verification) | — |
| **ZTM** | `verify_all()` | — | `dict` | Verify all memories | — |
| **ZTM** | `audit_trail(memory_id)` | — | `list` | Get audit log | — |
| **ZTM** | `contradictions(memory_id)` | — | `list` | Contradiction list (winner/loser relationships) | — |
| **ZTM** | `provenance(memory_id)` | — | `list` | Provenance tracking (derived_from, etc.) | — |
| **ZTM** | `verify_audit_chain()` | — | `dict` | Audit log hash-chain verification | — |
| **Access Log** | `access_log()` | — | `list` | Read audit query | — |
| **Access Log** | `verify_access_chain()` | — | `dict` | Access log hash-chain verification | — |
| **Access Log** | `purge_access_log(before)` | — | `dict` | Purge access log entries | — |
| **Graph** | `add_relation(subj, pred, obj)` | `user_id` or `visibility` | `dict` | Add entity relation (RDF triple) | — |
| **Graph** | `search_relations(entity)` | `user_id` or `visibility` | `list` | Search relations by entity name | — |
| **Graph** | `get_graph(entity)` | `user_id` or `visibility` | `dict` | Multi-hop subgraph traversal | — |
| **Raw** | `add_raw(content)` | `user_id` or `visibility` | `dict` | LLM-bypass direct insertion | — |
| **Raw** | `update_raw(memory_id, content)` | — | `dict` | Version-managed update (derived_from link) | — |
| **Stats** | `stats()` | — | `dict` | Confidence distribution, counts | — |
| **Stats** | `entity_stats()` | — | `dict` | Entity/edge counts | — |
| **Maintenance** | `backfill_hashes()` | — | `dict` | Backfill missing content_hash values | — |
| **Maintenance** | `cleanup_audit_trail(before)` | — | `dict` | Audit log count (dry-run only) | — |
| **Maintenance** | `cleanup_expired()` | — | `dict` | Delete expired memories | — |
| **Maintenance** | `forget(entity)` | — | `dict` | GDPR entity deletion | — |
| **Maintenance** | `reset()` | — | `None` | Clear all data (development only) | — |

#### add() — Conversation Extraction

All data goes through the LLM extraction pipeline:

```python
messages = [
    {"role": "user", "content": "I just moved to Tokyo"},
    {"role": "assistant", "content": "Welcome! How do you like it?"},
    {"role": "user", "content": "I love the food, especially sushi"},
]

m.add(messages, user_id="user-123")

# Or for organization-shared knowledge:
m.add(messages, visibility="organization")
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `data` | `list[dict]` | required | Conversation messages |
| `user_id` | `str\|None` | `None` | Data owner (mutually exclusive with visibility) |
| `visibility` | `str\|None` | `None` | `"organization"` for shared knowledge |
| `tags` | `list[str]\|None` | `None` | Tags |
| `metadata` | `dict\|None` | `None` | Custom metadata |
| `session_id` | `str\|None` | `None` | Session grouping |
| `custom_extraction_prompt` | `str\|None` | `None` | Override extraction prompt |
| `dry_run` | `bool` | `False` | Extract without persisting |

#### search() / get_all() — Integrated Results

Both return vector search results and knowledge graph relations in a single response:

```python
result = m.search("food preferences", user_id="user-123")
# {
#   "results": [
#     {
#       "memory_id": "...",
#       "content": "User loves sushi",
#       "similarity": 0.92,
#       "confidence": "high",        # may be demoted if tampered
#       "stored_confidence": "high",  # DB value
#       "freshness": -3,
#       "last_evaluated_at": "2026-03-14T...",
#       ...
#     }
#   ],
#   "relations": [
#     {"subject_name": "Alex", "predicate": "likes", "object_name": "sushi", ...}
#   ]
# }
```

When `user_id` is specified, results include both the user's memories and organization-shared knowledge (user_id=NULL).

### 6.2 FastAPI Server (Core CRUD + Admin Endpoints)

Core CRUD and admin endpoints are exposed. Management methods beyond the client are accessible via admin endpoints.

```bash
pip install "trustmem[server]"
python -m trustmem
# or: trustmem-server
```

#### Core Endpoints

| HTTP | Path | Auth | SDK Method |
|---|---|---|---|
| POST | `/v1/memories` | Yes (if configured) | `add` |
| POST | `/v1/memories/search` | Yes (if configured) | `search` |
| POST | `/v1/memories/search-raw` | Yes (if configured) | `search_raw` |
| GET | `/v1/memories` | Yes (if configured) | `get_all` |
| GET | `/v1/memories/{memory_id}` | Yes (if configured) | `get` |
| DELETE | `/v1/memories/{memory_id}` | Yes (if configured) | `delete` |
| GET | `/health` | No | — |
| GET | `/v1/version` | Yes (if configured) | — |

All endpoints accept `user_id` as a query parameter for scoping.

#### Admin Endpoints

| HTTP | Path | Auth | SDK Method |
|---|---|---|---|
| GET | `/v1/admin/stats` | Yes (if configured) | `stats` |
| GET | `/v1/admin/entity-stats` | Yes (if configured) | `entity_stats` |
| GET | `/v1/admin/verify/{memory_id}` | Yes (if configured) | `verify` |
| GET | `/v1/admin/verify-all` | Yes (if configured) | `verify_all` |
| GET | `/v1/admin/audit-trail/{memory_id}` | Yes (if configured) | `audit_trail` |
| GET | `/v1/admin/contradictions/{memory_id}` | Yes (if configured) | `contradictions` |
| GET | `/v1/admin/provenance/{memory_id}` | Yes (if configured) | `provenance` |
| GET | `/v1/admin/relations` | Yes (if configured) | `search_relations` |
| GET | `/v1/admin/graph` | Yes (if configured) | `get_graph` |
| POST | `/v1/admin/add-raw` | Yes (if configured) | `add_raw` |
| POST | `/v1/admin/add-raw-batch` | Yes (if configured) | `add_raw` (batch) |
| POST | `/v1/admin/update-raw` | Yes (if configured) | `update_raw` |
| POST | `/v1/admin/embed` | Yes (if configured) | — (embedding generation) |
| GET | `/v1/admin/list-all` | Yes (if configured) | `list_all` |
| GET | `/v1/admin/memory-detail/{memory_id}` | Yes (if configured) | `get` (detailed) |
| GET | `/v1/admin/access-log` | Yes (if configured) | `access_log` |
| GET | `/v1/admin/access-log/verify` | Yes (if configured) | `verify_access_chain` |
| DELETE | `/v1/admin/access-log` | Yes (if configured) | `purge_access_log` |
| POST | `/v1/admin/cleanup-expired` | Yes (if configured) | `cleanup_expired` |
| POST | `/v1/admin/forget` | Yes (if configured) | `forget` |
| POST | `/v1/admin/reset` | Yes (if configured) | `reset` |

### 6.3 HTTP Client (TrustMemClient — Separate Package)

Lightweight HTTP client for connecting to a remote TrustMem server. No `trustmem` dependency.

```bash
pip install trustmem-client
```

```python
from trustmem_client import TrustMemClient

# From environment variables (TRUSTMEM_BASE_URL, TRUSTMEM_API_KEY)
client = TrustMemClient()

# Explicit (overrides environment variables)
client = TrustMemClient(base_url="https://example.com", api_key="tm-xxx")

# Same scoping model as TrustMem SDK
client.add([{"role": "user", "content": "Hello"}], user_id="user-123")
result = client.search("Hello", user_id="user-123")
all_data = client.get_all(user_id="user-123")
client.delete(memory_id)
client.close()
```

| Method | Scope Args | Return | Description |
|---|---|---|---|
| `add(data)` | `user_id` or `visibility` | `dict` | Add memory |
| `search(query)` | `user_id` or `visibility` | `dict` | Semantic search |
| `get_all()` | `user_id` or `visibility` | `dict` | Get all memories |
| `delete(memory_id)` | — | `bool` | Delete memory |
| `close()` | — | `None` | Close HTTP connection |

### 6.4 Exception Hierarchy

Multi-inherits from both `TrustMemError` and stdlib exceptions for backward compatibility:

```python
class TrustMemError(Exception): ...              # Base
class NotFoundError(TrustMemError, KeyError): ... # Memory not found
class ValidationError(TrustMemError, ValueError): ...  # Invalid input
class PermissionDeniedError(TrustMemError, PermissionError): ...
class ConfigurationError(TrustMemError, RuntimeError): ...
```

### 6.5 TypedDict Return Types

SDK methods use TypedDict annotations for type checking:

```python
class SearchResult(TypedDict):
    results: list[MemoryRecord]
    relations: list[RelationRecord]

class AddResult(TypedDict, total=False):
    extracted: list[ExtractedFact]
    entities: list[dict[str, Any]]
    relations: list[dict[str, Any]]
    stats: AddStats
```

---

## 7. Extraction Pipeline

### 7.1 Overview

Converts conversation messages into atomic facts, entities, and relations using a 3-stage LLM pipeline. Located in `src/trustmem/extraction/pipeline.py`.

### 7.2 Pipeline Stages

```
Input: messages[]
  │
  ▼ Stage 1: LLM Fact & Entity Extraction
  │  → facts: [{"content": "...", "confidence": "high|medium|low"}]
  │  → entities: [{"name": "Alex", "type": "person"}]
  │  → relations: [{"subject": "Alex", "predicate": "likes", "object": "sushi"}]
  │  Key behaviors:
  │  - Only extract from USER messages (assistant exception: factual answers)
  │  - Verify factual claims against LLM knowledge
  │  - Assign confidence based on reliability
  │
  ▼ Stage 2: Embedding & Similarity Search + MinHash Pre-filter
  │  → Vectorize each fact, search for similar existing memories
  │  → MinHash pre-filter: Jaccard >= 0.98 → auto-NOOP without LLM call
  │
  ▼ Stage 3: LLM Conflict Resolution (batched)
  │  → ADD / CONTRADICT / NOOP per fact
  │  → Coexistence rules prevent false contradictions
  │  → CONTRADICT: winner/loser_confidence determined by LLM
  │
  ▼ Commit
     → add_memory() + provenance + audit trail + entity/edge upsert
```

### 7.3 Stage 3 Actions

| Action | Behavior |
|---|---|
| ADD | Store as new memory. Also used for coexisting facts |
| NOOP | Skip (duplicate or irrelevant) |
| CONTRADICT | Store as new memory AND create `contradicts` provenance edge. Winner/loser confidence updated |

### 7.4 Conflict Resolution Rules

- **Temporal updates** (leadership changes, job changes, address changes): `winner: "new"`, `loser_confidence: "low"`
- **Coexisting values** (multiple preferences, languages, hobbies): `ADD`, not CONTRADICT
- **Subjective conflicts** (likes vs hates): `winner: "unknown"`, `loser_confidence: "medium"`

### 7.5 MinHash Pre-filter

Character-level 3-gram MinHash (128 permutations) for near-duplicate detection. Facts with Jaccard similarity >= 0.98 against existing memories are auto-NOOPed without an LLM call.

| Parameter | Value |
|---|---|
| `CONFLICT_SIMILARITY_THRESHOLD` | 0.60 |
| `MINHASH_NOOP_THRESHOLD` | 0.98 |
| `MINHASH_NUM_PERM` | 128 |

---

## 8. Knowledge Graph

### 8.1 Two-Layer Graph Structure

| Graph | Purpose | Tables |
|---|---|---|
| Provenance Graph | Contradiction tracking | `memory_provenance` |
| Entity Relation Graph | Knowledge representation | `entity_nodes` + `entity_edges` |

### 8.2 Entity Confidence

Entities use the same confidence model as memories:

| Mention Count | Confidence |
|---|---|
| 1-2 | `medium` (default) |
| 3+ | `high` (promoted) |

Entity confidence increases via `mention_count` — each additional mention from the extraction pipeline increments the count and may promote confidence.

### 8.3 Graph Access

| Operation | Access Method |
|---|---|
| Auto-extraction | `TrustMem.add()` triggers LLM entity/relation extraction |
| Search | `TrustMem.search()` / `get_all()` includes `relations` key |
| Manual CRUD | `TrustMem.add_relation()` / `search_relations()` / `get_graph()` |

### 8.4 Graph Scoping

Entity nodes and edges are scoped by `user_id`. Graph queries with `user_id` merge user-specific results and shared results (user_id=NULL), matching the memory search merge behavior.

---

## 9. Security

### 9.1 Authentication

API Key Bearer authentication via `TRUSTMEM_API_KEY`:

```bash
# No auth (default, development)
python -m trustmem

# With auth (production)
TRUSTMEM_API_KEY=tm-xxx python -m trustmem
```

- When set: all `/v1/*` endpoints require `Authorization: Bearer <key>`
- `/health` is always public
- API key comparison uses `secrets.compare_digest()` to prevent timing attacks
- Startup warning logged when binding to `0.0.0.0` without API key

### 9.2 CORS

Default: localhost only (`http://localhost:8080`, `http://localhost:3000`, `http://127.0.0.1:8080`, `http://127.0.0.1:3000`).

```bash
# Production: restrict to your domain(s)
CORS_ORIGINS=https://app.example.com,https://admin.example.com

# Development: allow all (not recommended)
CORS_ORIGINS=*
```

- `allow_methods`: GET, POST, DELETE, OPTIONS
- `allow_headers`: Authorization, Content-Type

### 9.3 Request Body Size Limit

`TRUSTMEM_MAX_BODY_SIZE` environment variable (default: 10 MB / 10,485,760 bytes). Requests exceeding this limit receive HTTP 413.

### 9.4 Error Handling

- Global exception handler prevents stack trace leakage (500 responses return generic `"Internal server error"`)
- HTTPException detail messages use generic text (`"Invalid request data"`) — specifics logged server-side only
- `TrustMemConfig.__repr__()` masks secret fields (first 4 chars + `***`)

### 9.5 Data Integrity

| Mechanism | Description |
|---|---|
| `content_hash` | SHA-256 hash for tamper detection (`verify()` / `verify_all()` + Always Verify at read time) |
| Always Verify | Read-time hash re-computation on `search` / `get_all` / `get` |
| Provenance DAG | `memory_provenance` tracks contradiction relationships |
| Audit Trail | All confidence changes recorded in `memory_audit_trail` (hash-chained) |
| Access Log | All read operations recorded in `memory_access_log` (hash-chained) |
| Parameterized queries | SQL injection prevention |
| Thread-safe cache | `threading.Lock` protects embedding cache operations |

### 9.6 Data Isolation

- Data is scoped by `user_id` (set per method)
- TrustMem enforces `user_id` scoping — authentication and authorization are delegated externally
- Tenant isolation (if needed) is handled at the application layer via separate DB files or containers

---

## 10. Deployment

### 10.1 Installation Options

```bash
# Embedded SDK only (SQLite, default)
pip install trustmem

# With FastAPI server
pip install "trustmem[server]"

# With Qdrant vector store
pip install "trustmem[qdrant]"

# With Neo4j graph store
pip install "trustmem[neo4j]"

# Everything
pip install "trustmem[all]"

# HTTP client only (separate package, no trustmem dependency)
pip install trustmem-client
```

### 10.2 Quick Start

```python
# .env: OPENAI_API_KEY=sk-...
from trustmem import TrustMem

m = TrustMem()

# Add memories from conversation
m.add([
    {"role": "user", "content": "I moved to Tokyo last month"},
    {"role": "assistant", "content": "Welcome to Tokyo!"},
], user_id="user-123")

# Search (hybrid: vector + keyword + RRF fusion)
# Returns user's memories + shared knowledge (user_id=NULL)
result = m.search("where does Alex live", user_id="user-123")
print(result["results"])    # Memory list with confidence, freshness
print(result["relations"])  # Knowledge graph relations

# Organization-shared knowledge
m.add([{"role": "user", "content": "Company policy: remote OK"}],
      visibility="organization")
result = m.search("company policy", visibility="organization")

# Get all
all_data = m.get_all(user_id="user-123")

# Delete
m.delete("memory_id_here")
```

### 10.3 Server + Client

```bash
# Server side
pip install "trustmem[server]"
TRUSTMEM_API_KEY=tm-xxx python -m trustmem
```

```python
# Client side
from trustmem_client import TrustMemClient

client = TrustMemClient(
    base_url="http://localhost:8080",
    api_key="tm-xxx",
)
client.add([{"role": "user", "content": "Hello"}], user_id="user-123")
result = client.search("Hello", user_id="user-123")
client.close()
```

### 10.4 Deployment Patterns

| Pattern | Components | Target |
|---|---|---|
| **pip install** | SQLite (all-in-one) | Individual dev, PoC |
| **pip + external stores** | SQLite + Qdrant / Neo4j | Small-medium teams |
| **Docker Compose** | SQLite + Qdrant + Neo4j + FastAPI | Medium organizations |

### 10.5 CI/CD

GitHub Actions workflow (`.github/workflows/ci.yml`):
- Tests on Python 3.11 and 3.12
- Runs both trustmem and trustmem-client test suites
- Uses `DATABASE_URL=:memory:` for test isolation

---

## 11. Architecture Decision Records (ADR)

| ADR | Decision | Rationale |
|---|---|---|
| ADR-001 | ZTM v2: Confidence-based trust model | LLM judgment (high/medium/low) is sufficient for trust evaluation. Simple and deterministic |
| ADR-002 | CONTRADICT with winner/loser | Both memories kept + `contradicts` provenance edge. Winner promoted to high, loser demoted by LLM |
| ADR-003 | Coexistence rules | Multiple values for same category (hobbies, foods, languages) coexist without false CONTRADICT |
| ADR-004 | User/Assistant separation | Only user-stated facts extracted. Assistant responses extracted only for factual answers not rejected by user |
| ADR-005 | Fact verification via LLM knowledge | LLM checks political leaders, capitals, dates against its training data. Wrong/outdated facts receive low confidence |
| ADR-006 | PersistentHandler is the sole implementation | LLM is required. AIClient is mandatory at construction |
| ADR-007 | Audit Trail is auto-recorded | Embedded in `add_memory()` to prevent missed records |
| ADR-008 | content_hash coexists with UUID memory_id | Backward compatibility + content addressing |
| ADR-009 | Mem0/Letta-style two-layer architecture | TrustMem (embedded, 29 methods) + FastAPI (Core CRUD + Admin endpoints) + TrustMemClient (HTTP, separate package) |
| ADR-010 | Client exposes core CRUD only | Server exposes add/search/get_all/delete for TrustMemClient. Admin endpoints for direct access |
| ADR-011 | All config via .env / environment variables | `TrustMemConfig.from_env()` loads everything |
| ADR-012 | search/get_all returns vector + graph results | `{"results": [...], "relations": [...]}` — single API for both |
| ADR-013 | API Key Bearer auth | `TRUSTMEM_API_KEY` unset = no auth (dev); set = Bearer required |
| ADR-014 | Single data ingestion path | All data via `add(list[dict])` → LLM pipeline |
| ADR-015 | Entity confidence matches memory model | Entities use same high/medium/low system. Promoted to high at 3+ mentions |
| ADR-016 | Freshness is informational only | `last_evaluated_at` days returned in search, no ranking penalty |
| ADR-017 | Manual operation patterns abolished | All automated via LLM pipeline |
| ADR-018 | `secrets.compare_digest()` for API key | Prevents timing attacks |
| ADR-019 | Generic HTTPException messages | Prevents internal error detail leakage |
| ADR-020 | `verify_memory()` excludes `contradicts` edges from hash | Contradictions are independent facts, not provenance |
| ADR-021 | Schema migration framework | `schema_version` table for incremental migrations |
| ADR-022 | Embedding cache 10K limit | `_cache_embedding()` helper; oldest 10% evicted on overflow |
| ADR-023 | user_id scoping | Single-layer scoping. user_id per method. Tenant isolation via separate DB/containers |
| ADR-024 | visibility is method arg only, not DB column | `visibility="organization"` = syntactic sugar for `user_id=NULL` |
| ADR-025 | Scope validation required | Scoped methods require `user_id` or `visibility="organization"`. Both None or both set raises ValueError |
| ADR-026 | Search merge: user + org-shared | `user_id` specified returns user's memories + org-shared (NULL). `visibility="organization"` returns NULL only |
| ADR-027 | get_all() vs list_all() | `get_all()` is scope-enforced (general use), `list_all()` is scope-ignored (admin use) |
| ADR-028 | Always Verify: read-time hash verification | search/get_all/get re-compute content_hash. Confidence only goes down (Zero Trust). DB unchanged |
| ADR-029 | Entity scoping: hash(name\|type\|user_id) | Entities are deterministically scoped. Graph queries merge user + shared results |
| ADR-030 | Authorization is external | TrustMem manages user_id scoping. Authentication and authorization are delegated to external services |

---

## 12. Appendix

### A. File Structure

```
memoryos/                                ← Monorepo root
├── pyproject.toml                       ← trustmem package config
├── README.md
├── CLAUDE.md                            ← Claude Code handover document
│
├── src/trustmem/                        ← pip install trustmem
│   ├── __init__.py                      ← TrustMem export
│   ├── sdk.py                           ← TrustMem class (29 methods)
│   ├── server.py                        ← FastAPI server (Core CRUD + Admin endpoints)
│   ├── __main__.py                      ← python -m trustmem entry point
│   ├── constants.py                     ← Validation constants
│   ├── config.py                        ← TrustMemConfig (env var loading + validation)
│   ├── exceptions.py                    ← TrustMemError hierarchy
│   ├── types.py                         ← TypedDict definitions
│   │
│   ├── handler/
│   │   ├── protocol.py                  ← HandlerProtocol (interface)
│   │   ├── persistent.py                ← PersistentHandler (sole implementation)
│   │   └── factory.py                   ← Handler factory
│   │
│   ├── ai/
│   │   └── client.py                    ← AIClient (OpenAI/Azure/compatible)
│   │
│   ├── vectorstore/
│   │   ├── base.py                      ← VectorStore ABC
│   │   ├── sqlite.py                    ← SQLiteVectorStore
│   │   └── qdrant.py                    ← QdrantVectorStore
│   │
│   ├── graphstore/
│   │   ├── base.py                      ← GraphStore ABC
│   │   ├── sqlite.py                    ← SQLite-based graph
│   │   └── neo4j.py                     ← Neo4jGraphStore
│   │
│   ├── extraction/
│   │   ├── pipeline.py                  ← 3-stage extraction pipeline
│   │   ├── prompts.py                   ← LLM prompt templates (Stage 1 / Stage 3)
│   │   └── entities.py                  ← Entity helpers (ID generation, confidence)
│   │
│   ├── ztm/
│   │   ├── pipeline.py                  ← ZTM v2 (confidence model definition only)
│   │   └── constants.py                 ← CONFIDENCE_LEVELS, DEFAULT_CONFIDENCE
│   │
│   └── storage/
│       └── sqlite_store.py              ← SQLiteStore (schema, CRUD, migrations)
│
├── tests/                               ← trustmem tests
│   ├── conftest.py                      ← MockAIClient + fixtures
│   ├── test_sdk.py                      ← SDK full test coverage
│   ├── test_server.py                   ← FastAPI server tests
│   └── test_*.py                        ← Additional test modules
│
├── trustmem-client/                     ← pip install trustmem-client
│   ├── pyproject.toml                   ← httpx only dependency
│   ├── src/trustmem_client/
│   │   ├── __init__.py                  ← TrustMemClient export
│   │   ├── client.py                    ← TrustMemClient (4 methods + close)
│   │   └── py.typed                     ← PEP 561 marker
│   └── tests/
│       └── test_client.py
│
└── docs/
    ├── design/
    │   └── TrustMem_Design.md           ← This document
    └── guides/
        └── Quickstart.md
```

### B. Environment Variables

#### Core

| Variable | Default | Description |
|---|---|---|
| `OPENAI_API_KEY` | — | OpenAI API key (required for `openai` provider) |
| `DATABASE_URL` | `~/.trustmem/trustmem.db` | SQLite database path |

#### AI Client

| Variable | Default | Description |
|---|---|---|
| `EMBED_PROVIDER` | `openai` | `openai` / `azure` / `openai_compat` |
| `LLM_PROVIDER` | `openai` | `openai` / `azure` / `openai_compat` |
| `EMBED_MODEL` | `text-embedding-3-small` | Embedding model name |
| `LLM_MODEL` | `gpt-4o-mini` | LLM model name |
| `OPENAI_BASE_URL` | — | OpenAI-compatible server URL |
| `AZURE_OPENAI_ENDPOINT` | — | Azure OpenAI endpoint |
| `AZURE_OPENAI_API_KEY` | — | Azure OpenAI API key |
| `AZURE_API_VERSION` | `2024-02-01` | Azure API version |

#### Vector Store

| Variable | Default | Description |
|---|---|---|
| `VECTOR_STORE` | `sqlite` | `sqlite` / `qdrant` |
| `EMBED_DIM` | `1536` | Embedding vector dimension |
| `QDRANT_URL` | `http://localhost:6333` | Qdrant server URL |
| `QDRANT_API_KEY` | — | Qdrant API key |
| `QDRANT_COLLECTION` | `trustmem` | Qdrant collection name |

#### Graph Store

| Variable | Default | Description |
|---|---|---|
| `GRAPH_STORE` | `sqlite` | `sqlite` / `neo4j` |
| `NEO4J_URI` | `bolt://localhost:7687` | Neo4j connection URI |
| `NEO4J_USER` | `neo4j` | Neo4j username |
| `NEO4J_PASSWORD` | — | Neo4j password |
| `NEO4J_DATABASE` | `neo4j` | Neo4j database name |

#### Server

| Variable | Default | Description |
|---|---|---|
| `HOST` | `0.0.0.0` | Server bind address |
| `PORT` | `8080` | Server port |
| `TRUSTMEM_API_KEY` | — | API key for Bearer auth (unset = no auth) |
| `CORS_ORIGINS` | `localhost` | Comma-separated allowed CORS origins |
| `TRUSTMEM_MAX_BODY_SIZE` | `10485760` | Max request body size in bytes (10 MB) |

#### Client

| Variable | Default | Description |
|---|---|---|
| `TRUSTMEM_BASE_URL` | `http://localhost:8080` | Server URL for TrustMemClient |
| `TRUSTMEM_API_KEY` | — | API key (shared with server config) |

### C. Validation Constants

```python
VALID_ENTITY_TYPES = {
    "person", "organization", "location", "product",
    "concept", "event", "other",
}

MAX_CONTENT_LENGTH = 32_000  # ~8K tokens
```

### D. Coding Conventions

- Memory IDs generated with `uuid.uuid4()`
- Confidence is `"high"` / `"medium"` / `"low"` (string, 3 values)
- visibility is a method argument only, not a DB column. `"organization"` = user_id=NULL
- Audit Trail recorded in `memory_audit_trail` for all confidence changes (`old_confidence` / `new_confidence`)
- PersistentHandler requires `ai_client` (None raises TypeError)
- Scoped methods require `user_id` or `visibility="organization"` (both None or both set raises ValueError)
- Entity IDs are deterministic: `hash(name|type|user_id)`

---

> **Document End** — TrustMem Zero Trust Memory Design Specification v7.1
