Saltar a contenido

Notificaciones redundantes

Custodiam usa dos canales de notificación push con políticas de redundancia distintas según la criticidad del mensaje. Esta página recoge la visión operativa del sistema.

Por qué dos canales

Las notificaciones de Custodiam son funcionales (un coordinador convoca a voluntarios a un servicio preventivo o de emergencia) y una notificación que no llega es un fallo del producto, no de UX. Un voluntario que no recibe la convocatoria a una emergencia activa porque Firebase Cloud Messaging tuvo un incidente regional no es aceptable. La política de notificaciones del proyecto es redundancia activa para mensajes críticos.

A su vez, Firebase Cloud Messaging (FCM) sigue siendo el canal nativo de Android y iOS con mejor entrega en background, batería, y soporte de notificaciones ricas (imagen, deep link, acciones). Sustituirlo por completo penaliza al 99 % de los mensajes para protegerse del 1 % de incidentes. Por eso FCM se mantiene como canal principal y se añade ntfy (open-source self-hosted) como respaldo automático cuando FCM falla o devuelve un timeout en mensajes marcados como críticos.

Topología

custodiam-app (móvil)custodiam-apiServicios externosSelf-hostedfirebase_messaging SDKntfy client(HTTP polling)NotificacionesServiceaudit_logFirebase Cloud Messaging(Google)ntfy(contenedor en custodiam-infra) HTTPS fallback automáticopushHTTP push

Política de envío por tipo

Tipo de notificación Canal primario Fallback Reintentos
Emergencia activa (convocatoria) FCM ntfy automático si FCM falla o timeout > 5 s 3 reintentos exponenciales
Convocatoria a servicio preventivo FCM ntfy automático si FCM responde error 1 reintento
Confirmación / cambio de estado FCM sin fallback sin reintentos
Informativa (cambio de turno, recordatorio) FCM sin fallback sin reintentos

El criterio operativo: solo las notificaciones donde un fallo de entrega tiene impacto sobre la operación real de la agrupación activan el fallback. Las notificaciones informativas pueden perderse sin coste; las de emergencia no.

Flujo de emergencia paso a paso

sequenceDiagram
    participant Coord as Coordinador
    participant App as App del coordinador
    participant API as custodiam-api
    participant FCM as Firebase FCM
    participant NTFY as ntfy
    participant Vol as App voluntarios (N dispositivos)

    Coord->>App: Crea convocatoria de emergencia
    App->>API: POST /servicios
tipo=emergencia, voluntarios_filtrados API->>API: Filtra voluntarios disponibles + permisos RBAC API->>FCM: Envía push a tokens FCM
(canal principal) alt FCM responde 200 FCM-->>Vol: Push notification
(payload + deep link) Note over Vol: Tap → app abre en pantalla del servicio else FCM falla o timeout > 5 s API->>NTFY: Publica en canal /custodiam-emergencia NTFY-->>Vol: Push vía ntfy (canal secundario) Note over API: Log fallo FCM + telemetría end Vol->>API: PATCH /servicios/{id}/voluntarios/{me}
{respuesta: "acepto" | "rechazo"} API-->>App: Notifica al coordinador (vía FCM)

Lecciones operativas

  • iOS exige permisos explícitos. La primera vez que la app se abre, debe solicitarlos. Si el usuario los deniega, no hay notificaciones — la app debe mostrar un aviso reconductivo en el primer flujo afectado (intento de fichar, recepción de servicio).
  • Token FCM rota. La app re-registra el token en cada arranque y lo sincroniza con el backend. El backend almacena varios tokens por voluntario (uno por dispositivo activo) y borra los que caducan tras 30 días sin uso.
  • ntfy requiere autenticación. Aunque el servidor es público en el dominio del proyecto, los canales sensibles (emergencia, servicios) están protegidos con tokens emitidos por el backend al hacer login.
  • El backend NO usa el SDK Admin de Firebase para enviar. Usa la HTTP v1 API directamente con un service account JSON cuyas credenciales viven en docker/.env.sops (cifradas con sops + age). Esto evita arrastrar el SDK gigante de Google al backend.

Configuración por entorno

Entorno FCM ntfy
dev (Docker Compose) Cuenta Firebase del proyecto, app es.custodiam.dev Contenedor local ntfy en puerto 8080
tunnel (Cloudflare) Cuenta Firebase del proyecto, app es.custodiam.dev Contenedor ntfy expuesto en ntfy.custodiam.es
prod Cuenta Firebase del proyecto, app es.custodiam Contenedor ntfy expuesto en ntfy.custodiam.es

Los tres modos están documentados en ADR-020.

Referencias