Skip to main content

Mutable Store API

Info

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 tenantId for SaaS isolation (auto-injected from AuthContext)

Comparison to Other Stores:

FeatureConversations (1a)Immutable (1b)Mutable (1c)Vector (2)Facts (3)
ScopingmemorySpace**NO scoping****NO scoping**memorySpacememorySpace
PrivacyPrivate to space**TRULY Shared****TRULY Shared**Private to spacePrivate to space
VersioningN/A (append)Auto**None**AutoAuto
UpdatesAppend onlyNew version**In-place**New versionNew version
Use CaseChatsKB, policies**Live data**Search indexExtracted 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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Logical grouping (e.g., 'inventory', 'config', 'counters')
keystringYes—Unique key within namespace
valueanyYes—JSON-serializable value
userIdstringNo—Link to user (enables GDPR cascade). Must reference existing user.
metadataRecord<string, unknown>No—Additional metadata to store with the record
optionsSetMutableOptionsNo—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 invalid
  • CortexError('INVALID_KEY') - Key is empty or invalid
  • CortexError('VALUE_TOO_LARGE') - Value exceeds size limit
  • CortexError('USER_NOT_FOUND') - userId doesn't reference existing user
  • CortexError('CONVEX_ERROR') - Database error

get()​

Get current value for a key.

Signature:

cortex.mutable.get(
namespace: string,
key: string
): Promise<any | null>

Parameters:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace
keystringYes—Key

Returns:

  • any | null - Current value, or null if 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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace
keystringYes—Key
updater(current: any) => anyYes—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 exist
  • CortexError('UPDATE_FAILED') - Updater function threw error
  • CortexError('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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace
keystringYes—Key
amountnumberNo1Amount 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
Note

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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace
keystringYes—Key
amountnumberNo1Amount 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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace
keystringYes—Key
optionsDeleteMutableOptionsNo—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, and delete operations

Querying​

list()​

List keys in a namespace with filtering, sorting, and pagination.

Signature:

cortex.mutable.list(
filter: ListMutableFilter
): Promise<MutableRecord[]>

Parameters:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace to list
keyPrefixstringNo—Keys starting with prefix
tenantIdstringNo—Multi-tenancy filter (auto-injected from AuthContext)
userIdstringNo—Filter by user
limitnumberNo100Max results
offsetnumberNo—Pagination offset
updatedAfternumberNo—Filter by updatedAt > timestamp
updatedBeforenumberNo—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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace to count
tenantIdstringNo—Multi-tenancy filter (auto-injected from AuthContext)
userIdstringNo—Filter by user
keyPrefixstringNo—Keys starting with prefix
updatedAfternumberNo—Filter by updatedAt > timestamp
updatedBeforenumberNo—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:

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

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

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​

PatternBest ForQuery PerformanceUpdate PerformanceComplexity
Hierarchical KeysPrefix queries, flat relationshipsExcellentExcellentLow
Hierarchical NamespacesOrganizational structureGoodExcellentLow
Nested Object ValuesRelated data, small datasetsModerateModerateMedium
HybridLarge systems, mixed accessExcellentExcellentMedium

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:

ScenarioRecommended PatternWhy
Store metadata (< 50 fields)Complex ObjectRelated data, atomic updates
Product catalog (1000s of items)Hierarchical KeysScalable, independent updates
App configurationComplex ObjectRead/updated together
Multi-tenant inventoryHierarchical KeysPer-tenant prefix queries
Workflow stateComplex ObjectState transitions are atomic
Metrics/countersHierarchical KeysIndependent increments
Small lists (< 100 items)Complex Object (array)Simple, atomic
Large lists (> 1000 items)Hierarchical KeysScalable 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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace to purge
options.dryRunbooleanNofalsePreview what would be deleted without deleting
options.tenantIdstringNo—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:

ParameterTypeRequiredDefaultDescription
namespacestringYes—Namespace to purge from
keyPrefixstringNo—Keys starting with prefix
userIdstringNo—Filter by user
updatedBeforenumberNo—Delete keys updated before this timestamp
tenantIdstringNo—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:

  • userId to User (optional, for user-specific data)
  • mutableRef from 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)
Note

Mutable data changes frequently, so mutableRef stores snapshots (value at time of reference), not live links.

Info

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:

CodeDescription
`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​


Questions? Ask in GitHub Discussions.