# Namespace and Entity Design in Feather DB: Multi-Tenant Context Isolation > Feather DB's namespace and entity system gives you multi-tenant memory isolation without separate database files. One .feather file can serve thousands of users with strict per-user scope and fast filtered retrieval. - **Category**: Architecture - **Read time**: 6 min read - **Date**: June 16, 2026 - **Author**: Feather DB (Engineering) - **URL**: https://getfeather.store/theory/feather-db-namespace-entity-design --- ## The isolation problem When you build a multi-tenant AI product — a SaaS assistant where every user has their own memory, or a multi-agent system where each agent maintains its own context — you need strict isolation. User A's memories must not appear in User B's search results, even if they're semantically similar. And you need this isolation to be fast: a per-user ANN search must not slow down as the total number of users grows. Feather DB solves this with a two-level isolation system: namespaces for tenant boundaries, and entities for topic grouping within a namespace. ## Namespaces: tenant-level isolation A namespace is a string key that partitions the index. Every memory add and every search can be scoped to a namespace. Memories in namespace `"user-alice"` are completely invisible to searches in namespace `"user-bob"`. ```python import feather_db as fdb db = fdb.DB.open("saas_memory.feather", dim=768) # Add a memory scoped to Alice alice_vec = embed("Alice prefers dark mode and compact layouts.") db.add(alice_vec, text="Alice prefers dark mode and compact layouts.", namespace="user-alice") # Add a memory scoped to Bob bob_vec = embed("Bob works on a team of 15 engineers.") db.add(bob_vec, text="Bob works on a team of 15 engineers.", namespace="user-bob") # Search scoped to Alice — Bob's memories never appear query_vec = embed("What does the user prefer for their UI?") alice_results = db.search(query_vec, k=5, namespace="user-alice") # Search scoped to Bob — Alice's memories never appear bob_results = db.search(query_vec, k=5, namespace="user-bob") ``` Under the hood, Feather DB maintains a per-namespace HNSW subgraph. ANN search on namespace `"user-alice"` traverses only the nodes in that namespace's subgraph. Search latency scales with the number of memories for that user, not with the total number of memories across all users — which is the property that makes multi-tenant Feather DB practical at scale. ## Entities: topic grouping within a namespace Within a namespace, you can further group memories by entity. An entity is a string that tags a memory as belonging to a logical object — a user profile, a conversation thread, a document, a project. Entities enable you to retrieve all memories associated with a specific object without a semantic query. ```python # Within Alice's namespace, tag memories by topic db.add(embed("Alice likes Python over JavaScript."), text="Alice likes Python over JavaScript.", namespace="user-alice", entity="preferences-language") db.add(embed("Alice is working on a FastAPI backend for their startup."), text="Alice is working on a FastAPI backend for their startup.", namespace="user-alice", entity="work-context") db.add(embed("Alice mentioned her cat is named Pixel."), text="Alice mentioned her cat is named Pixel.", namespace="user-alice", entity="personal-facts") # Search within Alice's namespace, filtered to preferences only results = db.search(query_vec, k=5, namespace="user-alice", entity="preferences-language") ``` Entities are also useful for deduplication and update. If you want to replace all memories for a given entity (e.g., after a user updates their profile), you can delete by entity and re-add: ```python # Delete all memories for an entity and rewrite db.delete_by_entity(namespace="user-alice", entity="preferences-language") # Re-add updated preferences db.add(embed("Alice now prefers TypeScript over Python for backend."), text="Alice now prefers TypeScript over Python for backend.", namespace="user-alice", entity="preferences-language") ``` ## Attribute filters: secondary filtering within results After namespace and entity scoping, you can apply attribute filters to narrow results further. Attributes are key-value metadata set on individual memories. Attribute filters do not modify the ANN graph traversal — they apply as an exact post-filter over the ANN candidates. ```python # Add memories with type attribute db.add(embed("Alice's Q2 OKR: ship the API v2 by June."), text="Alice's Q2 OKR: ship the API v2 by June.", namespace="user-alice", entity="work-context") mem = db.get_last() mem.meta.set_attribute("type", "goal") mem.meta.set_attribute("quarter", "Q2-2026") # Filter to only goal-type memories in Q2 results = db.search(query_vec, k=5, namespace="user-alice", filter={"type": "goal", "quarter": "Q2-2026"}) ``` Note the correct API for setting attributes: use `meta.set_attribute(key, val)`, not `meta.attributes[key] = val`. The latter does not persist due to a pybind11 copy issue where the Python dict is a copy, not a reference to the underlying C++ object. ## The multi-tenant SaaS pattern For a SaaS product serving many users, the recommended pattern is: - **namespace = user_id** — hard tenant boundary, enforced at the DB level - **entity = topic_category** — logical grouping ("preferences", "work", "personal", "current-project") - **attributes** — secondary metadata for filtering (type, source, confidence, created_at) ```python import feather_db as fdb from datetime import datetime db = fdb.DB.open("saas_memory.feather", dim=768) def add_user_memory(user_id: str, text: str, category: str, memory_type: str, importance: float = 1.0): vec = embed(text) mem = db.add(vec, text=text, namespace=user_id, entity=category) mem.meta.set_attribute("type", memory_type) mem.meta.set_attribute("created_at", datetime.utcnow().isoformat()) mem.meta.set_attribute("importance", importance) return mem def search_user_memory(user_id: str, query: str, category: str = None, k: int = 5): vec = embed(query) return db.search(vec, k=k, namespace=user_id, entity=category) # None = search all entities # Usage add_user_memory("user-42", "User works at a fintech startup.", category="work-context", memory_type="fact", importance=1.5) add_user_memory("user-42", "User mentioned anxiety about the Series A.", category="emotional-state", memory_type="observation", importance=0.8) results = search_user_memory("user-42", "What is the user working on professionally?") ``` ## Namespace vs entity vs attribute: when to use which Isolation layerScopeUse forSearch impact namespaceHard partitionTenant boundaries (user_id, agent_id)ANN subgraph — only this namespace searched entityLogical groupTopic categories, document sourcesANN subgraph within namespace attribute filterKey-value matchType, status, date, confidencePost-filter over ANN candidates Namespaces are the strongest guarantee: memories in different namespaces never mix. Entities subdivide within a namespace and also use subgraph traversal. Attribute filters are the most flexible but weakest isolation — they filter after ANN, so they don't reduce traversal cost, only result set size. ## Operational considerations A single `.feather` file can hold thousands of namespaces. The file format stores namespace metadata in the header; the HNSW subgraphs are stored contiguously per namespace. This means a 100-user deployment and a 10,000-user deployment use the same file format and the same client code — you don't need separate files per user or a separate database per tenant. For very large deployments (100K+ namespaces), consider sharding by namespace hash across multiple `.feather` files with a routing layer. Each shard file still benefits from the parallel HNSW load (4.7× faster cold start) and int8 RAM quantization (1.7× less RAM). **Install:** `pip install feather-db` · **GitHub:** [github.com/feather-store/feather](https://github.com/feather-store/feather) --- *This is the machine-readable mirror of the theory post at [getfeather.store/theory/feather-db-namespace-entity-design](https://getfeather.store/theory/feather-db-namespace-entity-design). For the full Feather DB documentation, see [getfeather.store/llms-full.txt](https://getfeather.store/llms-full.txt).*