Tarifa de fundador: $15 al mesfija mientras sigas suscrito.Ver los dos planes

Cómo funciona la bóveda, y dónde se detiene.

Escrito para quien está decidiendo si confiar en esto: alguien que revisa código, un clínico, un periodista o alguien que lee las cabeceras de respuesta. Dice qué protege el diseño, y es claro con lo que no protege.

Vigente al 3 de septiembre de 2026. Todo lo que se afirma aquí habla del código tal como está desplegado hoy, nunca del que pensamos publicar.

Por qué tiene que estar hecho así.

Los pensamientos intrusivos son el contenido que más vergüenza da. De daño, sexuales, blasfemos. Si crees que quien opera el servicio puede leer la transcripción, te guardas cosas, y una herramienta a la que le mientes no sirve de nada.

Así que el cifrado no está aquí para cumplir una lista de requisitos. Está para que se pueda escribir la frase honesta.

Qué está sellado.

Todo lo que pueda llevar lo que de verdad dijiste.

RegistroQué contiene
Cada mensajeEl texto en sí, el tuyo y el del chat.
El título y el tipo de cada conversaciónUn solo bloque sellado. En qué tipo de conversación estuviste es un dato clínico, así que nunca queda en una columna legible.
Lo que el chat recuerda de tiEl perfil que guarda entre una conversación y otra.
Tus respuestas del cuestionario y las notas de las leccionesLo que respondiste al principio, qué cubrió cada lección y lo que el chat te ha enseñado.
Tus escaleras de prácticaCada plan, cada paso y cada resultado, sellados enteros.
Los momentos que anotasTodo el historial en un único documento sellado, no una fila por entrada. Una fila por momento nos diría con qué frecuencia te pasa, y ese es el único patrón que el diseño se niega a guardar.
Tu plan para las malas rachasTus primeras señales, lo que te ayudó, lo que conviene evitar y lo primero que harías.
La tarjeta para la persona a la que le pides ayudaLo que le pides y las palabras que acordaron. Es lo único de aquí que nunca llega al modelo, porque esas palabras también son de otra persona.

Todo eso es texto cifrado con AES-256-GCM, sellado en tu navegador antes de salir. El servidor nunca tiene una clave.

Una prueba corre una conversación entera y después busca en todas las colecciones de la base de datos una frase que estuvo en ella. Falla si la encuentra. Es la única prueba que comprueba la promesa misma y no algo que la sustituya.

Cómo se protege un mensajeUn mensaje escrito en tu móvil se cifra en el propio dispositivo con una clave que nunca sale de él. Lo que viaja al servidor es texto cifrado ilegible, y eso es todo lo que Maybe llega a tener.escrito en tu móvilcifrado antes de salirla clave se queda contigox9#kQ2mF8@vLpR4$nT7&wJ1ztodo lo que llegamos a tener

Iniciar sesión y desbloquear son dos puertas distintas.

Iniciar sesión demuestra que el correo es tuyo. Se envía un enlace, lo abres, y no hay ninguna contraseña de cuenta guardada en ningún lado. Perder la sesión cuesta un enlace nuevo y nada más.

Desbloquear la bóveda es un paso aparte, con secretos aparte. Una clave de datos aleatoria se genera una sola vez en tu navegador, no se deriva de nada y no cambia nunca. Cada forma de desbloquear envuelve esa misma clave por separado.

clave de datos             AES-256 aleatoria, generada una vez, nunca derivada, nunca cambia

envuelta por:
  llave de acceso          HKDF(la salida PRF que calcula tu autenticador)
  frase de contraseña      HKDF(Argon2id(frase de contraseña, sal))
  código de recuperación   HKDF(Argon2id(código, sal))
  dispositivo de confianza una clave aleatoria que el navegador guarda y no puede exportar

Una llave de acceso es Face ID, Touch ID o una llave de seguridad, a través de la extensión PRF de WebAuthn: el autenticador calcula 32 bytes a partir de un secreto que nunca entrega, y solo después de que demuestres que estás ahí. La frase de contraseña y el código de recuperación pasan por Argon2id antes de convertirse en claves, y tu navegador rechaza parámetros por debajo de un mínimo compilado en el código, de modo que un servidor no pueda devolver unos débiles para abaratar los intentos.

Como la clave de datos nunca cambia, cambiar la frase de contraseña vuelve a envolver un bloque pequeño y no toca ningún mensaje. Dos métodos de desbloqueo son el mínimo, y el servidor rechaza una eliminación que dejaría menos, así que un teléfono perdido nunca puede ser la única entrada. Un dispositivo de confianza no cuenta para esos dos: vive en una sola máquina y muere con ella.

Tu bóveda se pierde solo si pierdes todos los métodos a la vez. Eso viene de que no tengamos ninguna clave, y no hay forma de resolverlo sin que tengamos una. Lo que conservas en ese caso es la cuenta que pagas: reiniciarla borra los registros sellados y todas las envolturas, mantiene tu suscripción y tu avance en las lecciones, y empieza una bóveda nueva.

Dónde vive la clave.

En un objeto de clave que el navegador no exporta, dentro de un Web Worker dedicado. Nunca en el almacenamiento de sesión, nunca en el estado de la aplicación.

Lo que eso consigue, exactamente: impide que roben la clave, y no impide que la usen. Un script inyectado en la página no puede leer la clave, pero sí puede pedirle al worker que descifre mientras la pestaña está abierta. Eso convierte el robo único de una clave que sirve para siempre en un oráculo que funciona solo mientras estás ahí, y solo si la política de la página le deja sacar algo. No se lo deja.

Algunas extensiones de navegador parchean los workers y los rompen. Cuando pasa, el mismo código corre en el hilo principal, la clave sigue sin poder exportarse, y lo que se pierde es la frontera del worker, que hacía más difícil llegar a una referencia suelta. La ruta del worker siempre se intenta primero.

Qué ata un mensaje a su lugar.

Cada texto cifrado lleva ligados dentro la versión, tu id de usuario, la conversación, la posición del mensaje, quién lo dijo y qué versión de la clave lo selló. Sin eso, un servidor podría reordenar, repetir o injertar mensajes de una conversación en otra, y tu navegador los descifraría tan tranquilo.

Las envolturas usan la misma construcción en lugar del modo estándar de envoltura de claves, porque el estándar no puede llevar ese vínculo y por eso no puede frenar una vuelta atrás a una envoltura ya sustituida.

El texto plano se rellena hasta bloques de 256 bytes antes de sellarse. La longitud de un mensaje, junto a su marca de tiempo, dice algo real sobre una transcripción de salud mental.

El único lugar donde tus palabras se pueden leer.

Para escribir una respuesta, el modelo tiene que leer tu mensaje. Tu mensaje y la parte reciente de la conversación se envían a Anthropic sin cifrar. Todo lo que sigue estrecha esa ventana, en vez de fingir que no está.

El modelo es Claude Sonnet 5, a propósito. Es el modelo actual sobre el que Anthropic ofrece retención cero de datos: las entradas y las salidas no se guardan después de la petición y no se usan para entrenar, bajo nuestro acuerdo de retención cero. Anthropic conserva treinta días los prompts de sus modelos Fable y Mythos para trabajo de seguridad, así que cambiar a uno de esos sería un cambio en la política de privacidad, y te lo diríamos antes.

La ruta del chat no registra nada y no guarda nada en caché. Lee metadatos, los conteos, las marcas de tiempo y los topes que fijaste, para hacer valer esos topes antes de retransmitir. Después escribe dos cosas, y las dos son conteos y no contenido: los tokens que devolvió el proveedor, sumados al total del mes, y, si la petición falló, una fila que dice que falló. Escribir en la consola es un error de lint en esa ruta, porque el hosting captura la salida estándar en sus logs.

Sobre esa fila de fallo hay que ser exacto, porque es lo único de esta página que se añadió para poder depurar. Guarda dónde ocurrió el fallo y de qué tipo fue, los dos elegidos de una lista fija en nuestro propio código, más un código de estado HTTP y un código de error corto del proveedor que se compara con una lista antes de guardar cualquiera de los dos. No guarda ningún mensaje, ningún objeto de error, ninguna traza, ninguna petición y ninguna cuenta, tampoco la tuya. El error que levantó el proveedor nunca se inspecciona más allá de esos dos campos, porque un error de un cliente HTTP lleva dentro la petición que lo causó, y en esta ruta la petición es lo que escribiste. Las filas se borran solas a los treinta días.

El framework del agente corre con su memoria apagada y su telemetría apagada. Enciende la exportación remota en cuanto ve sus propias claves de nube en el entorno, así que producción se niega a arrancar si están puestas, y el pipeline de despliegue revisa el entorno antes de desplegar. Una prueba aparte corre la ruta del chat con un modelo sustituto y falla si alguna petición llega a un host fuera de Anthropic.

Lo que todavía podemos ver.

Se usa para facturar y para prevenir abusos, y está listado completo.

  • El correo con el que inicias sesión.
  • Cuándo inicias sesión, y un hash con clave de la dirección de red desde la que lo hiciste.
  • Cuántos mensajes envías, y a qué hora exacta.
  • La longitud de cada mensaje sellado, redondeada hacia arriba al siguiente múltiplo de 256 bytes.
  • Los totales diarios de tokens y un costo mensual, sin ninguna conversación asociada a ninguno de los dos.
  • El estado de tu suscripción.
  • Los cinco topes que elegiste en Limits, y cualquier cambio pendiente sobre ellos.
  • Qué lecciones terminaste y qué opción fija elegiste en su pregunta. Nunca texto libre.

Las marcas de tiempo son la señal residual más fuerte. Sesiones largas de madrugada, que crecen semana a semana, tienen sentido clínico y se leen sin una sola palabra en claro. No se puede quitar mientras haya sincronización entre tus dispositivos, así que se nombra aquí en vez de esconderse.

Hay cosas que faltan en la base de datos a propósito, y cada una parecía un metadato inofensivo. No existe ningún campo legible con el tipo de conversación. El registro de consumo no lleva id de conversación, porque un rastro por conversación reconstruye un grafo de conducta. Los contadores de seguridad no llevan ningún id de usuario. Los correos y las direcciones de red se guardan solo como hashes con clave.

Un administrador puede buscar una cuenta por correo y ver los metadatos de arriba, reenviar un enlace de inicio de sesión, devolver el dinero de una suscripción y cerrar la sesión de una cuenta en todas partes. Cada una de esas acciones queda anotada con quién la hizo y cuándo. Un administrador no puede leer una conversación, por la misma razón que no puede nadie más. Los campos exactos que esa pantalla puede devolver están fijados por una prueba, así que meter uno nuevo hay que defenderlo.

Por qué no hay escalamiento de crisis.

No hay escalamiento a una persona ni una marca de crisis por usuario. Es una decisión de producto con una razón clínica además de una de privacidad.

Los pensamientos intrusivos de daño están entre las formas más comunes que toma este bucle. Son lo contrario de lo que quiere quien los tiene, no indican riesgo, y la práctica los saca a propósito. Un clasificador afinado para autolesión se dispararía con esa práctica todo el tiempo. Y peor: mostrar una tarjeta de crisis cada vez que aparece un pensamiento así enseña que el pensamiento es peligroso, que es justo la creencia que la práctica existe para desgastar.

En su lugar hay tres capas, y ninguna lee nada. Un botón Buscar ayuda ahora («pedir ayuda ahora») está en todas las pantallas, nunca condicionado, dibujado con datos que vienen dentro de la página. El chat puede ofrecer esos mismos números con una llamada de herramienta que lleva una categoría y ningún texto libre. Un prefiltro en el navegador detecta el lenguaje de método y plan cuando al modelo se le escapa, y nunca se transmite. Lo que se cuenta es un total diario global sin ningún id de usuario.

La consecuencia, dicha sin rodeos: no podemos contactar a alguien que esté en crisis. La bienvenida lo dice, y la página de seguridad lleva las líneas que contestan a cualquier hora.

Modelo de amenazas.

AmenazaProtegidoSin proteger
Alguien con acceso a la base de datosLas transcripciones, los títulos, la memoria y los planes. Reordenar, repetir e injertar mensajes queda bloqueado por el vínculo que se describe más arriba.El correo, las horas de inicio de sesión, las direcciones hasheadas, los conteos, las marcas de tiempo y los totales de tokens.
Una copia de seguridad robada de la base de datosTodo lo que lleva contenido. Una envoltura solo se puede atacar por la frase de contraseña, pagando el costo de Argon2id en cada intento.Los mismos metadatos. Una frase de contraseña débil cae igual, y por eso existe el mínimo.
Un despliegue comprometido, o JavaScript malicioso servido por nosotrosNada. Ese es el límite, y de eso trata la sección siguiente.Todo.
Una citación judicialSolo podemos entregar texto cifrado, metadatos y registros de facturación.Los metadatos se pueden entregar y por sí solos ya son sensibles. Un aviso TOLA australiano puede obligar a construir capacidad nueva, con una prohibición de divulgarlo.
Cross-site scriptingEl robo de la clave. La clave no se puede exportar desde el navegador.El uso de la clave mientras la pestaña está abierta. Lo sujeta una política que no deja que ningún script envíe un byte a ningún lugar que no sea este.
Una cookie de sesión robadaTus palabras. Una sesión autentica peticiones y no entrega material de clave. Quien la robe descarga texto cifrado y ahí se queda.Los metadatos y las acciones de la cuenta. Las destructivas piden iniciar sesión otra vez.
El proveedor del modeloNada durante la inferencia, por construcción.La ventana enviada, en texto plano. Solo por contrato: retención cero de datos, nada de entrenamiento, y el modelo lo elegimos nosotros.

El punto débil de verdad.

En el cifrado que entrega un sitio web hay que volver a confiar en cada carga. Cada vez que la página se abre, el servidor tiene otra oportunidad de mandar código distinto, y un código que sostiene tu frase de contraseña un instante podría quedársela. Una app nativa se firma una vez y la revisa una tienda. Una app web no.

La integridad de subrecursos no arregla esto. Quien pueda cambiar el JavaScript puede cambiar el HTML que lleva los hashes, y aquí los dos vienen del mismo origen. Defiende de un servidor de recursos comprometido mientras la página en sí sigue siendo honesta, que es otra amenaza.

Lo que sí ayuda, y en qué punto está hoy cada control. Cualquier servicio que diga que el cifrado en el navegador vuelve imposible el acceso no está siendo franco contigo, y esta tabla está para dejar claro que nosotros tampoco lo decimos.

ControlHoy
Revisión obligatoria en las rutas de manejo de claves y de la APIEl archivo de code owners está puesto. La protección de rama que lo haría obligatorio todavía no está activada, así que hasta entonces es solo una recomendación.
Compilaciones reproducibles con los hashes del bundle publicadosSin construir. Es la respuesta honesta más fuerte que puede dar una app web, y está en la lista.
Una superficie mínima de dependencias en la ruta criptográficaDos librerías, una para Argon2id y otra para hablar con el worker, fijadas a versiones exactas y nunca actualizadas de forma automática.
Ningún script de terceros donde la bóveda está abiertaPuesto. La facturación pasa por una redirección a Stripe, así que el script de Stripe nunca corre dentro de la app. La analítica corre solo en las páginas públicas, y la política de seguridad de contenido de esas páginas la mantiene ahí.
Una política de seguridad de contenido en la appPuesta. Scripts solo desde nuestro origen, sin imágenes remotas, sin marcos, sin incrustaciones, y ningún script puede conectarse a otro lugar que no sea este. No hay nonce por petición, que al probarlo rompió las páginas prerrenderizadas, y no hay Trusted Types, así que un script inyectado en línea sí correría. Lo que no podría hacer es sacar nada.

La salida del modelo se muestra como markdown con el HTML crudo desactivado, y la política no permite imágenes remotas. Una imagen puesta ahí por una inyección de prompt que apunte al servidor de otra persona sería la vía más directa para sacar una conversación, y es el ataque que esperamos ver primero, así que es el que está cerrado con más fuerza.

Límites contra el abuso.

Estos son nuestros, y nadie debería toparse con ninguno. Los topes que tú mismo fijas en Limits son otra cosa: que uno de esos te diga que no es parte de la práctica.

ContadorLímitePor qué
Enlaces de inicio de sesión por dirección de correo5 por horaEl envío no está autenticado y escribe a una dirección que tecleó un desconocido.
Enlaces de inicio de sesión por dirección de red20 por horaEl mismo ataque repartido entre muchas direcciones, que el primer conteo no alcanza a ver.
Peticiones de chat por persona10 por minutoEl único tope en la ruta del chat que cuenta sus propias peticiones.

Los contadores guardan un hash con clave del sujeto y caducan con la ventana que cuentan, así que nunca se vuelven un registro de cuándo alguien usó una app de TOC. Cuando el contador no se puede leer, el límite deja pasar la petición: un limitador caído no puede convertirse en una puerta cerrada en una app de salud mental.

No hay ningún límite del lado del servidor para desbloquear una bóveda, y no puede haberlo de forma útil. Las envolturas se descargan una vez y cada intento ocurre en tu navegador, así que el servidor nunca se entera de que un intento falló. Quien esté en posición de adivinar ya tiene la envoltura y adivina sin conexión, donde la única defensa es lo que cuesta cada intento de Argon2id. Un conteo de fallos que el cliente decidiera reportar sería un control solo de nombre.

Lo que puedes comprobar desde fuera.

Abre las herramientas de desarrollo de tu navegador en cualquier pantalla dentro de la app y lee la cabecera Content-Security-Policy. La línea que importa es la que deja a los scripts conectarse solo a este origen. Después mira la pestaña de red mientras envías un mensaje: lo que sale hacia nuestro servidor es texto cifrado, y no sale nada hacia ningún otro lado.

Haz lo mismo mientras suena una exposición. La voz es un archivo de modelo que servimos desde este origen, y una vez que tu navegador lo tiene, el habla ocurre dentro de un worker en tu máquina, así que no sale nada mientras escuchas. No hay ningún proveedor de voz en la lista de peticiones porque no hay ningún proveedor de voz. El audio que sale se sella con la clave de tu bóveda y se queda en el dispositivo que lo hizo, y por eso una exposición que armaste aquí tiene que volver a generar su audio en otro lado.

La página de privacidad lista lo que guardamos con las mismas palabras que esta página, y las dos cambian juntas.

Lo que todavía no puedes comprobar, dicho sin adornos. El código fuente hoy no es público, así que las pruebas que se nombran en esta página son nuestra palabra. Los hashes de compilación publicados, que te dejarían comparar el código que recibiste contra una compilación conocida, están en la lista y no están hechos.

Si encuentras algo, escribe a hello@usemaybe.app. La misma dirección está publicada en /.well-known/security.txt. Lo que más valoraríamos son las críticas a la jerarquía de claves y al modelo de amenazas.