Documentación

Multi-tenancy estricto

Cómo betool aísla tus datos, secretos y ejecuciones de las demás organizaciones.

Multi-tenancy estricto

El aislamiento entre organizaciones es el invariante fundacional de betool. Así se aplica, capa por capa.

A nivel de base de datos

Cada tabla de negocio lleva una columna org_id (UUID), indexada y restringida por una clave foránea a organisation(id).

Todas las consultas del lado servidor pasan por métodos de store que filtran sistemáticamente por el org_id de la sesión actual. Ningún camino de código expone una consulta arbitraria; ningún cliente puede forzar un org_id distinto.

Para el pequeño número de recursos que pueden compartirse entre organizaciones (normalmente cuentas de LLM compartidas a nivel de plataforma), una columna is_shared BOOLEAN NOT NULL DEFAULT FALSE habilita un opt-in explícito. Reglas:

  • Cualquier lectura se autoriza con WHERE org_id = %s OR is_shared = TRUE.
  • Cualquier mutación de una fila is_shared = TRUE requiere el rol de hyperadmin (operador de plataforma).
  • Cualquier mutación invalida la caché globalmente — sin divergencia por organización.

A nivel de sistema de archivos

Cada organización tiene su propia raíz de almacenamiento:

<data_root>/
└── orgs/
    └── <org_id>/
        ├── fixtures/
        ├── uploads/
        ├── knowledge/
        └── exchanges/

Los helpers del servidor (org_*_dir(org_id)) resuelven estas rutas. Ningún código de negocio puede escribir fuera de la raíz de una organización.

A nivel de caché

Todas las cachés de la aplicación (resolución de proveedor de LLM, contextos actuales, archivos parseados) son por organización. Cuando se muta un recurso is_shared = TRUE, la caché se invalida globalmente — no hay ninguna ventana durante la cual una organización pueda ver un valor obsoleto.

A nivel de secretos

Las credenciales (claves de LLM, tokens de autenticación de operadores, secretos de webhook) se almacenan en la bóveda de cada organización. Nunca se devuelven en texto plano por la API: las rutas GET solo devuelven has_api_key: bool.

En el plan Enterprise, la bóveda puede conectarse a HashiCorp Vault, AWS Secrets Manager o Azure Key Vault.

A nivel de ejecución

Cuando se ejecuta un pipeline:

  • Los operadores solo tienen acceso a las cuentas externas declaradas dentro de su organización.
  • Los modelos de LLM se resuelven en el orden propio > compartido (fallback automático) — siempre.
  • Los archivos adjuntos solo son legibles bajo la raíz de la organización actual.

Si un operador intenta acceder a un recurso fuera de su ámbito, el error es explícito (OrgScopeViolation) — no un timeout silencioso.

Checklist práctica

Cada vez que se añade un nuevo recurso compartible, el equipo responde a estas 4 preguntas:

  1. Si aparece una segunda organización de backoffice, ¿mi código se comporta correctamente?
  2. Si muto una fila compartida, ¿se invalidan todas las cachés por organización?
  3. ¿Mis secretos permanecen estrictamente del lado servidor?
  4. ¿Mi GET permite que los pickers de otras organizaciones listen el recurso sin exponer el secreto?

Si alguna respuesta es no, el recurso no se despliega.