# Memory Bank Architecture for Persistent Context in OpenWork: Den DB Schema and Full-Text Search

> Explore OpenWork's memory bank architecture for persistent context. Learn how Den DB's dual-table schema with full-text search stores agent context across sessions.

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

---

**OpenWork implements a persistent memory bank using Den DB's dual-table relational architecture with portable timestamps and full-text indexing to maintain agent context across sessions.**

The `different-ai/openwork` repository provides a robust memory bank system that enables AI agents to retain conversational context, code snippets, and embeddings between sessions. This architecture is built upon the Den DB layer, which defines a normalized schema designed for multi-tenant isolation and portable migrations.

## Core Database Schema

The memory bank persists data across two relational tables that separate primary records from contextual snippets.

### The memory Table

Located in [`packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-db/src/schema/memory.ts), the `memory` table stores the primary records for each piece of persisted context. Key columns include `id`, `user_id`, `org_id`, `scope`, `content`, `source`, `created_at`, and `updated_at`.

The `user_id`, `org_id`, and `scope` columns enforce **multi-tenant isolation**, ensuring that contextual data remains segregated between different users and organizations.

### The memory_context Table

The `memory_context` table maintains a one-to-many relationship with the `memory` table, storing linked snippets and origin metadata. Its schema includes `id`, `memory_id`, `snippet`, `origin`, and `created_at` columns.

This separation allows the system to store primary conversation content in `memory` while keeping related excerpts and contextual references in `memory_context`.

## Portable Timestamp Normalization

To guarantee consistent timestamp precision across different database environments, the schema utilizes `CURRENT_TIMESTAMP(3)` as the default for `created_at` fields.

The migration helpers in [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts) include the `memoryCreatedAtDefaultIsPortable` function, which normalizes these defaults during database migrations. This ensures that timestamp precision remains consistent whether the database is provisioned locally or in production.

## Full-Text Search Implementation

The architecture supports efficient natural-language retrieval through a **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) handles idempotent index creation, guarding against duplicate index errors during schema migrations. This enables agents to perform semantic searches across stored memories using MySQL's natural language mode.

## Den API Integration

The memory bank is exposed to OpenWork agents and UI components via the **Den API**, implemented in `ee/apps/den-api/src/routes/memory/*`. These REST endpoints translate HTTP requests into CRUD operations against the underlying tables.

The `/v1/memory` routes provide interfaces for creating, reading, updating, and deleting memory entries, while `/v1/memory/search` leverages the full-text index to return relevant context based on natural language queries.

## Implementation Examples

The following TypeScript examples demonstrate how to interact with the memory bank schema directly.

Insert a new memory entry with multi-tenant scoping:

```typescript
await executor.query(
  `INSERT INTO memory (id, user_id, org_id, scope, content, source)
   VALUES (?, ?, ?, 'user', ?, 'chat')`,
  [memoryId, userId, orgId, chatContent],
);

```

Add a contextual snippet linked to the parent memory:

```typescript
await executor.query(
  `INSERT INTO memory_context (id, memory_id, snippet, origin)
   VALUES (?, ?, ?, 'active_conversation')`,
  [contextId, memoryId, excerpt],
);

```

Perform a full-text search across stored memories:

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

```

## Key Source Files

| File | Role |
|------|------|
| [`packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-db/src/schema/memory.ts) | Defines the `memory` and `memory_context` table schemas |
| [`ee/packages/den-db/src/fulltext.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/fulltext.ts) | Contains migration logic for portable timestamps and full-text index creation |
| `ee/apps/den-api/src/routes/memory/*` | Implements REST endpoints for memory bank operations |
| [`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) | Validation script for schema integrity and index verification |
| [`docs/memory-bank-architecture.md`](https://github.com/different-ai/openwork/blob/main/docs/memory-bank-architecture.md) | High-level design documentation |

## Summary

- OpenWork's memory bank architecture utilizes **two relational tables** (`memory` and `memory_context`) to separate primary records from contextual snippets.
- The **Den DB layer** enforces portable timestamp defaults via `memoryCreatedAtDefaultIsPortable` to ensure cross-environment consistency.
- **Full-text search** capabilities rely on the `memory_content_fulltext` index, managed idempotently by `ensureMemoryFulltextIndex`.
- Multi-tenant isolation is enforced through `user_id`, `org_id`, and `scope` columns across all memory records.
- The **Den API** exposes CRUD and search operations via `/v1/memory` endpoints, enabling persistent context for agents and UI components.

## Frequently Asked Questions

### What tables comprise the memory bank architecture in OpenWork?

The architecture consists of the `memory` table, which stores primary contextual records, and the `memory_context` table, which holds linked snippets and origin metadata. This dual-table design, defined in [`packages/den-db/src/schema/memory.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-db/src/schema/memory.ts), separates core content from supplementary contextual data.

### How does OpenWork ensure timestamp consistency across different database environments?

The system uses `CURRENT_TIMESTAMP(3)` defaults and 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) to normalize timestamp precision during migrations. This guarantees consistent millisecond-level precision regardless of where the database is provisioned.

### What indexing strategy enables fast natural-language search in the memory bank?

OpenWork creates a **FULLTEXT index** named `memory_content_fulltext` on the `memory.content` column. The `ensureMemoryFulltextIndex` function ensures this index exists idempotently, allowing queries using `MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE)` to retrieve relevant memories efficiently.

### How does the Den API expose memory bank functionality to agents?

The Den API implements REST endpoints under `/v1/memory` in `ee/apps/den-api/src/routes/memory/*`, providing interfaces for creating, reading, updating, and deleting memory entries. A dedicated search endpoint leverages the full-text index to enable semantic retrieval of persisted context across sessions.