evo-ai — The multi-tenant platform

One deployment, many customers, provably separate.

How one evo-ai deployment safely serves many customers: identity, data isolation, per-tenant configuration, automatic onboarding, and the two ways an application integrates with it.

The shape of the problem

A single evo-ai deployment is meant to serve many organizations at once. That demands three things that usually fight each other:

  • Hard isolation — no tenant can ever see another's data, even through a bug or a cleverly worded question.
  • Zero-touch onboarding — new customers appear without an operator wiring them up by hand.
  • Per-tenant flexibility — each customer can bring their own model, data sources, and access rules.

evo-ai addresses all three with a small number of composable mechanisms.

Identity: who is asking, and for whom

Every request is authenticated one of two ways:

  • User JWTs — issued by the identity provider (EvoPlatform), signed RS256, verified against its published JWKS. The token carries a tenant_id claim. evo-ai reads the tenant only from these verified claims — never from the request — so a caller cannot assert a tenant they weren't granted.
  • Service keys — for trusted application backends that query on behalf of their own users. A backend presents a service key (a svc_-prefixed bearer token, stored server-side only as a SHA-256 hash) and asserts the target tenant via an X-Data-Tenant header. This lets a product like an EHS SaaS add an "Ask AI" panel that queries each site's data under one trusted credential, while still scoping every request to the right tenant.

Data isolation: multiple independent layers

Isolation is never trusted to a single check. For any tenant's data:

  • Physical vector separation — each (tenant, collection) is its own physical Qdrant collection.
  • Metadata filtering — plus a tenant_id filter on every query, so even within shared infrastructure the query is scoped.
  • Server-bound analytics scope — the SQL analytics path binds the tenant id server-side inside the curated views; generated SQL cannot widen it.

Two mechanisms would have to fail simultaneously to cross a tenant boundary — and the analytics path adds a third, independent one.

Per-tenant configuration

Each tenant configures the platform to its needs, independently:

  • Model choice — any cloud provider (via LiteLLM) or a local model (via Ollama), selected per tenant. Provider API keys are encrypted at rest.
  • Embeddings — recorded per collection at creation time and fixed for that collection's life (changing embeddings means a new collection, never a silent reinterpretation of existing vectors).
  • Data sources — each tenant's own connectors (files, wikis, tickets, databases, documentation sites), synced on independent schedules.

Automatic onboarding: source auto-discovery

Customers create their own accounts, so the platform must notice them with no human in the loop. When a discovery source is configured, every scheduler tick reconciles the upstream list of tenants against what evo-ai has provisioned:

  • New active tenant → a data source is created and begins syncing.
  • Suspended / past-due → syncing pauses (respecting billing state).
  • Reactivated → syncing resumes.
  • Deleted → the tenant's documents are purged from the index and the source removed. Deletion means deletion — an offboarding guarantee, not a best-effort.

The same mechanism can provision multiple sources per tenant — for example, a records source (the tenant's live data) and a documentation source (the product manual), each with its own cadence.

Role-scoped answers within a tenant

Isolation isn't only between tenants — it can extend within one. A trusted application can tell evo-ai which datasets the asking user is allowed to see (by passing the permitted source types with the request). evo-ai then restricts both vector retrieval and the analytics SQL views to those datasets: the planning model never even sees the disallowed views, they're omitted from the generated query, and any stray reference is rejected before execution. A user without access to a given dataset gets the same "not covered" response they'd get if the data didn't exist — enforced server-side, so it can't be bypassed from the client.

The two integration models

An application integrates with the platform one of two ways, depending on trust:

  • Direct user tokens — end users (or a thin client) call evo-ai with their own JWTs. Simplest; the token is the authorization.
  • Trusted backend + service key — the application's server calls evo-ai with a service key and asserts each user's tenant (and, optionally, that user's permitted datasets). This is how an existing SaaS embeds an AI panel: the product keeps control of authentication and authorization, evo-ai provides the grounded answers, and every request is still tenant-scoped and auditable.

Each mechanism is small and independently verifiable — verified-claims identity, default-deny links, physical + metadata + SQL isolation, encrypted per-tenant config, reconciling auto-discovery, server-side role scoping. Together they let a single deployment onboard customers automatically, keep each one's data provably separate, and still give every tenant its own model, sources, and access rules.

Back to the evo-ai overview, or see security in local mode and guardrails. Questions? Get in touch.