Skip to content
Stan

Toutes les études de cas

Étude de cas

Isolation multi-tenant chez elizaOS Cloud — RLS Postgres, chiffrement & migration progressive

Sur elizaOS Cloud (10 000+ utilisateurs sur une base Postgres partagée), j'ai déplacé le modèle d'isolation multi-tenant depuis la couche applicative vers la base de données en utilisant PostgreSQL Row-Level Security (RLS), combiné à une couche de chiffrement transparent pour les secrets des « characters », et une migration progressive des données préexistantes sans interruption de service.

Eliza Labs · elizaOS Cloud 8 min
  • PostgreSQL
  • Row-Level Security
  • Drizzle ORM
  • chiffrement
  • multi-tenant
  • agents IA
  • elizaOS
  • tests E2E
  • migration

TL;DR

Chez elizaOS Cloud (10 000+ utilisateurs sur une base Postgres partagée), j’ai déplacé le modèle d’isolation multi-tenant depuis la couche applicative vers la base de données en utilisant PostgreSQL Row-Level Security (RLS), combiné à une couche de chiffrement transparent pour les secrets des « characters », et une migration progressive des données préexistantes sans interruption de service.

Contexte

elizaOS est un framework open-source d’agents IA largement adopté, et elizaOS Cloud est son offre managée, construite sur une base Postgres multi-tenant partagée.

Le problème classique qu’on rencontre dans ce type d’architecture est simple mais critique :

👉 l’isolation des données est initialement faite dans l’application

Concrètement :

  • chaque requête SQL devait inclure un filtre du type WHERE owner_id = current_user
  • chaque développeur devait respecter cette règle manuellement
  • et donc chaque oubli potentiel = fuite de données inter-tenant

À l’échelle de plusieurs milliers d’utilisateurs et de centaines de tables, ce modèle devient structurellement dangereux.

Problème fondamental

Le vrai problème n’est pas seulement le bug humain.

C’est le modèle lui-même :

  • l’isolation dépend de la discipline des développeurs
  • la cohérence dépend de chaque requête SQL écrite
  • les agents IA peuvent agir au nom d’un utilisateur (donc complexité supplémentaire)
  • les entités ne sont pas seulement des users, mais aussi des agents et des « worlds »

Donc le modèle user_id = X est insuffisant.

Décision : déplacer l’isolation dans Postgres

La décision a été de sortir complètement la logique d’isolation de l’application et de la déplacer dans PostgreSQL via RLS.

Principe

Chaque requête exécutée par l’application est automatiquement filtrée par la base selon un contexte de session :

  • un identifiant d’entité courant est injecté dans la session DB
  • les policies RLS utilisent ce contexte pour filtrer les lignes
  • aucune requête applicative n’a besoin de répéter les conditions d’accès

Modèle d’isolation : entity-level (pas user-level)

Une des complexités spécifiques à elizaOS :

👉 le modèle n’est pas uniquement « user → data »

On a :

  • users
  • agents (qui agissent pour un user)
  • worlds / contexts
  • entités dérivées manipulées par des agents

Donc j’ai conçu un modèle d’isolation par entité, avec délégation :

  • un agent peut accéder aux données de son owner
  • mais uniquement dans ce périmètre
  • et les policies RLS gèrent cette logique directement

Cela permet de garder :

  • flexibilité agentique
  • sans casser l’isolation multi-tenant

Chiffrement des secrets « characters »

En parallèle du RLS, un autre problème critique était la gestion des secrets liés aux characters :

  • API keys
  • instructions privées
  • données sensibles liées au comportement des agents

Le choix a été de :

  • chiffrer ces secrets au niveau base
  • avec une couche transparente dans l’accès aux données
  • chiffrement à l’écriture / déchiffrement à la lecture
  • intégré dans le même flux que le contexte RLS

👉 Résultat : même en cas de mauvaise requête ou d’erreur applicative, les données restent protégées.

Migration des données préexistantes

Un des points les plus sensibles : la migration des données historiques (pré RLS).

On ne pouvait pas faire de « big bang ».

J’ai donc mis en place une migration en 3 étapes :

1. Backfill des ownerships

  • reconstruction des owner_id manquants
  • heuristiques selon type d’entité
  • validation des cas ambigus

2. Mode hybride

  • activation des policies RLS
  • mais tolérance contrôlée pour les données migrées
  • monitoring strict des accès

3. Mode strict

  • activation complète des règles RLS
  • suppression de la logique legacy applicative

Chaque étape était :

  • mesurée
  • testée
  • validée sur base réelle
  • avec rollback possible

Tests et validation

J’ai mis en place une suite de tests complète :

  • tests end-to-end sur vraie base Postgres
  • tests avec RLS activé vs désactivé
  • tests de migration dans les deux sens
  • vérification des permissions multi-tenant réelles
  • simulation d’accès croisés entre tenants

👉 L’objectif : garantir que le comportement était identique côté app, mais sécurisé côté DB.

Impact architectural

Ce changement a complètement modifié le modèle de sécurité :

Avant

  • isolation en application
  • dépendance aux développeurs
  • risque structurel de fuite

Après

  • isolation garantie par Postgres
  • impossible d’oublier un filtre
  • logique centralisée et auditable

Résultat

  • élimination de toute classe de bug liée à l’oubli de filtrage multi-tenant
  • isolation forte entre utilisateurs, agents et entités
  • chiffrement natif des secrets applicatifs
  • migration sans interruption de service
  • base prête pour évolution agentique à grande échelle

Ce que je changerais

Avec le recul :

  • j’aurais introduit plus tôt une policy-as-code testée automatiquement
  • car les RLS policies évoluent comme du code critique
  • et un changement silencieux peut casser l’isolation sans symptôme visible immédiat

Point clé

Le vrai changement n’est pas technique :

👉 on est passé d’une confiance dans l’application 👉 à une garantie structurelle fournie par la base de données


Toutes les études de cas

Un poste à pourvoir, ou simplement une discussion ? On en parle.