Clone
1
Memory Spaces
Shinwoo PARK edited this page 2026-05-11 14:49:57 +09:00

Memory Spaces

IdentityDB supports hard-isolated memory spaces so different people, tenants, projects, or contexts can live in separate graphs.

Think of each space as a separate dimension:

  • topics in space A do not automatically connect to topics in space B
  • aliases are resolved inside one space only
  • hierarchy traversal stays inside one space
  • semantic search stays inside one space
  • duplicate detection stays inside one space

That means you can safely store TypeScript in both A and B without merging them.

When spaces are useful

Spaces are a good fit when you need to isolate memory by:

  • user
  • customer or tenant
  • project
  • environment
  • agent persona
  • private vs shared knowledge

Core API

You can manage spaces explicitly:

await db.upsertSpace({ name: 'A' });
await db.upsertSpace({ name: 'B' });

const spaces = await db.listSpaces();
const alpha = await db.getSpaceByName('A');

And then scope reads and writes with spaceName:

await db.addFact({
  spaceName: 'A',
  statement: 'TypeScript belongs to A.',
  topics: [
    { name: 'TypeScript', category: 'entity', granularity: 'concrete' },
  ],
});

await db.addFact({
  spaceName: 'B',
  statement: 'TypeScript belongs to B.',
  topics: [
    { name: 'TypeScript', category: 'entity', granularity: 'concrete' },
  ],
});

Same topic name, different spaces

The same normalized topic name can exist independently in multiple spaces.

const alphaTopic = await db.getTopicByName('TypeScript', { spaceName: 'A' });
const betaTopic = await db.getTopicByName('TypeScript', { spaceName: 'B' });

These are separate topic rows with separate facts and relationships.

Alias isolation

Aliases are also scoped by space.

await db.addTopicAlias('TypeScript', 'TS', { spaceName: 'A' });
await db.addTopicAlias('TeamSpeak', 'TS', { spaceName: 'B' });

const inA = await db.resolveTopic('TS', { spaceName: 'A' });
const inB = await db.resolveTopic('TS', { spaceName: 'B' });

The same alias can therefore mean different canonical topics in different spaces.

Hierarchy isolation

Hierarchy is isolated too.

await db.linkTopics({
  spaceName: 'A',
  parentName: 'programming language',
  childName: 'TypeScript',
});

await db.linkTopics({
  spaceName: 'B',
  parentName: 'language family',
  childName: 'TypeScript',
});

const alphaParents = await db.getTopicParents('TypeScript', { spaceName: 'A' });
const betaParents = await db.getTopicParents('TypeScript', { spaceName: 'B' });

Those queries can return different results even though the child topic name is identical.

Semantic search and duplicate detection isolation

Search and duplicate detection also stay inside the requested space.

await db.indexFactEmbeddings({ provider, spaceName: 'A' });
await db.indexFactEmbeddings({ provider, spaceName: 'B' });

const alphaMatches = await db.searchFacts({
  spaceName: 'A',
  query: 'TypeScript runtime tooling',
  provider,
});

await db.ingestStatement('Bun makes TypeScript tooling fast.', {
  spaceName: 'A',
  extractor: new NaiveExtractor(),
  embeddingProvider: provider,
  duplicateThreshold: 0.95,
});

A fact in space B will not be used as a duplicate candidate for a write in space A.

Default behavior

If you do not provide spaceName, IdentityDB uses the default space.

That preserves backwards compatibility with older code while still letting new code opt into explicit isolation.

Practical recommendation

A simple pattern is:

  • use one space per user if you want personal memory isolation
  • use one space per tenant if you are building multi-tenant software
  • use one space per project if the same agent needs independent project memories
  • keep shared organizational knowledge in a separate dedicated shared space