kesto 0.9.0

Lightweight backup orchestrator in a container: dumps MariaDB/PostgreSQL and host directories into rustic repositories on S3, with a live React panel.
# Despliegue de producción de Kesto (SRS §8).
#
# La clave maestra y los secretos NO viven aquí: entran por el entorno del
# host (`.env` del despliegue o gestor de secretos). Copia `.env.example` a
# `.env` y rellénalo antes de `podman compose up -d`.
services:
  kesto:
    image: ghcr.io/atareao/kesto:latest
    container_name: kesto-app
    restart: always
    ports:
      - "8080:8080"
    # Secretos OPCIONALES por `.env`: solo entran si el operador los
    # descomenta ahí. NO se cablean en `environment:` porque una `OIDC_*`
    # presente pero VACÍA se lee como «presente» (src/config.rs::resolve_auth
    # deduce el modo de la mera presencia de las tres): declararlas aquí con
    # valor por defecto vacío forzaría el modo OIDC y rompería el arranque por
    # defecto, que debe ser `Disabled`.
    env_file:
      - .env
    environment:
      RUST_LOG: info,kesto=debug
      DATABASE_URL: sqlite:///app/data/kesto.db
      BIND_ADDRESS: 0.0.0.0:8080
      # Obligatoria siempre. Sin ella el arranque falla (src/config.rs).
      KESTO_MASTER_KEY: ${KESTO_MASTER_KEY:?falta KESTO_MASTER_KEY}
    volumes:
      # Socket del daemon para el `exec` de bollard, en solo lectura.
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Fuentes de copia de ejemplo del §8, en solo lectura.
      - /var/www:/backup_sources/var_www:ro
      - /etc/nginx:/backup_sources/nginx_config:ro
      # Persistencia interna (SQLite) de la aplicación.
      - kesto_data:/app/data
    # En un host estándar el socket del daemon es `root:docker` con modo
    # `srw-rw----`, y el proceso corre como uid/gid 10001: sin el grupo del
    # daemon no puede abrirlo. El gid DEPENDE DEL HOST y se pasa por `DOCKER_GID`
    # (`getent group docker`) sin editar este fichero. El defecto es el propio gid
    # del proceso (10001): inofensivo, y mantiene el arranque en dev (sin daemon)
    # sin configurar nada. En local lo cubre `just run` con `--userns=keep-id`.
    group_add: ["${DOCKER_GID:-10001}"]

volumes:
  kesto_data:

# ---
# Ejemplo de fuente SQLite (§4): un servicio autoalojado cuya base vive DENTRO
# de su propio contenedor (Vaultwarden, Miniflux, Immich…). NO se monta en
# Kesto ni se lee del host: se declara como target `sqlite` con
# `container_name` —el contenedor donde vive el fichero— y `db_path` —la ruta
# absoluta del `.db` dentro de él—, sin `db_user`, `db_password`, `db_name` ni
# `host_path`. Kesto copia con `sqlite3 <db_path> .dump` por el socket del
# daemon (consistente en caliente: válido con WAL y sin parar el servicio).
#
# El contenedor del target DEBE traer el binario `sqlite3`: si su imagen no lo
# incluye, añade una capa que lo instale (`apk add --no-cache sqlite` /
# `apt-get install -y sqlite3`) o usa una imagen que ya lo traiga.
#
# Alta equivalente por API (el panel hace lo mismo en Ajustes → Fuentes):
#
#   curl -fsS -X POST http://localhost:8080/api/v1/targets \
#     -H 'content-type: application/json' \
#     -d '{"name":"vaultwarden","target_type":"sqlite",
#          "container_name":"vaultwarden","db_path":"/data/db.sqlite"}'
#
# services:
#   vaultwarden:
#     image: vaultwarden/server:latest
#     container_name: vaultwarden
#     restart: always
#     volumes:
#       - vaultwarden_data:/data     # aquí vive /data/db.sqlite
#
# volumes:
#   vaultwarden_data: