Saltar a contenido

Protección de datos

Dónde residen los datos personales en un deployment, qué sale de él y bajo qué configuración, qué controles existen en el código con una prueba detrás y cuáles siguen siendo incidencias abiertas. Escrito para quien responde a una revisión de protección de datos de un deployment — un delegado de protección de datos, un responsable de seguridad, un operador — y honesto sobre la diferencia entre lo que el software puede hacer y lo que un deployment ha decidido de verdad.

Una página no es cumplimiento

Nada de lo que hay aquí es prueba de que un deployment cumpla el RGPD. La base de código se puede desplegar dentro de un entorno conforme; que uno lo sea depende de los providers configurados, de la retención decidida, de los acuerdos firmados y del operador que lo lleva. Cada una de esas condiciones aparece abajo como algo que hay que obtener y verificar, nunca como algo que se da por supuesto.

Por defecto no sale nada

Un deployment recién instalado lo guarda todo en su propio PostgreSQL y en su propio disco, y no envía nada a ninguna parte. Cada salto al exterior es una fila o un ajuste que alguien añade después: un perfil de modelo que nombra un provider, una colección que elige un parser, un método de búsqueda en un spec, un token de Logfire, un host de mem0, un servidor MCP, un bot de canal. Quita la fila y el salto desaparece.

Así que un deployment que no quiere ninguna tercera parte en la cadena se configura así y el software coopera:

Asunto La respuesta local
El modelo de chat Un perfil ollama o litellm — sin clave, apuntando a un endpoint que alojas tú. Cualquiera de los 28 providers con base_url admite también una pasarela tuya
Parseo de documentos pymupdf, el valor por defecto, se ejecuta en el worker. El OCR de LiteParse también, o en un servidor de OCR que registres como servicio local. LlamaParse es una elección por colección que necesita una clave del vault; sin ella no se parsea nada fuera
Embeddings Un Ollama que alojas tú, registrado como servicio local en Knowledge → Integrations y elegido por colección como provider ollama. Sin clave, y el único provider que puede usar una colección app-scoped
Trazas Deja LOGFIRE_TOKEN sin definir y no vincules ningún token observability a un spec ni a un entorno. Los runs siguen registrando localmente un id de traza
Búsqueda, navegación, memoria, herramientas No vincules ningún secreto search, ninguna capability web_fetch, browser_use ni memory_mem0, ninguna conexión MCP. El navegador web puede quedarse si cada sitio que puede abrir está en allowed_domains y sus pasos van a un modelo que alojas tú
Correo Tu propio relay SMTP
Voz, imágenes y vídeo Para la voz a texto, ningún provider: deja el dictado en off o browser y los bots de canal desactivados, porque todavía no se puede nombrar un servidor propio de voz a texto (un paso previsto). Para la síntesis de voz y el vídeo, ninguna capability text_to_speech ni video_generation. Para las imágenes, perfiles en un provider que alojes tú, o ningún perfil de ese tipo

Los embeddings incluidos: el catálogo nombra OpenRouter y OpenAI, a los que una colección llega con una clave del vault, y ollama, al que una colección llega a través de un servicio local — una fila que nombra un servidor que opera la organización o el deployment. Una colección en ollama no envía sus chunks ni sus consultas a ningún host que no sea ese, y no le paga a nadie. Un deployment que deba mantener los documentos en su propio hardware crea allí todas sus colecciones.

Quién es responsable de qué

Parte Rol Qué significa aquí
La organización que lo despliega (una ciudad, una empresa) Responsable del tratamiento Decide finalidades, retención, a qué providers puede llegar un agent, y firma los acuerdos con ellos
Vstorm, como autor del software Ninguno de los dos, en un deployment autoalojado El código se ejecuta en tu infraestructura y nada llama a casa. Vstorm nunca ve tus datos
Vstorm, si opera el deployment por ti Encargado del tratamiento Un contrato de encargo de tratamiento es un contrato entre nosotros, no un ajuste. Tiene que existir antes de publicar el primer agent
Un provider de modelos, embeddings, parseo, búsqueda u observabilidad Subencargado, elegido por configuración La plataforma registra qué provider y qué endpoint usa cada agent. Su ubicación, su retención y sus condiciones de entrenamiento son suyas, y se verifican en cada deployment

La plataforma no entrena ni afina nada. Envía prompts, documentos y resultados de herramientas a los providers que configura un deployment y guarda lo que vuelve. Que un provider use el tráfico de la API para entrenar es una propiedad de la cuenta y de las condiciones del provider, y la lista de comprobación del final pide la declaración en lugar de darla por supuesta.

Dónde residen los datos personales

Todo lo de abajo está en el PostgreSQL del propio deployment, en su propio disco o en un servicio que el deployment eligió. Toda tabla que pertenece a una organización lleva un organization_id; una fila hija — un mensaje, un adjunto, una valoración — queda acotada a través de la conversación o el usuario del que cuelga, y leerla pasa por la comprobación del padre.

La base de datos

