Problema

Mantener un archivo de respaldo distribuido entre dispositivos de diferentes usuarios implica varios retos simultáneos: los datos deben permanecer cifrados en cada nodo, la pérdida de cualquier máquina no debe comprometer la integridad del conjunto y la incorporación de nuevos participantes debe poder gestionarse sin exponer una autoridad central. En entornos de homelab o grupos de trabajo colaborativo, los administradores suelen combinar hardware heterogéneo (Raspberry Pi, PCs antiguos, NAS) y redes domésticas sin NAT‑traversal. El patrón problemático es la falta de un proceso fiable para:

  1. Encriptar y fragmentar los archivos antes de enviarlos a nodos no confiables.
  2. Garantizar redundancia mediante erasure coding que sobreviva a fallos de nodo.
  3. Automatizar la admisión de nuevos nodos mediante un flujo de pull request que valide la identidad del solicitante.

Cuando cualquiera de estos puntos falla, el sistema se vuelve inseguro, pierde disponibilidad o se vuelve imposible de escalar.

Causa

Los fallos habituales provienen de tres áreas clave:

  • Capa de almacenamiento no confiable – Utilizar soluciones diseñadas para clústers controlados (Ceph, MinIO) asume confianza mutua entre nodos. En un modelo voluntario, esa suposición se rompe y la capa expone datos sin cifrar o permite que un nodo corrupto altere la replicación.
  • Gestión de claves frágil – Emplear identificadores cortos de GPG (p.ej. los últimos 8 bits) permite colisiones rápidas (Evil32). Los bots que verifican la membresía pueden aceptar claves forjadas si no comparan la huella completa.
  • Orquestación de contenedores imprecisa – Docker‑Compose ignora automáticamente archivos docker-compose.override.yml cuando se lanza con -f compose.yml. Además, la pérdida de un disco externo desconecta el contenedor sin que el proceso lo detecte, dejando el nodo “muerto” mientras el resto sigue operando.

Estos problemas se combinan en entornos donde los administradores confían en scripts de CI/CD para validar la membresía y en hardware que cambia de forma frecuente.

Solución

Una arquitectura reutilizable se basa en tres pilares:

  1. Capa de almacenamiento basada en Tahoe‑LAFS – Tahoe está diseñada para servidores no confiables. Cada archivo se cifra localmente, se fragmenta con Reed‑Solomon y se distribuye a los “storage servers”. La redundancia se controla mediante el parámetro --encoding (p.ej. k=10,m=4 para 10 fragmentos de datos y 4 de paridad). La capa también expone un frontend SFTP que puede encapsularse en un contenedor.
  2. Identidad basada en GPG con huella completa – El flujo de incorporación usa una acción de GitHub que consulta los servidores de claves públicas, extrae la huella SHA‑256 completa y la compara contra la que aparece en el PR. Sólo si coinciden se permite el merge. El bot también firma un archivo members.yaml con la clave del repositorio, creando una cadena de confianza verificable.
  3. Orquestación robusta con Docker‑Compose y supervisión – Se define un docker-compose.yml que incluye un depends_on para montar el disco externo mediante un volumen nombrado. Un script de entrypoint verifica la presencia del dispositivo (/dev/disk/by-id/...) y aborta el contenedor si no está disponible, evitando que el nodo quede “fantasma”. Además, se habilita restart: unless-stopped y se añade un healthcheck que ejecuta tahoe check --quiet.

Para la conectividad entre nodos se emplea Tinc como mesh VPN. Cada nodo tiene un archivo de configuración generado a partir de una plantilla que incluye la clave pública del nodo y la dirección IP virtual. Tinc maneja automáticamente NAT‑traversal, por lo que no se necesita abrir puertos en los routers domésticos.

