Saltar al contenido
openplate

La aplicación

Sincronización entre dispositivos

Cómo activar la sincronización entre dispositivos, el cifrado y la clave de recuperación custodiada por el operador

Esta página es una traducción automática de la documentación en inglés.

En tu propia instancia, openplate es por defecto una app local: tu diario reside en el IndexedDB del navegador en el dispositivo que utilizas, y nada sale de él hasta que apuntas la app a un servidor central. Mover ese diario entre dispositivos es lo único que necesita una cuenta, por lo que reside en un servicio independiente, openplate-core, con su propia imagen, base de datos y secretos.

En tu propia instancia la sincronización es opcional. Sin configurar, openplate no pierde ninguna función. En el servicio alojado cada cuenta se sincroniza, por lo que el diario también tiene una copia cifrada en el servidor.

Qué viaja entre tus dispositivos

Todo lo que el motor de sincronización considera una entidad se fusiona fila por fila, de modo que un cambio realizado en un dispositivo llega a los demás y una eliminación hecha en uno quita la fila en el resto. A partir de esta versión, esto incluye tus alimentos personales, tu registro de comidas, tus entradas de peso, tu perfil y tus objetivos, tus ayunos, tu despensa, tu rutina de ayuno y las marcas de actividad y logros asociados a la racha. Tus comidas guardadas también viajan, como una lista completa en lugar de fila por fila, siendo esta la última parte del diario que aún espera una fusión por filas.

Tus claves para compartir y de investigación viajan también, en una parte sellada del blob que tus propios dispositivos pueden abrir, y también puede hacerlo un operador que tenga tu clave de recuperación (consulta El cifrado y lo que guarda el operador). Si concedes acceso a tu diario a un profesional de la salud, no puede leerlo.

Hay tres cosas que nunca viajan, actives lo que actives:

  • Fotos del plato. Residen en una base de datos independiente en el dispositivo que las tomó, quedan excluidas de la carga útil de sincronización y de cada archivo de copia de seguridad, y se eliminan solas según la programación que definas.
  • Tu clave del proveedor de IA. Reside en su propia base de datos en el dispositivo donde la introdujiste y no va a nadie salvo al proveedor que hayas elegido.
  • El registro de lo que eliminaste. Tu dispositivo registra las eliminaciones que llevó a cabo para poder demostrar que fueron deliberadas, y esa lista permanece en el dispositivo. Las eliminaciones en sí viajan; la lista no.

Qué necesita

  • Una instancia de openplate-core en ejecución: la alojada, la tuya propia o cualquier servidor de terceros que implemente el protocolo. Para ejecutar la tuya propia, usa docker/topologies/compose.core.yml: consulta self-hosting.md y topologies.md.
  • CORE_URL configurada en la app, apuntando a ese servicio.
  • Una página segura. El inicio de sesión deriva tus claves mediante la Web Crypto API del navegador, que los navegadores solo ofrecen sobre https:// o en localhost. Consulta self-hosting.md.
  • Una cuenta. En tu propia instancia el registro es por invitación a menos que configures OPEN_SIGNUP=true, así que tú mismo generas la primera: self-hosting.md.

Cómo activarla

CORE_URL es el único interruptor.

  • Sin configurar (por defecto): no se muestra ninguna interfaz de sincronización y no sale ninguna petición de sincronización de la app.
  • Configurada: aparecen las pantallas de sincronización y se comunican con esa URL. Su origen se añade automáticamente al connect-src de la CSP de producción: no necesitas CSP_CONNECT_EXTRA para ello.

Reinicia la app tras cambiarla. Si la desactivas de nuevo, las pantallas de sincronización desaparecen y la app deja de realizar peticiones. Tu diario local no se modifica en ningún caso.

Añadir un segundo dispositivo: apúntalo a la misma CORE_URL e inicia sesión con la misma dirección de correo electrónico y contraseña que utilizaste en el primero. Ese es todo el procedimiento, y no hace falta copiar nada del primer dispositivo.

Debe ser una dirección a la que un navegador pueda acceder. El cliente de sincronización se ejecuta en la página, por lo que un hostname de compose como http://core:3000 no funciona: utiliza la URL pública que resuelven los dispositivos de tus usuarios. Un valor mal formado detiene el arranque a propósito, para que una errata no parezca que "la sincronización está discretamente desactivada".

La consola de investigación (/study)