Almacén Guarda Datos personales que contiene Finalidad
users, sessions, organization_members Cuentas e inicios de sesión Correo, nombre, avatar, contraseña con hash o el id de la cuenta de Google, hash del refresh token, dirección IP y user agent por sesión; lo que la persona dijo a los agents sobre sí misma - idioma de la consola, zona horaria, rol, en qué trabaja, horario laboral - y los primeros mensajes escritos para su chat vacío Autenticación y autorización; lo que se le dice a cada agent sobre la persona
browser_sign_ins Los inicios de sesión que una persona guardó en sitios, para el navegador web El sitio, la organización, cuándo se usó por última vez y la propia sesión (cookies y el localStorage del sitio), sellada en el vault en el ámbito de la persona Un recorrido posterior en su nombre empieza ya con la sesión iniciada. La exportación de datos personales lista los sitios, nunca la sesión
conversations, messages, tool_calls Todos los chats en todas las superficies El texto que la gente escribió, las respuestas y el razonamiento del modelo, argumentos y resultados de herramientas, un resumen continuo de los hilos largos La función central del producto; el historial al que vuelve la persona
voice_calls Cada conexión de una llamada de voz El id de usuario de quien llama (ninguno para un visitante), el agent, el run, la conversación y el embed a los que perteneció, el provider y el modelo, el id de la llamada en el provider, el id de la clave del vault con la que se sostuvo, el worker que la mantiene, su heartbeat, sus horas de inicio, fin y colgado, su coste, sus segundos y su número de activaciones de guardrails. Ni audio ni transcripción - las palabras de la llamada son filas normales de messages, cada una marcada con un turn_id y, cuando quien llama habló por encima, interrupted Colgar una llamada cuyo proceso murió, y contar los minutos de llamada mensuales de la organización
chat_files Adjuntos de un mensaje Nombre de archivo, tipo, tamaño, el texto extraído (parsed_content) y la ruta de los bytes en disco Responder sobre un archivo
generated_media Una fila por cada imagen, clip de voz o vídeo que generó un agent La organización, la conversación en la que se hizo si el run tenía una, el nombre del archivo, su ruta de almacenamiento y su tamaño - no su contenido, que es un archivo bajo MEDIA_DIR Contar los medios generados de cada organización contra su cuota, y borrarlos con su conversación o tras su periodo de retención
context_files Conocimiento permanente que un builder escribió para los agents Lo que el autor pusiera ahí — y llega al prompt literalmente. Consulta Archivos de contexto Instrucciones y hechos que un agent debe saber siempre
artifacts, artifact_versions y sus archivos Páginas que los agents publicaron — informes, dashboards Lo que muestre la página, que el run construyó con lo que podía leer; el propietario, el run que escribió cada versión y, cuando hay uno activo, la clave del enlace público, sus ajustes (una contraseña como hash bcrypt) y cuántas veces se abrió — un número y una hora, nada sobre el visitante. Consulta Aplicaciones Compartir un resultado bajo un enlace estable
agent_memory_files Notas que un agent escribió sobre una persona o un chat de grupo Lo que el agent decidiera que valía la pena recordar, con clave person:<user_id> o una sala de chat Continuidad entre conversaciones
rag_documents, knowledge_bases y una tabla vectorial por colección Documentos subidos y sincronizados, sus chunks y sus embeddings El texto del documento y sus vectores, la ruta original del archivo en el origen Recuperación
agent_runs, tool_approvals, run_manifests Lo que costó y lo que hizo cada run El prompt de sistema y la última petición entregada al modelo, argumentos de herramientas a la espera de aprobación, la persona que decide y su nota Budgets, aprobaciones, historial de runs
agent_triggers Runs programados y disparados por eventos El prompt y la configuración y el filtro del origen de eventos Ejecutar un agent sin una persona
app_admin_audit_logs Quién cambió accesos o gastó dinero — el rastro de la organización y el del administrador del deployment comparten tabla Actor, suplantador, dirección IP, la acción y un mapa details. El mapa nombra sobre todo campos, pero algunas entradas guardan valores: el correo de la cuenta suplantada, el correo de una cuenta que un administrador borró, una nota de publicación Rendición de cuentas. Consulta Gobernanza
notifications, notification_deliveries Cada notificación dirigida a alguien y su envío por correo. La fila se escribe incluso cuando el destinatario ha desactivado todos los canales para ese evento — es lo que impide escribir dos veces la misma ocurrencia — y simplemente no se muestra ni se envía nunca; solo la fila de envío es condicional El destinatario, un resumen prerrenderizado (nunca un comentario en bruto ni un valor secreto) y las variables tipadas a partir de las cuales un correo se vuelve a renderizar; una fila de envío añade su canal, estado, intentos y un motivo de fallo depurado — nunca el texto de error del provider Alertas que una persona lee una vez, sin supervisión. Consulta Gobernanza
notification_preferences, announcements Interruptores de canal por evento, y las difusiones propias del app admin Un id de usuario por interruptor; el remitente de una difusión, su cuerpo y las organizaciones y el rol a los que iba dirigida — nunca la lista resuelta de destinatarios, que solo se puede reconstruir a partir de notifications hasta que esa fila caduca en el barrido de retención de notificaciones — después de lo cual la entrada de audit detrás de ella conserva solo un recuento, nunca los destinatarios Exclusión voluntaria, y el registro propio de quien la escribió
embed_visitors, channel_identities, channel_sessions Desconocidos en una página alojada y personas en Slack, Teams, Telegram o Mattermost Una clave de visitante aleatoria; un id de usuario de la plataforma, nombre de usuario y nombre visible; el id del chat Retomar el hilo correcto
message_ratings Pulgares y comentarios sobre las respuestas Quien valora y su comentario Revisión de calidad
suggestions Lo que el trabajo por iniciativa propia propuso a una persona, y si lo aceptó La persona, un título y una línea de detalle sacados de sus peticiones recientes, el prompt de rutina o el perfil que fijaría, y una clave que impide volver a proponer algo descartado Propuestas sobre las que decide la persona
watches Lo que una persona pidió al trabajo por iniciativa propia vigilar en sus propias conexiones La persona, cuál de sus conexiones, qué buscar y qué omitir con sus propias palabras, cada cuánto y cuándo revisó por última vez Vigilancias que configura la persona
ml_service_calls Cada llamada a los servicios de ML y cada transcripción: dictado, un mensaje de voz de un canal, la API, el dictado de un visitante en una página alojada o un widget La organización, quién pidió, qué servicio, recuentos de bytes y unidades, la duración y cómo terminó; en una transcripción también el proveedor y el modelo, el id de la clave del vault con la que se envió, por dónde llegó, el agent y el bot de canal para los que fue y la duración de la grabación. La fila de un mensaje de voz de un canal nombra a la persona con la que está vinculada la cuenta del remitente, y a nadie si el remitente no está vinculado. La fila de un visitante no nombra a nadie - ni al visitante, que no tiene cuenta, ni a quien publicó la página. Borrar a la persona, el agent, el bot o la clave quita el vínculo y conserva la fila - nada de lo que se envió y nada de lo que volvió Informe de uso, y la vista de un operador sobre una integración que falla
agent_workspaces, sandbox_operations Archivos sobre los que trabajó un agent y el registro de lo que ejecutó Para el backend state, los propios archivos, en JSON; para un contenedor, el id de sesión y cada comando, destino y resumen del resultado La sandbox. Consulta La sandbox
organization_secrets, model_profiles, mcp_connections, channel_bots Credenciales y hacia dónde apuntan Solo el cifrado sellado, con una pista; el provider, el modelo y la base_url en claro Llegar a los providers. Consulta Secretos

