# Memory Bank Architecture for Persistent Context in OpenWork

> Discover OpenWork's memory bank architecture for persistent context. Learn how Den DB, dual tables, and full-text search maintain agent and user context across sessions.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: architecture
- Published: 2026-08-09

---

**OpenWork implements a persistent memory bank using Den DB with dual-table relational storage, portable timestamp defaults, and full-text search capabilities to maintain agent and user context across sessions.**

The memory bank architecture in OpenWork provides durable storage for contextual information such as chat memories, snippets, and embeddings. Built on the Den DB layer within the `different-ai/openwork` repository—documented in [`docs/memory-bank-architecture.md`](https://github.com/different-ai/openwork/blob/main/docs/memory-bank-architecture.md)—this system ensures that conversational context and agent state survive application restarts while supporting multi-tenant isolation.

## Core Schema Design

The foundation of OpenWork's persistent context relies on two relational tables defined in [`ee/packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/schema/memory.ts).

### The memory and memory_context Tables

The schema implements a parent-child relationship between primary records and their contextual metadata:

- **`memory`**: Stores the primary record for each piece of context with columns `id`, `user_id`, `org_id`, `scope`, `content`, `source`, `created_at`, and `updated_at`. This table supports multi-tenant isolation through `user_id` and `org_id` fields, while `scope` enables granular access control.

- **`memory_context`**: Maintains contextual snippets linked to parent memory entries via `memory_id`. This table includes `id`, `memory_id`, `snippet`, `origin`, and `created_at` columns to store excerpts and provenance information.

Both tables utilize a **portable `created_at` default** set to `CURRENT_TIMESTAMP(3)` to ensure consistent millisecond-precision timestamps across different database environments.

## Migration Strategy and Portability

The Den DB layer enforces schema consistency through idempotent migration helpers located in [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts).

### Portable Timestamp Defaults

The `memoryCreatedAtDefaultIsPortable` function normalizes timestamp defaults during migrations, guaranteeing that the `created_at` column behaves identically whether running in development, staging, or production environments. This normalization prevents timestamp precision drift that could compromise temporal queries.

### Idempotent FULLTEXT Indexing

To enable efficient natural-language retrieval, the system creates a **FULLTEXT index** named `memory_content_fulltext` on the `memory.content` column. The `ensureMemoryFulltextIndex` function guards this operation idempotently, ensuring the index exists regardless of database provisioning method or migration history.

## API Surface and Integration

The memory bank exposes CRUD operations through the Den API, implemented in `ee/apps/den-api/src/routes/memory/*`.

### Den API Routes

The `/v1/memory` endpoints translate HTTP requests into database operations against the `memory` and `memory_context` tables. These routes enforce the multi-tenant isolation model by filtering queries against `user_id`, `org_id`, and `scope` parameters. Agents and UI components interact with persisted context through standardized REST interfaces for reading, writing, and searching memory entries.

## Practical Implementation Examples

The following TypeScript examples demonstrate interacting with the memory bank schema:

```typescript
// Insert a new memory entry (used by the "store chat" capability)
await executor.query(
  `INSERT INTO memory (id, user_id, org_id, scope, content, source)
   VALUES (?, ?, ?, 'user', ?, 'chat')`,
  [memoryId, userId, orgId, chatContent],
);

```

```typescript
// Add a snippet linked to the memory (e.g., an excerpt)
await executor.query(
  `INSERT INTO memory_context (id, memory_id, snippet, origin)
   VALUES (?, ?, ?, 'active_conversation')`,
  [contextId, memoryId, excerpt],
);

```

```typescript
// Full-text search across stored memories
const matches = await executor.query(
  `SELECT id, scope FROM memory
   WHERE MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE) AND id = ?`,
  [searchTerm, memoryId],
);

```

## Summary

- **Dual-table architecture**: The `memory` and `memory_context` tables in [`ee/packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/schema/memory.ts) separate primary records from contextual snippets.
- **Portability guarantees**: The `memoryCreatedAtDefaultIsPortable` function in [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts) ensures consistent timestamp behavior across environments.
- **Full-text search**: The `memory_content_fulltext` index enables natural-language queries, maintained idempotently via `ensureMemoryFulltextIndex`.
- **Multi-tenant API**: Den API routes in `ee/apps/den-api/src/routes/memory/*` expose the memory bank through `/v1/memory` endpoints with isolation by `user_id`, `org_id`, and `scope`.
- **Validation**: The schema is validated through [`ee/packages/den-db/scripts/verify-memory-schema.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/scripts/verify-memory-schema.ts).

## Frequently Asked Questions

### How does OpenWork ensure memory persistence across application restarts?

OpenWork persists context through the Den DB relational layer, storing data in the `memory` and `memory_context` tables with durable defaults. The portable timestamp normalization in [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts) ensures schema consistency across deployments, while the SQL schema itself guarantees data survives application restarts.

### What is the purpose of the memory_context table?

The `memory_context` table stores supplementary snippets and origin metadata linked to parent records in the `memory` table via the `memory_id` foreign key. This separation allows the system to maintain granular contextual excerpts—such as conversation fragments or source attributions—without bloating the primary memory records.

### How does the full-text search implementation work?

The implementation creates a MySQL FULLTEXT index named `memory_content_fulltext` on the `memory.content` column. The `ensureMemoryFulltextIndex` function in [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts) manages this index idempotently during migrations, enabling natural-language queries using `MATCH ... AGAINST` syntax for efficient content retrieval.

### Where is the memory bank schema defined and validated?

The schema definition resides in [`ee/packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/schema/memory.ts), which specifies the table structures and relationships. Validation occurs through [`ee/packages/den-db/scripts/verify-memory-schema.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/scripts/verify-memory-schema.ts), a test harness that confirms schema integrity, timestamp portability, and index presence against live database instances.