Cada servicio de tu router OpenWrt, detrás de un interruptor que
puede pulsar tu familia.
NetGrip es un panel companion que corre en el propio router, junto a LuCI:
WireGuard, WiFi de invitados, QoS, DNS y más, un clic cada uno, con
snapshot, comprobación de salud y rollback automático si algo sale mal.
## Míralo antes de instalarlo
- **[demo.netgrip.cloudless.club](https://demo.netgrip.cloudless.club)** corre
el panel real con datos de ejemplo: pulsa cada interruptor, abre cada
tarjeta, no se aplica nada en ningún sitio.
- **[netgrip.cloudless.club](https://netgrip.cloudless.club)** lista
**todas y cada una de las funcionalidades**, pantalla a pantalla, con el
razonamiento detrás de cada una.
## ¿Por qué NetGrip?
Seguía entregándoles routers OpenWrt a personas que solo quieren WiFi y una
VPN que funcionen. LuCI es una herramienta para ingenieros: cada toggle te
pide una sección, una interfaz y una opción. Los portales del fabricante son
más amables, pero son apps cerradas que solo funcionan en su firmware. Yo
quería la máquina intermedia: el router de verdad, detrás de botones que
pueda pulsar un familiar. NetGrip toma los servicios que LuCI ya expone vía
rpcd y les pone un interruptor delante. Lo pulsas, y NetGrip hace snapshot
de la config, aplica el cambio, espera, comprueba que el servicio levantó, y
se deshace solo si no fue así.
Cuatro reglas lo dan forma:
- **Vive en el router.** Un único binario Go estático con la UI embebida
(12-15 MB en disco según arquitectura, y ~16-18 MB de RAM en uso,
medidos en routers reales), empaquetado como `.apk`/`.ipk` real de OpenWrt
que sobrevive al sysupgrade. Sin contenedores, sin caja extra, sin Node en
un router y sin LuCI: sirve su propia UI y habla directo con rpcd/ubus de
OpenWrt, siendo `ethtool-full` su única dependencia extra.
- **Seguro por construcción.** Cada cambio pasa por un executor con
allowlist, con snapshot de config y rollback automático. El panel no puede
salirse del camino trazado.
- **Se adapta al dispositivo.** El mismo binario lee la caja donde corre y
muestra solo lo que aplica allí: un router, un punto de acceso o un switch
gestionado reciben cada uno las pantallas que tienen sentido, y lo que
necesita algo que esa caja no tiene (una radio, un uplink, un paquete)
simplemente se aparta.
- **Sin cuentas nuevas.** El login se valida contra rpcd, la misma sesión
que usa LuCI; un admin por router, nada extra que administrar. AGPL-3.0,
sin premium de ningún tipo.
## Qué te llevas
**Un resumen que responde a "¿está todo bien?"** Salud del sistema, estado
WAN y tráfico en vivo en una sola pantalla, más un chasis ethernet con
estado por puerto, nombres de dispositivo y detección de switches no
gestionados. Es la captura de arriba.
**Clientes que se pueden gestionar de verdad.** Cada estación con velocidad
y señal, reservas de IP en un clic, bloqueo y límites de ancho de banda por
dispositivo.
**Una tarjeta por servicio, cada una con su interruptor real.** WireGuard
(peers con código QR) y OpenVPN (`.ovpn` listo para descargar), DNS con
protección rebind, SQM basado en cake con nota de bufferbloat, DDNS, WiFi
de invitados e IoT en subredes aisladas, reenvío de puertos y un conmutador
router/AP que mueve el rol WAN sin editar `network` y `firewall` a mano.
**Cuidado del sistema sin ceremonia.** Acceso y sesión, tarjetas de
seguridad, asistente de primer arranque, auto-actualización (el panel se
actualiza a sí mismo en el sitio, sin reinstalar), actualizaciones de
firmware vía owut/ASU que reconstruyen la imagen con tus paquetes dentro, y
la malla de roaming dibujada como grafo radial vía usteer.
**Y más:** análisis de tráfico avanzado por aplicación (netifyd) con
timeline de 24 h, la entrada opcional `luci-app-netgrip` bajo LuCI >
Servicios, e interfaz ES/EN que cambia en un clic. Y esto todavía no es
todo: **[en la web están todas las
funcionalidades](https://netgrip.cloudless.club)**, cada una con sus
capturas a tamaño real.
**Agente de NetPulse integrado.** El mismo binario reporta métricas, eventos
WiFi y clientes a [NetPulse](https://netpulse.cloudless.club): el router
aparece etiquetado como NetGrip en su flota sin instalar nada más. Es una
capacidad adicional, no un requisito: si no usas NetPulse, nada cambia.
**MQTT y Home Assistant, si los quieres.** Opcional y desactivado por defecto:
apúntalo a un broker MQTT y Home Assistant descubre el router solo. Publica el
estado en `netgrip//state` y crea **23 entidades**: 17 sensores (CPU,
núcleo más cargado, temperatura, memoria, uptime, modo, versión, clientes
totales/WiFi/cable/débil, señal WiFi mínima y media, y tráfico de entrada y
salida de la red local), 4 interruptores (WiFi de invitados, banIP, IPv6 y SQM)
y 2 botones (recargar los feeds de banIP y reiniciar). Los interruptores y
botones actúan: Home Assistant manda la orden por `netgrip//command/...`
y el router contesta en `netgrip//result`.
Se configura desde la tarjeta MQTT de la página Sistema, o escribiendo
`/etc/netgrip/mqtt.env` (permisos 600). La tarjeta prueba la conexión antes
de guardar, y un servidor de NetPulse puede enviarle la configuración del
broker al router (aplicada con el mismo snapshot-y-rollback que cualquier
otro cambio). Con TLS: el certificado se verifica contra las raíces del
sistema, con una casilla para aceptar uno autofirmado. No se
publica nada hasta que lo actives.
**Asistentes de IA (MCP), si los quieres.** NetGrip puede exponer un
endpoint local de [MCP](https://modelcontextprotocol.io/) para que un
asistente de IA consulte el estado del router en su propio vocabulario.
Está desactivado por defecto y la primera tanda de tools es de solo
lectura:
1. Actívalo y pon un token:
`uci set netgrip.mcp='mcp'; uci set netgrip.mcp.enabled=1; uci set netgrip.mcp.token=; uci commit netgrip`
(el primer comando crea la sección; un `uci set netgrip.mcp.enabled=1`
directo en una instalación nueva falla con "Invalid argument").
2. Apunta tu cliente MCP a `http://:8080/mcp` (o el puerto que use
el panel) y envía el token como `Authorization: Bearer `.
El endpoint nunca acepta la cookie de sesión del navegador, responde 404
mientras está desactivado y tiene rate limit por IP (60 peticiones/minuto
por defecto, ajustable con `uci set netgrip.mcp.rate_per_min=`). Los
tools (`server_status`, `list_clients`, `wireless_status`, `wan_status`,
`multiwan_status`, `list_leases`, `list_devices`) leen las mismas sondas y
cachés que el panel, y cada llamada queda en el log.
## Lista de funcionalidades
| Área | Qué te llevas |
| --- | --- |
| Resumen | Salud del sistema, estado WAN, gráfica de tráfico en vivo, chasis ethernet por puerto, detección de switch no gestionado |
| Clientes | Velocidad y señal por estación, IPs reservadas, bloqueo, límite de ancho de banda por dispositivo |
| LAN y DHCP | Reservas, rango, tiempo de concesión |
| IPv6 | Servidor / relay, DNS anunciado |
| Dual WAN | Políticas y reparto de mwan3 |
| Modo router / AP | Mover el rol WAN sin editar network y firewall a mano |
| WiFi | SSID, WPA2/WPA3, canal y ancho, potencia, separar o unificar bandas, red oculta |
| WiFi invitados e IoT | Subredes aisladas, un interruptor cada una |
| Malla / roaming | Grafo radial, 802.11r/k/v vía usteer |
| VPN | WireGuard (servidor + cliente, peers con QR) y OpenVPN (servidor + cliente, descarga .ovpn) |
| DNS | Protección contra rebinding |
| QoS | SQM/cake con nota de bufferbloat |
| DDNS | Actualización DNS dinámica |
| Redirección de puertos | Redirects DNAT |
| UPnP | miniupnpd / NAT-PMP |
| Firewall | Reglas de tráfico y políticas de zona |
| Control parental | Horarios de internet por dispositivo |
| Tráfico | Análisis por aplicación (netifyd), línea de 24 h |
| Historial de uso | Totales diarios y mensuales (vnstat + nlbwmon) |
| Protección | Feeds de banIP y detección de AdGuard Home |
| Almacenamiento USB | SMB (ksmbd o samba) + DLNA |
| Asistente inicial | Configuración guiada |
| Auto-actualización | Actualización en sitio con firma del binario verificada |
| Firmware | Reconstrucción owut/ASU con tus paquetes dentro |
| Seguridad | Snapshots de config, rollback automático, temporizador de confirmación anti-bloqueo |
| Copia de seguridad | Descargar y restaurar la config |
| Administración | Acceso, sesión, seguridad, contraseña, reinicio programado |
| Software | Lista de paquetes vía apk/opkg |
| Logs | Log del sistema |
| MQTT / Home Assistant | Discovery + 23 entidades (sensores, interruptores, botones) |
| Agente de NetPulse | Telemetría de flota integrada, opcional |
| LuCI | No lo necesita; entrada opcional luci-app-netgrip |
| MCP | Endpoint con token Bearer y tools de solo lectura para agentes |
| Idioma | ES / EN |
## Ponlo en tu router
Requisitos: un target amd64, ARM64, ARMv5/6/7, MIPS little-endian o MIPS
big-endian compatible, con OpenWrt 24.10 o 25.12. El instalador consulta al
gestor de paquetes la arquitectura exacta del router y elige el paquete y ABI
Go genérico correspondientes. El panel escucha en el puerto 8090 y entra con
tus credenciales de LuCI.
SSH al router y ejecuta:
```sh
wget -qO- https://raw.githubusercontent.com/gnacho/netgrip/main/install.sh | sh
```
El script elige el paquete correcto para la arquitectura y el gestor del
router (`apk` en OpenWrt 25.12+, `opkg` en 24.10), instala la última
release, habilita y arranca el servicio, e imprime la URL del panel.
¿Prefieres revisarlo antes? Lee [install.sh](install.sh). Para fijar
versión: `NETGRIP_VERSION=vX.Y.Z sh install.sh`.
Abre `http://:8090` y entra con el mismo usuario y contraseña
que usas en LuCI.
Instalación manual (paquetes)
Descarga el paquete correcto desde la página de
[releases](https://github.com/gnacho/netgrip/releases/latest) y, en el
router:
```sh
# OpenWrt 25.12 (apk; el sufijo es la arquitectura genérica del binario)
apk add netgrip--r1-arm64.apk
# OpenWrt 24.10 (ipk; el sufijo es la arquitectura exacta del paquete)
opkg install netgrip_-1_aarch64_cortex-a53.ipk
/etc/init.d/netgrip enable && /etc/init.d/netgrip start
```
El postinst añade el binario, el init y el enlace rc.d a
`/etc/sysupgrade.conf`, así que el panel vuelve tras una actualización de
firmware. El paquete opcional `luci-app-netgrip` incrusta el panel bajo LuCI
> Servicios de la misma forma.
Instalación manual (binario suelto) y build desde fuente
Un binario suelto funciona, pero muere en cada actualización de firmware;
el paquete es el camino recomendado.
```sh
# dropbear de busybox no tiene scp; mejor por pipe:
cat netgrip-linux-arm64 | ssh root@ "cat > /usr/sbin/netgrip && chmod 755 /usr/sbin/netgrip"
/usr/sbin/netgrip -listen 0.0.0.0 -port 8090
```
Compilar desde fuente requiere Go 1.24+ y Node 22+:
```sh
git clone https://github.com/gnacho/netgrip.git
cd netgrip
cd app && npm ci && npm run build && cd ..
VERSION=dev sh deploy/build-binaries.sh
```
Ese comando compila el frontend una vez y genera binarios genéricos para amd64,
ARM64, ARMv5/6/7, mipsle y mips en `dist/`. Puedes pasar uno o más targets,
como `linux-armv7` o `linux-mipsle`, para compilar solo esos binarios.
Referencia para curiosos: flags, servicio y API JSON
Flags (los defaults sirven para casi cualquier router):
| Flag | Default | Descripción |
| --- | --- | --- |
| `-listen` | `0.0.0.0` | Dirección a la que bindear. |
| `-port` | `8090` | Puerto. El firmware GL.iNet sirve su propia web en el 8080, de ahí el default. |
| `-rpcd-url` | `http://127.0.0.1/ubus` | Endpoint JSON-RPC de rpcd para validar el login. |
El timeout de sesión se cambia desde la tarjeta Acceso de la UI
(`options.main.session_timeout` en UCI). El servicio corre con procd:
```sh
/etc/init.d/netgrip status
logread -e netgrip -f
/etc/init.d/netgrip restart
```
Detrás de cada tarjeta hay una API JSON, la misma que usa el frontend:
lecturas por `GET /api/board`, `/api/system`, `/api/wan`, `/api/wifi`,
`/api/lan`, `/api/dns`, `/api/usteer`, `/api/clients`, `/api/netifyd`,
`/api/dpi/apps`, `/api/dpi/timeline` y `/api/nftqos`; escrituras por los
`POST` correspondientes (`/api/wireguard`, `/api/openvpn`, `/api/sqm`,
`/api/guestwifi`, `/api/iotwifi`, `/api/portforward`, `/api/netifyd`,
`/api/nftqos`) con forma
`{ "state": ..., "rolled_back": ..., "status": "applied|rolled_back|failed" }`.
Toda escritura exige cookie de sesión.
## Qué viene
Reciente: integración con Home Assistant sobre MQTT (estado, sensores e
interruptores/botones que actúan, descubiertos automáticamente), siete ABI
Go genéricos por release de amd64 a MIPS, e interfaz bilingüe cuyo idioma
del documento sigue al idioma activo. Lo siguiente: un feed de paquetes propio para que owut/ASU conserve
NetGrip dentro de tu imagen de firmware
([#63](https://github.com/gnacho/netgrip/issues/63)) y mantener la demo
pública al día del panel. Las ideas y reportes en los
[issues](https://github.com/gnacho/netgrip/issues) dirigen qué se construye.
## Desarrollo
Stack: Go (binario estático único) + React 19 + TypeScript + Vite + Tailwind,
embebido con `go:embed`. Sin base de datos externa.
```sh
cd app && npm ci
npm run dev # dev server del frontend (el proxy /api está en vite.config.ts)
go build -o netgrip ./cmd/netgrip
go test ./...
```
En cada tag de release, el CI compila y precomprime el frontend una sola vez,
cross-compila siete binarios para ABI Go genéricos y los distribuye en paquetes
`.apk`/`.ipk` específicos de cada target mediante el SDK de OpenWrt. El resumen
de la release muestra el tamaño de cada binario y del frontend comprimido.
Tamaño del binario (assets de release, con la UI embebida):
| Arquitectura | Tamaño (aprox) |
| --- | ---: |
| arm64 | 12,7 MB |
| amd64 | 13,8 MB |
| armv5 / armv6 | 13,4 MB |
| armv7 | 13,3 MB |
| mips / mipsle | 15,1 MB |
El uso de RAM en ejecución es de ~16-18 MB RSS.
Los 12-15 MB son el daemon autónomo entero, no la UI: el runtime de Go más
el backend (cliente ubus/rpcd, sondas, MQTT, el agente de NetPulse,
self-update) más el frontend embebido. El frontend en sí son ~344 KB gzip;
el resto es lo que hace que NetGrip corra autónomo donde una capa solo-UI
(como LuCI) pesa unos cientos de KB sobre servicios que OpenWrt ya trae.
## Licencia
AGPL-3.0-only. Ver [LICENSE](LICENSE).
Construido por gnacho como proyecto personal self-hosted. Si NetGrip te
sirve, una estrella en GitHub es la forma de dar las gracias; issues y PRs
son bienvenidos.