# How to Configure memU Memory Service for Multi-User Scenarios with user_id Scoping

> Configure memU for multi-user isolation using user_id scoping. Learn how to pass custom UserConfig and scope operations with the where filter for secure multi-user memory management.

- Repository: [NevaMind AI/memU](https://github.com/nevamind-ai/memu)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Configure memU for multi-user isolation by passing a custom `UserConfig` with a Pydantic model to `MemoryService`, then scope every operation using the `where` filter (or `user` dict for `memorize`) containing valid `user_id` fields.**

The NevaMind-AI/memU repository provides a Python memory service that treats user isolation as a core architectural feature. By leveraging the `UserConfig` model and consistent `user_id` scoping, you can deploy a single `MemoryService` instance that securely partitions data across unlimited users without manual filtering logic.

## Understanding memU's User-Scoped Architecture

memU implements multi-user isolation through a **configurable user model** defined in [`src/memu/app/settings.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/app/settings.py). The default `DefaultUserModel` contains a single optional field:

```python
class DefaultUserModel(BaseModel):
    user_id: str | None = None

```

When you instantiate `MemoryService`, it stores this model as `self.user_model = self.user_config.model` (see [`src/memu/app/service.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/app/service.py) lines 59-64). Every subsequent memory operation validates incoming filters against this schema, ensuring type safety and preventing scoping errors.

The service enforces isolation through a **`where`** dictionary that must contain fields matching your user model. This filter propagates through all CRUD operations, vector searches, and retrieval calls, automatically partitioning the underlying data store per user.

## Configuring the User Model for Multi-User Isolation

To enable strict multi-user scoping, define a custom Pydantic model with `user_id` as a required field and pass it to `MemoryService`:

```python
from pydantic import BaseModel, Field
from memu.app import MemoryService
from memu.app.settings import UserConfig

class StrictUserModel(BaseModel):
    user_id: str = Field(..., description="Unique user identifier")

user_cfg = UserConfig(model=StrictUserModel)
service = MemoryService(user_config=user_cfg)

```

This configuration makes `user_id` mandatory for all operations. If you omit the `user_config` parameter, memU defaults to `DefaultUserModel` where `user_id` remains optional.

## Scoping Memory Operations with user_id

Once configured, every memory operation requires a scope filter. The **`memorize`** method accepts a `user` dictionary, while retrieval and CRUD methods accept a `where` dictionary:

```python

# Store memory for a specific user

await service.memorize(
    resource_url="conversation.txt",
    modality="conversation",
    user={"user_id": "alice"}
)

# Retrieve only alice's memories

results = await service.retrieve(
    queries=[{"role": "user", "content": {"text": "What are my preferences?"}}],
    where={"user_id": "alice"}
)

```

As implemented in [`tests/test_inmemory.py`](https://github.com/NevaMind-AI/memU/blob/main/tests/test_inmemory.py) and demonstrated in [`examples/example_1_conversation_memory.py`](https://github.com/NevaMind-AI/memU/blob/main/examples/example_1_conversation_memory.py), consistently passing the `user_id` ensures complete data isolation.

## Extending Scope Beyond user_id for Complex Multi-Tenant Patterns

For scenarios requiring finer granularity—such as per-agent or per-session isolation—extend the user model with additional fields:

```python
class MultiScopeModel(BaseModel):
    user_id: str = Field(..., description="Primary user")
    agent_id: str | None = None
    session_id: str | None = None

service = MemoryService(user_config=UserConfig(model=MultiScopeModel))

# Store with multiple scope levels

await service.memorize(
    resource_url="chat.log",
    modality="conversation",
    user={"user_id": "bob", "agent_id": "assistant-1", "session_id": "sess-42"}
)

# Query specific agent-session combination

memories = await service.retrieve(
    queries=[{"role": "user", "content": {"text": "Previous context"}}],
    where={"user_id": "bob", "agent_id": "assistant-1"}
)

```

All fields defined in your model become valid keys for the `where` filter, enabling flexible multi-tenant architectures.

## How Data Isolation Works Under the Hood

memU enforces scoping through three layers in the codebase:

- **Validation layer**: The `_normalize_where` function in [`src/memu/app/retrieve.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/app/retrieve.py) (lines 87-104) validates that all keys in the `where` dict exist in `self.user_model`. It raises `ValueError` immediately if you supply an undefined field, preventing malformed queries.

- **Filtering layer**: The `matches_where` function in [`src/memu/database/inmemory/repositories/filter.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/database/inmemory/repositories/filter.py) performs the actual equality matching between the `where` filter and stored memory item metadata.

- **Repository layer**: All CRUD and search methods in [`src/memu/database/inmemory/repositories/memory_item_repo.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/database/inmemory/repositories/memory_item_repo.py) (such as `list_items` and `vector_search_items`) invoke `matches_where` to ensure only matching user-scope records are returned.

This architecture guarantees that even if you accidentally omit the `where` filter in application code, the service validates against the model schema, and the repository layer filters all database hits.

## Summary

- **Define strict scoping** by creating a Pydantic model with required `user_id` fields and passing it via `UserConfig` to `MemoryService`.
- **Scope every operation** using the `user` parameter for `memorize` and `where` parameter for `retrieve`, `list_memory_items`, and `delete_memory_items`.
- **Extend isolation** by adding optional fields like `agent_id` or `session_id` to your user model for multi-dimensional partitioning.
- **Rely on built-in validation** via `_normalize_where` and `matches_where` to prevent data leakage without writing custom filter logic.

## Frequently Asked Questions

### What happens if I forget to include the user_id filter in a query?

If you configured `MemoryService` with a model that requires `user_id`, the `_normalize_where` validator in [`src/memu/app/retrieve.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/app/retrieve.py) raises a `ValueError` when it detects missing required fields. If using the default optional model, omitting `user_id` returns unscoped results from the repository layer, effectively exposing all users' data—always configure a strict model for production multi-user deployments.

### Can I scope by multiple identifiers like tenant_id and user_id simultaneously?

Yes. Extend your Pydantic model to include any combination of scope fields (e.g., `tenant_id`, `organization_id`, `user_id`). Pass all required identifiers in the `where` dict for retrieval operations or the `user` dict for `memorize`. The repository layer in [`memory_item_repo.py`](https://github.com/NevaMind-AI/memU/blob/main/memory_item_repo.py) enforces equality matching on all provided fields.

### How does memU prevent accidental data leakage between users?

The service prevents leakage through schema validation and automatic filtering. The `where` dict keys are validated against your user model at runtime, and the `matches_where` function ensures repository queries only return memory items where the stored metadata exactly matches the provided scope values. No cross-user data appears in result sets unless explicitly requested with a broad `where` filter.

### Does user_id scoping work with persistent database backends?

Yes. While the examples reference the in-memory repository ([`src/memu/database/inmemory/repositories/memory_item_repo.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/database/inmemory/repositories/memory_item_repo.py)), the scoping mechanism relies on the abstract repository interface. Any storage backend implementing `list_items`, `vector_search_items`, and `matches_where` respects the `where` filtering logic, ensuring consistent multi-user isolation across SQLite, PostgreSQL, or vector database implementations.