Mutable Store API
Last Updated: 2026-01-09
Complete API reference for shared mutable data with ACID transaction guarantees.
Overview
The Mutable Store API (Layer 1c) provides methods for storing TRULY SHARED mutable data across ALL memory spaces. This layer has NO memorySpace scoping - it's globally shared just like Layer 1b (Immutable).
Critical Distinction:
- NO memorySpaceId parameter - This layer is shared across ALL spaces
- Accessible from any memory space
- Perfect for: Inventory, config, counters, live shared state
Key Characteristics:
- TRULY Shared - ALL memory spaces can access
- NO Isolation - Not scoped to memorySpace (unlike L1a, L2, L3)
- Mutable - Designed to be updated in-place
- ACID - Atomic transactions
- Current-value - No version history (by design)
- Fast - Optimized for frequent updates
- Purgeable - Can delete keys
- Multi-Tenant - Optional
tenantIdfor SaaS isolation (auto-injected from AuthContext)
Comparison to Other Stores:
| Feature | Conversations (1a) | Immutable (1b) | Mutable (1c) | Vector (2) | Facts (3) |
|---|---|---|---|---|---|
| Scoping | memorySpace | NO scoping | NO scoping | memorySpace | memorySpace |
| Privacy | Private to space | TRULY Shared | TRULY Shared | Private to space | Private to space |
| Versioning | N/A (append) | Auto | None | Auto | Auto |
| Updates | Append only | New version | In-place | New version | New version |
| Use Case | Chats | KB, policies | Live data | Search index | Extracted facts |
Core Operations
set()
Set a key to a value (creates or overwrites).
Signature:
cortex.mutable.set(
namespace: string,
key: string,
value: any,
userId?: string,
metadata?: Record<string, unknown>,
options?: SetMutableOptions
): Promise<MutableRecord>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Logical grouping (e.g., 'inventory', 'config', 'counters') |
key | string | Yes | — | Unique key within namespace |
value | any | Yes | — | JSON-serializable value |
userId | string | No | — | Link to user (enables GDPR cascade). Must reference existing user. |
metadata | Record<string, unknown> | No | — | Additional metadata to store with the record |
options | SetMutableOptions | No | — | Additional options (graph sync is automatic when CORTEX_GRAPH_SYNC=true) |
Returns:
interface MutableRecord {
_id: string;
namespace: string;
key: string;
value: any;
userId?: string; // OPTIONAL: User link (GDPR-enabled)
tenantId?: string; // Auto-injected from AuthContext for multi-tenancy
metadata?: Record<string, unknown>;
createdAt: number; // Unix timestamp
updatedAt: number; // Unix timestamp
}
Example 1: System data (no userId)
// Set inventory quantity (system-wide)
const record = await cortex.mutable.set("inventory", "widget-qty", 100);
console.log(record.value); // 100
console.log(record.userId); // undefined (system data)
// Update (overwrites)
await cortex.mutable.set("inventory", "widget-qty", 95); // Now 95
// No version history - previous value (100) is gone!
Example 2: User-specific data (with userId)
// Set user session data (GDPR-enabled)
const session = await cortex.mutable.set(
"user-sessions",
"session-abc123",
{
startedAt: new Date(),
lastActivity: new Date(),
pagesViewed: 5,
},
"user-123", // ← Links to user (enables GDPR cascade)
);
console.log(session.userId); // "user-123"
// When user requests deletion:
await cortex.users.delete("user-123", { cascade: true });
// This session is automatically deleted!
Errors:
CortexError('INVALID_NAMESPACE')- Namespace is empty or invalidCortexError('INVALID_KEY')- Key is empty or invalidCortexError('VALUE_TOO_LARGE')- Value exceeds size limitCortexError('USER_NOT_FOUND')- userId doesn't reference existing userCortexError('CONVEX_ERROR')- Database error
get()
Get current value for a key.
Signature:
cortex.mutable.get(
namespace: string,
key: string
): Promise<any | null>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace |
key | string | Yes | — | Key |
Returns:
any | null- Current value, ornullif key doesn't exist
Example:
const qty = await cortex.mutable.get("inventory", "widget-qty");
if (qty !== null) {
console.log(`Current quantity: ${qty}`);
if (qty > 0) {
// Process order
}
} else {
console.log("Product not found");
}
update()
Atomically update a value.
Signature:
cortex.mutable.update(
namespace: string,
key: string,
updater: (current: any) => any
): Promise<MutableRecord>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace |
key | string | Yes | — | Key |
updater | (current: any) => any | Yes | — | Function that receives current value, returns new value |
Returns:
MutableRecord- Updated record
Side Effects:
- ACID: Read, compute, write atomically
- No race conditions (Convex handles locking)
Example:
// Decrement inventory
await cortex.mutable.update("inventory", "widget-qty", (current) => {
if (current === null || current <= 0) {
throw new Error("Out of stock");
}
return current - 1;
});
// Increment counter
await cortex.mutable.update("counters", "total-refunds", (current) => {
return (current || 0) + 1;
});
// Update object
await cortex.mutable.update("config", "api-settings", (current) => {
return {
...current,
endpoint: "https://api.example.com/v2",
timeout: 30000,
};
});
Errors:
CortexError('KEY_NOT_FOUND')- Key doesn't existCortexError('UPDATE_FAILED')- Updater function threw errorCortexError('CONVEX_ERROR')- Database error
increment()
Atomically increment a numeric value (convenience helper).
Signature:
cortex.mutable.increment(
namespace: string,
key: string,
amount?: number
): Promise<MutableRecord>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace |
key | string | Yes | — | Key |
amount | number | No | 1 | Amount to increment |
Returns:
MutableRecord- Updated record with incremented value
Example:
// Initialize counter
await cortex.mutable.set("counters", "page-views", 0);
// Increment by 1 (default)
await cortex.mutable.increment("counters", "page-views");
// Increment by custom amount
await cortex.mutable.increment("counters", "page-views", 10);
const views = await cortex.mutable.get("counters", "page-views");
console.log(`Total views: ${views}`); // 11
This is a convenience wrapper around update(). For more control, use update() directly.
decrement()
Atomically decrement a numeric value (convenience helper).
Signature:
cortex.mutable.decrement(
namespace: string,
key: string,
amount?: number
): Promise<MutableRecord>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace |
key | string | Yes | — | Key |
amount | number | No | 1 | Amount to decrement |
Returns:
MutableRecord- Updated record with decremented value
Example:
// Initialize inventory
await cortex.mutable.set("inventory", "widget-qty", 100);
// Customer order (decrement)
await cortex.mutable.decrement("inventory", "widget-qty", 5);
const remaining = await cortex.mutable.get("inventory", "widget-qty");
console.log(`Remaining: ${remaining}`); // 95
getRecord()
Get full record with metadata (not just the value).
Signature:
cortex.mutable.getRecord(
namespace: string,
key: string
): Promise<MutableRecord | null>
Returns:
interface MutableRecord {
_id: string;
namespace: string;
key: string;
value: any;
userId?: string;
metadata?: Record<string, unknown>;
createdAt: number; // Unix timestamp
updatedAt: number; // Unix timestamp
}
Example:
// get() returns value only
const value = await cortex.mutable.get("config", "timeout");
console.log(value); // 30
// getRecord() returns full record with metadata
const record = await cortex.mutable.getRecord("config", "timeout");
console.log(record.value); // 30
console.log(record.createdAt); // 1729987200000
console.log(record.updatedAt); // 1729987200000
Use when: You need access to timestamps or metadata.
delete()
Delete a key.
Signature:
cortex.mutable.delete(
namespace: string,
key: string,
options?: DeleteMutableOptions
): Promise<DeleteResult>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace |
key | string | Yes | — | Key |
options | DeleteMutableOptions | No | — | Additional options (graph cleanup is automatic when CORTEX_GRAPH_SYNC=true) |
Returns:
interface DeleteResult {
deleted: boolean;
namespace: string;
key: string;
}
Example:
// Remove product from inventory
const result = await cortex.mutable.delete("inventory", "discontinued-widget");
console.log(`Deleted: ${result.deleted}`);
// Graph cleanup is automatic when CORTEX_GRAPH_SYNC=true
await cortex.mutable.delete("inventory", "old-item");
transaction()
Execute multiple operations atomically.
Signature:
cortex.mutable.transaction(
operations: TransactionOperation[]
): Promise<TransactionResult>
Parameters:
interface TransactionOperation {
op: "set" | "update" | "delete" | "increment" | "decrement";
namespace: string;
key: string;
value?: any; // Required for "set" and "update" ops
amount?: number; // For "increment"/"decrement" (default: 1)
}
Returns:
interface TransactionResult {
success: boolean;
operationsExecuted: number;
results: unknown[];
}
Example:
// Record sale and update inventory (atomic)
await cortex.mutable.transaction([
{ op: "decrement", namespace: "inventory", key: "widget-qty", amount: 1 },
{ op: "increment", namespace: "counters", key: "total-sales", amount: 1 },
{ op: "set", namespace: "state", key: "last-sale", value: Date.now() },
]);
// All operations succeed together or fail together (ACID)
// Multi-item inventory transfer
await cortex.mutable.transaction([
{ op: "decrement", namespace: "inventory", key: "product-a", amount: 10 },
{ op: "increment", namespace: "inventory", key: "product-b", amount: 10 },
]);
// Complex state update
const result = await cortex.mutable.transaction([
{ op: "set", namespace: "config", key: "api-version", value: "v2" },
{ op: "delete", namespace: "cache", key: "old-api-data" },
{ op: "increment", namespace: "counters", key: "config-changes" },
]);
console.log(result.success); // true
console.log(result.operationsExecuted); // 3
Errors:
CortexError('TRANSACTION_FAILED')- Transaction rolled back- Key must exist for
update,increment,decrement, anddeleteoperations
Querying
list()
List keys in a namespace with filtering, sorting, and pagination.
Signature:
cortex.mutable.list(
filter: ListMutableFilter
): Promise<MutableRecord[]>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace to list |
keyPrefix | string | No | — | Keys starting with prefix |
tenantId | string | No | — | Multi-tenancy filter (auto-injected from AuthContext) |
userId | string | No | — | Filter by user |
limit | number | No | 100 | Max results |
offset | number | No | — | Pagination offset |
updatedAfter | number | No | — | Filter by updatedAt > timestamp |
updatedBefore | number | No | — | Filter by updatedAt < timestamp |
sortBy | "key" | "updatedAt" | "accessCount" | No | — | Sort field |
sortOrder | "asc" | "desc" | No | "asc" | Sort direction |
Example:
// List all inventory items
const items = await cortex.mutable.list({
namespace: "inventory",
limit: 100,
});
items.forEach((item) => {
console.log(`${item.key}: ${item.value}`);
});
// List specific prefix with sorting
const widgets = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "widget-",
sortBy: "updatedAt",
sortOrder: "desc",
});
// List items updated in the last 24 hours
const recentItems = await cortex.mutable.list({
namespace: "inventory",
updatedAfter: Date.now() - 24 * 60 * 60 * 1000,
sortBy: "updatedAt",
sortOrder: "desc",
});
// Pagination example
const page2 = await cortex.mutable.list({
namespace: "inventory",
limit: 20,
offset: 20, // Skip first 20 items
});
count()
Count keys in namespace with optional filters.
Signature:
cortex.mutable.count(
filter: CountMutableFilter
): Promise<number>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace to count |
tenantId | string | No | — | Multi-tenancy filter (auto-injected from AuthContext) |
userId | string | No | — | Filter by user |
keyPrefix | string | No | — | Keys starting with prefix |
updatedAfter | number | No | — | Filter by updatedAt > timestamp |
updatedBefore | number | No | — | Filter by updatedAt < timestamp |
Example:
// Total inventory items
const total = await cortex.mutable.count({ namespace: "inventory" });
// Count items with prefix
const widgetCount = await cortex.mutable.count({
namespace: "inventory",
keyPrefix: "widget-",
});
// Count user-specific items
const userCount = await cortex.mutable.count({
namespace: "user-sessions",
userId: "user-123",
});
// Count items updated in the last 24 hours
const recentCount = await cortex.mutable.count({
namespace: "inventory",
updatedAfter: Date.now() - 24 * 60 * 60 * 1000,
});
// Count items not updated in the last week (stale items)
const staleCount = await cortex.mutable.count({
namespace: "cache",
updatedBefore: Date.now() - 7 * 24 * 60 * 60 * 1000,
});
exists()
Check if key exists.
Signature:
cortex.mutable.exists(
namespace: string,
key: string
): Promise<boolean>
Example:
if (await cortex.mutable.exists("inventory", "widget-qty")) {
const qty = await cortex.mutable.get("inventory", "widget-qty");
} else {
// Initialize
await cortex.mutable.set("inventory", "widget-qty", 100);
}
Namespaces
Common Namespaces
inventory - Product quantities
await cortex.mutable.set("inventory", "product-x", 150);
await cortex.mutable.update("inventory", "product-x", (qty) => qty - 1);
config - Live configuration
await cortex.mutable.set(
"config",
"api-endpoint",
"https://api.example.com/v2",
);
await cortex.mutable.set("config", "max-retries", 3);
counters - Shared counters/metrics
await cortex.mutable.update("counters", "total-requests", (n) => (n || 0) + 1);
await cortex.mutable.update("counters", "active-users", (n) => n + 1);
docs - Live documentation references
await cortex.mutable.set("docs", "api-version", "v2.1.0");
await cortex.mutable.set(
"docs",
"changelog-url",
"https://docs.example.com/changelog",
);
state - Agent collaboration state
await cortex.mutable.set("state", "active-campaign-id", "camp-2025-q4");
await cortex.mutable.update("state", "agents-available", (list) => [
...list,
"agent-5",
]);
Complex Data Types
The Mutable Store supports any JSON-serializable value, not just primitives. You can store:
Storing Objects
// Store complex configuration object
await cortex.mutable.set("config", "email-settings", {
smtp: {
host: "smtp.example.com",
port: 587,
secure: true,
},
from: "noreply@example.com",
templates: {
welcome: "templates/welcome.html",
reset: "templates/reset.html",
},
});
// Retrieve and use
const emailConfig = await cortex.mutable.get("config", "email-settings");
sendEmail(emailConfig.smtp, emailConfig.from);
Storing Arrays
// Store product catalog
await cortex.mutable.set("inventory", "featured-products", [
{ id: "prod-1", name: "Widget A", price: 19.99, stock: 150 },
{ id: "prod-2", name: "Widget B", price: 29.99, stock: 75 },
{ id: "prod-3", name: "Widget C", price: 39.99, stock: 200 },
]);
// Atomically update array
await cortex.mutable.update("inventory", "featured-products", (products) => {
return products.map((p) =>
p.id === "prod-1" ? { ...p, stock: p.stock - 1 } : p,
);
});
Storing Nested Structures
// Store complete store configuration
await cortex.mutable.set("stores", "store-15", {
id: "store-15",
name: "Downtown Location",
address: {
street: "123 Main St",
city: "Springfield",
state: "IL",
zip: "62701",
},
hours: {
monday: { open: "09:00", close: "21:00" },
tuesday: { open: "09:00", close: "21:00" },
sunday: { open: "10:00", close: "18:00" },
},
departments: [
{ name: "Produce", manager: "Alice" },
{ name: "Dairy", manager: "Bob" },
{ name: "Bakery", manager: "Carol" },
],
metrics: {
revenue: 125000,
customersToday: 450,
averageTransaction: 67.5,
},
});
Size Considerations
// Good: Reasonable size (< 100KB recommended)
await cortex.mutable.set("config", "app-settings", {
theme: "dark",
language: "en",
notifications: true,
// ... dozens of settings
});
// Caution: Very large objects (> 1MB)
await cortex.mutable.set("cache", "entire-product-catalog", {
// Thousands of products...
// Consider breaking into multiple keys instead
});
// Better: Split large datasets
await cortex.mutable.set("catalog", "page-1", products.slice(0, 100));
await cortex.mutable.set("catalog", "page-2", products.slice(100, 200));
await cortex.mutable.set("catalog", "page-3", products.slice(200, 300));
Hierarchical Data Patterns
For hierarchical/relational data like "Produce" → "Inventory" → "Grocery Store 15" → "Grocery Stores", Cortex provides several patterns:
Pattern 1: Hierarchical Keys (Recommended for Queries)
Use delimiters in keys to create hierarchies:
// Pattern: namespace:key-with:delimiters
await cortex.mutable.set(
"grocery-stores",
"store-15:inventory:produce:apples",
{
quantity: 150,
unit: "lbs",
price: 2.99,
supplier: "Local Farms",
},
);
await cortex.mutable.set(
"grocery-stores",
"store-15:inventory:produce:bananas",
{
quantity: 200,
unit: "lbs",
price: 1.49,
supplier: "Tropical Import Co",
},
);
await cortex.mutable.set("grocery-stores", "store-15:inventory:dairy:milk", {
quantity: 50,
unit: "gallons",
price: 4.99,
supplier: "Dairy Fresh",
});
// Query all produce for store 15
const produce = await cortex.mutable.list({
namespace: "grocery-stores",
keyPrefix: "store-15:inventory:produce:",
});
// Query all inventory for store 15
const allInventory = await cortex.mutable.list({
namespace: "grocery-stores",
keyPrefix: "store-15:inventory:",
});
Benefits:
- Easy prefix queries
- Flat structure (no deep nesting)
- Good performance
- Easy to add/remove items
Pattern 2: Hierarchical Namespaces
Use delimiters in namespaces for organizational hierarchy:
// Pattern: hierarchical:namespace with flat keys
await cortex.mutable.set(
"grocery-stores:store-15:inventory",
"produce-apples",
{
quantity: 150,
price: 2.99,
},
);
await cortex.mutable.set(
"grocery-stores:store-15:inventory",
"produce-bananas",
{
quantity: 200,
price: 1.49,
},
);
await cortex.mutable.set(
"grocery-stores:store-15:metrics",
"daily-revenue",
12500,
);
await cortex.mutable.set(
"grocery-stores:store-15:metrics",
"customer-count",
450,
);
// Query entire namespace
const storeInventory = await cortex.mutable.list({
namespace: "grocery-stores:store-15:inventory",
});
const storeMetrics = await cortex.mutable.list({
namespace: "grocery-stores:store-15:metrics",
});
Benefits:
- Logical namespace organization
- Clear separation of concerns
- Easy to list all keys in a category
Pattern 3: Nested Object Values (Recommended for Related Data)
Store hierarchy as nested object in value:
// Store entire store hierarchy in one key
await cortex.mutable.set("grocery-stores", "store-15", {
id: "store-15",
name: "Downtown Location",
inventory: {
produce: {
apples: { quantity: 150, price: 2.99, unit: "lbs" },
bananas: { quantity: 200, price: 1.49, unit: "lbs" },
oranges: { quantity: 100, price: 3.49, unit: "lbs" },
},
dairy: {
milk: { quantity: 50, price: 4.99, unit: "gallons" },
cheese: { quantity: 75, price: 6.99, unit: "lbs" },
},
bakery: {
bread: { quantity: 100, price: 3.99, unit: "loaves" },
donuts: { quantity: 200, price: 1.99, unit: "each" },
},
},
metrics: {
revenue: 12500,
customers: 450,
avgTransaction: 27.78,
},
});
// Update specific nested value atomically
await cortex.mutable.update("grocery-stores", "store-15", (store) => ({
...store,
inventory: {
...store.inventory,
produce: {
...store.inventory.produce,
apples: {
...store.inventory.produce.apples,
quantity: store.inventory.produce.apples.quantity - 10,
},
},
},
}));
// Or use helper function
async function updateNestedInventory(
storeId: string,
department: string,
item: string,
updates: Partial<any>,
) {
await cortex.mutable.update("grocery-stores", storeId, (store) => ({
...store,
inventory: {
...store.inventory,
[department]: {
...store.inventory[department],
[item]: {
...store.inventory[department][item],
...updates,
},
},
},
}));
}
await updateNestedInventory("store-15", "produce", "apples", { quantity: 140 });
Benefits:
- Single atomic update for related data
- Natural data representation
- Smaller transaction scope
- Easy to retrieve entire hierarchy
Trade-offs:
- Large objects can be slow to update
- Need careful atomic updates
- Harder to query subsets
Pattern 4: Hybrid (Recommended for Large Systems)
Combine approaches for optimal performance:
// Store metadata in nested object
await cortex.mutable.set("grocery-stores", "store-15-meta", {
id: "store-15",
name: "Downtown Location",
address: { street: "123 Main St", city: "Springfield" },
departments: ["produce", "dairy", "bakery"],
});
// Store inventory items individually with hierarchical keys
await cortex.mutable.set("inventory", "store-15:produce:apples", {
quantity: 150,
price: 2.99,
unit: "lbs",
});
await cortex.mutable.set("inventory", "store-15:produce:bananas", {
quantity: 200,
price: 1.49,
unit: "lbs",
});
// Store aggregated metrics separately
await cortex.mutable.set("metrics", "store-15-daily", {
revenue: 12500,
customers: 450,
date: new Date().toISOString(),
});
Benefits:
- Optimal for large datasets
- Fast queries and updates
- Good separation of concerns
- Scalable architecture
Helper Functions for Hierarchical Data
// Parse hierarchical keys
function parseKey(key: string): string[] {
return key.split(":");
}
function buildKey(...parts: string[]): string {
return parts.join(":");
}
// Example usage
const key = buildKey("store-15", "inventory", "produce", "apples");
await cortex.mutable.set("grocery-stores", key, { quantity: 150 });
const parts = parseKey(key); // ["store-15", "inventory", "produce", "apples"]
console.log(`Store: ${parts[0]}, Department: ${parts[2]}, Item: ${parts[3]}`);
// Query helpers
async function listByPrefix(namespace: string, ...prefixParts: string[]) {
const prefix = buildKey(...prefixParts);
return await cortex.mutable.list({
namespace,
keyPrefix: prefix,
});
}
// Get all produce for store 15
const produce = await listByPrefix(
"grocery-stores",
"store-15",
"inventory",
"produce",
);
// Get all inventory for store 15
const inventory = await listByPrefix("grocery-stores", "store-15", "inventory");
Choosing the Right Pattern
| Pattern | Best For | Query Performance | Update Performance | Complexity |
|---|---|---|---|---|
| Hierarchical Keys | Prefix queries, flat relationships | Excellent | Excellent | Low |
| Hierarchical Namespaces | Organizational structure | Good | Excellent | Low |
| Nested Object Values | Related data, small datasets | Moderate | Moderate | Medium |
| Hybrid | Large systems, mixed access | Excellent | Excellent | Medium |
Recommendations:
- Inventory systems: Hierarchical Keys or Hybrid
- Configuration: Nested Object Values
- Metrics/Analytics: Hierarchical Keys
- Multi-tenant data: Hierarchical Namespaces
- Small datasets (< 100 items): Nested Object Values
- Large datasets (> 1000 items): Hierarchical Keys + Hybrid
Complete Real-World Example: Grocery Store Chain
Here's a comprehensive example using the Hybrid pattern for a multi-store inventory system:
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Setup: Initialize grocery store chain
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Store chain metadata (nested object - small, related data)
await cortex.mutable.set("chain", "metadata", {
name: "Fresh Foods Chain",
totalStores: 25,
headquarters: "Springfield, IL",
storeIds: ["store-1", "store-15", "store-23"],
});
// Store individual store configs (nested object per store)
await cortex.mutable.set("stores", "store-15", {
id: "store-15",
name: "Downtown Location",
address: "123 Main St, Springfield",
manager: "Alice Johnson",
departments: ["produce", "dairy", "bakery", "meat"],
openHours: { open: "09:00", close: "21:00" },
});
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Inventory: Hierarchical keys (scalable, fast queries)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Set initial inventory (hierarchical keys: store:dept:item)
await cortex.mutable.set("inventory", "store-15:produce:apples", {
quantity: 150,
unit: "lbs",
price: 2.99,
supplier: "Local Farms",
lastRestocked: new Date(),
});
await cortex.mutable.set("inventory", "store-15:produce:bananas", {
quantity: 200,
unit: "lbs",
price: 1.49,
supplier: "Tropical Import",
});
await cortex.mutable.set("inventory", "store-15:dairy:milk", {
quantity: 50,
unit: "gallons",
price: 4.99,
supplier: "Dairy Fresh",
});
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Operations: Customer purchases (atomic updates)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Customer buys 10 lbs of apples
await cortex.mutable.update("inventory", "store-15:produce:apples", (item) => {
if (item.quantity < 10) {
throw new Error("Insufficient stock");
}
return {
...item,
quantity: item.quantity - 10,
};
});
// Multi-item purchase (ACID transaction with decrement operations)
// Note: transaction() uses array-based operations, not callback functions
await cortex.mutable.transaction([
// Buy apples, bananas, and milk (using decrement)
{
op: "decrement",
namespace: "inventory",
key: "store-15:produce:apples",
amount: 5,
},
{
op: "decrement",
namespace: "inventory",
key: "store-15:produce:bananas",
amount: 3,
},
{
op: "decrement",
namespace: "inventory",
key: "store-15:dairy:milk",
amount: 2,
},
// Increment sales counter
{
op: "increment",
namespace: "metrics",
key: "store-15-daily-sales",
amount: 1,
},
]);
// All operations succeed together or fail together (ACID)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Queries: Get inventory by hierarchy level
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Get all produce for store 15
const produce = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "store-15:produce:",
});
console.log(`Store 15 produce items: ${produce.length}`);
produce.forEach((item) => {
console.log(
`- ${item.key.split(":")[2]}: ${item.value.quantity} ${item.value.unit}`,
);
});
// Get ALL inventory for store 15
const allInventory = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "store-15:",
});
// Get specific item across all stores
const allApples = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "store-", // Gets all stores
});
const applesOnly = allApples.filter((r) => r.key.endsWith(":apples"));
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Aggregation: Chain-wide reports
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Get total inventory across all stores
async function getChainwideInventory(department?: string) {
const allStores = await cortex.mutable.get("chain", "metadata");
const inventory: Record<string, number> = {};
for (const storeId of allStores.storeIds) {
const prefix = department ? `${storeId}:${department}:` : `${storeId}:`;
const items = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: prefix,
});
items.forEach((record) => {
const itemName = record.key.split(":").pop()!;
inventory[itemName] = (inventory[itemName] || 0) + record.value.quantity;
});
}
return inventory;
}
// Total produce across all stores
const chainProduce = await getChainwideInventory("produce");
console.log("Chain-wide produce:", chainProduce);
// { apples: 3750, bananas: 5000, oranges: 2100 }
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Restocking: Bulk updates
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Restock all produce for store 15 (using transaction for atomicity)
// Note: For simple numeric inventory, use increment operations
async function restockDepartment(
storeId: string,
department: string,
items: Record<string, number>,
) {
// Build array of increment operations
const operations = Object.entries(items).map(([itemName, quantity]) => ({
op: "increment" as const,
namespace: "inventory",
key: `${storeId}:${department}:${itemName}`,
amount: quantity,
}));
// Execute all increments atomically
await cortex.mutable.transaction(operations);
}
await restockDepartment("store-15", "produce", {
apples: 100,
bananas: 150,
oranges: 75,
});
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Low Stock Alerts
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function checkLowStock(storeId: string, threshold: number = 20) {
const inventory = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: `${storeId}:`,
});
const lowStock = inventory.filter((item) => item.value.quantity < threshold);
if (lowStock.length > 0) {
console.log(`[ALERT] Low stock for ${storeId}:`);
lowStock.forEach((item) => {
const [store, dept, product] = item.key.split(":");
console.log(
` - ${dept}/${product}: ${item.value.quantity} ${item.value.unit} remaining`,
);
});
}
return lowStock;
}
await checkLowStock("store-15", 25);
This example demonstrates:
- Complex objects as values (store metadata, inventory items)
- Hierarchical keys for scalable queries (
store:dept:item) - Nested objects for related configuration
- Atomic transactions across multiple keys
- Prefix-based queries for hierarchy traversal
- Aggregation across hierarchies
- Real-world business logic (purchases, restocking, alerts)
Best Practices
1. Use Descriptive Namespaces
// Good namespaces
"inventory";
"config-production";
"counters-analytics";
"docs-api";
// Avoid: Generic namespaces
"data";
"stuff";
"temp";
2. Atomic Operations Only
// Avoid: Race condition
const current = await cortex.mutable.get("inventory", "widget-qty");
await cortex.mutable.set("inventory", "widget-qty", current - 1);
// Another agent could modify between get and set!
// Recommended: Atomic update
await cortex.mutable.update("inventory", "widget-qty", (qty) => qty - 1);
// ACID guarantees no race condition
3. Initialize Before Use
// Safe initialization
async function getOrInitialize(
namespace: string,
key: string,
defaultValue: any,
) {
const value = await cortex.mutable.get(namespace, key);
if (value === null) {
await cortex.mutable.set(namespace, key, defaultValue);
return defaultValue;
}
return value;
}
const qty = await getOrInitialize("inventory", "new-product", 0);
4. Use Transactions for Multi-Key Operations
// Recommended: Atomic multi-key update (array-based API)
await cortex.mutable.transaction([
{ op: "decrement", namespace: "inventory", key: "product-a", amount: 1 },
{ op: "increment", namespace: "counters", key: "total-sales", amount: 1 },
]);
// Both or neither - ACID guarantees
// Avoid: Non-atomic (could fail partway)
await cortex.mutable.decrement("inventory", "product-a", 1);
await cortex.mutable.increment("counters", "total-sales", 1);
// Second could fail, leaving inconsistent state
5. Choose Appropriate Data Structure
Use Complex Objects When:
// Related data that changes together
await cortex.mutable.set("stores", "store-15", {
name: "Downtown",
manager: "Alice",
address: { street: "123 Main", city: "Springfield" },
// All fields updated as a unit
});
// Small datasets (< 100 items)
await cortex.mutable.set("config", "app-settings", {
theme: "dark",
language: "en",
features: { chat: true, voice: false },
});
// Configuration that's read together
await cortex.mutable.set("email", "settings", {
smtp: { host: "...", port: 587 },
templates: { welcome: "...", reset: "..." },
});
Use Hierarchical Keys When:
// Large datasets (> 100 items)
await cortex.mutable.set("inventory", "store-15:produce:apples", {...});
await cortex.mutable.set("inventory", "store-15:produce:bananas", {...});
// Can have thousands of products
// Need prefix queries
const produce = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "store-15:produce:",
});
// Independent updates (no related data)
await cortex.mutable.update("inventory", "store-15:dairy:milk", ...);
// Doesn't affect produce or other items
// Different access patterns
const apples = await cortex.mutable.get("inventory", "store-15:produce:apples");
// Fast direct access to single item
Decision Matrix:
| Scenario | Recommended Pattern | Why |
|---|---|---|
| Store metadata (< 50 fields) | Complex Object | Related data, atomic updates |
| Product catalog (1000s of items) | Hierarchical Keys | Scalable, independent updates |
| App configuration | Complex Object | Read/updated together |
| Multi-tenant inventory | Hierarchical Keys | Per-tenant prefix queries |
| Workflow state | Complex Object | State transitions are atomic |
| Metrics/counters | Hierarchical Keys | Independent increments |
| Small lists (< 100 items) | Complex Object (array) | Simple, atomic |
| Large lists (> 1000 items) | Hierarchical Keys | Scalable queries |
6. Use TypeScript for Type Safety
Define interfaces for complex objects:
// Define your data structures
interface InventoryItem {
quantity: number;
unit: string;
price: number;
supplier: string;
sku: string;
lastRestocked: Date;
}
interface StoreConfig {
id: string;
name: string;
manager: string;
address: {
street: string;
city: string;
state: string;
zip: string;
};
departments: string[];
openHours: {
open: string;
close: string;
};
}
// Type-safe operations
async function setInventoryItem(
storeId: string,
department: string,
item: string,
data: InventoryItem,
): Promise<void> {
const key = `${storeId}:${department}:${item}`;
await cortex.mutable.set("inventory", key, data);
}
async function getInventoryItem(
storeId: string,
department: string,
item: string,
): Promise<InventoryItem | null> {
const key = `${storeId}:${department}:${item}`;
return await cortex.mutable.get("inventory", key);
}
// Usage is type-safe
await setInventoryItem("store-15", "produce", "apples", {
quantity: 150,
unit: "lbs",
price: 2.99,
supplier: "Local Farms",
sku: "PROD-APL-001",
lastRestocked: new Date(),
});
const apples = await getInventoryItem("store-15", "produce", "apples");
if (apples) {
console.log(apples.quantity); // TypeScript knows this is a number
// apples.invalidField // TypeScript error!
}
Integration with Vector Layer
Mutable data CAN be referenced by Vector memories (as snapshots):
// 1. Store mutable data
await cortex.mutable.set(
"config",
"api-endpoint",
"https://api.example.com/v2",
);
// 2. Create Vector memory referencing it (snapshot)
await cortex.vector.store("config-agent", {
content: "API endpoint changed to v2",
contentType: "raw",
source: { type: "system", timestamp: new Date() },
mutableRef: {
namespace: "config",
key: "api-endpoint",
snapshotValue: "https://api.example.com/v2", // Value at time of indexing
snapshotAt: new Date(),
},
metadata: {
importance: 80,
tags: ["config", "api"],
},
});
// 3. Later, mutable value changes
await cortex.mutable.set(
"config",
"api-endpoint",
"https://api.example.com/v3",
);
// Vector memory still has snapshot of v2
// Current value is v3 (from mutable store)
Note: Vector's mutableRef is a snapshot, not live. Use for audit/history, not current values.
Purging
purge()
Delete a single key. Alias for delete() for API consistency.
Signature:
cortex.mutable.purge(
namespace: string,
key: string
): Promise<PurgeResult>
Returns:
interface PurgeResult {
deleted: boolean;
namespace: string;
key: string;
}
Example:
const result = await cortex.mutable.purge("inventory", "discontinued-product");
console.log(`Deleted: ${result.deleted}`);
purgeNamespace()
Delete entire namespace with optional dry-run mode.
Signature:
cortex.mutable.purgeNamespace(
namespace: string,
options?: PurgeNamespaceOptions
): Promise<PurgeNamespaceResult>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace to purge |
options.dryRun | boolean | No | false | Preview what would be deleted without deleting |
options.tenantId | string | No | — | Multi-tenancy filter (auto-injected from AuthContext) |
Returns:
interface PurgeNamespaceResult {
deleted: number; // Count of keys deleted (or would be deleted in dryRun mode)
namespace: string;
keys?: string[]; // List of keys (only in dryRun mode)
dryRun: boolean;
}
Example:
// Preview what would be deleted (dryRun mode)
const preview = await cortex.mutable.purgeNamespace("test-data", {
dryRun: true,
});
console.log(
`Would delete ${preview.deleted} keys: ${preview.keys?.join(", ")}`,
);
// Actually delete
const result = await cortex.mutable.purgeNamespace("test-data");
console.log(`Deleted ${result.deleted} keys from ${result.namespace}`);
purgeMany()
Delete keys matching filters with date-based cleanup support.
Signature:
cortex.mutable.purgeMany(
filter: PurgeManyFilter
): Promise<PurgeManyResult>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | string | Yes | — | Namespace to purge from |
keyPrefix | string | No | — | Keys starting with prefix |
userId | string | No | — | Filter by user |
updatedBefore | number | No | — | Delete keys updated before this timestamp |
tenantId | string | No | — | Multi-tenancy filter (auto-injected from AuthContext) |
Returns:
interface PurgeManyResult {
deleted: number; // Count of keys deleted
namespace: string;
keys: string[]; // List of deleted keys
}
Example:
// Delete keys with prefix
const result = await cortex.mutable.purgeMany({
namespace: "cache",
keyPrefix: "temp-",
});
console.log(`Deleted ${result.deleted} keys: ${result.keys.join(", ")}`);
// Delete all keys for a specific user
await cortex.mutable.purgeMany({
namespace: "user-sessions",
userId: "user-123",
});
// Delete old keys (not updated in 30 days)
await cortex.mutable.purgeMany({
namespace: "cache",
updatedBefore: Date.now() - 30 * 24 * 60 * 60 * 1000,
});
// Combine filters: delete old cache entries for a user
await cortex.mutable.purgeMany({
namespace: "user-cache",
userId: "user-123",
updatedBefore: Date.now() - 14 * 24 * 60 * 60 * 1000,
});
Use Cases
Use Case 1: Live Inventory (Complex Hierarchical Data)
// Initialize inventory with complex objects and hierarchical keys
await cortex.mutable.set("inventory", "store-15:produce:apples", {
quantity: 150,
unit: "lbs",
price: 2.99,
supplier: "Local Farms",
sku: "PROD-APL-001",
perishable: true,
expiryDate: new Date("2025-10-30"),
});
// Customer orders 10 lbs (atomic decrement with complex object)
await cortex.mutable.update("inventory", "store-15:produce:apples", (item) => {
if (item.quantity < 10) throw new Error("Out of stock");
return {
...item,
quantity: item.quantity - 10,
lastSold: new Date(),
};
});
// Check availability with full details
const apples = await cortex.mutable.get("inventory", "store-15:produce:apples");
console.log(`${apples.quantity} ${apples.unit} available at $${apples.price}`);
// Restock (atomic increment + update metadata)
await cortex.mutable.update("inventory", "store-15:produce:apples", (item) => ({
...item,
quantity: item.quantity + 50,
lastRestocked: new Date(),
supplier: "Local Farms", // Confirm supplier
}));
// Query all produce for this store
const allProduce = await cortex.mutable.list({
namespace: "inventory",
keyPrefix: "store-15:produce:",
});
console.log(`Store 15 has ${allProduce.length} produce items`);
Use Case 2: Live Configuration (Complex Nested Objects)
// Set application configuration as nested object
await cortex.mutable.set("config", "app-settings", {
api: {
endpoint: "https://api.example.com/v1",
timeout: 30000,
retries: 3,
rateLimits: {
perMinute: 60,
perHour: 1000,
},
},
features: {
enableChat: true,
enableVoice: false,
maxConcurrentChats: 5,
enableFileUpload: true,
},
notifications: {
email: true,
sms: false,
push: true,
channels: ["email", "push"],
},
security: {
sessionTimeout: 3600,
requireMFA: false,
allowedOrigins: ["https://app.example.com"],
},
});
// Agents check config (type-safe access)
const config = await cortex.mutable.get("config", "app-settings");
if (
config.features.enableChat &&
activeChats < config.features.maxConcurrentChats
) {
// Accept new chat
}
// Update specific nested value (atomic)
await cortex.mutable.update("config", "app-settings", (cfg) => ({
...cfg,
features: {
...cfg.features,
maxConcurrentChats: 10, // Immediate effect for all agents
},
}));
// Or update API endpoint
await cortex.mutable.update("config", "app-settings", (cfg) => ({
...cfg,
api: {
...cfg.api,
endpoint: "https://api.example.com/v2",
timeout: 60000, // Also increase timeout
},
}));
Use Case 3: Shared Counters
// Initialize counters
await cortex.mutable.set("counters", "total-requests", 0);
await cortex.mutable.set("counters", "successful-requests", 0);
await cortex.mutable.set("counters", "failed-requests", 0);
// Each agent increments (atomic)
await cortex.mutable.update("counters", "total-requests", (n) => n + 1);
if (requestSucceeded) {
await cortex.mutable.update("counters", "successful-requests", (n) => n + 1);
} else {
await cortex.mutable.update("counters", "failed-requests", (n) => n + 1);
}
// Get stats
const total = await cortex.mutable.get("counters", "total-requests");
const success = await cortex.mutable.get("counters", "successful-requests");
console.log(`Success rate: ${((success / total) * 100).toFixed(1)}%`);
Use Case 4: Agent Collaboration State (Complex Workflow)
// Track active workflow with complex state
await cortex.mutable.set("workflows", "refund-request-12345", {
id: "refund-request-12345",
type: "refund",
status: "in-progress",
createdAt: new Date(),
customer: {
id: "user-123",
name: "Alex Johnson",
email: "alex@example.com",
},
order: {
orderId: "ORD-98765",
amount: 299.99,
items: ["Widget A", "Widget B"],
purchaseDate: new Date("2025-10-15"),
},
workflow: {
currentStep: "manager-approval",
completedSteps: ["initiated", "customer-verified"],
pendingSteps: ["manager-approval", "payment-processing", "complete"],
},
agents: {
initiator: "support-agent-1",
currentAssignee: "manager-agent",
history: [
{
agentId: "support-agent-1",
action: "initiated",
timestamp: new Date(),
},
{ agentId: "support-agent-1", action: "verified", timestamp: new Date() },
],
},
notes: ["Customer reports defective product", "Original packaging intact"],
});
// Manager approves (atomic state transition)
await cortex.mutable.update(
"workflows",
"refund-request-12345",
(workflow) => ({
...workflow,
status: "approved",
workflow: {
...workflow.workflow,
currentStep: "payment-processing",
completedSteps: [...workflow.workflow.completedSteps, "manager-approval"],
pendingSteps: workflow.workflow.pendingSteps.filter(
(s) => s !== "manager-approval",
),
},
agents: {
...workflow.agents,
currentAssignee: "finance-agent",
history: [
...workflow.agents.history,
{ agentId: "manager-agent", action: "approved", timestamp: new Date() },
],
},
approvalDetails: {
approvedBy: "manager-agent",
approvedAt: new Date(),
amount: 299.99,
},
}),
);
// Finance agent processes payment
await cortex.mutable.update("workflows", "refund-request-12345", (workflow) => {
if (workflow.status !== "approved") {
throw new Error("Workflow not approved");
}
return {
...workflow,
status: "completed",
workflow: {
...workflow.workflow,
currentStep: "complete",
completedSteps: [
...workflow.workflow.completedSteps,
"payment-processing",
],
pendingSteps: [],
},
completedAt: new Date(),
};
});
Performance
Optimized for Frequent Updates
// Mutable store is designed for high-frequency updates
for (let i = 0; i < 1000; i++) {
await cortex.mutable.update("counters", "page-views", (n) => n + 1);
}
// Fast! No versioning overhead
Caching Recommendations
// For frequently-read, rarely-updated values
class MutableCache {
private cache = new Map<string, { value: any; cachedAt: Date }>();
private ttl = 60000; // 60 second cache
async get(namespace: string, key: string) {
const cacheKey = `${namespace}:${key}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.cachedAt.getTime() < this.ttl) {
return cached.value;
}
const value = await cortex.mutable.get(namespace, key);
this.cache.set(cacheKey, { value, cachedAt: new Date() });
return value;
}
}
Limitations
No Version History
// Version 1
await cortex.mutable.set("config", "timeout", 30);
// Version 2 (v1 is GONE)
await cortex.mutable.set("config", "timeout", 60);
// Can't retrieve v1 - it's overwritten!
const current = await cortex.mutable.get("config", "timeout");
console.log(current); // 60 only
// If you need history, use immutable store instead!
Backup Strategy
// Snapshot mutable data for backups
async function backupMutableNamespace(namespace: string) {
const all = await cortex.mutable.list({ namespace });
const backup = {
namespace,
timestamp: new Date(),
data: all.reduce(
(acc, record) => {
acc[record.key] = record.value;
return acc;
},
{} as Record<string, any>,
),
};
// Store backup (could use immutable store or external storage)
await cortex.immutable.store({
type: "mutable-backup",
id: `${namespace}-backup-${Date.now()}`,
data: backup,
});
return backup;
}
// Restore from backup
async function restoreMutableNamespace(backupId: string) {
const backup = await cortex.immutable.get("mutable-backup", backupId);
for (const [key, value] of Object.entries(backup.data.data)) {
await cortex.mutable.set(backup.data.namespace, key, value);
}
}
Graph-Lite Capabilities
Mutable records can participate in the graph structure via userId:
Mutable Record as Graph Node:
- Represents live/current state data
- Can be linked to users for GDPR compliance
Edges:
userIdto User (optional, for user-specific data)mutableReffrom Memories (snapshots of mutable state)
Graph Pattern:
// User → Mutable Records (via userId)
const userSessions = await cortex.mutable.list({
namespace: 'user-sessions',
userId: 'user-123',
});
const userPreferences = await cortex.mutable.get('user-preferences', 'user-123-prefs');
// Memory → Mutable (via mutableRef - snapshot)
await cortex.vector.store('agent-1', {
content: 'Config changed to value X',
mutableRef: {
namespace: 'config',
key: 'api-endpoint',
snapshotValue: 'https://api.example.com/v2',
snapshotAt: new Date()
}
});
// Graph:
User-123
└──[userId]──> Mutable: user-sessions/session-abc
└──[userId]──> Mutable: user-cache/cache-xyz
Memory-def
└──[mutableRef]──> Mutable: config/api-endpoint (snapshot at time T)
Mutable data changes frequently, so mutableRef stores snapshots (value at time of reference), not live links.
GDPR: Mutable records with userId are included in cascade deletion traversal.
Learn more: Graph Capabilities
Error Handling
MutableValidationError
The Mutable API exports MutableValidationError for client-side validation errors. This error is thrown before any network requests when input validation fails.
Import:
import { MutableValidationError } from "@cortex/sdk/mutable";
// or from main export:
import { MutableValidationError } from "@cortex/sdk";
Error Structure:
class MutableValidationError extends Error {
name: "MutableValidationError";
code: string; // Error code for programmatic handling
field?: string; // Which field failed validation
}
Example:
import { MutableValidationError } from "@cortex/sdk";
try {
await cortex.mutable.set("", "key", "value"); // Invalid namespace
} catch (error) {
if (error instanceof MutableValidationError) {
console.log(`Validation failed: ${error.message}`);
console.log(`Error code: ${error.code}`); // "MISSING_NAMESPACE"
console.log(`Field: ${error.field}`); // "namespace"
}
}
Common Error Codes:
| Code | Description |
|---|---|
MISSING_NAMESPACE | Namespace is required but was empty or undefined |
INVALID_NAMESPACE | Namespace format is invalid (must be alphanumeric, hyphens, underscores, dots, colons) |
NAMESPACE_TOO_LONG | Namespace exceeds 100 characters |
MISSING_KEY | Key is required but was empty or undefined |
INVALID_KEY | Key format is invalid |
KEY_TOO_LONG | Key exceeds 255 characters |
MISSING_VALUE | Value is required for set operations |
VALUE_TOO_LARGE | Value exceeds 1MB size limit |
INVALID_USER_ID | userId format is invalid |
INVALID_LIMIT_RANGE | limit must be between 0 and 1000 |
INVALID_UPDATER_TYPE | Updater must be a function |
MISSING_OPERATIONS | Transaction requires operations array |
EMPTY_OPERATIONS_ARRAY | Transaction operations array cannot be empty |
INVALID_OPERATION_TYPE | Transaction operation must be set, update, delete, increment, or decrement |
Summary
Mutable Store provides:
- Shared live data across agents
- ACID transaction guarantees
- Current-value semantics (fast)
- No version overhead
- Atomic updates (no race conditions)
Use for:
- Real-time inventory
- Live configuration
- Shared counters/metrics
- Agent collaboration state
- Frequently-changing reference data
Don't use for:
- Data needing version history (use
cortex.immutable.*) - Private conversations (use
cortex.conversations.*) - Searchable knowledge (use
cortex.vector.*+ immutable) - Audit trails (use
cortex.immutable.*for versioning)
Next Steps
- Immutable Store API - Versioned shared data
- Conversation Operations API - Private conversations
- Governance Policies API - Multi-layer retention rules
Questions? Ask in GitHub Discussions.