La app incluye siempre una ruta /study, que permanece inerte en una instancia ordinaria. Solo cobra vida cuando el servidor central con el que se comunica tiene SYNC_RESEARCH=true, que por defecto es desactivado por defecto: una instancia que levantes sin tocar ese flag no ejecuta ningún estudio, no almacena ningún grafo de estudio y no ofrece nada en lo que inscribirse. Lee openplate-core de .env.example antes de activarlo: hace que el servidor almacene datos personales relacionados con la salud, lo cual es una tarea muy distinta a alojar un diario cifrado.

El cifrado y lo que guarda el operador

Tu contraseña nunca sale de tu navegador. Se procesa con Argon2id y HKDF la divide en ramas independientes. Dos ramas se quedan en el dispositivo para descifrar la clave de datos y tus ajustes privados. Una tercera rama va al servicio como credencial de inicio de sesión. Son equivalentes criptográficos independientes, no dependen jerárquicamente entre sí, por lo que tener una no revela nada sobre las demás. Tu diario se cifra en el dispositivo antes de subirse y permanece cifrado en tránsito; el servicio almacena texto cifrado opaco, y lo único que ve aparte de eso es una dirección de correo electrónico junto con el tamaño y la hora de tus subidas.

El operador guarda una clave de recuperación. Al registrarte, la app genera un código de recuperación, cifra tu clave de datos con él y envía el código al servidor, que lo conserva protegido bajo un secreto propio. Eso es lo que hace que «olvidé mi contraseña» devuelva tu diario en lugar de una cuenta vacía: el enlace de restablecimiento (enviado por correo, o entregado en mano por el operador en una instancia sin correo) devuelve el código a tu navegador, que lo utiliza para descifrar la clave de datos y volver a cifrarla con la nueva contraseña.

Dicho de forma directa, porque es la concesión que hace este diseño: el operador de una instancia puede restaurar, y por tanto en principio leer, un diario que resida en ella. En una instancia que alojes tú mismo, tú eres ese operador. En una instancia que una organización gestione para ti, ellos ya ven cada foto del plato que pasa por su proxy de IA, así que esto cambia la promesa menos de lo que parece, y es lo que permite un restablecimiento de contraseña que no pierde tus datos.

Antes de M192 no había custodia, y el coste era el inverso: olvidar la contraseña y perder el código de recuperación significaba que los datos se perdían, tanto para nosotros como para ti, y la app tenía que mostrar el código una sola vez y pedir a la persona que lo guardara para siempre. Nadie lo hace.

Las fotos del plato nunca forman parte de una carga de sincronización. Permanecen en el dispositivo que las tomó, se excluyen de las exportaciones JSON y, en una instancia gestionada, la copia que llega al proxy de IA se lee una vez y el proxy no la almacena. El operador conserva durante un tiempo limitado la foto que se envía junto con el informe de una estimación incorrecta.

Compartir un diario con un médico

En una instancia cuyo servidor central define SYNC_SHARING=true, una persona puede permitir que un profesional clínico, como un dietista, lea su diario. El profesional clínico envía a la persona un enlace de conexión. La persona lo abre y escribe los doce caracteres que el profesional clínico lee en voz alta, de modo que una clave incorrecta se detecte antes de compartir nada. A continuación, la persona autoriza el acceso compartido en Ajustes → Compartir: la app vuelve a envolver la clave de los datos con la clave pública del profesional clínico y sube esa copia envuelta. El navegador del profesional clínico la desenvuelve y muestra el diario en /shared. El servidor almacena la copia envuelta y nunca la clave para abrirla. Un acceso compartido se puede revocar en cualquier momento. Un compartimento privado del diario guarda las claves propias de investigación y de uso compartido de la persona, y un profesional clínico nunca puede abrirlo. Esas claves también van en la exportación JSON, sin cifrar, para que un dispositivo restaurado pueda seguir abriendo lo que se compartió con él; self-hosting.md explica lo que esto implica para el archivo.

La misma pantalla contiene el interruptor para el pulso, un recuento opcional a nivel de instancia de comidas y escaneos; architecture.md detalla lo que envía.

Lo que solía haber aquí: la pasarela

Hasta la versión M192, una persona podía unirse a una pasarela de IA independiente desde el mismo enlace de invitación, y esa conexión viajaba con la cuenta dentro del compartimento sellado. Ya no existe esa pasarela: el servidor central asumió el proxy de IA, así que en una instancia gestionada por una organización, una cuenta con sesión iniciada y una cuota realiza los escaneos a través del único servidor con el que ya se comunica, y no existe ningún paso de conexión que trasladar a otra parte. Una instancia que alojes tú mismo no cambia: una clave que configures se queda en el dispositivo en el que la configuraste.

Edita esta página en GitHub