Volver al Blog
Ingeniería 15 de julio de 2025 · 14 min lectura

Herramientas Desktop Local-First: Por Qué Movimos 73K Líneas Fuera del Cloud

GM

Gonzalo Monzón

Fundador & Arquitecto Principal

No todo pertenece al cloud. Cuando tu carga de trabajo implica entrenamiento ML con datos privados, comunicación IoT en tiempo real, automatización del navegador con anti-detección o procesamiento de audio local — la latencia, la privacidad y el acceso al hardware hacen que las arquitecturas cloud-first sean impracticables. Construimos 5 herramientas desktop local-first sumando ~73K líneas de código en 114 archivos. Cada herramienta corre independientemente en la máquina del usuario, sincroniza con nuestra plataforma cloud cuando es necesario y maneja cargas que serían imposibles (o ilegales) de ejecutar remotamente.

Esta es la historia de ingeniería: por qué local-first, cómo funciona cada herramienta y los patrones arquitectónicos que permiten a un equipo pequeño mantener 73K líneas en 5 apps desktop sin volverse loco.

¿Por Qué Local-First? El Cloud No Siempre Es la Respuesta

La computación cloud resuelve problemas reales — escalabilidad, disponibilidad, zero-ops. Pero algunas cargas tienen restricciones que hacen el despliegue cloud imposible o incómodo:

  • Privacidad: Entrenamiento ML con datos de clientes que no pueden salir de la máquina. El RGPD no se preocupa por la certificación de tu proveedor cloud cuando los datos no deberían transmitirse en absoluto
  • Acceso a hardware: Los dispositivos IoT conectados vía MQTT, puertos Serial y Bluetooth no tienen endpoints cloud. Necesitas un proceso en la misma red — o el mismo puerto USB
  • Anti-detección: La automatización del navegador para scraping requiere perfiles de navegador persistentes, plugins stealth y patrones de comportamiento humano. Ejecutar esto desde un rango de IP cloud es una señal de detección
  • Latencia: El procesamiento de audio en tiempo real (speech-to-text, text-to-speech) necesita respuesta sub-100ms. Un viaje de ida y vuelta a una API añade 200-500ms de latencia que rompe el flujo conversacional
  • Coste: Ejecutar un trabajo de entrenamiento ML con GPU 24/7 en infraestructura cloud cuesta más al mes que el portátil haciendo el entrenamiento. El hardware local ya está pagado

Nuestra respuesta: Electron para el shell, Fastify para la API local, SQLite para persistencia y sync con Cadences cloud para los datos que importan. Cada herramienta es una aplicación standalone que funciona offline, sincroniza cuando se conecta y maneja su carga localmente.

La Arquitectura Compartida: 5 Herramientas, 1 Patrón

Las 5 herramientas siguen el mismo patrón arquitectónico, lo que reduce drásticamente el coste de mantenimiento:

┌─────────────────────────────┐
│       Electron Shell        │  ← Integración SO, tray, notificaciones
│  ┌───────────────────────┐  │
│  │    Fastify HTTP API   │  │  ← localhost:PORT, rutas versionadas
│  │  ┌─────────────────┐  │  │
│  │  │  SQLite (local)  │  │  │  ← better-sqlite3 o sql.js WASM
│  │  └─────────────────┘  │  │
│  │  ┌─────────────────┐  │  │
│  │  │ Cadences Sync   │  │  │  ← WebSocket/REST al cloud
│  │  └─────────────────┘  │  │
│  └───────────────────────┘  │
└─────────────────────────────┘

Este patrón significa que cada herramienta expone la misma interfaz: una API HTTP local que otras herramientas (o Cadences cloud) pueden llamar. El ML Trainer expone /v1/embeddings. El Scraper expone /api/jobs. El IoT Hub expone /api/devices. Mismo patrón, diferente dominio.

Por Qué Fastify (No Express)

Fastify nos da validación de schema, arquitectura de plugins y ~3x el throughput de Express — y en desktop, donde el único cliente es la UI Electron local, ese margen significa que podemos manejar streams WebSocket, subidas de archivos y peticiones de inferencia ML simultáneamente sin bloquear. El sistema de plugins mantiene las rutas de cada herramienta modulares.

