Documentación de Mentat — en evolución continua.
ReferenciaSecurity Rules y permisos

Security Rules y permisos

Las reglas (firestore.rules) encarnan el aislamiento multi-tenant y la regla de cero writes directos del cliente. El Admin SDK (Cloud Functions) las bypassa; toda mutación operativa va por ahí.

Multi-tenant

Cada documento cuelga de /organizations/{orgId}/…. Un usuario solo lee los datos de las orgs donde es miembro, verificado por:

exists(/organizations/{orgId}/members/{request.auth.uid})

Patrón general

ColecciónLecturaEscritura
ontologyTypes, objects, actionTypes, dataSourcesmiembrossolo Cloud Functions
actionExecutions, auditEvents, ingestionRunsmiembrosdeny-all (inmutables)
membersmiembrossolo Cloud Functions
savedViewssolo el ownersolo Cloud Functions
dataSourceSecretsdeny-all (ni lectura)deny-all
/users/{uid}solo el propio usuariodeny-all (allow update: if false)
/accessRequests, /rateLimitsdeny-alldeny-all (solo la Function escribe)
⚠️

Los secretos (API keys de REST, token de webhook) no viven en el doc legible de la DataSource: se aíslan en /dataSourceSecrets (deny-all incluso para lectura), porque las rules no pueden ocultar campos en una lectura.

Inmutabilidad

actionExecutions, auditEvents e ingestionRuns son append-only: create, update y delete están en if false para todos, incluido admin. Es la garantía del audit trail.

SavedViews privadas

allow read: if isMemberOf(orgId) && resource.data.owner == request.auth.uid;

El listado del cliente consulta where('owner','==',uid), que matchea la regla por-doc. La escritura va solo por callable.

Roles

Se resuelven en el backend leyendo el member doc (sin custom claims — decisión sellada, a revisar: ver la nota de Storage abajo):

RolAlcance
admintodo + usuarios
editorOntología / Actions / DataSources
operatorejecutar Actions, mutar datos operacionales
viewerlectura

Storage

storage.rules cubre los uploads de CSV bajo el prefijo de la org (organizations/{orgId}/datasource-uploads/). La Cloud Function valida el tenant del path antes de leer (el Admin SDK bypassa las rules de Storage).

⚠️

Gate de rol interino (2026-06-09). El diseño original leía el rol del miembro con un firestore.get cross-service dentro de la Storage Rule. En prod ese cross-service get devuelve DENY (probado con bisect: admin + text/csv → 403; sin el get → 200), aunque en el emulador funciona. Por eso canUpload(orgId) quedó en isSignedIn() (gate: signed-in + path tenant-scoped + content-type/size). La autorización por rol real de la ingesta la hace la callable ingestDataSource (requireOrgRole admin/editor/operator), que además revalida el tenant del path. La lectura de Storage sigue if false.

Endurecimiento pendiente: rol por custom claims en el token para reponer el gate por rol en Storage sin cross-service get. Detalle y plan en operacion/troubleshooting → “Estado abierto”.

Auditar las rules: hay tests en apps/functions/src/rules/ (firestore-rules.test.ts) que se corren contra el emulador. La lógica desplegada coincide byte-a-byte con el repo.