Ser dueño de mis datos, parte 1: integrar una solución de calendario autoalojada
(emilygorcenski.com)- A medida que se combinaron los viajes de trabajo frecuentes, los viajes y la gestión de relaciones en varias zonas horarias, surgió la necesidad de un sistema de calendario propio para reducir la dependencia de Big Tech y controlar directamente los datos de agenda
- La nueva arquitectura pone en el centro un servidor CalDAV Baïkal, reúne Airtrail, correo IMAP e ICS de la escuela de idiomas, los incorpora a CalDAV y luego los vuelve a publicar como archivos
.icspara compartir - El método de escribir eventos en YAML y convertirlos a ICS en CI/CD fue suficiente para un prototipo, pero los errores manuales y la carga de mantenimiento crecieron, por lo que terminó reemplazándose el enfoque de archivos estáticos
- Un script de Python recopila y clasifica eventos cada 15 minutos, crea por separado archivos ICS familiares y laborales, y en el calendario de trabajo marca algunos eventos como PRIVATE
- Google Calendar y Google Script se usan solo para sincronizar el calendario laboral, y tras integrar la API de Airtrail, al ingresar un vuelo una sola vez se refleja en 15 minutos en el calendario personal y en 1 hora en el calendario de trabajo
Por qué se volvió compleja la gestión del calendario
- Debido a los desplazamientos frecuentes, a familiares, amistades y colegas les resultaba difícil saber la zona horaria actual o si ya había abordado un vuelo, y coordinar compromisos en varias zonas horarias exigía seguir haciendo cálculos de tiempo
- Vuelos, trenes, bloqueos previos al embarque y tiempos de traslado al aeropuerto tenían que registrarse repetidamente en varios lugares, lo que aumentaba la carga de gestionar la agenda
- Las incomodidades del ecosistema de calendarios existente abarcaban los estándares, las apps de frontend y la experiencia de usuario en general
- Aunque un proveedor de correo generara entradas de calendario para vuelos, a veces omitía conexiones o se equivocaba con la zona horaria
- El organizer de los elementos generados podía no ser el usuario, lo que dificultaba compartirlos o modificarlos
- Las condiciones necesarias iban más allá de guardar eventos simples e incluían compartir y control
- Los eventos debían mostrarse como blocker en el calendario laboral
- La pareja debía poder suscribirse al calendario
- La entrada de eventos debía hacerse como máximo una sola vez
- Debía ser posible editarlos desde varios dispositivos
- Los datos debían estar bajo control directo
- No se podía usar la opción de compartir el calendario laboral con la pareja
- Los requisitos adicionales incluían importar adjuntos de correo
.ics, importar por HTTP el ICS del calendario de la escuela de idiomas, integrar el rastreador de vuelos autoalojado Airtrail, diferenciación por colores, flag privado para el calendario laboral, actualizaciones frecuentes y el uso de cualquier frontend
Dónde chocó con el límite el enfoque YAML·ICS
- Las formas existentes de compartir solían ofrecer todas las funciones solo dentro de una plataforma común o un entorno con la misma cuenta
- Ecosistema de Gmail u Outlook.com
- Compartición de cuentas dentro del mismo entorno, como Exchange
- Había dos vías alternativas comunes
- Publicar datos iCal por HTTP como un calendario de solo lectura
- Enviar archivos iCal
.icspor correo electrónico
- La versión inicial consistía en subir archivos
.icsal sitio web usando una URL pública pero difícil de adivinar, y crear varias URLs según el propósito - Los datos de eventos se escribían en YAML y luego se volvían a serializar como archivos ICS mediante un pequeño script en el pipeline de CI/CD
- Bastaba para un prototipo, pero a medida que creció la escala se hicieron frecuentes los errores al escribir YAML a mano, y una tarea que originalmente debía requerir poco esfuerzo empezó a volverse pesada
Arquitectura cambiada a CalDAV y Baïkal
- La nueva estructura abandona los archivos estáticos y pasa a un sistema basado en CalDAV alojado
- CalDAV es una extensión de la especificación de autoría distribuida WebDAV y proporciona las funciones necesarias para aplicaciones de calendario
- Al tener un servidor CalDAV, se pueden ver y gestionar eventos desde varios dispositivos, como laptop o teléfono, usando la app frontend que se prefiera
- Como no hay muchos servidores CalDAV fáciles de suscribir sin autenticación, hizo falta un script que consultara periódicamente el servidor, extrajera los eventos y los publicara como archivos iCal en el sitio web
- El flujo completo es el siguiente
- Consultar eventos desde las fuentes de datos
- Publicar eventos de forma programática en CalDAV
- Obtener todos los eventos desde CalDAV y escribirlos en archivos
.ics - Servir los archivos
.icspor HTTP
Instalación de Baïkal y conexión de clientes
- La herramienta elegida fue Baïkal, un servidor ligero autoalojado de CalDAV/CardDAV para gestionar calendarios y contactos
- Se ejecutó la imagen
ckulka/baikal:0.9.5con Docker Compose, se definieron puertos y volúmenes locales, y se inició condocker compose up -d - Baïkal también puede usar MySQL, pero funciona con SQLite, y SQLite simplifica más la administración
- Para exponerlo en la web se usó un reverse proxy con nginx
proxy_passconecta con el puerto local de Baïkal/.well-known/caldavredirige con 301 adav.php- Esta redirección es necesaria al agregarlo en la app de calendario de iPhone o Mac
- TLS se configuró con Let’s Encrypt y
certbot, ycertbotactualiza automáticamente el archivo de configuración de nginx - Tras entrar al dominio desde el navegador, se creó una cuenta de administrador y luego usuarios y calendarios
- Se conectó con las apps de calendario predeterminadas de macOS e iOS, y en la configuración de macOS/iOS se usaron manual settings en lugar de automático o advanced
Clasificación de eventos y alcance de publicación del calendario laboral
- La especificación iCalendar incluye la propiedad opcional
CATEGORIESen el componenteEVENT, pero las herramientas usadas, como macOS Calendar, iOS Calendar y Google Calendar, no implementan ese campo - Como la clasificación con texto libre dificulta mantener consistencia, se modeló el tipo de evento con enums de Python
- Ejemplos:
TerminType,CultureType,SocialType,AwayType,TransportType - Incluyen valores como
MEETUP,CONFERENCE,CLASS,APPOINTMENT,MEETING,EXAM
- Ejemplos:
- La razón para dividir los enums en varios grupos fue ordenar conceptualmente los tipos de evento
- En este sistema de clasificación también entra la lógica que define qué tanto se publica al calendario laboral
- Los valores enum de dos dígitos se tratan por defecto como private en el calendario laboral
- Eventos como consultas médicas se serializan como privados en un archivo separado del calendario laboral
- Esta clasificación se usa para mejorar la capacidad de encontrar eventos y su identificación visual, y para hacerlos más fáciles de distinguir de un vistazo en frontends con soporte de colores
Script de sincronización en Python y publicación de ICS
- Para automatizarlo, se conectó un script de Python a un cron job
- El script reúne varias entradas, las inserta en Baïkal y luego las exporta otra vez como ICS compartibles
- Importar eventos desde correo por IMAP
- Extraer eventos de vuelos desde la API de Airtrail
- Importar el archivo ICS alojado por la escuela de idiomas
- Hacer push de los eventos recopilados a Baïkal
- Obtener todos los eventos de Baïkal y volver a serializarlos en uno o más archivos ICS compartibles
- La integración con IMAP ofrece una función parecida a Google Calendar, ya que importa automáticamente invitaciones de calendario recibidas por correo y las agrega al calendario
- El script de entrada del cron crea por separado
Calendarfamiliar y laboral, obtiene los eventos de Baïkal y los bifurca- Agrega eventos al ICS familiar
- Los agrega al ICS laboral solo cuando la dirección de correo laboral no aparece en el resultado serializado del evento
- Según el resultado de
is_work_public, establece la clasificación comoPUBLICoPRIVATE
- El script de integración con Baïkal usa solicitudes
PROPFINDy autenticación HTTP Digest para obtener eventos remotos - Si en la descripción del evento aparece el patrón
CATEGORIES:, extrae la categoría del campo de descripción y la coloca enevent.categories, eliminando esa cadena de la descripción - Los archivos ICS compartibles se sirven desde nginx con URLs difíciles de adivinar, generadas a partir de cadenas hash aleatorias con salt
- Este cron job se ejecuta cada 15 minutos
Sincronización con Google Calendar del trabajo
- Además de ver eventos en el calendario personal, era necesario que en el calendario laboral los traslados y vuelos aparecieran como tiempos bloqueados y que los colegas pudieran saber el estado
- Para copiar al calendario laboral se utilizó Google Script Engine
- El script base es una versión modificada de GAS-ICS-Sync
- La versión modificada lee la propiedad
CATEGORIESdel calendario y aplica codificación por colores en el calendario laboral - Google Script se ejecuta cada 30 minutos
- Aunque usar Google Calendar contradice el objetivo de reducir la dependencia de Big Tech, se distingue como una elección del entorno de trabajo y no como una dependencia para uso personal
Resultados operativos y costos
- Este sistema se ha usado durante unos 6 meses, con pequeños ajustes y agregados
- El cambio más reciente fue la integración de la API de Airtrail
- Al reservar un vuelo, los datos se ingresan en el rastreador de vuelos
- Se agregan al calendario personal en menos de 15 minutos
- Se copian automáticamente al calendario laboral en menos de 1 hora
- Frente a requerimientos de movilidad complejos, el tiempo dedicado a gestionar la agenda se redujo mucho, lo que se tradujo en una mejora en la calidad de vida
- El costo total puede mantenerse al mínimo y también puede operarse fácilmente en un NAS doméstico
- Actualmente, para ejecutar el sitio web y las integraciones, se mantienen los datos en Suiza y se paga aproximadamente 100 dólares mensuales en tiempo de servidor
- Ese costo corresponde a una configuración algo excesiva, con la idea de optimizarla con el tiempo
- Haber podido montarlo rápidamente en el host Docker de una instancia VM justificó asumir ese costo adicional, y la simplificación de la gestión del calendario ya está ahorrando un tiempo equivalente a esos 100 dólares mensuales
1 comentarios
Opiniones de Hacker News
Creo que habrá rechazo, pero para mí CalDAV no es muy bueno
Si de verdad fuera fácil y excelente, habría muchas más soluciones CalDAV autohospedadas y la usabilidad sería mejor; Radicale tampoco me impresionó
En realidad, como prácticamente solo necesitaba ofrecerme un archivo de calendario, intenté implementar mi propio servidor CalDAV, pero hasta exponer un solo archivo resultó tan poco intuitivo y complejo que desistí
Entiendo la necesidad si hay que compartir un calendario con otra persona mediante una app compatible con CalDAV, pero para alojar un calendario de uso personal me parece casi una pérdida de tiempo
En su lugar, al principio subía un archivo iCal que se actualizaba dinámicamente a un bucket de S3 y lo sincronizaba con ICSx5 en Android. Con HTTPS bastaba, sin CalDAV
Sin embargo, todavía faltan buenas apps de calendario libres y de código abierto para Android, y como ICSx5 no me funciona bien en GrapheneOS, ahora uso Proton Calendar casi de la misma forma, porque admite enlaces directos a archivos iCal
El enfoque de usar el formato iCalendar está bien, pero el alcance del soporte de funciones de iCalendar varía mucho entre programas de calendario, así que al final termino metiendo la mayor parte de los metadatos en el campo de descripción en vez de usar propiedades específicas
Mi caso de uso es un software que trae datos de varios sitios, como Eventbrite, Meetup, empresas de trivias en pubs y calendarios públicos de eventos, filtra solo los eventos sociales que me interesan y los combina en un único calendario que puedo ver en la app de calendario que uso normalmente
Además, sufrimos muchísimo para manejar los servidores CalDAV de Exchange y Google que no cumplían la especificación, y los usuarios nos culpaban a nosotros por todos esos problemas
Ahora que trabajo en Google, veo que las apps de gCal usan un protocolo completamente distinto
En vez de ser un formato liviano, intentó hacer demasiadas cosas de una sola vez, quedó sobrediseñado y terminó siendo un protocolo como una bola de pelos de ideas distintas enredadas
Por eso muchos servicios no intentan cumplir por completo toda la especificación, porque se sale del alcance de lo que realmente quieren
Me da la impresión de que las especificaciones de formatos de datos del W3C siempre terminan así, aunque en las especificaciones de protocolos de más bajo nivel parece ocurrir relativamente menos
Lo leí hace unos 15 años, pero recuerdo que también abordaba en parte cómo protocolos como estos se pierden dentro de comités durante el proceso de diseño
El solo hecho de que este proyecto haya empezado alrededor de 2001 muestra lo antiguo que es el dolor de cabeza de los calendarios
Lo recomiendo si quieres entender por qué no es muy bueno, y también es una buena historia sobre el choque entre startups y código abierto, la creación de productos y el desarrollo de software en general
http://www.dreamingincode.com/ ; https://en.m.wikipedia.org/wiki/Dreaming_in_Code
Para la sincronización en Android uso DAVX5, una app open source sin root que ejecuta un servicio de sincronización en segundo plano, pero la configuración tuvo muchos problemas y quizá se deba a diferencias en la implementación de CalDAV. Sincronizar desde DAVX5 hacia el calendario de ZOHO también tarda bastante
Evolution en Ubuntu tiene un soporte de CalDAV bastante bueno, pero cada vez que cambio entre la vista de correo y la vista de calendario vuelve a actualizar todo el calendario. La actualización del calendario de ZOHO tarda mucho y los límites de velocidad de ZOHO también son estrictos, así que en la práctica termino recibiendo una especie de bloqueo suave
La versión reciente de Thunderbird tiene una incompatibilidad con CalDAV porque espera otro código HTTP de éxito. ZOHO devuelve 200, pero Thunderbird espera otro código. Como referencia, el código CalDAV de Thunderbird está escrito en JS
Este problema solo ocurre con el segundo calendario, y en cuanto agregas el segundo empieza a fallar. Al menos con ZOHO es bastante doloroso
Otro servidor CalDAV recomendable es Radicale
Está escrito en Python y tiene un diseño bastante modular, así que se le pueden agregar plugins para autenticación, autorización y demás
Los datos se guardan como archivos de texto plano, y los rastreo y respaldo con git
Con el tiempo acumulé algunas modificaciones interesantes. La autenticación se maneja con pam del sistema anfitrión, y para compartir calendarios ejecuto un script periódico que crea enlaces simbólicos del calendario para todos los usuarios autorizados. Eso sí, tiene un problema de explosión combinatoria
También agregué listas de control de acceso con un plugin de permisos que usa propiedades CalDAV personalizadas y un plugin web modificado
A los calendarios públicos les doy permiso de lectura al usuario
publicmediante ACL, y con un hack en nginx permito acceso amplio para operaciones de lecturaEstos calendarios públicos se pueden ver en un calendario web: https://gitlab.nomagic.uk/popi/js_calendar_from_ics
Creo que debería escribir un post en el blog sobre esta configuración
Este artículo era justo lo que necesitaba
En mi servidor personal uso el calendario SoGO de Mailcow, y para el calendario del trabajo uso Fastmail, pero no se llevan bien entre sí
No he encontrado una forma de integrar correctamente el calendario de Mailcow con otras herramientas, así que paso tiempo sincronizándolo de manera semimanual
Hace tiempo quería separar los calendarios, pero la única alternativa que conocía era Nextcloud, y me parece algo pesado
Baïkal se ve bien y tengo ganas de probarlo pronto. Con esto también podría explorar cambiar Mailcow por otro servicio de correo autohospedado
Últimamente estoy saliendo de la nube de forma bastante agresiva. Tenía cientos de GB de datos, pero aunque hice la migración de a poco, tardé menos de 2 semanas, y funciona mucho mejor de lo que esperaba
El único servicio que dejé en manos de terceros es el correo electrónico, porque no quiero lidiar con registros MX e IMAP/SMTP
Para todo lo demás, me ayudó mucho una Asus PN40 con CPU Celeron de doble núcleo. Su ventaja es que es fácil de acceder y tiene ranuras para M.2 NVME y unidades SATA, lo que permite armar una configuración de backup segura en un solo lugar
Coincido en que CalDAV es algo sospechoso, pero Nextcloud cubrió la mayoría de mis necesidades. Incluso con Redis, algunas operaciones pueden ser lentas, pero maneja bien calendario, reemplazo de Dropbox/Google Drive, documentos, fotos, backups y sincronización automática
Si a eso se le suman varias soluciones open source, también queda resuelto todo lo de streaming de música y video, monitoreo, notificaciones, VPN, lector, herramientas de administración y todo tipo de utilidades
Aunque esta mini PC no es muy potente, mi ISP es excelente, así que puede con todo dejando bastante margen, y hasta podría sumar sin problema a 5 o 6 personas más como yo
Se siente como si lo viejo volviera a ser nuevo
A principios de los 2000 alojaba mi calendario con Mozilla Sunbird en un servidor Win2k personal mediante el módulo IIS WebDAV
Creo que CardDAV todavía no existía, aunque podría estar equivocado
Al final importé todos esos datos a Google Calendar, y es bastante divertido porque, si me voy muy atrás, todavía quedan cosas como tareas de la universidad
Perdí algunas citas porque no se subieron al servidor, y lo usaba con CalDAV junto con un servidor DAViCal
Sunbird fue discontinuado, y últimamente no he buscado si existe un reemplazo adecuado que corra en Windows
Baikal se ve interesante
Antes usaba DAViCal para compartir calendarios entre varios dispositivos Apple iOS. Cuando funcionaba, funcionaba bien, pero parecía volverse inestable con los cambios de versión de iOS, así que terminé abandonándolo
Si algún día vuelvo a revisar esto, pienso mirar DAViCal y Radicale, y ahora también Baikal
https://www.davical.org/
https://radicale.org/v3.html
Hace poco me pasé a Nextcloud porque en el hosting compartido se encargan del despliegue y tiene una UI web integrada, así que la carga de configuración era mucho menor
Uso Baikal desde hace varios años y es bastante sólido. Eso sí, lo uso solo y no tengo todos los requisitos del texto original
En el escritorio me conecto con el calendario integrado de Thunderbird, y en el teléfono uso DAVx5 de F-Droid y Fossify Calendar
Fossify Calendar nació como un fork de la app Calendar de Simple Mobile Tools, pero pese al nombre es la app de calendario con más funciones que he visto hasta ahora
Me gusta porque su simpleza consiste en mostrar todas las opciones directamente, sin obligarte a meterte por varios menús y diálogos
En el trabajo uso Outlook y en casa Google Calendar, pero sincronizar por HTTP es totalmente inestable
Parece que arruina constantemente las zonas horarias, y a veces publica en UTC y a veces no, así que los horarios de las reuniones siempre quedan mal
No entiendo por qué estos dos grandes proveedores no pueden arreglar una función tan básica
Si usar mi propio sistema resolviera esto, lo haría de inmediato, pero me preocupa que pase lo mismo: que una reunión creada en Outlook se importe con 2 horas de diferencia
Además, muchas empresas directamente prohíben sincronizar calendarios personales y laborales por problemas de filtración de datos
Me gustaría hacer algo así también con las fotos. Hace tiempo que quiero alejarme de Dropbox y Google, y mis prioridades son dos
Primero, quiero crear algo tipo recuerdos a partir de mis fotos y enviarlo al teléfono. Sería una forma de agruparlas por aniversarios, actividades parecidas,
yo y X (X puede ser mi cónyuge, familia, amigos, etc.), cambios a lo largo del tiempo, y criterios similaresSegundo, necesito poder guardar en mi servidor las fotos tomadas desde los dispositivos
Ya tengo 3 máquinas TrueNAS con 2 TB de espacio en ubicaciones distintas, y quiero ir construyendo esta función poco a poco
https://www.synology.com/en-global/dsm/feature/photos
https://www.synology.com/en-global/dsm/feature/moments
Otra alternativa es usar DecSync sin servidor
Thunderbird o Evolution se sincronizan con apps de Android, y calendarios, contactos, tareas y RSS se replican a otros pares. También está disponible en F-Droid
https://github.com/39aldo39/DecSync