Skip to content
Development
Skill

/agentsop-multi-tenant-rag

Security-first SOP for multi-tenant RAG systems. Activate when a calling agent is building, reviewing, or debugging any retrieval pipeline whose vector store is shared across more than one user, organisation, workspace, customer, or permission scope. Encodes the single

From plugin
skillalchemy
28747 skills
Install
$ npx -y skills add agentsope/SkillAlchemy --skill agentsop-multi-tenant-rag --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/agentsop-multi-tenant-rag

Context preview

The summary Claude sees to decide when to auto-load this skill.

Security-first SOP for multi-tenant RAG systems. Activate when a calling agent is building, reviewing, or debugging any retrieval pipeline whose vector store is shared across more than one user, organisation, workspace, customer, or permission scope. Encodes the single

SKILL.md

agentsop-multi-tenant-rag.SKILL.md
name: agentsop-multi-tenant-rag
version: 0.1.0
description: |
  Security-first SOP for multi-tenant RAG systems. Activate when a calling agent
  is building, reviewing, or debugging any retrieval pipeline whose vector store
  is shared across more than one user, organisation, workspace, customer, or
  permission scope. Encodes the single non-negotiable rule — **filter at the
  vector store query, never after retrieval / never after rerank** — together
  with the per-vendor query-time filter APIs (Pinecone namespaces +
  `$eq`/`$in`, Weaviate `multiTenancyConfig` + tenant handle, Qdrant
  `is_tenant` payload index + `Filter.must`, Chroma `where`, pgvector RLS),
  and the cross-framework adapters (LlamaIndex `MetadataFilters`, LangChain
  `filter=` dict). Frame the work as preventing CVE-2024-41892 / EchoLeak /
  Slack-AI-class cross-tenant leakage, not as "adding a filter for relevance".
trigger_keywords:
  - "multi-tenant RAG"
  - "tenant isolation"
  - "metadata filter"
  - "namespace per tenant"
  - "multitenancy vector store"
  - "RLS pgvector"
  - "cross-tenant leak"
  - "RAG security"
  - "workspace separation"
when_to_use:
  - "any RAG / retrieval pipeline whose corpus is shared across >1 tenant, customer, org, user, or permission scope"
  - "reviewing PRs that touch `vector_store.query` / `index.as_retriever` / `similarity_search` / `vectorstore.query_points`"
  - "designing ingestion for a SaaS knowledge base, internal corpus split by team, or per-customer corpus"
  - "responding to a 'we got the wrong document' bug — confirm it isn't actually a cross-tenant leak first"
  - "before shipping any RAG endpoint that exposes retrieved content to end users"
when_not_to_use:
  - "single-tenant / single-user corpora with no access scopes — over-engineering"
  - "purely internal eval / offline notebooks against fully public data"
  - "the retrieval boundary is enforced by a per-tenant collection / index that the application can never address by name — already isolated"

Multi-Tenant RAG · Security-First Isolation SOP

> Third-person operating model for a coder agent that owns retrieval correctness > across tenant boundaries. The audience is the LLM agent writing or reviewing > the code — not the end user.

> **One sentence**: *Isolation lives at the vector store query boundary, not > at the model. Anything that reaches the LLM's context window has already > leaked.*

---

1. 何时激活 (Activation Rules)

Activate this skill whenever **any** of the following holds:

1. The codebase contains a retrieval call (`vector_store.query`, `query_points`, `similarity_search`, `as_retriever().retrieve(...)`, raw `pgvector` `ORDER BY embedding <-> $1`) **and** the corpus serves more than one tenant, customer, organisation, workspace, user, or permission scope. 2. The user mentions any of: multi-tenant RAG, namespace, tenant, workspace, `tenant_id`, `org_id`, `user_id`, "cross-customer", "shared index", "knowledge base per team". 3. A bug report says "User A saw User B's document", "wrong company's data surfaced", "the assistant cited a doc I don't have access to", or anything that smells like cross-context bleed. 4. PR review: any new code calling a vector store **without** a tenant-scoped filter argument, or filtering only on the returned `nodes` / `documents` list after retrieval. 5. A new RAG endpoint is about to ship and tenant scoping has not been explicitly audited.

Do **not** activate when:

  • The corpus is fully public and there is no per-tenant view (e.g. open-data

Q&A).

  • The retrieval pipeline already enforces a **physically separate** index /

collection per tenant via infrastructure the application code cannot override (e.g. one Pinecone index per customer with credentials issued per-tenant). In that case the isolation lives in IAM, not in this skill.

---

2. 核心心智模型 (Core Mental Model)

Three principles. If a design violates any of them the system is exploitable, regardless of how good the LLM prompt is.

Principle 1 — Isolation lives at the boundary, not at the model

The boundary is the vector store query call. **Whatever crosses that boundary is in the trust set.** An LLM, a reranker, or a postprocessor that "filters out" foreign-tenant chunks is operating *inside* the breach: those tokens have already been embedded, retrieved, scored, and exposed to attacker-controlled prompts. CVE-2024-41892 (Pinecone, 2024) is the canonical demonstration — RBAC checks executed *after* retrieval allowed sentinel data to cross namespace boundaries before the access check fired. (See *we45*, *CSO Online* references.)

> **Operational corollary**: any code shaped `results = vs.query(...); results > = [r for r in results if r.metadata["tenant"] == ctx.tenant]` is a security > defect, not a style nit. The leak already happened — you only hid it from > the user.

Principle 2 — Embed the tenant key at ingestion, not just at query

A query-time filter is only enforceable if every vector carries the key. The two failure shapes:

  • *Forgotten field*: a single document ingested without `tenant_id` becomes a

shared global record returned to all tenants — it matches no filter predicate that requires equality.

  • *Mutable field*: `tenant_id` derived from the document body / LLM extraction

rather than from the request's authenticated session — attacker can craft document content that re-labels itself.

The tenant key **must** come from the authenticated session at ingestion time and be written into the immutable payload / metadata column. Treat it like a foreign key to your tenants table.

Principle 3 — Defence in depth: namespace **and** filter, not namespace **or** filter

Most production-grade stacks layer two mechanisms:

| Layer | Mechanism | What it stops | |---|---|---| | Storage | Per-tenant namespace / shard / collection / RLS policy | Operator bugs, mis-routed queries, ops mistakes | | Query | `filter={tenant_id: $session.tenant}` on eve

Read more
Ships withskillalchemy

From thought to skill. From signal to structure.

Get the whole plugin
Stats
289
Stars
17
Forks
Active
Maintenance
Python
Language
MIT
License
7d ago
Last commit
2mo ago
Created

Repo: agentsope/SkillAlchemy

Other skills on skillalchemy.