Today you build a SaaS-shaped code assistant: each tenant’s corpus is isolated, retrieval is filtered server-side, citations fail closed, and evals run per tenant. Shared prompt soup is not isolation. Day 30 is a project day — ship something you can demo with two tenants and a CI gate that proves zero cross-hits.
⚡ TL;DR: One KB per tenant or shared index with mandatory
tenant_idmetadata filters enforced from the JWT (never from the model). Per-tenant golden packs. Webhook ingest per connected repo. IAM that cannot read another tenant’s prefix.
Lab / project goals
- Ingest two fake tenants (
acme,globex) into separate S3 prefixes - Retrieve with hard filters derived from auth
- Cite or refuse using Day 26’s gate
- Run eval packs per tenant with zero cross-tenant retrieval hits
- Document offboarding: delete prefix + index + keys
Architecture sketch
Git webhook → validate → SQS(tenant_id, paths, sha)
→ embed worker → s3://corp-rag/{tenant_id}/...
→ KB sync or OpenSearch upsert (metadata tenant_id, git_sha)
API → authZ → Retrieve(filter tenant_id=jwt.tid) → Converse → citation gate → response
def retrieve(tenant_id: str, question: str, k: int = 8):
assert tenant_id and tenant_id.replace("-", "").isalnum()
return bedrock_retrieve(
knowledge_base_id=KB_ID,
query=question,
filter={"equals": {"key": "tenant_id", "value": tenant_id}},
k=k,
)
Clients must not pass tenant_id as a free form field. Take it from the verified JWT only.
Isolation options
| Pattern | Pros | Cons |
|---|---|---|
| KB / index per tenant | hard isolation | quotas, ops cost |
| Shared index + filter | cheaper | filter bugs = breach |
| Cell per enterprise | blast-radius control | more platform work |
Start shared+filter for SMB; dedicated KB/cell for enterprise. Regardless, add an automated cross-tenant probe in CI.
def test_no_cross_tenant():
for q in load("eval/acme.jsonl"):
paths = retrieve("acme", q["question"])
assert all(meta_tenant(p) == "acme" for p in paths)
Project idea: dual-tenant bakeoff
Seed each tenant with deliberately similar file names (auth.ts) but different implementations. Ask “how do we verify JWTs?” for each tenant and assert citations never cross. Add a freshness footer (git_sha) per Day 27.
Deliverables checklist
- [ ] Tenant id from JWT only
- [ ] Metadata
tenant_idon every chunk - [ ] Citation gate + freshness sha footer
- [ ] Per-tenant eval in CI with cross-hit assertion
- [ ] Offboarding runbook tested once
- [ ] IAM policies reviewed for prefix isolation
Series navigation
Last updated September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