messages.search_vector es un índice de texto completo sobre el mismo contenido, y conversations.summary_messages es una compresión de él escrita por el modelo. Ambos son copias del chat y se van con él.

Medios generados

Una imagen, un clip de voz o un vídeo que genera un agent es un archivo bajo generated_<org>/ y una fila generated_media que lo nombra. La fila registra la organización, la conversación si el run tenía una, y el tamaño, así que el total de la organización queda acotado por GENERATED_MEDIA_QUOTA_MB (5120), comprobado antes de cada llamada de generación de pago. Cuenta cada archivo, también uno creado sin conversación: de una vista previa o una ejecución de prueba, de un inciso /btw en un canal, de un run de la API que no nombra conversación, o un archivo cuya conversación se borró por un camino que lo dejó atrás.

El archivo se borra con su conversación, con la cuenta a la que pertenecía esa conversación, con la tarea diaria sweep-generated-media cuando supera GENERATED_MEDIA_RETENTION_DAYS (90) aunque su conversación siga ahí, y por prefijo con su organización. 0 desactiva cualquiera de los dos límites. Un archivo sin conversación no es nada que un miembro pueda borrar para liberar espacio, así que la misma tarea lo borra a los 7 días, sea cual sea GENERATED_MEDIA_RETENTION_DAYS, 0 incluido. Un enlace a él - en una respuesta de un canal o de la API - deja de funcionar entonces.

Borrar una conversación, una cuenta o una organización elimina las filas en su propia transacción y encola la desvinculación de los bytes como tarea en segundo plano en esa misma transacción, así que los bytes se van solo si se van las filas, y un reinicio tras el commit no pierde la desvinculación.

Esa tarea reintenta un prefijo que el almacenamiento no elimina entero - el de una organización - y un archivo suelto de una conversación o una cuenta borradas que el almacenamiento rechaza, porque nada más podría volver a encontrar esos bytes. Un archivo que el almacenamiento sigue rechazando tras el último intento de la tarea se queda sin fila bajo generated_<org>/ hasta que se va su organización, y el informe de abajo lo cuenta.

El barrido borra primero el archivo y su fila solo cuando el archivo ya no está; un archivo que el almacenamiento se niega a borrar conserva su fila, y el siguiente barrido lo vuelve a intentar.

Los archivos generados antes de que existiera la tabla no tienen fila. Se siguen sirviendo, pero no cuentan contra la cuota y el barrido no los ve: se van con su organización, o a mano, y el informe de protección de datos los cuenta entre los archivos sin fila.

Fuera de la base de datos

Almacén Guarda Se borra cuando
MEDIA_DIR en el host de la API (volumen media_data) Adjuntos de chat bajo <user_id>/, avatares, logos de embeds, las imágenes, la voz y los vídeos que generaron los agents bajo generated_<org>/ y una copia temporal de cada documento bajo _rag_tmp mientras se parsea Se borra un documento desde el producto. Los bytes de un adjunto de chat se desvinculan tras el commit que borra su conversación o a su propietario. Un archivo generado se va con la conversación en la que se hizo, tras GENERATED_MEDIA_RETENTION_DAYS (90) sea cual sea la suerte de la conversación - a los 7 días si no tiene conversación -, y con su organización; consulta Medios generados. Consulta Qué alcanza el borrado
SANDBOXD_WORKSPACE_ROOT en el host de la sandbox Los archivos de cada workspace respaldado por contenedor Se borra la conversación, o SANDBOXD_WORKSPACE_TTL los barre; sin definir, se conservan indefinidamente. Consulta Cuánto sobrevive cada cosa
Redis Cubos de rate limit con clave en quien llama — en una superficie pública eso es la dirección IP en claro, dentro de la clave, durante el TTL de la ventana; claves de deduplicación de triggers y canales; estado del intercambio OAuth; invitaciones en espera; la vista en directo de una navegación — su última captura, sus pasos y lo que la persona que maneja el navegador pulsó y escribió hasta que la navegación lo lee; los pasos repetibles de una persona — las direcciones y etiquetas de lo que pulsaron sus navegaciones terminadas, nunca lo que escribió Al expirar: una vista en directo una hora después del último fotograma de la navegación, los pasos repetibles dos semanas después de la última vez que se hizo la tarea, todo lo demás en minutos
background_jobs en PostgreSQL El payload de cada job en segundo plano, su último error y la respuesta de un run asíncrono de la API El barrido de retención elimina un job terminado tras BACKGROUND_JOB_RETENTION_DAYS (14). Los payloads son ids y rutas, con dos excepciones: el event_context de un disparo por evento — el remitente, el asunto y el cuerpo de un mensaje de Gmail, una incidencia de GitHub, un payload de webhook — y el prompt y la respuesta de un run asíncrono de la API se quedan en el job hasta entonces. Los logs del worker pasan por el mismo filtro de redacción que los de la API
Logfire, si está configurado Trazas de cada run La retención del provider. Hoy una traza lleva el prompt completo, la salida y los argumentos de las herramientas — consulta Trazas
Tu relay SMTP Invitaciones, enlaces mágicos, peticiones de aprobación, alertas de budget, informes de uso, y el propio correo del buzón de notificaciones - un run que termina o falla desatendido, la ingesta de un documento que se completa o falla, un evento de seguridad o un cambio de configuración, el propio anuncio de un admin Lo que el relay conserve. El correo de aprobación nombra el agent, la herramienta y un enlace, no los argumentos de la herramienta
Copias de seguridad Un pg_dump es la base de datos entera; el volumen de medios son los archivos Tu caducidad de copias. El borrado nunca alcanza una copia ya tomada — consulta Copias de seguridad

Qué sale del deployment

No sale nada mientras una fila o un ajuste no nombre un destino. Esta es la lista completa de destinos, con la configuración que decide cada uno.

Destino Qué se envía Lo decide Ubicación y condiciones
El modelo de chat La conversación hasta ese punto, los adjuntos pegados o descritos, los chunks recuperados, los resultados de herramientas; una vez por noche, para el trabajo por iniciativa propia, las peticiones recientes de una persona, su perfil y lo que su agent anotó sobre ella Un perfil de modelo: provider, model, base_url y una clave sellada. Veintisiete providers; ollama y litellm no llevan clave y se alcanzan en un endpoint que alojas tú, y openai, anthropic, google, huggingface y otros aceptan una base_url, así que un endpoint en la UE o una pasarela es un campo, no una bifurcación. Un perfil openai o azure en la Responses API envía store: false, así que el provider no guarda una respuesta almacenada y recuperable salvo que el perfil fije openai_store; la retención de OpenAI para vigilar abusos sigue aplicándose salvo que la cuenta tenga zero data retention La del provider. Verifícalo por perfil
El modelo de embeddings Cada chunk de cada documento de una colección, y cada consulta de recuperación Por colección, y solo ahí: embedding_provider (openrouter, openai u ollama, del catálogo) y, para los dos primeros, la clave del vault embedding_secret_id que paga. No hay clave de embeddings a nivel de deployment; una colección con clave que no nombre ninguna rechaza indexar y buscar. ollama no lleva clave y se alcanza en el servicio local que nombra la colección (embedding_endpoint_id), un host que operas tú La del provider, o tu propio host. Una elección permanente
LlamaCloud El documento entero Una colección cuyo pdf_parser sea llamaparse; tiene que nombrar una clave del vault (llamaparse_secret_id), no hay clave de deployment. El pymupdf por defecto parsea en el worker La de LlamaCloud, si se usa
Un servidor de OCR Páginas renderizadas de un documento Una colección cuyo pdf_parser sea liteparse y cuyo ocr_endpoint_id nombre un servicio local; sin él, el OCR se ejecuta en el worker Tu propio host — un servicio local está por construcción en la red del deployment
Un modelo de descripción de imágenes Las imágenes dentro de los documentos El image_description_model de una colección La de ese provider de modelos
Investigación web La consulta de búsqueda que compuso el agent web_research.method en el spec: duckduckgo (sin clave), tavily, brave o exa (un secreto search cada uno), o native, donde busca el provider del modelo de chat La del proveedor de búsqueda, o la del provider del modelo
Web fetch y el navegador web La URL; para el navegador, lo que escribe en los sitios que abre La capability en el spec; el navegador necesita además un endpoint CDP en BROWSER_CDP_ALLOWED_HOSTS, vacía por defecto El sitio consultado; el host del navegador y los sitios que abre
Proxy del navegador o navegador alojado de un agent Todo lo que el navegador carga y envía, como lo haría el navegador del despliegue - páginas, contenido de formularios, cookies, inicios de sesión - más el registro propio del proxy o del servicio browser_use.connection en el spec (#2130); built_in por defecto El proxy o el servicio de navegador alojado (Browserbase, Steel u otro), bajo el contrato propio de la organización
Los pasos del navegador web Cada paso de un recorrido va al propio modelo del agente: la URL de la página, su título, las etiquetas de los elementos visibles y hasta 4.000 caracteres de su texto visible, más una captura con vision activado — que puede ser el contenido de un sistema interno. Lo que contiene un campo no se repite: la tabla de elementos solo dice que el campo está relleno La capability browser_use en el spec El proveedor del modelo del agente, como en la conversación
La sandbox, hacia fuera Cualquier cosa del workspace, a cualquier host — la runtime workbench tiene red, shell y curl La capability sandbox y una runtime con needs_network; la aprobación de comandos controla qué se ejecuta, no adónde se conecta Adonde fuera el comando. El control de salida es el firewall del host de la sandbox, no un ajuste de aquí
Un servidor MCP Argumentos y resultados de herramientas mcp_connections.url, por organización o por persona La del operador del servidor
mem0 Los recuerdos escritos para una persona o un chat La base_url de la capability memory_mem0, que tiene que estar en MEM0_ALLOWED_HOSTS La del host de mem0 que permitas
Logfire Spans de cada petición y cada run LOGFIRE_TOKEN a nivel de deployment; un token observability en un spec, o logfire_token_secret_id en un entorno, redirige esos runs a otro proyecto. LOGFIRE_BASE_URL elige el deployment de EE. UU. o el de la UE. Sin definir en ninguna parte, no se envía nada La de Pydantic, EE. UU. o UE
Voz a texto, generación de imágenes Una nota de voz de un canal, una grabación dictada en /chat, o la grabación de un visitante anónimo en una página alojada o un widget; el prompt El provider al que se resuelve una grabación - el par propio del bot, la anulación del agent o el predeterminado de la organización, que un bot hereda salvo que esté desactivado - enviado con la clave del vault nombrada junto a ese par - una clave de API de groq, mistral u openai, visible para la organización en la organización y en un bot; para el dictado en /chat, además, el motor de la organización en server, y para una página o un widget su propio dictation: server. Un perfil para google u openai para las imágenes. El audio de un dictado no se guarda aquí. Con browser en una página o un widget, el navegador del visitante envía el audio a su fabricante y nada de él pasa por este despliegue La del provider, o tu propio host para las imágenes; con browser, la del fabricante del navegador
Un provider de voz realtime Durante una llamada de voz: el audio de quien llama, las instrucciones del agent y las definiciones de herramientas, los resultados de las herramientas, y los turnos anteriores de la conversación como texto cuando una llamada empieza o se reconecta La sección Voice del agent: provider, model (o un deployment de Azure) y la clave del vault secret_id, publicada; sin sección Voice el agent no atiende llamadas. Sobre WebRTC (OpenAI) el audio va directamente entre el navegador y el provider; sobre el relay (Azure, Gemini, xAI) pasa por este despliegue sin guardarse. Aquí no se conserva audio, y la sesión solo conserva transcripciones La del provider
Texto a voz El texto que se va a decir y cualquier indicación sobre cómo decirlo El provider de la capability text_to_speech: openai, elevenlabs o google, con la clave del binding La del provider
Generación de vídeo El prompt y la imagen inicial, si la hay El provider de la capability video_generation: google, byteplus, xai o alibaba, con la clave del binding. google guarda la interacción (store: true), porque Gemini solo entrega un clip por dirección desde una guardada; a los otros tres no se les pide La del provider
Slack, Teams, Telegram, Mattermost Las respuestas del agent; la dirección de correo de un miembro nuevo, para encontrarlo para una bienvenida en Slack, Mattermost o Teams Una fila channel_bots con su token en el vault El proveedor de mensajería ya tiene el chat
Inicio de sesión con Google Nada hacia fuera; Google devuelve el correo, el nombre, la foto y el id de la cuenta GOOGLE_CLIENT_ID La de Google
Tu relay SMTP El correo de arriba SMTP_HOST, SMTP_TLS La tuya

Los conectores de sincronización van al revés: un origen de Google Drive o S3 trae documentos hacia dentro, autenticado por un secreto connector, y a partir de ahí los documentos son la copia del deployment y siguen las reglas de arriba. Quién acaba pudiendo leer lo que un origen ha ingerido es una decisión que toma la fila del origen.

Controles, y dónde se prueba cada uno

Cada fila nombra el mecanismo en el código y la prueba o la página que lo fija, o la incidencia que lo hará. Una fila cuya última columna es una incidencia es una laguna, y así queda dicho.

Control Mecanismo Probado por
Aislamiento entre tenants organization_id en toda tabla que pertenece a una organización, resuelto desde X-Organization-Id a una membresía en cada petición; las filas hijas solo se alcanzan a través de la comprobación de su padre tests/integration/test_conversation_tenant_isolation.py y sus hermanos; Permisos
Acceso a una fila Tres capas: administrador del deployment, rol en la organización, grant por recurso a través de resolve_access. Un control que quien llama no puede usar no se renderiza Pruebas de rechazo en tests/api/; Permisos
Leer el chat de otra persona El propietario, una compartición explícita o el app admin del deployment — nunca un rol de la organización. El chat de un proyecto se lee solo a través del proyecto: quien puede ver el proyecto, incluida la persona que lo empezó, y nadie que ya no pueda. Las conversaciones tienen su propia comprobación, ConversationService._may_read, en vez de la fórmula de grants admin_conversations.py exige is_app_admin; tests/integration/test_conversation_tenant_isolation.py
Credenciales en reposo Cifrado de sobre por organización, claves maestras versionadas, rotación con ejecución en seco Secretos, cuatro garantías fijadas por pruebas
Contenido en reposo La aplicación no lo cifra. Los datos de Postgres, media_data y la raíz de workspaces del sandbox dependen del cifrado de disco o volumen que tú aportes. La excepción son los ficheros subidos y de chat con FILE_STORAGE_BACKEND=s3 y FILE_STORAGE_S3_ENCRYPTION en sse-s3 (el valor por defecto) o sse-kms bajo una clave que tú tienes: cada escritura pide entonces al almacén que los cifre. Su tercer modo, none, no envía cabecera de cifrado y existe para un almacén compatible con S3 sin KMS Control del operador. Configuración; agenticos cmd doctor indica en qué backend está un despliegue en marcha
En tránsito, entrante HTTPS en tu proxy; Strict-Transport-Security cuando ENVIRONMENT=production; cookies de sesión httpOnly, y secure según el esquema de la petición al iniciar sesión y al refrescar. La ruta de cambio de contraseña pone secure solo en una build de producción Despliegue; frontend/src/app/api/auth/login/route.ts
En tránsito, hacia los almacenes POSTGRES_SSLMODE y REDIS_SSL; agenticos cmd doctor informa de si la conexión que estableció iba cifrada Conexiones cifradas; tests/integration/test_store_tls.py
En tránsito, hacia los providers HTTPS a todo endpoint catalogado. Una base_url propia se rechaza sin host o con credenciales dentro, pero http:// se acepta, para un Ollama o una pasarela en la propia red del deployment; un perfil en HTTP plano que apunte fuera de esa red envía los prompts y la clave en claro. El punto 4 de la lista de comprobación enumera todos esos perfiles refused_field("base_url", ...) en el servicio de perfiles de modelo; el esquema es control del operador
Secretos en respuestas, logs, auditoría, exportaciones Ningún endpoint devuelve un texto en claro; SecretStr en todas partes; los specs referencian secretos por id Secretos
Datos personales en los logs app/core/logging.py redacta direcciones de correo, JWT, claves de API, tokens bearer y pares password= de cada registro de log, tanto en la API como en el worker tests/test_logging.py; el worker lo instala en app/worker/__main__.py, y cada proceso hijo de ingesta en app/worker/child.py (#440)
Datos personales que llegan al modelo La capability guardrails redacta IBAN, números de tarjeta, números de seguridad social estadounidenses, números de teléfono y direcciones de correo de los prompts, las respuestas, los resultados y los argumentos de herramientas cuando está configurada Capabilities; sus pruebas bajo tests/
Datos personales en una columna de fallo rag_documents.error_message y similares registran la etapa y la clase, nunca el texto del cliente app/services/rag/failures.py (#423)
Rendición de cuentas Las entradas de auditoría comparten la transacción que actúa y fallan en cerrado; la suplantación nombra a ambas personas; las exportaciones masivas quedan registradas Gobernanza
Exportación de auditoría GET /audit/export, CSV o JSONL sobre una ventana, con puerta en audit:read y registrada en el propio rastro Governance (#1422)
Prueba de no manipulación del rastro Cada entrada se une a una cadena de hashes por organización, y cada cadena lleva un checkpoint en su marca más alta, de modo que una entrada reescrita, una cola cortada y una cadena borrada son todas detectables. agenticos cmd audit-verify las recorre y termina con código distinto de cero ante una rotura Gobernanza (#1622, #1648). Detección, no prevención: quien tenga las credenciales de la propia base de datos puede volver a forjar una cadena o quitar el trigger que protege el checkpoint
Trazas observability.content por agent: full registra todo, none solo tiempo, tokens, coste y nombres de herramienta, y un especialista de ese agent hereda el modo Entornos (#1413); un término medio redacted se descartó, #1616
Retención programada Por organización y por clase —conversaciones y sus archivos, runs y manifiestos, workspaces, memoria de agentes, documentos subidos y auditoría— dentro de un valor por defecto, un techo y un suelo de auditoría de todo el despliegue. Un barrido diario borra de verdad y registra recuentos, nunca contenido. Las copias de seguridad y todo lo ya enviado a un colector externo quedan fuera. notifications no es una de esas clases: se barre con su propio calendario fijo en su lugar, una fila leída a los 90 días y cualquier fila al año sin importar el estado. Los propios announcements quedan excluidos, así que lo enviado sigue siendo consultable en el rastro de auditoría después de que sus entregas caduquen. Tampoco lo son los medios generados, que se van tras el GENERATED_MEDIA_RETENTION_DAYS de todo el despliegue Retención; test_retention.py, tests/integration/test_retention_sweep.py; el barrido de notificaciones es tests/integration/test_notification_retention.py (#1598, Decision 8); los medios generados son test_generated_media.py y tests/integration/test_generated_media.py (#2310)
Supresión de una persona El borrado de la cuenta concilia lo que lo bloquearía, desvincula los bytes de los adjuntos tras el commit y purga lo que ninguna cascada alcanza: las notas que los agents escribieron sobre ella, sus identidades de plataforma, sus workspaces. Borrar la memoria es también una llamada aparte, y alcanza a mem0 Qué alcanza un borrado para lo que se conserva a propósito; test_personal_data.py
Acceso a los propios datos Una persona lee en Ajustes → Memoria todo lo que cada agente de aquí ha escrito sobre ella, y puede suspender una nota, restaurarla o borrarla. Leer el almacén de otra persona es solo de la administradora del despliegue —no de un rol de organización— y queda auditado con el actor, el tenant, el sujeto y un motivo, nunca el contenido. Los almacenes externos (mem0) se nombran en vez de listarse Leerla, y borrarla; test_memory_self_service.py. Todo lo demás que se guarda sobre ella vuelve de GET /me/data/export, acotado y auditado (#1421)
Identidad corporativa Inicio de sesión con Google, contraseñas, OIDC genérico contra el proveedor que ya tienes — configurado solo por discovery desde OIDC_ISSUER — y tu propio directorio por LDAP o Kerberos. La dirección, el nombre visible y el identificador estable del directorio se guardan en la cuenta; sus grupos se leen en cada inicio de sesión y no se guardan, solo las membresías que los mapeos de las organizaciones hacen de ellos. Sin SAML ni SCIM Inicio de sesión con el directorio y grupos; OIDC_ISSUER, LDAP_URL
La matriz de controles que lee una revisión de seguridad Seguridad asigna a cada control su mecanismo y el test que lo sostiene, en el marco de HIPAA §164.312 y SOC 2 CC6–CC8; esta página y Ponerlo en marcha son el resto Seguridad (#1412)
Superficies públicas La clave de visitante de una página alojada es aleatoria, nunca derivada de la persona; la admisión y las subidas se limitan por dirección, y la dirección vive en una clave de Redis durante la ventana y en ningún otro sitio Canales
Avisos legales Las URL de Términos y Privacidad propias del deployment sustituyen a las páginas incorporadas El deployment

Trazas

instrument_pydantic_ai() se ejecuta con el valor por defecto de la biblioteca, así que un span contiene el mensaje del usuario, la respuesta del modelo y cada argumento y resultado de herramienta. Con LOGFIRE_TOKEN sin definir, sin token observability en ningún spec y sin logfire_token_secret_id en ningún entorno, no se envía nada y el id de la traza se sigue registrando localmente. Un deployment que necesite trazas sin el contenido pone el observability.content del agent en none: se registran tiempo, tokens, coste y nombres de herramienta, y ningún texto de mensaje sale — y un especialista de ese agent hereda el modo, escrito inline o inventado por el modelo del run.

No hay un tercer modo intermedio. Una exportación depurada por un filtro de PII es una garantía que nadie puede auditar — un identificador que el filtro deje pasar ya ha salido, y el operador cree que no —, así que la elección es a propósito entre todo el contenido y nada de él (#1616).

Qué alcanza el borrado

Borrar es lo que hace el producto hoy cuando alguien lo pide; la retención programada es #1420.

Acción Elimina Deja
DELETE /conversations/{id} (el propietario) La conversación, sus mensajes, llamadas a herramientas, valoraciones, comparticiones y filas chat_files, por cascada - entre ellos la transcripción de una llamada de voz, ya que sus turnos son mensajes; los bytes de los adjuntos bajo MEDIA_DIR, recogidos antes del borrado y desvinculados tras su commit; las imágenes, la voz y los vídeos que generaron sus agents: las filas en el borrado, los bytes mediante una tarea encolada con él; los objetos de medios descargados de ese hilo, por prefijo; un workspace de contenedor mediante purge_for_conversation Las filas de runs y los manifiestos que nombraron la conversación conservan su copia del prompt; una fila voice_calls conserva sus horas y su coste con el vínculo a la conversación vaciado
DELETE /memory/person/{user_id} (la persona, o members:manage) Cada fila de agent_memory_files con clave en la persona a lo largo de todos los agents de la organización, y lo mismo en cada almacén mem0 vinculado Las notas con clave en un chat de grupo en el que la persona habló
DELETE /users/{id} La cuenta, sus sesiones, su organización personal y sus colecciones personales con sus tablas vectoriales y ficheros, por teardown explícito; conversaciones fuera de un proyecto y ficheros de chat por cascada; sus bytes, desvinculados tras el commit; las imágenes, la voz y los vídeos que generaron los agents en esas conversaciones, las filas en el borrado y los bytes mediante una tarea encolada con él; y lo que ninguna cascada alcanza: las notas que los agents escribieron sobre ella (owner_key es una cadena, no una clave foránea), sus identidades de plataforma y sus workspaces de usuario Entradas de auditoría que nombran el id del actor y, en algunas acciones, el correo: se conservan a propósito, porque una entrada sin su actor es peor que una que nombra una cuenta borrada. Mensajes en conversaciones de otra persona, y los chats de proyecto que empezó, que se conservan para el equipo sin autor, con los medios generados en ellos; sus mensajes ahí se muestran como de un antiguo miembro. Los medios que la persona generó sin conversación - vistas previas y ejecuciones de prueba, incisos /btw en canales, runs de la API que no nombraron conversación -, que ninguna fila vincula con ella; se van a los 7 días
Borrar un documento o una colección Las filas, la tabla vectorial y el archivo almacenado, mediante un flow duradero tras el commit Nada, una vez que el flow ha corrido; los recuentos de sync_logs permanecen
Borrar una organización Todo lo acotado a ella, con el mismo desmontaje diferido - sus medios generados por prefijo, incluidos los archivos escritos antes de que se registraran Las colecciones personales que solo llevaban el id

Ninguno de estos alcanza una copia de seguridad. Una restauración devuelve lo que se había borrado, así que la caducidad de las copias forma parte de la política de retención y se escribe junto a ella.

Qué tiene que decidir y obtener un deployment

El software no puede aportar ninguna de estas cosas. Cada una es una prueba que pedirá la revisión, distinta de la capacidad técnica que la hace posible.

  • Un contrato de encargo con cada subencargado configurado — cada provider que nombre una fila de model_profiles o de organization_secrets, el provider de embeddings, LlamaCloud si lo usa alguna colección, el proveedor de búsqueda que nombre el method de un agent, el host de mem0, Logfire, el relay SMTP y Google si el inicio de sesión está habilitado.
  • Una declaración de ubicación de datos por provider, cotejada con la base_url que cada perfil usa realmente. Un provider con endpoint en la UE solo está en la UE si el perfil lo dice.
  • Una exclusión de entrenamiento por provider: el ajuste de cuenta o la cláusula contractual por la que los datos de la API no se usan para entrenar. La posición de la propia plataforma es una frase — no entrena nada — y el resto es cosa suya.
  • Un contrato de encargo con Vstorm, solo si Vstorm opera el deployment.
  • Un calendario de retención para conversaciones, archivos, memoria, documentos, runs y auditoría, y junto a él la caducidad de las copias de seguridad. Hasta que #1420 imponga uno, la retención es un borrado manual.
  • Cifrado de disco o de volumen en el host de la base de datos, el volumen de medios y el host de la sandbox, ya que la aplicación no cifra el contenido por sí misma — y una regla de salida en el host de la sandbox si los agents pueden ejecutar comandos.
  • Las páginas legales a las que enlaza el deployment, y quién responde a una solicitud de acceso o supresión. Los mecanismos están aquí — GET /me/data/export y el borrado de la cuenta — pero quién recibe la solicitud y en qué plazo es cosa del responsable.

Verificar un deployment

Comprobaciones reproducibles, desde el host, contra el deployment en marcha. Cada una imprime hechos que la revisión puede adjuntar; ninguna imprime una credencial ni los datos de una persona. Ejecútalas desde backend/, o a través de docker compose exec api.

# 1. ¿Arranca siquiera, y van cifradas las conexiones a los almacenes?
#    `postgres` informa del estado TLS de la conexión que el propio doctor
#    estableció.
uv run agenticos cmd doctor

# 2. Cada credencial sellada sigue abriéndose con las master keys configuradas.
uv run agenticos cmd vault-rotate --dry-run

# 3. Las cadenas de hashes del rastro de auditoría y sus checkpoints, recalculados.
#    Termina con código distinto de cero si alguna cadena ha sido alterada.
uv run agenticos cmd audit-verify

# 4. Todo lo que este deployment ha configurado realmente: los ajustes que
#    deciden qué sale, cada provider y endpoint que un agent puede alcanzar, las
#    credenciales guardadas por propósito, las colecciones y quién calcula sus
#    embeddings, los servidores de tu propia red, los servidores MCP, los
#    portales de trigger, las fuentes de sincronización y los bots de canal, las
#    capabilities que alcanzan una dirección propia, dónde se trazan los runs y
#    cuánto contenido lleva un span, cuánto de cada almacén alcanzaría un periodo
#    de retención, y los archivos bajo `MEDIA_DIR` a los que ya no apunta ninguna
#    fila.
uv run agenticos cmd data-protection-report --older-than 365

El punto 4 es el que hay que adjuntar. Imprime configuración y recuentos, nunca contenido: ningún texto de mensaje, ningún documento, ningún valor de secreto ni pista de uno, y un ajuste que guarda una credencial se informa como definido o no definido en vez de imprimirse. --older-than es el periodo de retención que se está considerando, en días, y la última columna de su tabla de retención es lo que ese periodo ya habría eliminado.

Su última sección cuenta los bytes que ya no nombra ninguna fila. Borrar una conversación o una cuenta desvincula ahora sus adjuntos tras el commit, así que ese camino ya no suma; lo que todavía encuentra viene de antes de esos borrados, o de una desvinculación que un barrido best-effort no completó.

El directorio temporal de parseo queda excluido, ya que por diseño no tiene fila. Los medios generados se cuentan desde que generated_media registra cada archivo, así que lo que aparece bajo un directorio generated_<org> es sobre todo un archivo generado antes de que existiera esa tabla, que solo se va con su organización. También puede ser un archivo cuya desvinculación falló tras borrarse su fila, o uno que aún se está guardando, antes de que se escriba su fila. Todo lo demás que se cuenta ahí son bytes que el producto ya no encuentra y no puede borrar.

Informa de un directorio y un recuento en vez de un nombre de archivo, porque una ruta almacenada conserva el nombre con el que se subió el archivo.

Dos secciones se leen de lo que se ejecuta y no de una tabla. Destinos de las capabilities enumera las capabilities vinculadas a un agent que alcanzan una dirección propia - web_research busca a través de DuckDuckGo sin credencial alguna y por tanto sin fila en ningún otro punto del informe - y Tracing se lee de cada versión ejecutable: la predeterminada y la que fija cada entorno con nombre, porque un run a través de ese entorno usa la observabilidad de esa versión y no la de la predeterminada.

Lo que ningún comando puede producir es la otra mitad de esta página: los acuerdos, las ubicaciones y las exclusiones de entrenamiento de la sección anterior. Adjúntalos junto a la salida.

Condiciones abiertas para una primera puesta en marcha

Enunciadas para el deployment para el que se escribió esta página, y ciertas para cualquier deployment hasta que cada una se cierre.

En el código, y permanentes:

  • Las trazas llevan contenido completo salvo que un agent ponga observability.content en none. No hay término medio filtrado ni lo habrá: una exportación parcialmente depurada es una garantía que nadie puede auditar (#1616).
  • El contenido en reposo es del operador. Los cuerpos de los mensajes, los documentos y sus vectores, y los workspaces del sandbox son columnas y ficheros en claro; el vault sella credenciales, no contenido. Los ficheros subidos pueden ir a un almacén compatible con S3 que los cifra en su lado, pero la base de datos y el volumen de medios los protege el cifrado de disco o de volumen, o nada.

Cerradas, y respondidas arriba en vez de aquí: la prueba de no manipulación del rastro de auditoría (#1622, #1648), el modo de contenido de trazas por agent y su herencia por los especialistas (#1413, #1699), el trazado en el proceso que ejecuta un agent disparado (#1700), la matriz de controles de HIPAA y SOC 2 en Seguridad (#1412), el borrado y la exportación de los datos de una persona (#1421), el backend S3 con cifrado en el servidor (#1423) y el inicio de sesión OIDC genérico (#1419). Las trazas no tienen término medio filtrado y no lo tendrán (#1616); para un deployment que no pueda exportar contenido, la respuesta es none.

En el deployment, decididas por su operador: los acuerdos, las ubicaciones, las exclusiones de entrenamiento, el calendario de retención, la caducidad de las copias de seguridad, el cifrado de disco, la salida de red de la sandbox y las páginas legales de la sección anterior. Cada una es la clase de prueba que data-protection-report deliberadamente no puede producir.

Una revisión que encuentre cada fila de arriba cerrada o aceptada por escrito tiene lo que esta página puede darle. El resto es del deployment.