Por Qué SQLite (No IndexedDB, No PostgreSQL)

SQLite es el único motor de base de datos que tiene sentido para herramientas desktop local-first:

  • Un solo archivo — sin servidor, sin connection string, sin conflictos de puertos. La base de datos es un archivo junto a la app
  • better-sqlite3 — API síncrona más rápida que las alternativas async para cargas desktop de un solo usuario. Sin callback hell para lecturas simples
  • Modo WAL — lecturas concurrentes durante escrituras. La UI Electron puede leer estado de dispositivos mientras el IoT Hub escribe datos de sensores
  • Backups portables — copia el archivo .sqlite y tienes un backup completo. Sin ceremonias de dump/restore

Herramienta 1: ML Trainer (11.5K Líneas)

El ML Trainer es un servidor API local compatible con OpenAI para entrenar y servir modelos ML. Expone los mismos endpoints que OpenAI — /v1/embeddings, /v1/chat/completions, /v1/classify, /v1/entities, /v1/predict, /v1/similarity — así que cualquier cliente que hable protocolo OpenAI puede usar modelos entrenados localmente sin cambios de código.

Tipos de Entrenamiento

TipoModeloCaso de Uso
EmbeddingsMiniLM-L6-v2Búsqueda semántica, matching de similitud
ClasificadorDistilBERTCategorización de texto, detección de intención
NERBERT-NERReconocimiento de entidades nombradas en texto de dominio
RegresiónCustomPredicción numérica con validación cruzada
Fine-tuning LoRAGPT-2 / TinyLlamaGeneración de texto específica de dominio

Los modelos se entrenan localmente usando @xenova/transformers (el port JS de Hugging Face Transformers) y se registran en Cadences como tipo LOCAL_MODEL. Esto significa que los workflows de Cadences pueden enrutar tareas IA a proveedores cloud (GPT-4, Claude, Gemini) o modelos locales según requisitos de coste, privacidad o rendimiento.

Herramienta 2: WhatsApp Agent (20.4K Líneas)

El WhatsApp Agent es un sistema de automatización basado en Playwright con una capa profunda de stealth que simula comportamiento humano. Es la herramienta más grande con 20.4K líneas, y la mayor parte de esa complejidad viene de una cosa: no ser detectado como bot.

El Módulo humanDelay

El sistema core de anti-detección simula patrones de interacción humana:

  • humanType — escribe mensajes carácter a carácter con delays variables. Cada pulsación tiene un delay con distribución gaussiana (media ~80ms, σ ~30ms) con pausas ocasionales en límites de palabras
  • humanClick — mueve el ratón a lo largo de una curva de Bézier hasta el elemento objetivo antes de clickar. Los parámetros de la curva son aleatorios y la velocidad del movimiento sigue una campana (rápido en medio, lento al inicio/final)
  • readingDelay — calcula un tiempo de lectura realista basado en la longitud del mensaje en palabras por minuto (~200-250 WPM con varianza). Tras recibir un mensaje, el agente lo "lee" antes de responder

Pipeline IA

Los mensajes fluyen a través de un Smart Pipeline con 5 proveedores IA (Cloudflare Workers AI, Gemini, Groq, OpenAI, DeepSeek) con failover automático. Los mensajes de voz se transcriben vía Groq/OpenAI Whisper o un endpoint local de Workers Whisper. Las imágenes se analizan vía Gemini/OpenAI Vision o modelos LLaVA locales.

Herramienta 3: Scraper (25.3K Líneas)

El Scraper es un motor de extracción de datos multipropósito con 7 tipos especializados. Con 25.3K líneas es la herramienta más grande — y la más arquitectónicamente diversa, porque cada tipo de scraper maneja fuentes de datos fundamentalmente diferentes.

Los 7 Tipos de Scraper

TipoLíneasObjetivoCaracterística Clave
Inmobiliario1.256Portales de anuncios de propiedadesAnti-detección con delays gaussianos + evasión anti-bot
Freelance~2.8009+ plataformas de empleo (4 regiones)Normalización cross-platform de ofertas
Documentos~1.200PDF, Excel, CSV, Word, TXT, JSONDetección de formato + extracción de contenido
FileSystem~900Escaneo de disco localDedup MD5/SHA256 entre unidades
ML Pipeline~600Puente TensorFlow.jsAlimenta datos de entrenamiento al ML Trainer
Legal~1.5005 fuentes de boletines oficialesMonitorización de licitaciones y normativa
API~800Endpoints REST/GraphQLPolling genérico de API con reglas de transformación

