Skip to content

Arquitectura

Ongrid es un sistema de cuatro capas. El edge corre en cada host monitorizado; el manager es la nube. Se comunican sobre un único túnel saliente (el control plane) más una ruta de subida directa auth-gated separada para la telemetría (el data plane).

El modelo de 4 capas

text
┌──────────────────────────────────────────────────────────────────┐
│ L4  Alert / notification                                         │
│      built-in rules + custom kinds → channels (Slack / TG / IM)  │
├──────────────────────────────────────────────────────────────────┤
│ L3  Intelligence (graph-kernel ReAct agent)                      │
│      coordinator → specialist sub-agents → ~30 skills            │
├──────────────────────────────────────────────────────────────────┤
│ L2  Observability triad + edge direct path                       │
│      Prometheus · Loki · Tempo  +  push_host_metrics RPC         │
├──────────────────────────────────────────────────────────────────┤
│ L1  Cluster (signal collection)                                  │
│      ongrid-edge + plugins on every host                         │
└──────────────────────────────────────────────────────────────────┘
  • L1 — Cluster. De dónde vienen las señales. Un ongrid-edge por host, más sus subprocesos de plugin (promtail, node_exporter, process_exporter, otelcol-contrib).
  • L2 — Trío de observabilidad. Prometheus / Loki / Tempo almacenan las señales. Se entregan en el docker-compose; la misma UI funciona contra equivalentes gestionados externos (Grafana Cloud, Mimir, VictoriaMetrics) si los cambias desde Settings. Una ruta directa del edge separada transporta push_host_metrics por el túnel para métricas de host de conjunto cerrado y baja cardinalidad (es lo que alimenta las alertas integradas de CPU / mem / disco / load incluso antes de configurar Prom).
  • L3 — Inteligencia. El agente ReAct con kernel de grafo: el coordinator descompone la pregunta, despacha a sub-agentes specialist, llama a skills, sintetiza una respuesta.
  • L4 — Alertas y notificaciones. Reglas integradas + kinds customizados se evalúan contra los streams de L2 / L1, disparan hacia canales (Slack, Telegram, Larksuite, DingTalk, WeCom, webhook crudo).

La apuesta estratégica es L1 + ruta directa del edge en L2: un único tarball, un túnel saliente, métricas de host fluyendo sin un round-trip por Prom. Eso es lo que hace que la instalación de 10 minutos sean realmente 10 minutos.

Edge → frontier → manager

text
   host (yours)                  cloud (yours, self-hosted)
 ┌──────────────────┐
 │ ongrid-edge      │
 │ ├─ plugins/      │           ┌─────────────────────────────┐
 │ │  promtail      │           │ frontier (broker, port 40012)│
 │ │  node_exporter │           │  · multiplexed geminio       │
 │ │  process_exp.  │── one ───▶│  · auth: access/secret key   │
 │ │  otelcol       │  outbound │  · service-end → manager     │
 │ └─ runtime       │   TCP     └──────────────┬──────────────┘
 │    geminio client│   :40012                 │ service-end (40011)
 └──────────────────┘                          ▼
                                 ┌─────────────────────────────┐
                                 │ ongrid (manager)            │
                                 │  · http API (nginx 443)     │
                                 │  · bounded contexts         │
                                 │  · agent runtime            │
                                 └──────────────┬──────────────┘


                                       Prom / Loki / Tempo /
                                       MySQL / Qdrant /
                                       SearXNG / Grafana
  • Una conexión TCP por host. ongrid-edge marca a frontier:40012 hacia fuera. Nada entrante. Sin port-forward, sin jumpbox, sin SaaS de reverse-tunnel.
  • Multiplex geminio. Muchos streams RPC lógicos viajan sobre una única conexión TCP. Bidireccional: el manager puede llamar al edge (bash, host_probe_*, query_processes) y el edge puede llamar al manager (push_host_metrics, report_register).
  • Frontier es el broker. Upstream singchia/frontier, incluido en el tarball de release. ADR-007 es la justificación (no reimplementamos lo que un broker externo ya hace).
  • El manager es un único binario Go. Una decena de bounded contexts (edges, alerts, incidents, agent, knowledge, channels, identity, audit…), todos detrás del front-door de nginx.

Data plane vs. control plane

Los edges tienen dos rutas de egreso distintas hacia la nube, por diseño:

