Prompt

I’m building search for private documents with row-level security — what architecture makes sense?

Technology / Search & Discovery Tools1 observationLast seen Oct 12, 2026

Latest observation

Oct 12, 2026OpenAI APIWeb search: off

A good architecture for search over private documents with row-level security (RLS) is usually:

Recommended pattern: index for retrieval, authorize at query time

Keep a search index for fast retrieval, but never treat it as the source of truth for access. Enforce permissions with a security filter derived from your app’s authorization model.

High-level flow

  1. Ingest documents

    • Store the canonical document in your primary database/object store.
    • Extract searchable text, metadata, embeddings, etc.
    • Index document content into your search system.
  2. Attach security metadata to each indexed record

    • Examples:
      • tenant_id
      • owner_user_id
      • allowed_group_ids
      • classification
      • project_id
    • Keep the metadata simple and queryable.
  3. At search time

    • Authenticate the user.
    • Compute their effective permissions:
      • tenant
      • groups
      • roles
      • explicit shares
    • Inject an RLS filter into the search query so only authorized documents can match.
  4. Post-filter again if needed

    • If the search engine returns snippets/highlights, optionally re-check authorization before rendering.
    • This is a defense-in-depth measure.

Common architectures

1) Search engine with built-in filter support

Examples: Elasticsearch/OpenSearch, Meilisearch, Typesense, Solr.

You index document content plus security fields, then query with filters like:

  • tenant_id = current_tenant
  • group_id IN user_groups
  • visibility = public

This is often the simplest and fastest approach.

Pros

  • Fast
  • Mature
  • Simple operationally
  • Works well for keyword search and faceting

Cons

  • Complex ACLs can make filters large
  • Fine-grained per-document permissions can get tricky
  • Need careful design to avoid filter explosion

2) Hybrid: search engine + authorization service

Use:

  • Search index for candidate retrieval
  • Auth service / ACL store for access checks

Query:

  1. Search returns top N candidate doc IDs
  2. Authorization service checks which IDs the user may see
  3. Final result set is intersection

Pros

  • Very flexible permission model
  • Handles dynamic sharing and complex ACLs
  • Search index stays simpler

Cons

  • Extra latency
  • More round trips
  • Harder to scale if candidate sets are large

This works well when permissions are highly dynamic or complex.


3) Precomputed per-tenant or per-principal indexes

Build separate indexes:

  • by tenant
  • by department
  • sometimes even per customer

Pros

  • Very strong isolation
  • Simple queries
  • Easy to reason about

Cons

  • Index sprawl
  • More expensive to maintain
  • Not practical if users belong to many overlapping ACL sets

This is best for strong tenant isolation, especially SaaS multi-tenancy.


Best practice for RLS

Prefer “security trimming” using indexed permission attributes

Instead of checking every document against an ACL store at query time, store enough permission info in the index so the search system can apply a filter efficiently.

Typical patterns:

  • Tenant boundary
    • tenant_id = X
  • Group-based access
    • allowed_group_ids contains any of user_group_ids
  • Direct sharing
    • shared_with_user_ids contains user_id
  • Role/classification
    • classification <= user_clearance

For many systems, this is enough.


Key design decision: how to model permissions

Option A: Denormalize permissions into the index

Store all searchable permission tokens on the document.

Example:

{
  "doc_id": "123",
  "tenant_id": "t1",
  "allowed_principals": ["u:7", "g:42", "g:99"],
  "content": "..."
}

Query by matching any principal the user has.

Good when the ACL set is moderate.

Option B: Use relationship tables and expand at query time

Keep permissions in relational tables:

  • document_acl(doc_id, principal_type, principal_id)
  • user_groups(user_id, group_id)

At query time:

  • resolve user’s principals
  • pass them into search filter

Good when authorization is already modeled relationally.

Option C: Materialize effective access lists

Precompute which docs each user can see.

Good for small systems, but usually not scalable if users/documents change often.


Security pitfalls to avoid

1) Don’t rely on the search index alone as a trust boundary

Always validate permissions somewhere authoritative. The index can be stale.

2) Be careful with snippets/highlights

A search result can leak sensitive terms even if the full doc is later denied. Ideally:

  • filter before retrieval
  • and only generate snippets for allowed docs

3) Avoid “filter after ranking” if possible

If the search engine ranks unauthorized docs and you drop them later, users may infer hidden content from result counts or ranking behavior.

4) Watch cache poisoning

If you cache search results, cache by:

  • query
  • tenant
  • effective permission context Not just by query text.

5) Consider deletes and revocations

Revoked access should propagate quickly:

  • index updates
  • permission cache invalidation
  • TTLs on auth caches

Recommended architecture for most SaaS apps

If I had to choose a default:

Use:

  • Primary store for canonical docs and ACLs
  • Search engine with indexed security fields
  • Auth service to compute effective permissions
  • Query-time security filters
  • Periodic reindex / near-real-time updates

Data model example

Document index:

{
  "doc_id": "d1",
  "tenant_id": "t1",
  "title": "Budget plan",
  "body": "...",
  "allowed_group_ids": ["g1", "g2"],
  "shared_with_user_ids": ["u9"]
}

Search query:

{
  "query": {
    "bool": {
      "must": [
        { "multi_match": { "query": "budget", "fields": ["title", "body"] } }
      ],
      "filter": [
        { "term": { "tenant_id": "t1" } },
        {
          "bool": {
            "should": [
              { "terms": { "allowed_group_ids": ["g1", "g8"] } },
              { "terms": { "shared_with_user_ids": ["u7"] } }
            ],
            "minimum_should_match": 1
          }
        }
      ]
    }
  }
}

When to use each approach

Use simple indexed filters if:

  • permissions are mostly tenant/group based
  • search must be fast
  • you want lower operational complexity

Use a hybrid ACL check if:

  • permissions are highly granular
  • sharing changes often
  • you need exact correctness over latency

Use separate indexes if:

  • tenant isolation is paramount
  • documents should never co-reside logically
  • you have a manageable number of tenants/indexes

Practical recommendation

For most private-document search systems:

  1. Separate tenant data
  2. Index permission tokens in the search engine
  3. Apply query-time security filters
  4. Recheck authorization before displaying content
  5. Invalidate quickly on permission changes

That gives you a good balance of:

  • performance
  • correctness
  • maintainability

If you want, I can also sketch:

  • a Postgres + OpenSearch reference architecture,
  • a vector search + RLS design,
  • or a multi-tenant ACL schema for this.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.