Short answer: RBAC decides which identities may open named collections and tenants after authentication; multi-tenancy isolates data, RBAC decides who may name a shard.
Role-based access control decides who may touch which collections, tenants, and objects after authentication has named the caller. Tenancy answers which shard holds a memory; authorization answers which operators and services may name that shard. Without RBAC, every authenticated key that can reach the cluster often can open too much. This chapter covers how Weaviate roles and permissions carve access, how Engram sits beside cluster RBAC with project-scoped API keys and hard user_id boundaries, and why audit logs belong in the same design. Cluster RBAC governs Engram-backed services and operators; Engram scopes govern which end-user memories an agent call may read or write. Derive user_id from verified sessions—never let the model invent the tenant string. Prove both layers with cross-user probes and canaries on every deploy.
Role-based access control in Weaviate decides who may touch which collections, tenants, and objects after authentication has already named the caller. Multi-tenancy isolates data into shards. RBAC decides whether a given identity is allowed to open a named shard or collection at all. This chapter explains how Weaviate roles and permissions work, how they layer onto tenant isolation, how audit trails support production memory systems, and how Weaviate Engram keeps everyday agent memory behind project-scoped API keys and hard user_id boundaries so application code does not invent its own authorization filter.
What Problem Does RBAC Solve That Tenancy Alone Does Not?
Tenancy answers a storage question. Which shard holds this memory? Authorization answers a people question. Which operators and services may name that shard? Without RBAC, every authenticated key that can reach the cluster often sees too much. Search apps read collections they should never open. Analysts get write access they do not need. A leaked key becomes a full data breach instead of a bounded incident.
Weaviate RBAC models that boundary as users, roles, and permissions. A permission names a resource type, an action, and optional filters such as collection or tenant patterns. A role bundles permissions. Users and OIDC groups receive roles. On each request Weaviate evaluates the caller’s roles against the requested action and either allows or denies it. That decision is authorization, not a hope that every client remembered a filter.
Once you accept that identity must be checked at the engine, the next question is how fine-grained those checks can be.
How Do Roles and Permissions Carve Access in Weaviate?
Weaviate ships predefined roles such as root for full control and viewer for cluster-wide read access. Production systems usually need custom roles instead. A search service might read one product collection and nothing else. A tenant manager might create and update tenants that match a naming pattern. A clinician role might read objects only inside one hospital tenant.
Permissions cover separate resource families. Collection permissions govern schema definitions. Data permissions govern objects. Tenant permissions govern tenant lifecycle and metadata. Those families do not substitute for each other. Creating a collection does not grant create-tenant rights on that collection. Reading collection metadata does not imply reading objects. Wildcards and regex-style name filters let you grant hospital_* without listing every future tenant by hand. Least privilege means granting the smallest set of actions and the narrowest filters that still let the service do its job.
Assign roles carefully when they include role-management powers. A role that can create roles and assign them can escalate itself. Keep that capability with trusted operators. Prefer OIDC groups for human access so joining or leaving a team group updates Weaviate access without rotating every key by hand.
Memory products still need a day-to-day path for agents that should never hold cluster-admin powers. Engram is that path for application memory.
How Does Engram Fit Beside Cluster RBAC?
Engram authenticates with a project-scoped API key. Every request stays inside that project. User-scoped topics still require a user_id, and Engram enforces hard isolation between users with Weaviate multi-tenancy underneath. Groups isolate use cases the same way. Your application should derive user_id from a verified session or service identity. Never let the model invent the tenant string. Never trust a free-form tool argument as the isolation key.
Think of the layers as complementary. Cluster RBAC decides whether the Engram-backed services and operators may manage the underlying Weaviate resources. Engram scopes decide which end-user memories a given agent call may read or write. Together they keep a compromised chat key from becoming a cluster-wide read, and they keep one dye-house customer from reading another’s notes even when both share a project.
A concrete shop-floor example makes the binding rule easier to test.
What Does Authenticated Memory Access Look Like in Engram?
A textile atelier runs two dye vats as separate memory users inside one Engram project. Floor software authenticates the operator session first. Only then does it open Engram with the project key and the vat’s verified user_id.
import os
from engram import EngramClient, HybridRetrieval
client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])
# Derived from the signed shop session — never from model output
session_vat = "dye-vat-east"
group = "textile_atelier"
run = client.memories.add(
"Vat East holds indigo batch IN-448 at 28C with a twenty-minute soak. "
"Rinse water must stay below 0.4 NTU before the second dip. "
"Do not borrow Vat West's mordant schedule for this cotton run.",
user_id=session_vat,
group=group,
)
client.runs.wait(run.run_id)
hits = client.memories.search(
query="indigo soak temperature and rinse clarity for cotton",
user_id=session_vat,
group=group,
retrieval=HybridRetrieval(alpha=0.5),
)
assert any("IN-448" in m.content for m in hits)
# Same query under a different authenticated vat must not leak East's batch
west_hits = client.memories.search(
query="indigo soak temperature and rinse clarity for cotton",
user_id="dye-vat-west",
group=group,
retrieval=HybridRetrieval(alpha=0.5),
)
assert not any("IN-448" in m.content for m in west_hits)
The Engram key proves the caller may use this project. The session-bound user_id proves which vat’s memories are in scope. On the Weaviate side, you can mirror the same least-privilege idea for humans and ops tools with tenant-scoped data permissions, for example a role that may read objects only for tenants matching dye-vat-* in a memory collection. RBAC then blocks an operator key that tries to open a different customer prefix even if someone pastes the wrong id into a script.
Authorization without evidence is hard to defend in an audit. That is why the last piece of the story is logging.
Why Do Audit Logs Belong in the Same Design?
Weaviate records authorization decisions when RBAC is enabled. Those logs show who attempted which action on which resource and whether the engine allowed or denied it. That trail matters after a key leak. You can see what the compromised identity tried and what the roles blocked. It also matters for routine compliance work. Regulators ask whether access controls exist and whether you can show them working.
Build the same discipline into the Engram-facing app. Log the authenticated principal and the user_id you derived before each memory write or search. Run cross-user probes in CI. Store a canary fact under dye-vat-east, query as dye-vat-west with matching language, and assert an empty result. Isolation and authorization both fail quietly unless you test them on every deploy.
Our next chapter, How should you design collections and schemas in Weaviate for memory?, moves from who may touch the data to how you shape collections and properties so memory workloads stay searchable, evolvable, and aligned with the isolation model you just secured.