Data Model for Workspaces, Projects, and Page Hierarchies in ai-memory Wiki

The ai-memory wiki implements a strictly-typed three-tier hierarchy in SQLite where Workspaces contain Projects, Projects contain Pages, and foreign-key constraints enforce referential integrity across all entities.

The ai-memory repository organizes wiki content through a hierarchical data model designed to prevent identifier confusion and maintain data consistency. This Rust-based system uses new-type wrappers for database keys and strict relational constraints to ensure every Page belongs to a valid Project, and every Project belongs to exactly one Workspace.

Core Entities in the ai-memory Wiki Data Model

The hierarchy consists of three distinct entities, each represented by strongly-typed structs and corresponding database tables.

Workspace (Top-Level Container)

A Workspace serves as the root container that groups related projects across the entire installation. Defined in crates/ai-memory-store/src/reader.rs within the WorkspaceScopeRow and WorkspaceSummary structs, each workspace carries a unique workspace_id: i64 (wrapped as WorkspaceId), along with name, created_at, and updated_at fields. The workspace name must be unique across the system, enforced by a unique index in the database migration V18__enforce_project_workspace_pairing.sql.

Project (Logical Grouping)

A Project belongs to exactly one workspace and acts as the logical grouping for documentation collections or codebases. The ProjectSummary struct in crates/ai-memory-store/src/reader.rs (lines 897-915) defines the fields: project_id: i64 (wrapped as ProjectId), workspace_id, name, slug, and timestamps. The migration enforces a UNIQUE (project_id, workspace_id) constraint to prevent a project from being associated with multiple workspaces simultaneously.

Page (Hierarchical Content)

Pages represent the leaf nodes of the hierarchy, storing markdown content and metadata under a specific project. Key structs include PageSummary (lines 955-970), PageMeta (lines 992-1009), and ObservationPage (lines 616-632), all defined in crates/ai-memory-store/src/reader.rs. Each page contains:

  • page_id: i64
  • project_id (foreign key to the parent project)
  • path: PagePath (new-type wrapping a slash-separated string like docs/architecture/overview.md)
  • body_hash, author, and timestamps

The PagePath type encodes the hierarchical location within the project, allowing nested directory structures without separate folder entities.

Typed Identifiers and Database Schema

All identifiers utilize strongly-typed new-type structs defined in crates/ai-memory-core/src/ids.rs to prevent accidental mix-ups at compile time:

  • WorkspaceId (lines 31-38): Wraps an i64
  • ProjectId (lines 44-51): Wraps an i64
  • PagePath (lines 101-108): Wraps a String storing slash-separated paths

The database schema in crates/ai-memory-store/migrations/V18__enforce_project_workspace_pairing.sql establishes foreign-key constraints ensuring every project_id references a valid workspace, and every page references a valid project. This migration also creates the unique indexes necessary for the workspace-project pairing integrity.

Implementing the Hierarchy in Code

The following Rust code demonstrates creating a workspace, adding a project, writing a page, and reading it back using the ai-memory store and wiki APIs:

// 1️⃣ Create a new workspace
let ws = ai_memory_store::ops::CreateWorkspace {
    name: "my-workspace".into(),
    description: None,
}.run(&mut store)?;

// 2️⃣ Create a project inside that workspace
let proj = ai_memory_store::ops::CreateProject {
    workspace_id: ws.id,
    name: "my-project".into(),
    slug: "my-project".into(),
    description: None,
}.run(&mut store)?;

// 3️⃣ Write a page into the project
let write_req = ai_memory_wiki::WritePageRequest {
    project_id: proj.id,
    path: PagePath::new("docs/introduction.md".into()),
    body: "## Introduction\nWelcome to the project".into(),

    author: "alice".into(),
    metadata: Default::default(),
};
let resp = ai_memory_wiki::Wiki::write_page(&wiki, write_req)?;

// 4️⃣ Read the page back
let read = ai_memory_store::reader::ReadPageArgs {
    project_id: proj.id,
    path: PagePath::new("docs/introduction.md".into()),
    include_body: true,
};
let page = ai_memory_store::reader::read_page(&store, read)?;
println!("Page body:\n{}", page.body);

Administrative operations such as renaming, deleting, or merging workspaces and projects are handled through the MCP protocol implementations in crates/ai-memory-mcp/src/admin.rs.

Summary

  • The ai-memory wiki uses a three-tier hierarchy: Workspace → Project → Page, stored in a single SQLite database.
  • Strongly-typed identifiers (WorkspaceId, ProjectId, PagePath) in crates/ai-memory-core/src/ids.rs prevent compile-time errors when traversing relationships.
  • Foreign-key constraints in migration V18__enforce_project_workspace_pairing.sql enforce that every project belongs to exactly one workspace and every page belongs to exactly one project.
  • Read operations use structs like WorkspaceSummary, ProjectSummary, and PageSummary defined in crates/ai-memory-store/src/reader.rs.
  • Write operations utilize WritePageRequest from crates/ai-memory-wiki/src/wiki.rs to store markdown content with hierarchical paths.

Frequently Asked Questions

Each project must belong to exactly one workspace, enforced by the workspace_id foreign key in the projects table and a unique constraint preventing cross-workspace duplication. This relationship is defined in the V18__enforce_project_workspace_pairing.sql migration and mirrored in the ProjectSummary struct.

What is the PagePath type used for in ai-memory?

PagePath is a new-type struct wrapping a String that stores slash-separated paths (e.g., docs/architecture/overview.md) to represent the hierarchical location of a page within its parent project. This eliminates the need for separate folder entities while maintaining logical organization.

How does ai-memory enforce referential integrity between entities?

The system uses SQLite foreign-key constraints defined in the database migrations to ensure every project_id references a valid workspace and every page references a valid project. Additionally, the Rust API uses strongly-typed identifiers (WorkspaceId, ProjectId) to prevent accidental ID swaps at compile time.

Where are the data structures for reading pages defined?

Page-related structs such as PageSummary, PageMeta, and ObservationPage are defined in crates/ai-memory-store/src/reader.rs at lines 955-970, 992-1009, and 616-632 respectively. These structs mirror the database schema and provide type-safe interfaces for querying page content and metadata.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →