The pattern is filter-at-query: store access metadata on every document (tenant, groups, explicit ACL entries) and have the application attach the user's resolved entitlements to every query as a mandatory filter that the engine evaluates during retrieval.
The two alternatives both fail. Post-filtering is a security and recall bug: restricted documents participate in scoring before being dropped, and selective permissions empty the result set. Per-user or per-role indexes explode combinatorially and go stale the moment permissions change.
Query-time filtering keeps one index, honors permission changes instantly, and puts enforcement in one place.
Why is post-filtering a security problem, not just a recall problem?
"Remove it from the results afterward" still lets restricted content shape the query: it occupies candidate slots, and its removal is observable. A user who can see 2% of the corpus gets near-empty answers (why filtering breaks graph indexes) while the system did scoring work over documents they were never entitled to touch. Access control belongs in retrieval, not in cleanup.
Why not an index per role?
Real RBAC means groups, nested groups, per-document shares, and revocations, far beyond a handful of static roles. Materializing indexes per access pattern means combinatorial storage, rebuild-on-permission-change, and a window where revoked access still serves. It only looks viable at whiteboard scale.
What does the working pattern look like?
- On ingest: write ACL metadata on each document: owner, allowed groups/roles, tenant.
- On query: the application resolves the user's entitlements (expand groups from your identity system) and attaches them as a filter:
tenant = X AND groups ∩ user_groups ≠ ∅. - In the engine: the filter executes as part of retrieval, so scoring only ever sees authorized documents.
Permission changes are metadata updates, not index rebuilds. Deny-rules and audit stay in the application layer, where identity lives.
User query
App resolves entitlements
groups from your identity system
Mandatory ACL filter
Engine filters during retrieval
Only authorized docs scored
What does this demand from the engine?
Everything rests on filtered search staying fast and correct at high selectivity. A user who can access 1% of the corpus is a 1%-selectivity filter on every single query, which is exactly where graph-index engines fall over.
TopK's query engine treats filter predicates as first-class query operations (vectorized filtering inside its reactor engine, designed so selective queries get faster, not slower) and publishes filtered-search performance across selectivities, which is precisely the capability RBAC-at-query-time depends on.