# How OpenMAIC Handles Asset Management in Client-Side Storage

> Discover how OpenMAIC manages client-side asset storage using IndexedDB. Learn about its three-tier architecture for blob storage, metadata tracking, and graceful eviction.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-01

---

**OpenMAIC stores all user-generated assets in a client-side IndexedDB database managed through TypeScript utility modules under `lib/persistence`, implementing a three-tier architecture of blob storage, metadata tracking, and grace-based eviction.**

The THU-MAIC/OpenMAIC repository implements a robust asset management system designed for offline-capable media workflows. Rather than relying solely on remote servers, the codebase persists images, audio, video, and PDFs locally using a Dexie-backed IndexedDB layer. This architecture ensures low-latency access while preventing unbounded storage growth through automated cleanup policies.

## Architecture of the Asset Storage Layer

The storage system relies on three tightly-coupled components that separate raw binary data from lifecycle metadata.

### The Binary Blob Store

Located in [[`lib/persistence/asset-byte-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts), this module manages the `assetBytes` table—a Dexie-backed IndexedDB store that maps generated UUIDs to JavaScript `Blob` objects. The module exposes four primary operations:

- **`storeBlob(blob)`**: Writes raw bytes to IndexedDB and returns a unique asset ID.
- **`readAssetBlob(assetId)`**: Retrieves the blob and returns a temporary object URL.
- **`deleteAssetBlob(assetId)`**: Immediately purges the binary data.
- **`clearAllAssets()`**: Wipes the entire blob store during workspace resets.

### The Metadata Collector

The [[`lib/persistence/asset-collector-schedule.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collector-schedule.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collector-schedule.ts) module tracks relationships between assets and stages (scenes within the workspace). When an asset is created, the `scheduleAsset({ assetId, stageId })` function records the association and timestamps the entry. This registry determines when an asset becomes orphaned and eligible for cleanup.

### Grace Period and Eviction Logic

[[`lib/persistence/asset-collection-grace.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collection-grace.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collection-grace.ts) implements the safety mechanism that prevents premature deletion. Assets marked as orphaned remain in IndexedDB for a configurable grace period (default approximately five minutes) after their originating stage is destroyed. This buffer allows for undo operations and prevents data loss during rapid workspace modifications.

## Asset Lifecycle Workflow

Understanding the complete lifecycle clarifies how these components interact during runtime.

1. **Creation**: Components such as the whiteboard or video exporter call `storeBlob()` to persist a file. This generates a UUID and writes the blob to the `assetBytes` table.
2. **Registration**: Immediately after storage, the component invokes `scheduleAsset()` to link the asset ID to its current stage ID in the metadata collector.
3. **Retrieval**: UI components request assets via `readAssetBlob()`, which creates an object URL via `URL.createObjectURL()` for immediate rendering.
4. **Cleanup**: When a stage is deleted, the collector marks associated assets as orphaned. After the grace period expires, [`asset-collection-grace.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-collection-grace.ts) triggers `deleteAssetBlob()` to free IndexedDB space.
5. **Fallback**: If `readAssetBlob()` returns null (indicating corruption or eviction), the application falls back to the original remote URL, ensuring continuous UI functionality.

## Working with the Storage API

Developers can interact with the asset management layer through the public utility functions exposed in the persistence modules.

### Storing a New Asset

When handling file uploads or canvas exports, persist the binary data and register its metadata:

```typescript
import { storeBlob } from '@/lib/persistence/asset-byte-store';
import { scheduleAsset } from '@/lib/persistence/asset-collector-schedule';

async function persistImage(file: Blob, stageId: string): Promise<string> {
  const assetId = await storeBlob(file);      // Writes to IndexedDB
  await scheduleAsset({ assetId, stageId });  // Registers relationship
  return assetId;
}

```

### Retrieving Assets for Display

Convert stored blobs back into displayable URLs:

```typescript
import { readAssetBlob } from '@/lib/persistence/asset-byte-store';

async function getImageUrl(assetId: string): Promise<string> {
  const blob = await readAssetBlob(assetId);
  // Handle eviction gracefully
  return blob ? URL.createObjectURL(blob) : '';
}

```

### Manual Eviction

While automatic cleanup handles most scenarios, explicit deletion is available:

```typescript
import { deleteAssetBlob } from '@/lib/persistence/asset-byte-store';

async function purgeAsset(assetId: string): Promise<void> {
  await deleteAssetBlob(assetId);  // Removes raw bytes from IndexedDB
}

```

### Resetting Workspace Storage

To completely clear the database and prevent re-initialization on next load:

```typescript
import { clearAllAssets } from '@/lib/persistence/asset-byte-store';
import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage';

await clearAllAssets();
localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, 'true');

```

The `MAIC_DISCARDED_DB` flag in [[`configs/storage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts) short-circuits the IndexedDB bootstrap sequence during the next session initialization.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [[`configs/storage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts) | Declares `LOCALSTORAGE_KEY_DISCARDED_DB` for reset state management. |
| [[`lib/persistence/asset-byte-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts) | Core IndexedDB wrapper for binary blob CRUD operations. |
| [[`lib/persistence/asset-collector-schedule.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collector-schedule.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collector-schedule.ts) | Tracks asset-to-stage relationships and cleanup eligibility. |
| [[`lib/persistence/asset-collection-grace.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collection-grace.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collection-grace.ts) | Implements grace-period logic before eviction. |
| [[`lib/persistence/bootstrap.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/bootstrap.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/bootstrap.ts) | Initializes the IndexedDB schema and wires collectors to the workspace lifecycle. |

## Summary

- **Client-side persistence**: OpenMAIC uses Dexie-backed IndexedDB in `lib/persistence` to store binary assets locally, eliminating network latency for media-heavy workflows.
- **Three-tier architecture**: The system separates blob storage ([`asset-byte-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-byte-store.ts)), metadata tracking ([`asset-collector-schedule.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-collector-schedule.ts)), and eviction logic ([`asset-collection-grace.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-collection-grace.ts)) to maintain data integrity.
- **Grace-based cleanup**: Assets survive for approximately five minutes after their stage is deleted, preventing accidental data loss while controlling storage growth.
- **Resilient retrieval**: The storage layer falls back to remote URLs when local assets are unavailable, ensuring UI stability.
- **Explicit API**: Functions like `storeBlob()`, `readAssetBlob()`, and `scheduleAsset()` provide type-safe interfaces for components while abstracting IndexedDB complexity.

## Frequently Asked Questions

### What database technology does OpenMAIC use for asset management?

OpenMAIC uses **IndexedDB** wrapped by the **Dexie** library for client-side storage. The [[`lib/persistence/asset-byte-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts) module manages the `assetBytes` table, which stores raw binary data as JavaScript Blob objects mapped to UUID keys.

### How does OpenMAIC prevent the browser storage from growing indefinitely?

The codebase implements a **grace-period eviction** strategy. When a stage (scene) is deleted, its associated assets enter an orphaned state but remain in IndexedDB for a configurable timeout (default ~5 minutes). The [[`asset-collection-grace.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-collection-grace.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-collection-grace.ts) module monitors these timers and triggers `deleteAssetBlob()` only after the safety window expires, balancing space efficiency with undo capabilities.

### What happens if an asset is missing from IndexedDB?

The storage layer implements a **fallback mechanism**. If `readAssetBlob()` returns null—indicating the asset was evicted, corrupted, or never stored—the application transparently falls back to the original remote URL. This ensures that media elements continue to display even when the local database is cleared or unavailable.

### How do I completely reset the asset storage in OpenMAIC?

Invoke `clearAllAssets()` from [[`asset-byte-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/asset-byte-store.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/asset-byte-store.ts) to wipe IndexedDB, then set the `MAIC_DISCARDED_DB` flag in `localStorage` using the `LOCALSTORAGE_KEY_DISCARDED_DB` constant from [[`configs/storage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/configs/storage.ts). This flag prevents the [[`bootstrap.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/bootstrap.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/persistence/bootstrap.ts) module from re-initializing the database during the next application load, effectively treating the session as a fresh installation.