El scraper inmobiliario implementa anti-detección con delays de petición con distribución gaussiana (no aleatorio uniforme, que es señal de detección), técnicas de evasión anti-bot y paginación inteligente que detecta la página inicial desde cualquier URL. El scraper freelance normaliza ofertas de empleo de 9+ plataformas de Europa, EEUU y LatAm en un formato unificado para que Cadences las procese.

Sistemas Complementarios

  • AI File Analyzer (735 líneas) — clasifica y resume documentos extraídos usando IA
  • Cross-Disk Sync (700 líneas) — sincroniza datos extraídos entre múltiples ubicaciones de almacenamiento
  • Remote Worker — consulta Cadences por trabajos de scraping, habilitando ejecución local disparada desde el cloud

Herramienta 4: IoT Hub (8.4K Líneas)

El IoT Hub gestiona dispositivos físicos a través de 3 protocolos core — MQTT v5.1, SerialPort v12 y HTTP polling — con 14 tipos de protocolo adicionales definidos en la arquitectura (CoAP, Modbus, Zigbee, Z-Wave, LoRa, BLE, RTSP, ONVIF...).

Registro de Dispositivos

60+ tipos de dispositivos organizados en 10 categorías: Ambiental (temperatura, humedad, calidad del aire), Seguridad (movimiento, puerta/ventana, humo), Energía (enchufes inteligentes, inversores solares), Cámara (cámaras IP con PTZ), Industrial (PLC, caudalímetros) y más. Cada tipo de dispositivo tiene una matriz de capacidades que determina qué protocolos, comandos y formatos de datos soporta.

Motor de Automatización (796 Líneas)

El Motor de Automatización es el cerebro del IoT Hub — un motor de reglas con 6 tipos de trigger:

  • device_state — se dispara cuando cambia un atributo del dispositivo (ej: movimiento detectado)
  • threshold — se dispara cuando un valor numérico cruza un umbral (ej: temperatura > 30°C)
  • schedule — se dispara a intervalos tipo cron
  • sunrise/sunset — se dispara relativo a la posición solar (útil para iluminación exterior y persianas)
  • webhook — se dispara por trigger HTTP externo desde Cadences u otros sistemas
  • scene — dispara múltiples acciones en secuencia (ej: "modo cine" = atenuar luces + cerrar persianas + encender proyector)

Un Camera Manager (619 líneas) maneja control PTZ (pan-tilt-zoom) e integración FFmpeg para grabación de streams. Todo el estado IoT se auto-sincroniza con Cadences cada 5 minutos.

Herramienta 5: AudioHub (7.4K Líneas)

AudioHub hace de puente entre JavaScript y Python para procesamiento de audio en tiempo real. Es la herramienta más pequeña pero resuelve un problema crítico: ejecutar Whisper STT y TTS localmente cuando la latencia cloud es inaceptable.

El Patrón del Puente Python

AudioHub lanza un subproceso Python y se comunica vía IPC JSON-line sobre stdin/stdout. Cada línea es un objeto JSON con un comando y payload:

// Node.js → Python (stdin)
{"cmd": "transcribe", "file": "/tmp/audio.wav", "model": "base"}

// Python → Node.js (stdout)
{"status": "ok", "text": "Hola, ¿cómo estás?", "confidence": 0.94}

El puente incluye detección de capacidades (¿qué paquetes Python están instalados? ¿Está CUDA disponible?) y un timeout de inicio de 10 segundos. Si el proceso Python falla, AudioHub hace fallback a proveedores cloud de STT.

Pipeline de Audio

  • STT: Whisper local (tamaño de modelo configurable: tiny/base/small/medium/large) con detección automática de idioma
  • TTS: Cadena de fallback de 3 proveedores — ElevenLabs (12 presets de voz), OpenAI TTS, luego Edge-TTS/gTTS/pyttsx3 como fallbacks gratuitos
  • Bluetooth: Escaneo de dispositivos para pinganillos y auriculares con monitorización heartbeat para estabilidad de conexión
  • Caché: Caché de archivos de audio basada en MD5 para evitar re-sintetizar texto idéntico

