Plan — Pasar el engine (nef-silo) a Hugging Face
DECIDIDO (2026-09-22): el engine va a OCI free, no a HF (ver
[CREDENCIAL].md). Este documento queda como referencia de la ruta HF, útil solo para corridas por lote (p. ej. extracción).
Fecha: 2026-09-22. Estado: planning (Linode caído; ver
2026-09-22-linode-caido-permanentemente.md). Repo ya en GitHub:
github.com:[usuario]/nef-silo.
Qué hay que reemplazar exactamente
El engine corre hoy como cron cada 30 min:
capturar && exportar --dias 7 --perfil editorial (ver run.sh).
| Necesidad | Detalle medido |
|---|---|
| Cómputo programado | cada 30 min, duración ~1–3 min por corrida |
| Estado persistente | data/silo.db (36 MB), estado.json, silo.jsonl, export-editorial.json |
| Deps | solo feedparser>=6.0 |
| Red | 122 fuentes RSS/API + RSSHub para X (51) y Telegram (6) |
Punto clave: el estado son ~40 MB. No hace falta un disco grande; hace falta persistencia entre corridas y un disparador cada 30 min.
Verdicto: HF SÍ sirve, pero con la pieza correcta — Scheduled Jobs
⚠️ Spaces no es la respuesta para esto: disco efímero (free), y un Space CPU básico se pausa tras 48 h sin visitas → un cron que no recibe tráfico se duerme. Requiere keepalive hack + storage persistente de pago (~$5/mes).
✅ HF Scheduled Jobs sí es el fit:
hf jobs scheduled run --name silo-cap "*/30 * * * *" <imagen> <comando>
# o con uv: hf jobs scheduled uv run "*/30 * * * *" script.py- cron nativo (sintaxis
*/30 * * * *), timeout default 30 min (nuestro job ~2 min). - Jobs = contenedores Docker efímeros, con red. Available en Pro / saldo > 0.
- Estado en un Storage Bucket montado como volumen (
/data) de lectura- escritura, o bajado/subido conhf_hubal inicio/final de cada corrida.silo.db(36 MB) cabe de sobra.
Arquitectura propuesta
[hf jobs scheduled "*/30 * * * *"]
│
▼
contenedor efímero
1. monta bucket -> /data (silo.db, estado.json, silo.jsonl)
2. python silo_engine.py capturar
3. python silo_engine.py exportar --dias 7 --perfil editorial
4. deja /data actualizado (bucket) -> surtido lo consume
El export-editorial.json resultante ya lo sirve nginx hoy; en HF habría que
publicarlo (bucket público, o el mismo repo del engine) para que surtido (Vercel)
lo lea.
🔴 El blocker real: RSSHub
silo_engine.py construye URLs de X y Telegram como
RSSHUB + "/twitter/user/<handle>" (línea 633) y /telegram/channel/... (636),
con RSSHUB_URL por defecto http://localhost:1200. En el Linode, RSSHub corría
como sidecar Docker con TWITTER_AUTH_TOKEN. En un Job no hay sidecar.
Tres salidas:
| Opción | Cómo | Consecuencia |
|---|---|---|
| A. RSSHub en la misma imagen | Dockerfile con Node + RSSHub; el comando arranca RSSHub en background y luego el engine | self-contained; imagen pesada (Node+deps), cold start mayor |
| B. RSSHub externo | RSSHUB_URL=https://rsshub.app (público) o una instancia propia | reintroduce un host; instancias públicas sin token suelen fallar en X |
| C. Sin X/TG | dejar solo fuentes RSS (65 de 122) | se pierde 51 X + 6 TG → degrada el engine |
Recomendación: A (todo en HF, sin tercer host), con B como parche rápido para la Fase 0.
Costo (a medir, no a suponer)
- Jobs = pago por segundo; Pro da $2/mes de crédito.
- Aritmética: 48 corridas/día × ~2 min × 30 días ≈ 48 h de CPU/mes. Hay que medir 2 o no.
- Storage Bucket: 40 MB → coste marginal ~0.
- Sospecha: varias corridas cortas con cold start cada 30 min pueden salir más caras que un VPS pequeño fijo. Medir antes de comprometerse.
Fases (cada una termina en artefacto verificable)
- Medir: un
hf jobs runmanual que ejecutecapturarUNA vez (RSSHUB externo o local en la imagen) + cronometrar duración y coste en la factura. → número de $/corrida. Decidir si HF es viable en coste. - Empaquetar: Dockerfile del engine (feedparser + código + opción RSSHub).
→
hf jobs run <imagen> python silo_engine.py --helpresponde. - Estado: bucket montado con un
silo.dbcopiado de local. →estado.jsonleído/escribido persiste entre dos corridas. - Scheduled Job cada 30 min con
--continue/idempotencia. → 2 corridas seguidas sin duplicar items. - Publicar salida (
export-editorial.json) para surtido. → surtido lee la nueva URL. - Apagar cualquier resto del Linode (cron/scripts) y actualizar
run/deploy.
Riesgos
- Cold start por corrida (descarga imagen + arranque) puede dominar el costo.
- Drift de RSSHub: sin el token de X, las rutas fallan silenciosamente.
- Límite de 30 min por job: si una corrida se cuelga, se corta (bien).
- Tool-hopping: esto es mover infra, no arreglar el engine. Si el objetivo real
es “que el engine siga vivo y barato”, un OCI free tier (VM always-on, ya
documentado en
2026-09-20-oci-free-mlops-lab.md) es la respuesta aburrida y fiable; HF brilla si además quieres el cómputo cerca de modelos/GPU.
Decisión pendiente (antes de Fase 1)
- RSSHub: ¿A (dentro de la imagen), B (externo) o C (soltar X/TG)?
- ¿HF de verdad, o el objetivo es solo “engine vivo”? Si es lo segundo, OCI free tier gana en simplicidad.