docs: add wiki guide for isolated memory spaces
@@ -25,6 +25,7 @@ await db.initialize();
|
|||||||
|
|
||||||
This creates the tables IdentityDB needs:
|
This creates the tables IdentityDB needs:
|
||||||
|
|
||||||
|
- `spaces`
|
||||||
- `topics`
|
- `topics`
|
||||||
- `facts`
|
- `facts`
|
||||||
- `fact_topics`
|
- `fact_topics`
|
||||||
@@ -32,7 +33,23 @@ This creates the tables IdentityDB needs:
|
|||||||
- `topic_aliases`
|
- `topic_aliases`
|
||||||
- `fact_embeddings`
|
- `fact_embeddings`
|
||||||
|
|
||||||
## 3. Add structured facts directly
|
## 3. Create or use isolated memory spaces
|
||||||
|
|
||||||
|
If you want independent memory graphs for different people, tenants, projects, or contexts, use spaces.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await db.upsertSpace({ name: 'A' });
|
||||||
|
await db.upsertSpace({ name: 'B' });
|
||||||
|
|
||||||
|
const spaces = await db.listSpaces();
|
||||||
|
const alpha = await db.getSpaceByName('A');
|
||||||
|
```
|
||||||
|
|
||||||
|
Any write or read can then opt into a specific space with `spaceName`.
|
||||||
|
|
||||||
|
See [Memory Spaces](Memory-Spaces) for the full model.
|
||||||
|
|
||||||
|
## 4. Add structured facts directly
|
||||||
|
|
||||||
Use `addFact()` when your application already knows the topics it wants to attach.
|
Use `addFact()` when your application already knows the topics it wants to attach.
|
||||||
|
|
||||||
@@ -54,7 +71,7 @@ await db.addFact({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. Model topic hierarchy explicitly
|
## 5. Model topic hierarchy explicitly
|
||||||
|
|
||||||
Use `linkTopics()` when you want hierarchy to be explicit rather than inferred.
|
Use `linkTopics()` when you want hierarchy to be explicit rather than inferred.
|
||||||
|
|
||||||
@@ -74,7 +91,7 @@ This is useful for reasoning such as:
|
|||||||
- `Bun` is a kind of `runtime`
|
- `Bun` is a kind of `runtime`
|
||||||
- `PostgreSQL` is a kind of `database`
|
- `PostgreSQL` is a kind of `database`
|
||||||
|
|
||||||
## 5. Add aliases for canonical topic resolution
|
## 6. Add aliases for canonical topic resolution
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
await db.addTopicAlias('TypeScript', 'TS');
|
await db.addTopicAlias('TypeScript', 'TS');
|
||||||
@@ -84,7 +101,7 @@ const canonicalTopic = await db.getTopicByName('TS', { includeFacts: true });
|
|||||||
|
|
||||||
This keeps one canonical topic row while still allowing alternate spellings or shorthand forms.
|
This keeps one canonical topic row while still allowing alternate spellings or shorthand forms.
|
||||||
|
|
||||||
## 6. Ingest free-form text through an extractor
|
## 7. Ingest free-form text through an extractor
|
||||||
|
|
||||||
When your application starts from raw text, use `ingestStatement()`.
|
When your application starts from raw text, use `ingestStatement()`.
|
||||||
|
|
||||||
@@ -119,7 +136,7 @@ await db.ingestStatement('I have worked with Bun and TypeScript since 2025.', {
|
|||||||
|
|
||||||
See [Extractors](Extractors) for a deeper explanation of the trade-offs.
|
See [Extractors](Extractors) for a deeper explanation of the trade-offs.
|
||||||
|
|
||||||
## 7. Add semantic search
|
## 8. Add semantic search
|
||||||
|
|
||||||
IdentityDB keeps semantic search provider-agnostic through an `EmbeddingProvider` interface.
|
IdentityDB keeps semantic search provider-agnostic through an `EmbeddingProvider` interface.
|
||||||
|
|
||||||
@@ -147,7 +164,7 @@ const matches = await db.searchFacts({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## 8. Enable duplicate-aware ingestion
|
## 9. Enable duplicate-aware ingestion
|
||||||
|
|
||||||
If you also provide an embedding provider during ingestion, IdentityDB can check whether a semantically similar fact already exists.
|
If you also provide an embedding provider during ingestion, IdentityDB can check whether a semantically similar fact already exists.
|
||||||
|
|
||||||
@@ -161,7 +178,7 @@ await db.ingestStatement('Bun makes TypeScript tooling fast.', {
|
|||||||
|
|
||||||
If a close enough match already exists, IdentityDB can return the existing fact instead of writing a duplicate.
|
If a close enough match already exists, IdentityDB can return the existing fact instead of writing a duplicate.
|
||||||
|
|
||||||
## 9. Close the connection
|
## 10. Close the connection
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
await db.close();
|
await db.close();
|
||||||
|
|||||||
3
Home.md
3
Home.md
@@ -16,6 +16,7 @@ IdentityDB is designed as the answer to those problems.
|
|||||||
|
|
||||||
IdentityDB turns memory into a relational graph with a stable application API:
|
IdentityDB turns memory into a relational graph with a stable application API:
|
||||||
|
|
||||||
|
- **Spaces** isolate independent memory graphs such as `A` and `B` so they behave like separate dimensions
|
||||||
- **Topics** are named nodes such as `TypeScript`, `Bun`, `2025`, or `programming language`
|
- **Topics** are named nodes such as `TypeScript`, `Bun`, `2025`, or `programming language`
|
||||||
- **Facts** are statements such as `I have worked with TypeScript since 2025.`
|
- **Facts** are statements such as `I have worked with TypeScript since 2025.`
|
||||||
- **Fact-topic links** connect one fact to many topics, which lets a single statement become a graph edge between concepts
|
- **Fact-topic links** connect one fact to many topics, which lets a single statement become a graph edge between concepts
|
||||||
@@ -30,6 +31,7 @@ This gives you a memory system that is easier to inspect than a black-box vector
|
|||||||
- Connect to **SQLite, PostgreSQL, MySQL, and MariaDB**
|
- Connect to **SQLite, PostgreSQL, MySQL, and MariaDB**
|
||||||
- Initialize the required schema automatically
|
- Initialize the required schema automatically
|
||||||
- Add facts and topics directly through a typed API
|
- Add facts and topics directly through a typed API
|
||||||
|
- Split memory into hard-isolated spaces so one tenant, person, or project cannot accidentally connect to another
|
||||||
- Ingest free-form text through pluggable extractors
|
- Ingest free-form text through pluggable extractors
|
||||||
- Resolve aliases to canonical topics
|
- Resolve aliases to canonical topics
|
||||||
- Traverse parent/child topic relationships
|
- Traverse parent/child topic relationships
|
||||||
@@ -60,6 +62,7 @@ That means IdentityDB can answer more than plain keyword lookup. It can tell you
|
|||||||
## Recommended reading order
|
## Recommended reading order
|
||||||
|
|
||||||
- [Getting Started](Getting-Started) — installation, initialization, and concrete examples
|
- [Getting Started](Getting-Started) — installation, initialization, and concrete examples
|
||||||
|
- [Memory Spaces](Memory-Spaces) — how to keep separate memory graphs isolated
|
||||||
- [Extractors](Extractors) — when to use `NaiveExtractor` vs `LlmFactExtractor`
|
- [Extractors](Extractors) — when to use `NaiveExtractor` vs `LlmFactExtractor`
|
||||||
|
|
||||||
## Repository
|
## Repository
|
||||||
|
|||||||
143
Memory-Spaces.md
Normal file
143
Memory-Spaces.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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
|
||||||
@@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
- [Home](Home)
|
- [Home](Home)
|
||||||
- [Getting Started](Getting-Started)
|
- [Getting Started](Getting-Started)
|
||||||
|
- [Memory Spaces](Memory-Spaces)
|
||||||
- [Extractors](Extractors)
|
- [Extractors](Extractors)
|
||||||
- [Repository](https://git.psw.kr/p-sw/IdentityDB)
|
- [Repository](https://git.psw.kr/p-sw/IdentityDB)
|
||||||
|
|||||||
Reference in New Issue
Block a user