The situation
A knowledge team wants an internal assistant that answers staff questions from company documents. The HR handbook and policies sit in S3. Engineering runbooks are in Confluence, deal notes in Salesforce, and a few hundred PDFs on a shared drive. Generation is settled: a Claude model on Amazon Bedrock writes the answers. Retrieval is not.
Two constraints shape the build. The corpus spans four repositories with four permission models, and the assistant must never surface a passage an employee is not cleared to read, so an HR investigation note cannot turn up inside an engineer’s answer. The team is also small, with no appetite for running and tuning a vector database by hand.
A year ago this had a ready-made answer. Amazon Kendra crawled document ACLs alongside document content, and dropping it in as the retriever was the path of least resistance. AWS moved Kendra into maintenance mode on 30 June 2026 and closed it to new customers on 30 July. Existing indexes keep running and stay supported. This team has none.
What actually matters
Enforcement has to happen inside retrieval. Once a forbidden passage reaches the model’s context, prompting does not reliably keep it out of the answer, and a filter over the generated text is guesswork applied after the leak. Bedrock has two mechanisms that sit inside retrieval, and they are not interchangeable.
The first is ACL awareness on a managed knowledge base. Turn it on per data source, and ingestion crawls each document’s allowed and denied users and groups alongside its content. A Retrieve call then carries a user context holding the caller’s email, and results come back filtered to what that person may read. SharePoint, OneDrive, Google Drive and Confluence add a real-time check against the source for every document returned, which catches permission changes made since the last sync. S3 has no permission system to crawl, so its ACLs come from a file the team writes. The web crawler supports none of it, since web pages carry no permission model.
Three properties of that mechanism decide whether it is safe here. AWS documents it as ACL-aware filtering rather than authorization: Bedrock authenticates nobody, so the email in the user context is trusted exactly as far as the application that supplied it. Matching is on that email alone, exact, with no alias resolution across identity providers, so an address that differs between Confluence and the directory yields no results and no error. And it fails closed everywhere, including in the cases that surprise people. Deny beats allow. A document with no ACL in an ACL-enabled S3 source is never ingested at all. A Retrieve without a user context returns zero results from ACL-enabled sources, while any source in the same knowledge base that has ACL awareness switched off still returns everything to everyone.
Connector coverage then decides the shape of the build. Managed knowledge bases connect to S3, Confluence Cloud and Data Center, SharePoint, OneDrive, Google Drive, Box, ServiceNow, the web crawler, and a custom source. Salesforce is absent from that list. It is a connector for the other kind of knowledge base, the one where you bring your own vector store, and that kind has no ACL crawling: enforcement there is metadata filtering, built on attributes assigned at ingestion and a filter expression the application composes from the caller’s identity. Three of this team’s four repositories land on one path, and the deal notes land on the other.
What we’ll filter on
- Enforcement inside retrieval, rather than a prompt instruction or a filter over the generated answer.
- Permissions crawled from the source, against a mapping from identity to filters that the team writes and maintains.
- Connector coverage across all four repositories.
- Control over ChunkingSplitting documents into retrievable pieces before embedding them – small enough to match precisely, big enough to still make sense., the embedding model, and the vector store.
- What comes back: ranked chunks the application composes into an answer, or a finished answer with citations.
- Availability to a team with no index already running.
The landscape
Managed knowledge base with ACL awareness. The build AWS now recommends, and the closest thing left to Kendra’s old shape. Bedrock runs ingestion, holds the vector store, and answers Retrieve or AgenticRetrieveStream. RetrieveAndGenerate does not work against a managed knowledge base, so the grounded paragraph and its citations get assembled by the application from the chunks that come back. Tuning is limited by design: chunking is default, fixed-size, hierarchical or none, semantic chunking is unavailable, and the vector store is not a choice. Data sources can be synced daily, weekly, monthly or on demand.
Vector knowledge base with metadata filtering. The knowledge base you assemble, with connectors for S3, Confluence, SharePoint, Salesforce, the web crawler and custom ingestion. You pick the embedding model, the chunking strategy (fixed-size, semantic, hierarchical or none), and the vector store: OpenSearch Serverless, an OpenSearch managed cluster, S3 Vectors, Aurora PostgreSQL, Neptune Analytics, or a third-party store such as Pinecone or MongoDB Atlas. Both Retrieve and RetrieveAndGenerate work, so a grounded answer with citations comes back in one call. Permissions are metadata filters. Attributes are attached at ingestion, which for S3 means a .metadata.json sidecar next to each object, capped at 10 KB, and every query carries a filter expression. That mapping from users to filter values is a subsystem the team designs, builds and keeps correct.
Amazon Quick. The buy option, sold on per-user subscriptions. Point Quick at a managed knowledge base and it passes the signed-in user’s identity to Bedrock on every query, with no access-control configuration on the Quick side. It removes the build and the levers in the same move: no chunking choices, no embedding choices, no retrieval call to write against.
Self-run OpenSearch. The full-control end. Run the vector index directly, write the ingestion pipeline, and enforce permissions in application code before or after the query. It suits teams with retrieval requirements a knowledge base cannot express, such as unusual ranking or an index shared with non-RAG search. For a small team with a compliance-sensitive corpus it is the most rope and the least help.
A company already running Kendra has a fifth path: a Kendra GenAI index can serve as the retrieval source behind a Bedrock knowledge base, keeping its connector catalogue and its user-context filtering while application code moves to the knowledge base API. Kendra closed to new customers on 30 July 2026, so it is not selectable here, and it is left out of the table below.
Evaluation
Side by side
| Approach | Permissions crawled from the source | Enforced inside retrieval | Covers Salesforce | Chunking and embedding control | Answer with citations in one call | You assemble the app |
|---|---|---|---|---|---|---|
| Managed KB, ACL-aware | ✓ (S3 from a file you write) | ✓ | ✗ | ✗ | ✗ | ✓ |
| Vector KB, metadata filters | ✗ | ✓ (filters you map) | ✓ | ✓ | ✓ | ✓ |
| Amazon Quick on a managed KB | ✓ | ✓ | ✗ | ✗ | ✓ (finished app) | ✗ |
| Self-run OpenSearch | ✗ | Your code | ✗ (you write ingestion) | ✓ | ✗ | ✓ |
Reading the table against the scenario: the team is building an application rather than adopting a finished one, which keeps them off Quick. No row covers all four repositories with crawled permissions, so the corpus splits. Three repositories have a managed connector with ACL awareness behind it; Salesforce does not, and the deal notes go either through a vector knowledge base with filters or through an export that lands them in S3 with ACL entries attached.
The solution
Put the HR documents, the Confluence runbooks and the shared-drive PDFs into a managed knowledge base with ACL awareness enabled on each data source, and keep the Salesforce deal notes in a second, vector knowledge base with metadata filters. Three jobs follow: turning the enforcement on, feeding it a verified identity, and keeping the permissions true.
Turning it on differs by source. Confluence crawls its own restrictions during ingestion and re-checks them live at query time, so nothing is authored by hand. S3 takes an ACL file the team maintains: a global JSON array mapping key prefixes to entries of Name (an email), Type (USER), and Access (ALLOW or DENY), with a per-document .metadata.json overriding the global file where one prefix is not enough. Every document needs an entry. An ACL-enabled S3 source skips anything without one, so a missing rule reads as a document that vanished rather than as a document anyone can see.
Feeding it an identity is where the security boundary actually lives. The application authenticates the user and passes the verified email in the user context on every Retrieve. Bedrock does not check that email against anything, so it has to come off the session, never off a client-supplied field. Two smaller rules follow. The address must match the one the source system holds, character for character, since there is no alias resolution. And every data source in a knowledge base needs ACL awareness enabled, because one source left without it returns its documents to every caller regardless of context.
Keeping it true is the part that separates a demo from a system. Crawled permissions and group memberships are only as fresh as the last sync, so the sync schedule is a compliance decision rather than a performance one. Real-time verification covers the gap for Confluence, SharePoint, OneDrive and Google Drive; S3 has none, so a tightening in the ACL file reaches retrieval only once the affected prefix is reindexed. Third-party identity provider credentials are cached for up to an hour, and permission changes are eventually consistent, usually landing within minutes. Test the loop adversarially before launch and after every change: a persona per group, a battery of queries aimed at the other groups’ material, zero cross-boundary results required.
Two design decisions sit underneath all of that. The content whose access genuinely varies document by document, the HR investigation notes, stays out of the index entirely; exclusion is the one permission strategy that cannot leak. And the Salesforce half keeps its metadata vocabulary small and coarse, mapped from containers rather than from per-record user lists, which drift immediately and outgrow what a filter expression can hold.
Worked example
Take the HR documents and the runbooks and trace the path end to end. The handbook lands under s3://kb-corpus/hr/handbook/ and the policies under s3://kb-corpus/hr/policies/, and the global ACL file grants each prefix to the groups that may read it:
[
{
"keyPrefix": "s3://kb-corpus/hr/handbook/",
"aclEntries": [
{ "Name": "dana@example.com", "Type": "USER", "Access": "ALLOW" }
]
}
]
A single document that needs an exception carries its own sidecar. severance-policy.pdf.metadata.json sits beside the object with an accessControlList array in the same entry format, and it takes precedence over the prefix rule. The investigation notes are never ingested; they stay in the HR system, and the assistant’s answer about them is that it cannot help.
The runbooks arrive through the Confluence connector with their space and page restrictions crawled alongside the content. Nothing is authored for them, and a restriction changed in Confluence after the last sync is caught by the live check at query time.
At query time an engineer signs in, the application resolves their verified email from the session, and the Retrieve call carries it:
{
"knowledgeBaseId": "KB12345678",
"retrievalQuery": { "text": "what is the on-call escalation path" },
"userContext": { "userId": "alex@example.com" }
}
Alex gets the handbook and the runbook spaces Confluence grants them. Dana in HR gets the handbook and the policies. Neither reaches the other’s restricted material, because the chunks never enter the context. When a persona test comes back empty and should not have, CheckIngestedDocumentAcl answers whether that user can reach that document, and GetIngestedDocumentAcl returns the whole ACL attached to it, which turns an absent result into a question with an answer.
What’s worth remembering
- A managed knowledge base crawls document ACLs from S3, Confluence, SharePoint, OneDrive, Google Drive and custom sources, and filters
Retrieveresults against a user context; the web crawler has no ACL support, and Salesforce is not a managed connector at all. - ACL awareness is filtering, not authorization: Bedrock does not authenticate the caller, so the application must supply a verified email, matched exactly with no alias resolution.
- It fails closed at every step. Deny beats allow, a document with no ACL is not ingested, and a
Retrievewithout a user context returns nothing from ACL-enabled sources. - A source with ACL awareness switched off returns its documents to everyone, even in a knowledge base where the other sources are enforced.
- A vector knowledge base gives up crawled ACLs for control of chunking, the embedding model and the vector store, plus
RetrieveAndGenerate; enforcement there is metadata filters mapped from identity server-side. - Crawled permissions are as fresh as the last sync, except on the four connectors with real-time verification, so the sync schedule and an adversarial persona test are both part of the compliance story.