Multi-tenant strict
L'isolation entre organisations est l'invariant structurant de betool. Voici comment il est appliqué, couche par couche.
Au niveau de la base de données
Chaque table métier porte une colonne org_id (UUID), indexée, contrainte par clé étrangère vers organisation(id).
Toutes les requêtes serveur passent par des store methods qui filtrent systématiquement par l'org_id de la session courante. Aucun chemin n'expose une requête arbitraire ; aucun client ne peut forcer un autre org_id.
Pour les ressources rares mutualisables (typiquement les comptes LLM partagés au niveau plateforme), une colonne is_shared BOOLEAN NOT NULL DEFAULT FALSE permet l'opt-in explicite. Règles :
- Toute lecture autorisée à
WHERE org_id = %s OR is_shared = TRUE. - Toute mutation d'une row
is_shared = TRUEexige le rôle hyperadmin (opérateur plateforme). - Toute mutation invalide le cache globalement — pas de divergence per-org.
Au niveau du système de fichiers
Chaque org a sa racine de stockage :
<data_root>/
└── orgs/
└── <org_id>/
├── fixtures/
├── uploads/
├── knowledge/
└── exchanges/
Les helpers serveur (org_*_dir(org_id)) résolvent ces chemins. Aucun code métier ne peut écrire en dehors de la racine d'une organisation.
Au niveau des caches
Tous les caches applicatifs (résolution de provider LLM, contextes courants, fichiers parsés) sont per-org. Quand une ressource is_shared = TRUE est mutée, le cache est invalidé globalement — pas de fenêtre où une org verrait l'ancienne valeur.
Au niveau des secrets
Les credentials (clés LLM, tokens d'auth opérateur, secrets webhook) sont stockés dans le coffre de chaque org. Ils sont jamais retournés en clair par l'API : les routes GET ne renvoient que has_api_key: bool.
Sur le plan Enterprise, le coffre peut être branché à HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault.
Au niveau de l'exécution
Quand un pipeline s'exécute :
- Les opérateurs n'ont accès qu'aux comptes externes déclarés dans l'org.
- Les modèles LLM utilisés sont résolus dans l'ordre
own > shared (fallback auto)— toujours. - Les fichiers attachés ne sont lisibles que sous la racine de l'org courante.
Si un opérateur tente d'accéder à une ressource hors-périmètre, l'erreur est explicite (OrgScopeViolation) — pas un timeout silencieux.
Test pratique
À chaque ajout d'une nouvelle ressource mutualisable, l'équipe répond aux 4 questions :
- Si une 2ᵉ org backoffice apparaît, mon code se comporte-t-il bien ?
- Si je modifie une row shared, tous les caches per-org sont-ils invalidés ?
- Mes secrets restent strictement serveur-side ?
- Mon GET permet-il aux pickers d'autres orgs de lister la ressource, sans exposer le secret ?
Si une réponse est non, la ressource n'est pas livrée.