La Capa de Sincronización Cloud

Cada herramienta sincroniza con Cadences cloud, diseñado en torno a un principio: local-first, cloud-eventual. Si se cae internet, cada herramienta sigue funcionando. Cuando vuelve la conectividad, los cambios sincronizan automáticamente.

  • ML Trainer — sincroniza metadatos y métricas de modelos entrenados. Los modelos se quedan en local (demasiado grandes para subir), pero sus capacidades se registran para que Cadences pueda enrutar peticiones de inferencia
  • WhatsApp Agent — sincroniza contactos, conversaciones y membresía de grupos al CRM de Cadences
  • Scraper — envía datos extraídos a tablas de datos de Cadences. El Remote Worker consulta por nuevos trabajos
  • IoT Hub — envía snapshots de estado de dispositivos cada 5 minutos. Recibe actualizaciones de reglas de automatización
  • AudioHub — sincroniza resultados de transcripción y métricas de uso de TTS

Lecciones Tras 73K Líneas de Código Desktop

1. Electron está bien, en serio

La crítica de "Electron es pesado" no aplica para herramientas internas. No las enviamos a millones de usuarios — corren en máquinas conocidas con 16GB+ de RAM. La velocidad de desarrollo usando tecnologías web para la UI, combinada con Node.js para acceso al sistema, supera al desarrollo desktop nativo por 5-10x para nuestro tamaño de equipo.

2. Fastify en desktop está infravalorado

Ejecutar un servidor HTTP completo dentro de una app Electron suena inusual, pero desbloquea patrones potentes: comunicación inter-herramienta, acceso API externo, recepción de webhooks y una separación limpia entre UI y lógica de negocio. Cada herramienta es su propio microservicio que resulta que tiene una UI de escritorio.

3. SQLite elimina una categoría entera de bugs

Sin particiones de red. Sin agotamiento de pool de conexiones. Sin desajustes de mapeo ORM. Las operaciones de base de datos son llamadas a funciones síncronas que o tienen éxito o lanzan excepción. Después de años luchando con límites de conexión de PostgreSQL e invalidación de caché de Redis, SQLite en desktop es refrescantemente simple.

4. El impuesto del stealth es real

Aproximadamente el 30% del código del WhatsApp Agent existe puramente para anti-detección: delays tipo humano, simulación de movimiento de ratón, gestión de huella del navegador, persistencia de sesión. Esto es una carga de mantenimiento que crece con cada actualización de la plataforma. Construye sistemas stealth solo cuando el caso de uso genuinamente lo requiere.

5. Los puentes Python funcionan sorprendentemente bien

IPC JSON-line sobre stdin/stdout es simple, debuggeable y suficientemente rápido para procesamiento de audio. Consideramos gRPC, WebSocket y HTTP para el puente Python — JSON-lines ganó porque no requiere infraestructura: lanza un proceso, escribe líneas, lee líneas. Sin puertos, sin certificados, sin service discovery.

Local-first no es para todo. Pero cuando tu carga necesita privacidad, acceso a hardware o latencia sub-100ms, el cloud es el default equivocado. 73K líneas de código desktop después, hemos aprendido que la mejor arquitectura es la que pone la computación donde los datos y los dispositivos realmente están — y a veces, eso es la propia máquina del usuario.

Etiquetas

Electron Local-First Desktop IoT Privacidad Machine Learning Automatización

Sobre el Autor

Gonzalo Monzón

Gonzalo Monzón

Fundador & Arquitecto Principal

Gonzalo Monzón es Arquitecto de Soluciones Senior e Ingeniero IA con más de 26 años construyendo sistemas críticos en Sanidad, Automatización Industrial e IA empresarial. Fundador de Cadences Lab, está especializado en conectar infraestructura legacy con tecnología de vanguardia.

Mantente al día

Recibe notificaciones cuando publiquemos nuevos artículos sobre automatización IA, casos de uso y guías prácticas.