SilentChat
Back to BlogEngineering

How SilentChat keeps tenants apart — including in the AI

Marc WagnerSeptember 16, 20263 min read
TL;DR

Every row that belongs to a customer carries a tenant ID, and every query filters on it — including the vector search the AI draws its answers from. The tenant ID comes from the login token, never from the request. Integration tests check access across tenant boundaries, and our own reviews found gaps that we closed before launch.

The principle

SilentChat is a multi-tenant application: all customers share the database, separation is logical. That's why every row belonging to a customer carries a tenant ID, and every query restricts to it.

The ID comes from the login token — for API access, from the API key — and is set by authentication. A request cannot send it along and therefore cannot forge it.

For lookups by a single ID there are building blocks that put the tenant filter into the query itself rather than comparing the result afterwards. An after-the-fact comparison is the step that is easiest to forget.

The AI only sees your content

The AI answers questions from your knowledge base. To do so, SilentChat splits articles into sections, computes a vector for each and stores both in the knowledge_chunks table — with the tenant ID on every row. The similarity search filters by tenant inside the query itself, before sorting by distance. That also applies to the fallback search without a language filter.

The system prompt is rebuilt for every answer from the respective tenant's AI settings; there is no cross-tenant cache for it. Every AI call is booked with tenant, feature and cost.

How we test it

  • Integration tests access foreign data over HTTP with a second tenant and expect “not found” — for the knowledge base, products, feedback, tickets, workflows and more.
  • Real-time channels check whether a user may join a room.
  • Audits look specifically for queries that load entries by ID only.

What doesn't exist: an external penetration test and a bug bounty programme. Both are on our list.

What we found

Before launch, our own reviews found several places where individual routes loaded entries by ID only — among others in the roadmap module, in comments on feature requests, and when assigning SLA policies to tickets. All of them were fixed before SilentChat went public, and regression tests keep them fixed.

The lesson: a list of known findings is no substitute for searching for the pattern. Where there was one, the neighbouring module is worth a look.

If you find a vulnerability, please write to security@silentchat.de.

multi-tenantaisecurityragengineering

Related articles

Tenant isolation in SilentChat — including in the AI | SilentChat