Strikte Mandantentrennung
Die Isolation zwischen Organisationen ist die grundlegende Invariante von betool. So wird sie Schicht für Schicht durchgesetzt.
Auf Datenbankebene
Jede Geschäftstabelle trägt eine Spalte org_id (UUID), indiziert und durch einen Fremdschlüssel auf organisation(id) eingeschränkt.
Alle serverseitigen Abfragen laufen über Store-Methoden, die systematisch nach der org_id der aktuellen Sitzung filtern. Kein Codepfad legt eine beliebige Abfrage offen; kein Client kann eine andere org_id erzwingen.
Für die wenigen Ressourcen, die organisationsübergreifend geteilt werden können (typischerweise gemeinsam genutzte LLM-Konten auf Plattformebene), ermöglicht eine Spalte is_shared BOOLEAN NOT NULL DEFAULT FALSE ein explizites Opt-in. Regeln:
- Jeder Lesevorgang ist mit
WHERE org_id = %s OR is_shared = TRUEautorisiert. - Jede Mutation einer Zeile mit
is_shared = TRUEerfordert die Hyperadmin-Rolle (Plattformbetreiber). - Jede Mutation invalidiert den Cache global — keine Divergenz pro Organisation.
Auf Dateisystemebene
Jede Organisation hat ihr eigenes Speicher-Stammverzeichnis:
<data_root>/
└── orgs/
└── <org_id>/
├── fixtures/
├── uploads/
├── knowledge/
└── exchanges/
Server-Helfer (org_*_dir(org_id)) lösen diese Pfade auf. Kein Geschäftscode kann außerhalb des Stammverzeichnisses einer Organisation schreiben.
Auf Cache-Ebene
Alle Anwendungs-Caches (Auflösung des LLM-Providers, aktuelle Kontexte, geparste Dateien) sind pro Organisation. Wenn eine Ressource mit is_shared = TRUE mutiert wird, wird der Cache global invalidiert — es gibt kein Zeitfenster, in dem eine Organisation einen veralteten Wert sehen könnte.
Auf Geheimnisebene
Zugangsdaten (LLM-Schlüssel, Operator-Auth-Tokens, Webhook-Geheimnisse) werden im Tresor jeder Organisation gespeichert. Sie werden von der API niemals im Klartext zurückgegeben: GET-Routen geben nur has_api_key: bool zurück.
Im Enterprise-Plan kann der Tresor an HashiCorp Vault, AWS Secrets Manager oder Azure Key Vault angebunden werden.
Auf Ausführungsebene
Wenn eine Pipeline läuft:
- Operatoren haben nur Zugriff auf die externen Konten, die innerhalb ihrer Organisation deklariert sind.
- LLM-Modelle werden in der Reihenfolge
eigenes > geteilt (automatischer Fallback)aufgelöst — immer. - Angehängte Dateien sind nur unter dem Stammverzeichnis der aktuellen Organisation lesbar.
Versucht ein Operator, auf eine Ressource außerhalb seines Geltungsbereichs zuzugreifen, ist der Fehler explizit (OrgScopeViolation) — kein stiller Timeout.
Praktische Checkliste
Wann immer eine neue teilbare Ressource hinzugefügt wird, beantwortet das Team diese 4 Fragen:
- Wenn eine zweite Backoffice-Organisation auftaucht, verhält sich mein Code dann korrekt?
- Wenn ich eine geteilte Zeile mutiere, werden dann alle Caches pro Organisation invalidiert?
- Bleiben meine Geheimnisse strikt serverseitig?
- Erlaubt mein GET den Auswahlmenüs anderer Organisationen, die Ressource aufzulisten, ohne das Geheimnis offenzulegen?
Wenn eine der Antworten Nein lautet, wird die Ressource nicht ausgeliefert.