Arquitectura
Arquitectura técnica de la plataforma Relo. Edge, ingest, almacenamiento, failover, seguridad y fraud detection.
Relo está diseñado para nunca perder un evento. Cada click, pixel, postback o pull pasa por capas independientes que pueden fallar sin afectar a las demás. Esta página explica cómo se conectan los servicios, cómo fluye la data y qué pasa cuando algo se rompe.
Vista de alto nivel
Las tres capas
Edge Network — Cloudflare Workers corren en 300+ PoPs. Click wrapper, web pixel, S2S proxy y API gateway. Aquí se generan ULIDs, se setean cookies y se hace rate limiting.
Processing Engine — Servidor dedicado en Hetzner con el servicio Go (6,684 líneas, 16 paquetes), ClickHouse y DragonflyDB. Atribución, scoring de fraude, resolución de identidad y escritura batch.
Platform & Activation — Supabase para config/pagos/agregados, R2 como data lake/WAL/backups, y DSP para reenvío de eventos a Meta, Google y TikTok.
Componentes principales
Click Wrapper
t.relo.mx — genera ULID, deja cookie first-party y responde 302 en <50 ms P99.
Web Pixel
p.relo.mx — loader JS de 143 líneas. Usa Beacon API + device fingerprinting.
S2S Proxy
s2s.relo.mx — verificación HMAC, terminación TLS 1.3, protección DDoS.
API Gateway
relo-api — 250+ endpoints, JWT auth, CORS y rate limiting.
Go Ingest
Servicio Go en Hetzner: fasthttp, batch writer, identity resolution, fraud scoring.
ClickHouse
Motor analítico columnar. Schema de 35 campos, 8.6x compresión, TTL 24 meses.
DragonflyDB
Grafo de identidad, cache de fraude y contadores en tiempo real. Sub-ms lookups.
Supabase
50+ tablas: config, partners, pagos, agregados. Tamaño constante por siempre.
Cloudflare R2
Data lake, archivo CSV y backups. 11 nueves de durabilidad, zero egress fees.
DSP Activation
Meta CAPI, Google Ads y TikTok API. Reenvío de eventos de compra en tiempo real.
Ciclo de vida de un evento
| Paso | Qué pasa | Almacenamiento | Latencia |
|---|---|---|---|
| 1. Llega el evento | Click, beacon o postback al edge worker | Memoria edge | <5 ms |
| 2. Procesamiento edge | ULID, cookies, headers geo | — | <10 ms |
| 3. R2 WAL | Escrito como NDJSON gzip antes de procesar | R2 | <20 ms |
| 4. Ingesta Go | HMAC, GeoIP, identidad, fraude | — | <5 ms |
| 5. Escritura batch | Buffer, flush cada 1k eventos o 1s | ClickHouse | <1 s |
| 6. Escritura dual | Compras también a Supabase | Supabase | <100 ms |
| 7. Actualización de identidad | Perfil de dispositivo + cross-device | DragonflyDB | <1 ms |
| 8. Agregación | MVs cada 5 min, sync a Supabase cada 10 min | Supabase | 5-10 min |
| 9. Recuperación DLQ | Revisa eventos fallidos cada 60s | R2 DLQ | 60 s |
| 10. Exportación DSP | Compras a Meta CAPI | Meta | <5 s |
Cómo entra cada fuente
- User hace click en
t.relo.mx/...2. Cloudflare Worker genera ULID y cookie first-party. 3. 302 redirect al destino final (ej.ad.admitad.com/...). 4. Evento de click se envía al Go service y se escribe en ClickHouse.
- Sitio de la marca carga
p.relo.mx/r.js. 2. Beacon API envía page views, product views, cart, leads, compras. 3. Pixel soporta first-party cookies y fingerprinting.
- AppsFlyer/Branch envía POST a
s2s.relo.mx/v1/postback/appsflyer. 2. Cloudflare Worker verifica HMAC y reenvía a Go ingest. 3. Go procesa, enriquece y escribe.
- Timer de systemd cada 60 min extrae eventos de la Pull API.
2. Solo eventos con
Is Primary Attribution = true. 3. Re-pulls seguros gracias aevent_iddeterminístico.
Failover por fuente
Click wrapper — 3 niveles
Web pixel — 3 niveles
S2S postbacks — 3 niveles
Disaster recovery
🔥 Servidor destruido
Edge detecta health check failure → fallback a KV/R2 → levanta nuevo servidor desde backup diario → replay de standby + DLQ. RTO: 2-4h. Cero pérdida.
🔌 Cloudflare caído
No llegan clicks/pixels/API. Go sigue vivo. AppsFlyer Pull continúa directo a Hetzner. Al recuperarse CF, los encolados se procesan. Cero pérdida.
💣 Supabase caído
Dashboard y portal pausados. ClickHouse y DragonflyDB siguen recibiendo todo. Al recuperarse, aggregator sincroniza ventana de 7 días. Cero pérdida.
⚡ Go service crashea
Systemd reinicia en <5 s. Durante el reinicio, clicks → KV → R2 DLQ, pixels → R2 DLQ. Al arrancar, recupera todo. RTO: <10 s. Cero pérdida.
💾 Disco ClickHouse
RAID-1 continúa con el disco sobreviviente. Se reemplaza el disco fallido y RAID se reconstruye. Cero pérdida, cero downtime.
🔐 DragonflyDB caído
Systemd reinicia. Grafo de identidad se reconstruye desde ClickHouse (~30 min para 90 días). Eventos siguen fluyendo. Degradación temporal, cero pérdida.
Backups y monitoreo
| Frecuencia | Qué hace |
|---|---|
| Cada 60s | Health check + auto-remediación (4 probes: CH, DF, R2, Supabase) |
| Cada 10 min | Aggregator sync a partner_daily_aggregates |
| Cada hora | AppsFlyer Pull (backup externo independiente, 90 días de retención) |
| Diario 4 AM | Backup incremental ClickHouse a R2 |
| Semanal (domingo) | Backup completo ClickHouse a R2 |
Verificación real de backups
$ crontab -l
# Health check corre dentro del servicio Go cada 60s
0 4 * * * /opt/relo-ingest/backup-clickhouse.sh --daily
0 4 * * 0 /opt/relo-ingest/backup-clickhouse.sh --full
$ tail -5 /var/log/ch-backup.log
[2026-03-12 04:00:01] Starting daily backup...
[2026-03-12 04:00:02] Exported events (last 7d): 1,247 rows
[2026-03-12 04:00:02] Exported partner_daily_aggregates: 892 rows
[2026-03-12 04:00:03] Uploading to R2: relo-backups/daily/2026-03-12/
[2026-03-12 04:00:04] ✓ Backup complete. Size: 17.6KB
Autenticación: 4 capas fail-closed
HMAC-SHA256 — S2S postbacks de AppsFlyer verificados contra X-AF-Signature. Si no hay secret configurado, todo rechazado con 503.
Bearer Token — endpoints admin protegidos con Authorization: Bearer <token>. Token vacío = todo rechazado.
JWT + RBAC — Supabase Auth con roles admin, client_manager, partner, viewer. Partners nunca ven revenue, solo su comisión.
Row-Level Security (RLS) — aislamiento a nivel de fila en PostgreSQL. Incluso con bugs en la app, la DB rechaza acceso no autorizado.
Detección de fraude
Motor de scoring con 5 señales, evaluado en ~2 µs con XGBoost compilado:
| Señal | Qué detecta |
|---|---|
| CTIT | Click-to-Install Time. Click injection y click flooding. |
| Geo mismatch | VPN fraud: clicks de un país, installs de otro. |
| Device fingerprint | Emuladores, granjas de dispositivos, IDs spoofeados. |
| Velocity checks | Rafagas anormales de clicks/installs por dispositivo/hora. |
| ML Ensemble | XGBoost entrenado con datos históricos. Score 0-255. |
Mejoras de alta disponibilidad (marzo 2026)
- Systemd watchdog —
Type=notifyconWatchdogSec=120. Mata y reinicia si el proceso se cuelga. - External health monitor — Cloudflare Worker cron hace ping a
/healthcada hora, independiente del proceso Go. - WAL integrity verification —
GET /wal/verifymuestrea archivos R2, descomprime y valida JSON. - DLQ depth monitoring —
/healthexpone profundidad de DLQ para clicks, S2S y pixel. - Supabase PITR + daily backups — recuperación a cualquier segundo dentro de la ventana de retención.
- DR drills —
drp-test.shvalida WAL + DLQ en pruebas automatizadas.
Checklist de arquitectura sana
- Health check de Go responde OK en /health
- DLQ depth en cero para clicks, S2S y pixel
- ClickHouse RAID-1 sin degradación
- DragonflyDB responde a PING y test RW
- Último backup diario en R2 se completó sin errores
- AppsFlyer Pull de la última hora trajo eventos
- Supabase aggregate sync lag < 30 min
- External CF Worker cron recibió heartbeat