AI Agent Quick Reference for Fortemi Multi-Memory Architecture
Memory = isolated PostgreSQL schema. Select via X-Fortemi-Memory header or MCP select_memory.
Default = public schema. Omitting header = default memory (archive_public).
All CRUD/tag/collection/template/version/attachment operations are memory-scoped.
Search is memory-scoped by default. Use federated search to query across multiple memories.
Max memories configurable via MAX_MEMORIES env var (default: 10, scales with hardware — see configuration docs).
| Scenario | Use Memory? | Instead Use | Notes |
|---|---|---|---|
| Separate client data with legal isolation requirements | YES | - | Hard schema-level isolation |
| Separate work vs personal | YES | - | If privacy/legal boundary required |
| Separate by project (>1000 notes per project) | YES | - | If projects are truly independent |
| Multi-tenant SaaS deployment | YES | - | One memory per tenant |
| Separate by topic within same project | NO | Tags or Collections | Memories too heavyweight |
| Temporary grouping of notes | NO | Collections | Memories are permanent structures |
| Filter search results by category | NO | Strict tag filtering | Search is memory-scoped already |
| Archive old project data | YES | - | Clone to archive memory |
| Test/staging environment isolation | YES | - | Separate memories for test data |
| Separate by time period (quarterly) | MAYBE | Tags if <5000 notes/quarter | Use rolling archives for >10k notes |
| Different embedding models per domain | NO | Embedding Sets | Each embedding set has its own model config; no need for separate memories |
| Team knowledge base (single team) | NO | Single memory + tags | Unless >50k notes |
Pattern: One memory per client (client-acme, client-globex)
Best for: Multi-tenant deployments, legal data isolation, SaaS platforms
Tradeoffs:
- Cross-memory search requires federated search (
search_memories_federated) - Perfect data isolation (schema-level)
- Simplifies backup/restore per client
- Overhead: ~1MB per empty memory, scales with data
Naming convention: client-{slug} or tenant-{id}
When to use: Always use for SaaS/multi-tenant scenarios where data isolation is legally or contractually required.
Pattern: One memory per major project (project-alpha, project-beta)
Best for: Teams working on distinct, unrelated projects with minimal knowledge overlap
Tradeoffs:
- Cross-project knowledge discovery requires explicit effort
- Simplifies project archival (delete entire memory)
- Search limited to single project context
When to merge: If projects share >30% of the same concepts/tags, use single memory with project tags instead.
Naming convention: project-{name} or proj-{slug}
When to use: Projects with >1000 notes each, minimal shared concepts, or need for independent lifecycle management.
Pattern: Separate domains (code-docs, research-papers, meeting-notes)
Best for: Different content types with different search patterns, embedding models, or retention policies
Tradeoffs:
- Same topic across domains requires federated search
- Risk: Domains often overlap, prefer tags within single memory
- Domain-specific embedding models can be achieved with embedding sets within a single memory
Naming convention: domain-{type} or kb-{category}
When to use: Only when content types have fundamentally different characteristics (drastically different note sizes, separate retention policies). Note: different embedding models do NOT require separate memories — use embedding sets instead.
Warning: Over-segmentation by domain reduces cross-domain discovery. Prefer single memory with document type metadata.
Pattern: Rolling archives (archive-2025-q4, archive-2026-q1, plus active for current work)
Best for: Compliance, data retention, performance (smaller active set), historical archival
Tradeoffs:
- Historical search requires federated query across periods (not yet supported)
- Excellent for compliance (immutable historical archives)
- Reduces active search space for performance
Operations: Clone current memory → archive at period boundary, then prune active memory.
Naming convention: archive-{YYYY}-q{N} or archive-{YYYY}-{MM}
When to use: Regulatory compliance requirements, >50k notes with clear temporal boundaries, or need to reduce active search space.
Pattern: Everything in default memory, use tags/collections for organization
Best for: Individual users, small teams, <50,000 notes, no regulatory isolation needs
Tradeoffs:
- No data isolation (soft boundaries via tags only)
- Larger search space (but still performant <50k notes)
- Simplest operational model
When to use: Default strategy unless you have specific requirements driving multi-memory adoption.
Recommended when: No regulatory/legal isolation requirements, team size <20, or total notes <50,000.
| Factor | Single Memory | Multiple Memories |
|---|---|---|
| Search speed | Scales with total notes | Scales with per-memory notes (smaller scope = faster) |
| Search scope | All notes searchable | Per-memory by default; use federated search for cross-memory queries |
| Data isolation | Tags only (soft) | Schema-level (hard, PostgreSQL enforced) |
| Storage overhead | Baseline | +~1MB per memory (all per-memory tables + indexes cloned per schema) |
| Backup granularity | All or nothing | Per-memory backup/restore possible |
| Cross-referencing | Links work anywhere | Links memory-scoped only (no cross-memory links) |
| Migration complexity | None | Export/import required between memories |
| Max recommended | 50,000 notes | 50,000 notes per memory |
| Operational overhead | Automatic | Automatic (maintenance runs across all schemas) |
| Embedding configs | Per embedding set (multiple models in one memory) | Per embedding set per memory |
| Tag isolation | Shared tag namespaces | Independent tag/collection namespaces per memory |
| Template sharing | Global templates | Templates memory-scoped (duplicate across memories) |
# Create memory
curl -X POST http://localhost:3000/api/v1/memories \
-H "Content-Type: application/json" \
-d '{"name": "project-alpha", "description": "Alpha project knowledge base"}'
# List all memories
curl http://localhost:3000/api/v1/memories
# Get memory stats
curl http://localhost:3000/api/v1/archives/project-alpha/stats
# Create note in specific memory
curl -X POST http://localhost:3000/api/v1/notes \
-H "Content-Type: application/json" \
-H "X-Fortemi-Memory: project-alpha" \
-d '{"title": "Note", "content": "Content", "metadata": {}}'
# Search in specific memory
curl http://localhost:3000/api/v1/search?q=query \
-H "X-Fortemi-Memory: project-alpha"
# Federated search across multiple memories
curl -X POST http://localhost:3000/api/v1/search/federated \
-H "Content-Type: application/json" \
-d '{"query": "search terms", "memories": ["project-alpha", "project-beta"]}'
# Clone memory (backup/snapshot)
curl -X POST http://localhost:3000/api/v1/archives/project-alpha/clone \
-H "Content-Type: application/json" \
-d '{"new_name": "project-alpha-backup-2026-02", "description": "Monthly backup"}'
# Delete memory (IRREVERSIBLE, CASCADE)
curl -X DELETE http://localhost:3000/api/v1/memories/project-alpha// Select active memory for MCP session (persists across calls)
await select_memory({ name: "project-alpha" });
// Check current active memory
const active = await get_active_memory();
// Returns: { name: "project-alpha" }
// All subsequent MCP calls use selected memory automatically
await capture_knowledge({
action: "create",
title: "Note",
content: "...",
metadata: {}
}); // goes to project-alpha
await search({
action: "text",
query: "..."
}); // searches project-alpha only
// List all memories (API-only, use GET /api/v1/archives)
// Note: Archive management is API-only; use select_memory for switching contexts
// Create new memory (API-only, use POST /api/v1/archives)
// Delete memory (API-only, use DELETE /api/v1/archives/:name)| Mistake | Why It's Wrong | Do This Instead |
|---|---|---|
| Creating memory per tag | Memories are heavyweight schema isolation (full table set + indexes per schema) | Use tags within single memory |
| Forgetting X-Fortemi-Memory header in HTTP API | Operations go to default memory silently, no error | Always set header explicitly or use select_memory in MCP |
| Searching without selecting memory | Search defaults to the public schema if no memory is selected | Always select memory first via X-Fortemi-Memory header or select_memory in MCP |
| Exceeding MAX_MEMORIES limit | Each clones the full per-memory table set with indexes | Increase MAX_MEMORIES per hardware tier, prefer tags/collections |
| Manual vacuum scheduling | Unnecessary — Fortemi handles maintenance automatically across all memory schemas | No action needed; auto-maintenance covers all schemas at normalized intervals |
| Assuming cross-memory links work | Links are memory-scoped, no foreign keys across schemas | Export/import notes or use same memory |
| Using memories for temporary grouping | Memories are permanent structures, expensive to delete | Use collections for temporary grouping |
| Over-segmenting by topic | Reduces discoverability, creates knowledge silos | Use single memory with tags unless >10k notes per topic |
| Creating memories just for different embedding models | Embedding configs are bound to embedding sets, not memories | Use multiple embedding sets within a single memory for different models |
| Expecting auto-migration between memories | No built-in migration, must export/import | Plan memory structure upfront, migrations are manual |
| Tool | Purpose | Uses Active Memory? | Returns Error if Non-Default? |
|---|---|---|---|
select_memory |
Set active memory for MCP session | Sets it (no active required) | No |
get_active_memory |
Check current active memory | No (retrieves state) | No |
capture_knowledge |
Create/update/delete notes | Yes | No |
search |
Search notes (text/semantic/combined/federated) | Yes | No (per-schema pools enable search in all memories) |
manage_tags |
Tag operations (list/add/remove/update) | Yes | No |
manage_collection |
Collection operations | Yes | No |
record_provenance |
Provenance tracking | Yes | No |
manage_concepts |
SKOS concept operations | Yes | No |
manage_embeddings |
Embedding set CRUD, membership, refresh | Yes | No |
- Time: ~200ms (clones all per-memory tables + indexes)
- Storage: ~1MB empty, scales with data
- Limit: 10 memories by default (scale via MAX_MEMORIES env var per hardware tier)
- Time: Proportional to data size (indexes rebuilt)
- Storage: Doubles (full copy including embeddings)
- Use case: Backups, snapshots, archival
- Time: ~500ms (CASCADE drop)
- IRREVERSIBLE: No recovery, no soft delete
- WARNING: Deletes all notes, tags, collections, embeddings, attachments
- Default memory: <100ms typical for <50k notes
- Non-default memory: Uses per-schema connection pool; similar performance to default
- Federated search: Queries multiple memories in parallel with unified ranking
START: Does user need multi-memory?
|
+--> NO legal/regulatory isolation required?
| +--> <50k total notes expected?
| +--> Use SINGLE MEMORY (default)
|
+--> Multi-tenant SaaS?
| +--> YES --> Strategy A: One memory per tenant
|
+--> Independent projects with <30% concept overlap?
| +--> YES --> Strategy B: One memory per project
|
+--> Content types need different retention/chunking policies?
| +--> YES --> Strategy C: One memory per domain
| (Note: different embedding models do NOT require separate memories — use embedding sets)
|
+--> Compliance requires historical immutability?
| +--> YES --> Strategy D: Rolling time-based archives
|
+--> Default to SINGLE MEMORY, use tags/collections
- Create target memory:
POST /api/v1/memories - Export notes: Use bulk export with tag filter
- Import to new memory:
POST /api/v1/noteswithX-Fortemi-Memoryheader - Verify: Check note counts match
- Update agents: Change MCP
select_memoryor API headers - Archive old memory: Clone to backup, then delete (optional)
- Export all memories: Per-memory bulk export
- Tag for provenance: Add
source-memory:{name}tag to all notes - Import to default memory: Omit
X-Fortemi-Memoryheader - Update links: Re-link notes (links don't migrate across memories)
- Verify: Check total note counts
- Delete old memories: After verification period
- Plan memory structure upfront (migrations are manual)
- Limit to <50 memories for performance
- Set
X-Fortemi-Memoryheader explicitly in all HTTP API calls - Use
select_memoryat start of MCP sessions - Use
searchfor per-memory search,search_memories_federatedfor cross-memory - Verify auto-maintenance is running (maintenance covers all schemas automatically)
- Clone memories before risky operations (deletion is irreversible)
- Monitor memory count (
list_archives) against MAX_MEMORIES limit - Use tags/collections for organization within memories
- Document memory naming conventions for team
- Cross-memory links: Reference notes across schema boundaries
- Memory templates: Pre-configured memories with tags/collections
- Auto-archival: Scheduled cloning to time-based archives
- Memory-level permissions: Fine-grained access control per memory
Default recommendation: Use single memory with tags/collections unless you have:
- Legal/regulatory data isolation requirements (multi-tenant, client separation)
-
50k notes with clear segmentation boundaries (projects, time periods)
Note: Different embedding models do NOT require separate memories — use embedding sets within a single memory instead.
Key principle: Memories are heavyweight. Over-segmentation reduces discoverability. When in doubt, use tags.