Back to Theory
Architecture6 min read · June 16, 2026

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.

F
Feather DB
Engineering

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".

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.

# 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:

# 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.

# 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)
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