# ADR 009: Multi-Tenant Team Memory Sharing Architecture
## Status
DRAFT
## Date
2025-12-02
## Context
MemoryGraph currently operates as a single-user memory system where all memories stored in a database instance are accessible to any client connected to that instance. As teams adopt MemoryGraph for collaborative development, there is a need to:
1. **Share Memories Across Team Members**: Enable teams to build collective knowledge while maintaining memory coherence
2. **Support Multiple Teams on Single Infrastructure**: Allow a single database instance to serve multiple teams with proper isolation
3. **Control Access and Visibility**: Provide different visibility levels (private, team, public) for memories
4. **Maintain Memory Consistency**: Ensure distributed agents don't create conflicts when accessing shared memories
5. **Enable Gradual Rollout**: Maintain backward compatibility with single-user deployments
### Current State
The existing architecture includes some multi-tenancy foundations:
**MemoryContext Model** (models.py:84-98):
```python
class MemoryContext(BaseModel):
project_path: Optional[str] = None
session_id: Optional[str] = None
user_id: Optional[str] = None
# ... other fields
```
**Current Capabilities**:
- `project_path` enables project-scoped memories
- `user_id` field exists but is not enforced or indexed
- `session_id` tracks workflow sessions
- No authentication or authorization layer
- No tenant isolation at database level
**Current Limitations**:
1. No tenant isolation - all memories visible to all clients
2. No access control - any client can read/modify any memory
3. No user authentication - `user_id` is client-provided, unverified
4. No team construct - no way to group users or memories by team
5. Potential for memory conflicts when multiple agents work concurrently
## Decision Drivers
### Functional Requirements
- **FR1**: Support multiple teams sharing a single database instance
- **FR2**: Enable memory visibility levels: private, team, public
- **FR3**: Provide role-based access control (RBAC) for memory operations
- **FR4**: Maintain memory consistency when multiple agents access shared memories
- **FR5**: Support project-scoped memories within team context
- **FR6**: Enable cross-team memory sharing for public/shared patterns
### Non-Functional Requirements
- **NFR1**: Zero impact on single-user deployments (backward compatibility)
- **NFR2**: Minimal query performance degradation (< 10% overhead)
- **NFR3**: Support all existing backends (Neo4j, Memgraph, SQLite, FalkorDB)
- **NFR4**: Graceful degradation when auth is unavailable
- **NFR5**: Clear migration path from single-user to multi-tenant
### Technical Constraints
- MCP protocol does not natively provide authentication context
- Backend must remain pluggable (5 different database implementations)
- Schema changes must be backward-compatible
- Client agents may not have authentication capabilities
## Considered Options
### Option 1: Database-Level Multi-Tenancy with Row-Level Security
**Approach**: Use database native features for tenant isolation.
**Schema Changes**:
```python
class MemoryContext(BaseModel):
tenant_id: str # Required field
user_id: str # Required field
project_path: Optional[str] = None
visibility: Literal["private", "team", "public"] = "team"
team_id: Optional[str] = None
```
**Cypher Constraints**:
```cypher
// Tenant-aware unique constraint
CREATE CONSTRAINT memory_id_tenant_unique
FOR (m:Memory) REQUIRE (m.tenant_id, m.id) IS UNIQUE;
// Tenant and team indexes
CREATE INDEX memory_tenant_index FOR (m:Memory) ON (m.tenant_id);
CREATE INDEX memory_team_index FOR (m:Memory) ON (m.team_id);
CREATE INDEX memory_visibility_index FOR (m:Memory) ON (m.visibility);
```
**Query Pattern**:
```cypher
MATCH (m:Memory)
WHERE m.tenant_id = $tenant_id
AND (
m.visibility = 'public'
OR (m.visibility = 'team' AND m.team_id IN $user_teams)
OR (m.visibility = 'private' AND m.user_id = $user_id)
)
RETURN m
```
**Pros**:
- Strong isolation at database level
- PostgreSQL RLS could provide additional guarantees (future backend)
- Clear security boundary
- Standard multi-tenancy pattern
**Cons**:
- Requires tenant_id on every query (breaking change)
- Complex migration from single-tenant
- All backends must support indexes on new fields
- Higher query complexity and overhead
- Breaks backward compatibility
### Option 2: Namespace-Based Soft Multi-Tenancy
**Approach**: Use memory ID namespace prefixing for logical isolation.
**Schema Changes**:
```python
class MemoryContext(BaseModel):
namespace: str = "default" # team:project or user:username
visibility: Literal["namespace", "public"] = "namespace"
created_by: Optional[str] = None # user_id
project_path: Optional[str] = None
```
**Memory ID Pattern**:
```
{namespace}:{uuid}
Examples:
- team-acme:550e8400-e29b-41d4-a716-446655440000
- user-alice:650e8400-e29b-41d4-a716-446655440001
- public:750e8400-e29b-41d4-a716-446655440002
```
**Query Pattern**:
```cypher
MATCH (m:Memory)
WHERE (
m.id STARTS WITH $namespace + ':'
OR m.context_visibility = 'public'
)
RETURN m
```
**Pros**:
- Minimal schema changes
- Backward compatible (default namespace)
- Simple to implement across all backends
- No additional indexes required
- Easy to understand and debug
**Cons**:
- Weaker isolation (convention over enforcement)
- Clients could bypass namespace filtering
- No true access control
- Namespace collisions possible
- Relies on client cooperation
### Option 3: Hybrid Multi-Tenancy with Optional Authentication
**Approach**: Layer multi-tenancy as an optional feature with progressive enhancement.
**Schema Changes**:
```python
class MemoryContext(BaseModel):
# Existing fields (unchanged for backward compatibility)
project_path: Optional[str] = None
session_id: Optional[str] = None
user_id: Optional[str] = None
# New optional multi-tenancy fields
tenant_id: Optional[str] = None # Required only in multi-tenant mode
team_id: Optional[str] = None
visibility: str = "project" # private | project | team | public
access_control: Optional[Dict[str, Any]] = None # Future RBAC
```
**Database Indexes** (created conditionally):
```cypher
// Only created if multi-tenant mode enabled
CREATE INDEX memory_tenant_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_tenant_id);
CREATE INDEX memory_team_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_team_id);
CREATE INDEX memory_visibility_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_visibility);
```
**Configuration**:
```python
# Environment variables
MEMORY_MULTI_TENANT_MODE=false # Default: single-tenant
MEMORY_AUTH_PROVIDER=none # none | jwt | oauth2
MEMORY_DEFAULT_TENANT=default # Used when mode=false
```
**Query Patterns**:
*Single-tenant mode (default)*:
```cypher
MATCH (m:Memory)
WHERE m.context_project_path = $project_path OR $project_path IS NULL
RETURN m
```
*Multi-tenant mode*:
```cypher
MATCH (m:Memory)
WHERE (
// Tenant isolation
m.context_tenant_id = $tenant_id OR m.context_tenant_id IS NULL
) AND (
// Visibility rules
m.context_visibility = 'public'
OR (m.context_visibility = 'team' AND m.context_team_id IN $user_teams)
OR (m.context_visibility = 'project' AND m.context_project_path = $project_path)
OR (m.context_visibility = 'private' AND m.context_user_id = $user_id)
)
RETURN m
ORDER BY m.importance DESC, m.created_at DESC
LIMIT $limit
```
**Pros**:
- **Backward compatible**: Single-tenant by default
- **Progressive enhancement**: Enable features as needed
- **Flexible deployment**: Works for individuals and teams
- **Graceful degradation**: Falls back to project-scoped if auth unavailable
- **Performance**: Zero overhead in single-tenant mode
- **Migration friendly**: Can enable per-deployment
**Cons**:
- More complex codebase (two modes to maintain)
- Documentation complexity
- Risk of mode-specific bugs
- Configuration required for multi-tenant
### Option 4: Client-Side Filtering with Trust Model
**Approach**: Rely on MCP client to enforce tenant boundaries.
**Schema Changes**: Minimal - add optional team_id to context
**Implementation**: MCP server returns all memories, client filters by team
**Pros**:
- No server changes required
- Maximum flexibility
- Zero performance impact
**Cons**:
- No security - relies entirely on client cooperation
- Data leakage risk
- Not suitable for untrusted environments
- Doesn't solve memory consistency issues
## Decision
We choose **Option 3: Hybrid Multi-Tenancy with Optional Authentication** for the following reasons:
### Primary Justifications
1. **Backward Compatibility**: Existing single-user deployments continue working without any changes
2. **Progressive Enhancement**: Teams can opt-in when ready, without forced migration
3. **Deployment Flexibility**: Same codebase serves personal use, small teams, and enterprises
4. **Performance**: No overhead for single-tenant deployments (95% of current users)
5. **Clear Migration Path**: Users can migrate gradually as their needs grow
### Implementation Strategy
**Phase 1: Schema Enhancement (v0.9.0)**
- Add optional multi-tenancy fields to MemoryContext
- Create conditional indexes
- Implement configuration system
- Maintain 100% backward compatibility
**Phase 2: Query Layer (v0.10.0)**
- Implement multi-tenant query filters
- Add visibility enforcement
- Create tenant-aware search methods
- Add performance benchmarks
**Phase 3: Authentication Integration (v1.0.0)**
- JWT token validation
- OAuth2 provider integration
- MCP protocol extension for auth context
- Session management
**Phase 4: Advanced RBAC (v1.1.0)**
- Role-based permissions
- Fine-grained access control
- Audit logging
- Cross-tenant sharing controls
## Multi-Tenancy Design Details
### Tenant Hierarchy
```
┌─────────────────────────────────────────┐
│ Tenant (Organization) │
│ tenant_id: "acme-corp" │
└────────────┬────────────────────────────┘
│
┌────┴────┐
│ │
┌────▼────┐ ┌▼─────────┐
│ Team A │ │ Team B │
│ "backend"│ │ "frontend"│
└────┬────┘ └┬─────────┘
│ │
┌───┴─┐ ┌─┴──┐
│User1│ │User2│
└─────┘ └────┘
Memory Visibility Levels:
├─ private: Visible only to creating user
├─ project: Visible within project_path scope
├─ team: Visible to all team members
└─ public: Visible across entire tenant
```
### Memory Visibility Rules
```python
def can_access_memory(memory: Memory, context: AccessContext) -> bool:
"""Determine if user can access a memory."""
# Single-tenant mode: project-scoped only
if not Config.MULTI_TENANT_MODE:
return (
memory.context.project_path == context.project_path
or memory.context.project_path is None
)
# Multi-tenant mode: visibility-based access control
if memory.context.visibility == "public":
return memory.context.tenant_id == context.tenant_id
if memory.context.visibility == "team":
return (
memory.context.tenant_id == context.tenant_id
and memory.context.team_id in context.user_teams
)
if memory.context.visibility == "project":
return (
memory.context.tenant_id == context.tenant_id
and memory.context.project_path == context.project_path
)
if memory.context.visibility == "private":
return (
memory.context.tenant_id == context.tenant_id
and memory.context.user_id == context.user_id
)
return False # Default deny
```
### Memory Consistency Strategy
**Challenge**: Multiple agents working concurrently could create conflicting memories or relationships.
**Approach**: Optimistic Concurrency with Last-Write-Wins
**Implementation**:
```python
class Memory(BaseModel):
# ... existing fields
version: int = 1 # Increment on each update
updated_at: datetime
updated_by: Optional[str] = None # user_id who made last update
```
**Update Pattern**:
```cypher
MATCH (m:Memory {id: $memory_id})
WHERE m.version = $expected_version // Optimistic lock
SET m += $updates,
m.version = m.version + 1,
m.updated_at = datetime(),
m.updated_by = $user_id
RETURN m.version as new_version
```
**Conflict Resolution**:
1. **Last-Write-Wins (Default)**: Most recent update wins, older updates rejected
2. **Merge on Conflict**: For specific fields (tags, usage_count), merge instead of replace
3. **Conflict Notification**: Optional webhook/event when conflicts detected
**Relationship Consistency**:
- Relationships are additive (multiple agents can create same relationship type)
- Duplicate relationships prevented by relationship_id uniqueness
- Strength/confidence can be averaged across multiple evidence instances
### Cache Invalidation
**Problem**: Distributed agents may cache memories, leading to stale data.
**Strategy**: Event-Driven Invalidation (Future Enhancement)
```python
class MemoryEvent(BaseModel):
event_type: Literal["created", "updated", "deleted"]
memory_id: str
tenant_id: str
team_id: Optional[str]
timestamp: datetime
```
**Invalidation Patterns**:
1. **TTL-Based**: Memories cached for max 5 minutes
2. **Version-Based**: Check version before using cached copy
3. **Event Streaming**: Pub/sub for real-time invalidation (Redis, NATS)
## Schema Changes
### New MemoryContext Fields
```python
class MemoryContext(BaseModel):
# === Existing fields (unchanged) ===
project_path: Optional[str] = None
files_involved: List[str] = Field(default_factory=list)
languages: List[str] = Field(default_factory=list)
frameworks: List[str] = Field(default_factory=list)
technologies: List[str] = Field(default_factory=list)
git_commit: Optional[str] = None
git_branch: Optional[str] = None
working_directory: Optional[str] = None
timestamp: datetime = Field(default_factory=datetime.utcnow)
session_id: Optional[str] = None
user_id: Optional[str] = None
additional_metadata: Dict[str, Any] = Field(default_factory=dict)
# === New multi-tenancy fields ===
tenant_id: Optional[str] = Field(
None,
description="Tenant/organization identifier. Required in multi-tenant mode."
)
team_id: Optional[str] = Field(
None,
description="Team identifier within tenant"
)
visibility: str = Field(
"project",
description="Memory visibility level: private | project | team | public"
)
created_by: Optional[str] = Field(
None,
description="User ID who created this memory (for audit/access control)"
)
```
### New Memory Fields
```python
class Memory(BaseModel):
# ... existing fields unchanged
# New concurrency control fields
version: int = Field(
default=1,
description="Version number for optimistic concurrency control"
)
updated_by: Optional[str] = Field(
None,
description="User ID who last updated this memory"
)
```
### New Indexes (Conditional)
```cypher
// Created only when MEMORY_MULTI_TENANT_MODE=true
CREATE INDEX memory_tenant_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_tenant_id);
CREATE INDEX memory_team_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_team_id);
CREATE INDEX memory_visibility_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_visibility);
CREATE INDEX memory_created_by_index IF NOT EXISTS
FOR (m:Memory) ON (m.context_created_by);
// Version index for optimistic locking
CREATE INDEX memory_version_index IF NOT EXISTS
FOR (m:Memory) ON (m.version);
```
## API Changes
### New Configuration Options
```bash
# Environment Variables
MEMORY_MULTI_TENANT_MODE=false # Enable multi-tenant features
MEMORY_DEFAULT_TENANT=default # Tenant for single-tenant mode
MEMORY_REQUIRE_AUTH=false # Require authentication
MEMORY_AUTH_PROVIDER=none # none | jwt | oauth2
MEMORY_JWT_SECRET= # JWT signing secret
MEMORY_JWT_ALGORITHM=HS256 # JWT algorithm
MEMORY_ENABLE_AUDIT_LOG=false # Log all access events
```
### Updated Tool Schemas
**store_memory** - Add optional tenant/team context:
```json
{
"type": "object",
"properties": {
"context": {
"type": "object",
"properties": {
"tenant_id": {"type": "string"},
"team_id": {"type": "string"},
"visibility": {
"type": "string",
"enum": ["private", "project", "team", "public"],
"default": "project"
}
}
}
}
}
```
**search_memories** - Add tenant filtering:
```json
{
"type": "object",
"properties": {
"tenant_id": {
"type": "string",
"description": "Filter by tenant (required in multi-tenant mode)"
},
"team_id": {
"type": "string",
"description": "Filter by team within tenant"
},
"visibility": {
"type": "array",
"items": {
"type": "string",
"enum": ["private", "project", "team", "public"]
}
}
}
}
```
### New Tools (Optional, v1.0+)
```python
Tool(
name="list_teams",
description="List teams the authenticated user belongs to",
inputSchema={
"type": "object",
"properties": {}
}
)
Tool(
name="share_memory",
description="Change memory visibility or share with another team",
inputSchema={
"type": "object",
"properties": {
"memory_id": {"type": "string"},
"visibility": {"type": "string"},
"share_with_teams": {
"type": "array",
"items": {"type": "string"}
}
}
}
)
```
## Consequences
### Positive
1. **Zero Breaking Changes**: Existing deployments work unchanged
2. **Opt-In Complexity**: Teams adopt multi-tenancy when ready
3. **Clear Separation**: Single-tenant and multi-tenant code paths clearly delineated
4. **Performance Preserved**: No overhead for single-user deployments
5. **Scalable Architecture**: Supports growth from individual to enterprise
6. **Standard Patterns**: Uses proven multi-tenancy approaches
7. **Future Proof**: Foundation for advanced RBAC and compliance features
### Negative
1. **Code Complexity**: Two operational modes to maintain and test
2. **Testing Burden**: Must test both single-tenant and multi-tenant paths
3. **Documentation**: More complex setup and configuration docs needed
4. **Migration Effort**: Teams need to plan and execute migration
5. **MCP Limitations**: No native auth in MCP, requires extensions
6. **Configuration Overhead**: More environment variables to manage
### Neutral
1. **Authentication Required**: Full multi-tenancy requires auth provider
2. **Query Complexity**: Multi-tenant queries more complex but manageable
3. **Index Overhead**: Additional indexes in multi-tenant mode
4. **Cache Strategy**: Need cache invalidation strategy for consistency
## Migration Path
### From Single-Tenant to Multi-Tenant
**Step 1: Enable Multi-Tenant Mode**
```bash
# Set environment variables
MEMORY_MULTI_TENANT_MODE=true
MEMORY_DEFAULT_TENANT=my-company
```
**Step 2: Backfill Tenant IDs**
```cypher
// Assign all existing memories to default tenant
MATCH (m:Memory)
WHERE m.context_tenant_id IS NULL
SET m.context_tenant_id = 'my-company',
m.context_visibility = 'team' // Make existing memories team-visible
```
**Step 3: Create Teams**
```cypher
// Create team entities (optional, for future features)
CREATE (t:Team {
id: 'backend-team',
tenant_id: 'my-company',
name: 'Backend Engineering',
created_at: datetime()
})
```
**Step 4: Assign Users to Teams**
```python
# In application config or database
user_teams = {
"alice": ["backend-team", "platform-team"],
"bob": ["backend-team"],
"carol": ["frontend-team"]
}
```
**Step 5: Enable Authentication** (Optional)
```bash
MEMORY_REQUIRE_AUTH=true
MEMORY_AUTH_PROVIDER=jwt
MEMORY_JWT_SECRET=your-secret-key
```
### Rollback Strategy
If multi-tenant mode causes issues, rollback is simple:
```bash
# Disable multi-tenant mode
MEMORY_MULTI_TENANT_MODE=false
# Server falls back to single-tenant behavior
# All memories remain accessible (no data loss)
```
## Performance Considerations
### Query Performance Impact
**Single-Tenant Mode**: No change (0% overhead)
**Multi-Tenant Mode**:
- Additional WHERE clauses: ~5-10% overhead
- Additional indexes improve performance for large datasets
- Visibility checks add minimal overhead with proper indexing
**Benchmark Targets**:
```
Single-tenant search: 50ms p50, 150ms p99
Multi-tenant search: 55ms p50, 165ms p99 (< 10% degradation)
```
### Storage Overhead
- New indexes: ~10-15% storage increase
- New fields: ~5-8 bytes per memory (negligible)
- Relationship data: No change
### Connection Pooling
Multi-tenant mode requires connection pool sizing based on:
- Number of active tenants
- Concurrent users per tenant
- Query patterns
**Recommended Settings**:
```python
# Single-tenant
max_connection_pool_size = 10
# Multi-tenant (< 10 teams)
max_connection_pool_size = 25
# Multi-tenant (10-50 teams)
max_connection_pool_size = 50
# Multi-tenant (50+ teams)
max_connection_pool_size = 100
```
## Security Considerations
### Threat Model
**T1: Tenant Data Leakage**
- **Risk**: Memories from one tenant visible to another
- **Mitigation**: Enforce tenant_id in all queries, default deny access policy
- **Detection**: Audit logging, query monitoring
**T2: User Impersonation**
- **Risk**: Malicious client provides false user_id
- **Mitigation**: Require authentication in multi-tenant mode, validate JWT
- **Detection**: Track client certificates/tokens, rate limiting
**T3: Privilege Escalation**
- **Risk**: User gains access to private memories
- **Mitigation**: Enforce visibility rules at database layer, not client
- **Detection**: Audit logging, anomaly detection
**T4: Data Modification by Unauthorized User**
- **Risk**: User updates/deletes memories they don't own
- **Mitigation**: Check created_by or team membership before updates
- **Detection**: Version tracking, audit trail
### Defense in Depth
1. **Database Layer**: Tenant/visibility filtering in all queries
2. **Application Layer**: Validate access before operations
3. **API Layer**: Require authentication tokens
4. **Audit Layer**: Log all access and modifications
5. **Monitoring**: Alert on anomalous access patterns
## Alternatives Considered (Summary)
| Option | Isolation | Performance | Compatibility | Complexity |
|--------|-----------|-------------|---------------|------------|
| **1: Database RLS** | Strong | Medium | Breaking | Medium |
| **2: Namespace Prefix** | Weak | High | Good | Low |
| **3: Hybrid (Chosen)** | Medium-Strong | High | Excellent | Medium-High |
| **4: Client-Side** | None | Highest | Perfect | Low |
## Open Questions
1. **MCP Authentication**: How to pass authentication context through MCP protocol?
- **Option A**: Custom MCP extension for auth headers
- **Option B**: Embed JWT in tool parameters
- **Option C**: Separate auth service with session tokens
- **Recommendation**: Start with Option B (parameters), evolve to Option A
2. **Cross-Tenant Memory Sharing**: Should we allow sharing memories across tenants?
- **Recommendation**: Phase 2 feature, not MVP
- Requires federated identity or shared namespace
3. **Memory Ownership Transfer**: Can memory ownership change (user leaves team)?
- **Recommendation**: Transfer to team on user deactivation
- Add `transferred_from` audit field
4. **Compliance**: GDPR, data residency, retention policies?
- **Recommendation**: Phase 2, add compliance module
- Support memory archival and deletion policies
## References
- [Multi-Tenant Data Architecture (Microsoft)](https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/considerations/tenant-lifecycle)
- [Row-Level Security in PostgreSQL](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
- [JWT Authentication Best Practices](https://auth0.com/docs/secure/tokens/json-web-tokens)
- [Optimistic Concurrency Control](https://en.wikipedia.org/wiki/Optimistic_concurrency_control)
- [Neo4j Multi-Tenancy Patterns](https://neo4j.com/developer/multi-tenancy/)
## Implementation Checklist
### Phase 1: Foundation (v0.9.0)
- [ ] Add multi-tenancy fields to MemoryContext model
- [ ] Add configuration system for tenant mode
- [ ] Create conditional index initialization
- [ ] Update schema.md documentation
- [ ] Add unit tests for single-tenant mode
- [ ] Add unit tests for multi-tenant mode
- [ ] Performance benchmarks (before/after)
### Phase 2: Query Layer (v0.10.0)
- [ ] Implement tenant-aware query filters
- [ ] Add visibility enforcement to all tools
- [ ] Update search_memories with tenant filtering
- [ ] Update get_memory with access control
- [ ] Update create_relationship with tenant validation
- [ ] Add migration script for backfilling tenant_id
- [ ] Integration tests for both modes
### Phase 3: Authentication (v1.0.0)
- [ ] JWT token validation
- [ ] OAuth2 provider integration
- [ ] Session management
- [ ] User-team mapping
- [ ] Audit logging
- [ ] Security testing
### Phase 4: Advanced Features (v1.1.0+)
- [ ] Fine-grained RBAC
- [ ] Cross-tenant memory sharing
- [ ] Memory transfer/ownership change
- [ ] Compliance features (retention, GDPR)
- [ ] Cache invalidation with pub/sub
- [ ] Real-time event notifications
## Success Metrics
- **Backward Compatibility**: 100% of existing single-tenant deployments work unchanged
- **Performance**: < 10% query overhead in multi-tenant mode
- **Adoption**: 30% of teams enable multi-tenant mode within 6 months
- **Security**: Zero tenant data leakage incidents
- **Reliability**: 99.9% uptime for multi-tenant deployments
## Conclusion
The hybrid multi-tenancy approach provides the best balance of backward compatibility, flexibility, and functionality. By making multi-tenancy opt-in with clear configuration, we can serve both individual developers and teams without breaking existing deployments. The phased implementation allows us to deliver value incrementally while building toward a robust, enterprise-ready system.
This architecture positions MemoryGraph as a scalable solution that grows with user needs—from personal memory system to team knowledge base to enterprise memory infrastructure.