Flujo de incorporación paso a paso

  1. Solicitante crea una rama, añade su huella GPG completa a join_requests/<username>.yaml y abre un PR.
  2. GitHub Action:
    • Descarga la clave desde los keyservers.
    • Verifica que la huella coincida con la declarada.
    • Firma el archivo members.yaml con la clave del repositorio.
  3. Revisor humano aprueba el PR. Al mergearse, la acción actualiza el archivo mesh.conf de Tinc y despliega la nueva configuración a través de un webhook que ejecuta docker exec tinc-node tinc reload.
  4. Nodo nuevo ejecuta docker compose up -d y, tras validar su clave GPG contra el archivo firmado, se une automáticamente al mesh y comienza a recibir fragmentos.

Cuándo aplicar esta solución

  • Entornos de confianza limitada – Cuando los nodos pertenecen a usuarios externos o a máquinas que no están bajo control administrativo directo.
  • Necesidad de redundancia verificable – Si el SLA requiere que al menos N copias estén disponibles y se pueda contar con un número exacto de réplicas.
  • Política de “no port‑forwarding” – Cuando los routers domésticos impiden la exposición directa y se prefiere una VPN mesh.
  • Escenarios de bajo a medio tráfico – Tahoe‑LAFS está optimizado para archivado y backup, no para alta velocidad de sincronización en tiempo real.

No es adecuada cuando:

  • Se requiere una solución de archivos compartidos en tiempo real (p.ej. Dropbox‑like) con latencia mínima.
  • El número de nodos supera varios cientos; la gestión de mesh y la tabla de miembros en Git pueden volverse engorrosas.
  • Se necesita facturación o incentivos económicos; proyectos como Storj o Sia ofrecen tokenomics que esta arquitectura no cubre.

Código

# docker-compose.yml (fragmento esencial)
version: "3.9"
services:
  tahoe:
    image: tahoe-lafs:latest
    restart: unless-stopped
    volumes:
      - tahoe-data:/tahoe
      - /dev/disk/by-id/usb-MyDisk:/storage:ro
    command: ["tahoe", "run", "--encoding", "k=10,m=4"]
    healthcheck:
      test: ["CMD", "tahoe", "check", "--quiet"]
      interval: 30s
      timeout: 5s
      retries: 3

  tinc:
    image: tinc:latest
    restart: unless-stopped
    network_mode: "host"
    volumes:
      - ./tinc/conf:/etc/tinc
    command: ["tincd", "-n", "mesh", "-D"]

volumes:
  tahoe-data:

Verificación

  1. Comprobar la salud del contenedor: docker ps --filter "health=healthy" debe listar tahoe y tinc.
  2. Validar la replicación: ejecutar tahoe query --verbose <file-id> y observar que el número de fragmentos activos coincide con k+m.
  3. Ensayar la incorporación: crear una clave GPG nueva, añadirla a join_requests/, abrir un PR y confirmar que la acción firma members.yaml. Luego, en el nodo recién creado, ejecutar docker compose up -d y observar en los logs de tinc la línea Node <username> joined.
  4. Simular fallo de nodo: detener el contenedor tahoe en un nodo y volver a iniciar. Verificar que los fragmentos perdidos se recuperan automáticamente desde los pares restantes mediante tahoe repair <file-id>.

Notas adicionales

  • Gestión de discos externos: monta siempre los discos bajo un identificador persistente (/dev/disk/by-id/...) para que la re‑asignación de /dev/sdX no rompa el contenedor.
  • Actualizaciones de Tahoe: la rama master no recibe parches frecuentes; considera forkear y aplicar correcciones locales, como el timeout de sesión SFTP que cuelga indefinidamente.
  • Seguridad de GPG: evita usar claves de menos de 2048 bits; la huella completa (SHA‑256) es imprescindible para impedir colisiones.
  • Escalado del mesh: si el número de nodos supera 50, evalúa dividir el mesh en sub‑grupos y usar un “super‑router” que conecte los sub‑meshes mediante túneles estáticos.
  • Monitoreo: Grafana + Prometheus pueden scrapear los endpoints /metrics expuestos por Tahoe y Tinc, proporcionando alertas cuando la tasa de reparación supera un umbral definido.