text
 ┌──────── ongrid-edge ────────┐
 │                              │
 │  ┌──────── runtime ────────┐ │     ── control plane ──▶ frontier:40012
 │  │ geminio client (RPC)    │ │       (TLS-by-default if cert provided)
 │  └─────────────────────────┘ │       multiplex, bidirectional, low rate
 │                              │
 │  ┌──────── plugins ────────┐ │     ── data plane ──▶ nginx :443
 │  │ promtail   → Loki push  │ │       https POST per batch
 │  │ otelcol    → OTLP push  │ │       auth_request → manager edgeauth
 │  │ exporters  → /metrics   │ │       high rate, large payloads
 │  └─────────────────────────┘ │
 │                              │
 └──────────────────────────────┘

¿Por qué la separación? ADR-014. Logs + trazas son de alto volumen, batch grande, naturalmente HTTP. Multiplexarlos sobre el túnel mata el throughput bajo carga. El push directo sobre nginx auth_request mantiene la postura de seguridad (solo los edges enrolados pueden empujar) y mantiene el data plane rápido.

¿Qué sigue viajando por el túnel?

  • Métricas — actualmente siguen siendo RPC push_host_metrics sobre geminio. remote_write directo del edge a Prometheus está en la roadmap cuando los tamaños de cluster lo justifiquen. Hasta entonces el volumen de métricas es tolerable.
  • Todos los RPCs — query_processes, bash, query_logs_tail, host_probe_*, expand_topology, lecturas de archivo, WebShell.

Mapa de contenedores (docker-compose)

Lo que sudo ./install.sh levanta en el host del manager:

ContenedorImagenPuertos del hostRol
ongridongrid:<version>9100 (métricas)El manager. Binario Go; API HTTP en :8080 proxiada por nginx.
ongrid-nginxongrid-web:<version>443, 80Terminator TLS + SPA + reverse proxy. Sirve /api/*, /grafana/*, /install.sh, /edge/*.
ongrid-mysqlmysql:8.03306Todo el estado operacional (edges, alertas, usuarios, audit log, configs de canal).
ongrid-frontiersingchia/frontier:1.2.540012Broker geminio. Los edges marcan al 40012; el manager marca al 40011 sobre la red del compose.
ongrid-prometheusprom/prometheus:v2.54.0(none)TSDB. Recibe remote_write desde el manager, recibe queries del skill query_promql.
ongrid-lokigrafana/loki:3.4.0(none)Backend de logs. Push en /loki/api/v1/push vía auth-gate de nginx.
ongrid-tempografana/tempo:2.5.0(none)Backend de trazas. OTLP push en /v1/traces vía auth-gate de nginx.
ongrid-grafanagrafana/grafana-oss:11.1.43000Dashboards. Embebidos como iframes bajo /grafana/ en la SPA.
ongrid-qdrantqdrant/qdrant(none)Vector store para la base de conocimiento.
ongrid-searxngsearxng/searxng(none)Meta-búsqueda autoalojada para el skill web_search.

Todos los servicios stateful hacen bind-mount a rutas del host bajo /var/lib/ongrid/, no a volúmenes named de docker — los operadores pueden hacer backup e inspeccionar archivos sin gimnasia de docker. Sobrescribe la raíz de datos con ONGRID_DATA_DIR y la raíz de logs con ONGRID_LOG_DIR.

Manager — bounded contexts

El binario del manager es un único proceso Go. Internamente está dividido en bounded contexts (cada uno posee su propio esquema de DB, sus propias rutas HTTP, sus propios workers en background). A grandes rasgos:

text
┌─────────┐   ┌─────────┐   ┌─────────────┐   ┌──────────────┐
│ identity│   │ edge    │   │ alert       │   │ incident     │
│ ┌─────┐ │   │ ┌─────┐ │   │ ┌─────────┐ │   │ ┌──────────┐ │
│ │users│ │   │ │edges│ │   │ │rules    │ │   │ │incidents │ │
│ │roles│ │   │ │tnls │ │   │ │events   │ │   │ │timeline  │ │
│ │JWT  │ │   │ │keys │ │   │ │evaluator│ │   │ │investig. │ │
│ └─────┘ │   │ └─────┘ │   │ └─────────┘ │   │ └──────────┘ │
└─────────┘   └─────────┘   └─────────────┘   └──────────────┘

┌─────────┐   ┌──────────┐   ┌─────────┐   ┌──────────┐   ┌─────────┐
│ agent   │   │ knowledge│   │ channels│   │ audit    │   │ skills  │
│ kernel  │   │ vault+   │   │ slack/  │   │ events / │   │ registry│
│ + sub-  │   │ embed+   │   │ telegram│   │ chains   │   │ marketp.│
│ agents  │   │ search   │   │ /lark/dt│   │          │   │         │
└─────────┘   └──────────┘   └─────────┘   └──────────┘   └─────────┘

El tráfico inter-BC va sobre llamadas a función Go (capa biz) — no hay RPC dentro del binario. El arch lint (make arch-lint) refuerza la dirección de dependencia (cmd → service → biz → data, sin ciclos).

Edge — runtime de plugins

ongrid-edge es un único binario Go que supervisa una pequeña flota de subprocesos. Cada subproceso es un collector estándar reskinned como "plugin":

text
ongrid-edge (PID 1)
 ├─ geminio runtime  ← control plane to frontier
 ├─ plugin: logs
 │   └─ promtail subprocess
 │       config: /etc/ongrid-edge/promtail.yaml
 │       data plane: https://<manager>/loki/api/v1/push
 ├─ plugin: traces
 │   └─ otelcol-contrib subprocess
 │       config: /etc/ongrid-edge/otelcol.yaml
 │       data plane: https://<manager>/v1/traces
 ├─ plugin: hostmetrics
 │   └─ node_exporter subprocess
 │       /metrics scraped by Prometheus inside the manager
 └─ plugin: procmetrics
     └─ process_exporter subprocess

ADR-015 es la justificación: cada collector es best-of-breed en su propio ecosistema; reinventarlos como libs de Go es desesperanzador. Así que el edge posee el contrato de runtime (entrega de config, healthcheck, captura de logs, upgrade) y delega el trabajo real con los datos a esos subprocesos.

Upgrades — stage-then-swap

ADR-024 gobierna los upgrades de bundle completo. El flujo:

  1. El operador deja edge-bundle-<arch>-<ver>.tar.gz + .sha256 en /opt/ongrid/edge/ en el manager (el install.sh y upgrade.sh del tarball de release lo hacen).
  2. El operador dispara "upgrade all edges" desde la UI. El manager envía MethodFetchPackage por el túnel.
  3. El edge descarga el bundle, verifica el sha256, prepara los archivos en /var/lib/ongrid-edge/.upgrade/incoming/.
  4. El edge escribe un marker y sale limpio. systemd lo reinicia.
  5. Al reinicio, apply-pending-upgrade.sh corre como root (vía ExecStartPre con +) — verifica el sha de cada archivo, hace backup de <dest> a <dest>.previous, mueve atómicamente el archivo nuevo en su lugar con mv.
  6. Si el nuevo agente no escribe un healthy_marker antes del próximo reinicio, apply-pending-upgrade.sh revierte cada .previous automáticamente.

Por eso un upgrade de edge es solo "reinicia la unit" — sin recableado frágil dentro del proceso.

Dónde viven las cosas en disco

En el manager:

RutaQué
/opt/ongrid/Archivo compose, configs, certs, .env, artefactos de edge.
/opt/ongrid/.envSecretos + tunables (modo 0600).
/opt/ongrid/certs/tls.crt, tls.key para nginx. Sustitúyelos en prod.
/opt/ongrid/edge/Bundles de upgrade de edge + binarios sueltos por arch.
/var/lib/ongrid/Raíz de bind-mount para contenedores stateful.
/var/log/ongrid/Archivos de log del manager y nginx.

En el edge:

RutaQué
/usr/local/bin/ongrid-edgeEl binario del agente.
/usr/local/lib/ongrid-edge/Binarios de plugin + apply-pending-upgrade.sh.
/etc/ongrid-edge/ongrid-edge.env (access/secret keys), configs de plugin.
/var/lib/ongrid-edge/.upgrade/Incoming de upgrade preparado + markers.
/var/log/ongrid-edge/Captura de stdout/stderr de plugins. El journal posee los logs del agente.
/etc/systemd/system/ongrid-edge.serviceUnit de systemd.

Dónde leer más