RUNBOOK — Levantar un nodo Embedda y conectarte (LAN + remoto)
Guía para el dueño del nodo haciéndolo él mismo. Paso a paso, cada comando, cada URL, qué esperás ver, y qué hacer cuando algo falla. Cubre las dos consolas: Atlas ops (
/monitor/) y Atlas Retail (tablero del comerciante).Cada paso está fundado en el código real del repo (script o
archivo:símbolo). Donde algo NO está automatizado, o es de la plataforma alojada y no de tu nodo, está marcado honestamente. No inventamos comandos ni puertos.
Leyenda de honestidad
| Etiqueta | Significa |
|---|---|
| SHIPPED | Está en el código de este repo y corre en tu nodo. |
| MANUAL | No hay automatización: hacés el paso a mano (con la evidencia de por qué). |
| APPLIANCE | Sólo aplica al camino SD/Raspberry Pi (Caddy :443), no al instalador de PC. |
| PLATAFORMA | Vive en la plataforma alojada (app.embedda.com), no en tu nodo self-host. |
| ROADMAP | Documentado/parcial; todavía no lo levanta ningún script de este repo. |
0 · Modelo mental (leé esto primero, 60 segundos)
Un nodo = una PC (o una Raspberry Pi) que corre dos procesos:
node-api— el backend. Escucha en:9097(todas las/api/v1/*). Fuente:tools/install-node.sh:38(APIPORT=9097).- El server web de Atlas — sirve la consola y proxya
/api/*alnode-apicon el token. Escucha en:5177. Fuente:tools/install-node.sh:38(WEBPORT=5177) +tools/atlas_preview_server.py:210-214.
Sobre eso, dos consolas salen del mismo nodo:
- Atlas ops →
/monitor/— la consola de operaciones (mundo, enjambre, cámaras, cerebro, misiones). - Consola de comercio → el Hub en
/apps/te lleva a la app del comerciante.
Y dos formas de entrar:
- LAN (misma red):
http://<ip-del-nodo>:5177/…(HTTP plano, sin cert). - Remoto (otra red / celular):
https://<tu-nodo>.analytics.embedda.com/…a través del relay ciego — un VPS que reenvía bytes cifrados sin verlos. Fuente:deploy/secure-relay/provision-relay.sh(nginxssl_preread, no termina TLS).
Invariante de comunicación remota (memoria del proyecto, confirmado en código): toda comm remota (datos + video) va nodo ↔ monitor por el relay ciego. Nunca nodo-remoto ↔ nodo-remoto. Intra-enjambre (misma LAN) usa el bus Zenoh, no el relay.
Aviso sobre "Atlas Retail" (leé antes de empezar). En
main(este repo), tantoembedda-retail("Minorista",/apps/embedda-retail/) comoatlas-retail("Atlas Retail", las 7 pantallas Resumen/Embudo/Tienda/Mostrador/ Cámaras/Cadena/Config, en/apps/atlas-retail/) se instalan y se sirven en un nodo actualizado: el instalador copiaapps/atlas-retail(install-node.sh:141) y el Caddy del nodo tienehandle_path /apps/atlas-retail/*(deploy/caddy/Caddyfile:371-375). Así, en el nodo servido por Caddy, Atlas Retail convive con Atlas ops (/monitor/). Salvedad honesta: el preview server de PC (tools/atlas_preview_server.py,:5177) mantiene un allowlistVERTICAL_APPSque todavía no incluyeatlas-retail(atlas_preview_server.py:28) → ahí/apps/atlas-retail/da 404 aunque los archivos se hayan copiado. Detalle y evidencia en §3.2, §3.4 y §5.
1 · Levantar el nodo
Opción A — PC / x86 (recomendada para vos) SHIPPED
El instalador tools/install-node.sh deja tu PC como nodo. Es user-level (sin root), idempotente, y arranca los servicios al iniciar sesión. Fuente/encabezado: tools/install-node.sh:1-24.
1.A.1 · Requisitos
- Linux con systemd de usuario (los servicios se instalan como
systemctl --user). Fuente:install-node.sh:207-255. python3(sirve la consola). Fuente:install-node.sh:163(command -v python3 || fail).- El binario
node-apiya compilado en modo release. El instalador no compila: espera el binario enapps/nexus-node-api/target/release/nexus-node-api. Fuente:install-node.sh:60+install-node.sh:161. - Opcional:
zeroconf(para que<nombre>.localresuelva) ynvidia-smi(autodetección de GPU). Fuentes:install-node.sh:204-205,install-node.sh:189.
Compilá el binario primero (una sola vez; el instalador te lo pide textual si falta):
cd /ruta/al/repo/node_nexus_v1
cargo build --release --manifest-path apps/nexus-node-api/Cargo.toml
Qué deberías ver: al final, el archivo
apps/nexus-node-api/target/release/nexus-node-apiexiste y es ejecutable. Si no,install-node.shaborta con "falta el binario node-api. Buildealo: …" (install-node.sh:161).
1.A.2 · Correr el instalador
Corriendo desde el repo (dev) el árbol no está firmado, así que hay que pasar --allow-unsigned (fail-closed: sin eso, aborta). Fuente: install-node.sh:97-103.
cd /ruta/al/repo/node_nexus_v1
bash tools/install-node.sh --allow-unsigned # nodo "embedda" → embedda.local
# o con nombre propio:
bash tools/install-node.sh --name micasa --allow-unsigned # → micasa.local
Qué hace, exactamente (todo en $HOME/.local/share/embedda, install-node.sh:37):
- Copia el binario a
~/.local/share/embedda/bin/nexus-node-api. (:171) - Copia la consola Atlas (
apps/nexus-atlas) y el server (tools/atlas_preview_server.py). (:172-173) - Copia las apps de nodo:
embedda-console,embedda-hub,embedda-retail,atlas-retailyembedda-consorcio(funcióninstall_atlas_companions,install-node.sh:134-145), y arma los.zipde descarga de las verticales. → Nota (corregido enmain): el instalador sí copiaatlas-retailyembedda-console(antes se decía que no). Que se sirvan depende de la capa: el Caddy del nodo publica/apps/atlas-retail/; el preview server de PC todavía no (§3.2/§3.4/§5). - Genera un token admin persistente (32 bytes hex) en
~/.local/share/embedda/etc/admin-token. (install-node.sh:185-186,openssl rand -hex 32). - Detecta acelerador:
gpusi haynvidia-smi, si nocpu(honesto: sólo reporta lo que hay). (install-node.sh:189) - Instala 3 servicios
systemctl --usery los arranca:embedda-node-api,embedda-atlas,embedda-mdns. (install-node.sh:209-255) - Fail-closed: espera ~30 s a que
node-apiresponda enhttp://127.0.0.1:9097/api/v1/node/atlas-descriptor; si no, falla con diagnóstico (qué URL probó, quésystemctl status/journalctlmirar). (install-node.sh:262-274)
Puertos exactos que quedan escuchando:
| Proceso | Puerto | Bind | Fuente |
|---|---|---|---|
node-api (backend, /api/v1/*) | 9097 | 0.0.0.0 | install-node.sh:38,213 (NEXUS_NODE_API_ADDR=0.0.0.0:9097) |
Server de Atlas (consola web + proxy /api) | 5177 | 0.0.0.0 | install-node.sh:38,237 (--host 0.0.0.0 --port 5177) |
mDNS (<nombre>.local) | anuncia :5177 | — | install-node.sh:201 (port=5177) |
Qué deberías ver al terminar (
install-node.sh:297-309):[embedda] ── NODO LISTO ───────────────────────────────────────────────── [embedda] Consola (PC): http://embedda.local:5177/monitor/ (o http://<IP>:5177/monitor/ ) [embedda] App (mobile): URL del nodo → http://<IP>:9097/api/v1 [embedda] 1er uso: Registrar → tu cuenta queda admin → operás.Además, en Linux con
xdg-open, abre solohttp://localhost:5177/monitor/?demo=0(install-node.sh:307-309).
1.A.3 · Arrancar / reiniciar / estado / desinstalar
Los 3 servicios son systemctl --user. Comandos (fundados en install-node.sh:42-45,255):
# estado
systemctl --user status embedda-node-api embedda-atlas embedda-mdns
# logs del backend
journalctl --user -u embedda-node-api -n 60 --no-pager
# reiniciar
systemctl --user restart embedda-node-api embedda-atlas
# refrescar SOLO la consola Atlas (sin tocar backend ni datos)
bash tools/install-node.sh --atlas-only --allow-unsigned # install-node.sh:111-138
# refrescar SOLO el backend (datos intactos; requiere binario release)
bash tools/install-node.sh --node-api-only --allow-unsigned # install-node.sh:146-159
# desinstalar TODO (borra binarios, consola y datos)
bash tools/install-node.sh --uninstall # install-node.sh:48-54
Los servicios tienen
Restart=always(install-node.sh:225,238,248), así que si el proceso muere, systemd lo relevanta.loginctl enable-lingerintenta que arranquen aunque no haya sesión abierta (best-effort,install-node.sh:257).
1.A.4 · ¿Hay un "doctor"?
Parcial / MANUAL. No hay un subcomando doctor en install-node.sh. La verificación fail-closed integrada es el chequeo del paso 1.A.2.7 (curl al atlas-descriptor, install-node.sh:262-274). Para chequear a mano después:
# backend vivo:
curl -s http://127.0.0.1:9097/api/v1/node/atlas-descriptor | head -c 200 ; echo
# consola viva:
curl -sI http://127.0.0.1:5177/monitor/ | head -1
# estado del bootstrap (cuentas / provisión):
curl -s http://127.0.0.1:9097/api/v1/setup/status ; echo
(La última ruta es setup.rs:180-194.)
Opción B — Appliance SD / Raspberry Pi 5 + Hailo APPLIANCE (resumen)
Para el nodo "de fábrica" headless (no una PC), el camino es un bundle turnkey + primer arranque que auto-provisiona. No lo vas a usar vos en una PC, pero acá va el resumen honesto de qué es.
deploy/make-sd-bundle.sharma el bundle arm64 (binariosnode-npu,node-api, etc. + Monitor estático + modelos.hef+ go2rtc/mediamtx + Caddy), y con--rootfs /mntlo inyecta en la SD + instala un oneshot de primer arranque. Fuente:deploy/make-sd-bundle.sh:1-27, 250-281.- El firstboot (
nexus-firstboot.service,make-sd-bundle.sh:268-281) verifica la firma del bundle (fail-closed contradeploy/factory-pubkey.pem), correinstall.shy toca/var/lib/nexus/.provisioned(el marcador quenode-apilee para el KPI time-to-first-incident,setup.rs:42-59). - El appliance sirve con Caddy en
:443(no con el server de Atlas de PC): rutas endeploy/caddy/Caddyfile(ver §3.4). Trae video WS cableado (/ws,/ws/video), cosa que el camino de PC no tiene (§3.4 / §4.5).
Honesto: el archivo que el prompt llamó
deploy/nexus-factory-enroll.shno existe con ese nombre en el repo. El "ZTP / auto-enrol de fábrica" está repartido enmake-sd-bundle.sh(firstboot),deploy/make-appliance.sh/deploy/build-nexus-img.sh(imagen) y, del lado del relay, endeploy/secure-relay/enable-auto-enroll.sh(la preauth). El zero-touch provisioning con identidad de fábrica Ed25519 es la dirección de diseño (memoria "despliegue seguro + masivo") — ROADMAP en cuanto a un único script de enrolamiento de fábrica.
2 · Login / primera cuenta
2.1 · Cómo funciona (SHIPPED)
- El nodo no exige un "código de activación" para crear la primera cuenta. En un nodo fresco (sin cuentas), el gate de bootstrap está abierto: el primer registro queda
admin. Después, el registro se cierra (403) salvo que se habiliteNEXUS_OPEN_SIGNUP(que crea viewers de menor privilegio). Fuente:apps/nexus-node-api/src/domains/session.rs:245-282(comentario + lógicausers_have_secure_admin→Role::Admin; si ya hay admin y open-signup off → "registro cerrado: pedile a un administrador que cree tu cuenta"). - La UI decide "registrar vs entrar" leyendo
GET /api/v1/setup/status({ has_users, bootstrap_open, hostname },setup.rs:180-194). Atlas lo consulta enatlas.js:refreshSetupStatusForAuth(atlas.js:13965-13989) y arma el panel:- Nodo fresco → "Primera vez en el nodo? Registrar" + hint "La 1ra cuenta del nodo queda admin · contraseña 12+ caracteres". (
atlas.js:13949-13957) - Nodo con admin → "Este nodo ya tiene admin. Entrá con tu usuario." (registro cerrado). (
atlas.js:13938-13947)
- Nodo fresco → "Primera vez en el nodo? Registrar" + hint "La 1ra cuenta del nodo queda admin · contraseña 12+ caracteres". (
- Política de contraseña: mínimo 12 caracteres, sin caracteres de control, y rechaza passwords comunes (
password,123456, …). Fuente:apps/nexus-node-api/src/auth.rs:250-274(validate_password_policy).
2.2 · El "token admin" (bootstrap secret): qué es y qué NO es
- El instalador genera un token admin aleatorio en
~/.local/share/embedda/etc/admin-token(install-node.sh:185-186) y se lo pasa alnode-apicomoNEXUS_NODE_API_ADMIN_TOKEN(install-node.sh:214,235-236). - Es un bearer interno servidor↔servidor: quien lo presente vale como
Admin(auth.rs:368-376, ramaEnvAdminToken). El server de Atlas lo inyecta sólo para rutas/api/v1/admin/*; el resto va con el token operador o con el Bearer del usuario logueado. Fuente:atlas_preview_server.py:45-51,175. - No es algo que vos tipees. No hay pantalla que te pida ese código para crear tu cuenta. La primera cuenta se crea con usuario + contraseña y punto. (El "código de activación" que sí existe es el de enrolamiento remoto al relay — la preauth —, ver §4.2. Son cosas distintas.)
2.3 · Paso a paso — crear tu cuenta admin
- Abrí la consola local:
http://localhost:5177/monitor/(ohttp://<IP>:5177/monitor/). - Aparece el panel "Conectá tu percepción" (
atlas.js:renderAuthPanel,:13990).- Campo URL del nodo: dejalo vacío (= este nodo). (
atlas.js:14034, placeholderhttp://192.168.1.28:9097.) - Usuario y Contraseña (12+).
- Campo URL del nodo: dejalo vacío (= este nodo). (
- Como es la 1ra vez, hacé click en Registrar (
atlas.js:14018-14019). → LlamaPOST /api/v1/auth/register(atlas.js:14169), el backend te da roladminy un token → auto-login (session.rs:273-274+atlas.js:14182-14200).
Qué deberías ver: el mensaje "Registrado como admin. Entrando..." (
atlas.js:14203) y la consola carga. Tu sesión queda en memoria del navegador (NODE_TOKEN_KEY = "nexus_token_v1",atlas.js:635,14128).
Para entrar después (ya existe admin): mismo panel, dejás URL vacía, ponés usuario+contraseña, botón Conectar → POST /api/v1/auth/login (atlas.js:14100-14104).
Dónde viven las cuentas: archivo ~/.local/share/embedda/data/users.json (install-node.sh:215, NEXUS_USERS_FILE). El banner del instalador ya te dice si el registro está abierto o cerrado mirando ese archivo (install-node.sh:301-305).
3 · Acceso LOCAL (LAN)
3.1 · Encontrá la IP del nodo
hostname -I | awk '{print $1}' # misma línea que usa el instalador (install-node.sh:275)
Qué deberías ver: una IP tipo
192.168.1.28. Esa es<IP>en las URLs de abajo. Si instalastezeroconf, también podés usarhttp://<nombre>.local:5177/…(install-node.sh:193-205). Si<nombre>.localno resuelve, usá la IP (ver §6).
3.2 · Las URLs de cada superficie (server de Atlas, :5177)
Estas son las rutas exactas que sirve tools/atlas_preview_server.py (el server del camino PC). Todas same-origin → sin CORS.
| Superficie | URL (LAN) | Fuente (atlas_preview_server.py) |
|---|---|---|
| Atlas ops (consola de operaciones) | http://<IP>:5177/monitor/ | :96-101 (/, /monitor, /monitor/ → index.html; /monitor/assets/…) |
| Raíz (alias de Atlas ops) | http://<IP>:5177/ | :96 (/ → Atlas index.html) |
| Hub (lanzador de apps) | http://<IP>:5177/apps/ | :102-108 (redirige 302 → /apps/embedda-hub/) |
Minorista / comercio (embedda-retail) | http://<IP>:5177/apps/embedda-retail/ | :112-121 (allowlist VERTICAL_APPS, :26) |
Consorcio / portería (embedda-consorcio) | http://<IP>:5177/apps/embedda-consorcio/ | :26,112-121 |
Descargas (.zip de las apps) | http://<IP>:5177/apps/downloads/… | :109-111 |
| API del nodo (para la app mobile) | http://<IP>:9097/api/v1 | install-node.sh:300 |
/apps/atlas-retail/y el preview server de PC. El allowlistVERTICAL_APPSdel preview server es exactamente{"embedda-hub", "embedda-retail", "embedda-consorcio"}(atlas_preview_server.py:28) — no incluyeatlas-retail. Por eso unGET /apps/atlas-retail/servido por el preview server de PC (:5177) cae al Hub → 404. Pero el instalador sí copiaapps/atlas-retail(install-node.sh:141) y el Caddy del nodo (:443) sí la sirve en/apps/atlas-retail/(deploy/caddy/Caddyfile:371-375, §3.4), igual que el Caddy del portal alojado (deploy/cloud/atlas/Caddyfile.operative:67-71). Es decir: en un nodo servido por Caddy, Atlas Retail (7 pantallas) está disponible; en el preview server de PC, todavía no (§5).
3.3 · Paso a paso — abrir el Hub, elegir consola, loguear
- Desde un dispositivo en la misma red, abrí
http://<IP>:5177/apps/. → Redirige al Hub (/apps/embedda-hub/). - Qué deberías ver: la portada del Hub con tres tiles (
apps/embedda-hub/index.html:67-82):- Atlas →
href="/monitor/"(index.html:67) - Minorista (Retail) →
href="../embedda-retail/"=/apps/embedda-retail/(index.html:73) - Consorcio →
href="../embedda-consorcio/"=/apps/embedda-consorcio/(index.html:79)
- Atlas →
- Click en Atlas → cae en
/monitor/→ panel de login (§2.3). - Para el comercio, click en Minorista →
/apps/embedda-retail/. - Logueá en cada una con tu usuario admin (la sesión es por-consola en el navegador).
Directo, sin pasar por el Hub: Atlas ops =
http://<IP>:5177/monitor/; Minorista =http://<IP>:5177/apps/embedda-retail/.
3.4 · (APPLIANCE) Rutas del Caddy del nodo :443
Si tu nodo es el appliance (Opción B), el que sirve es Caddy en :443, con rutas distintas al server de PC. Mapa real (deploy/caddy/Caddyfile):
| Ruta | Sirve | Fuente |
|---|---|---|
/ | node-home launcher (index.html) | Caddyfile:193-196 |
/console/ | Embedda Console (/opt/nexus/apps/embedda-console) | Caddyfile:347-354 |
/monitor/ | Atlas ops (estático desde /opt/nexus/apps/monitor) | Caddyfile:395-405 |
/apps/ | Hub (lanzador de apps) | Caddyfile:358-365 |
/apps/embedda-retail/ | Minorista | Caddyfile:366-370 |
/apps/atlas-retail/ | Atlas Retail (7 pantallas) | Caddyfile:371-375 |
/apps/embedda-consorcio/ | Consorcio / portería | Caddyfile:376-380 |
/apps/embedda-hub/ | Hub (ruta directa) | Caddyfile:381-385 |
/manual | este manual (página on-brand) | Caddyfile — ruta agregada, §7 |
/node, /config/devices, /config/space, /world, /field | páginas standalone del launcher | Caddyfile:223-281 |
/api/* | node-api :9097 | Caddyfile (node_api_routes) |
/ws | eventos → productor :9099 | Caddyfile |
/ws/* (incl. /ws/video) | video H.264+KLV → productor :9100 | Caddyfile:451-460 |
/go2rtc/*, /funnel/*, /media-relay/* | companions | Caddyfile:465-508 |
Diferencias que importan (Caddy del nodo vs. preview server de PC):
- El Caddy del nodo sí sirve
/apps/(Hub) y todas las verticales, incluida/apps/atlas-retail/(Caddyfile:358-390). El preview server de PC (atlas_preview_server.py) sirve Hub +embedda-retail/embedda-consorcio, pero noatlas-retail(allowlistVERTICAL_APPS,:28). O sea: la app de las 7 pantallas se ve en el nodo-Caddy, todavía no en el preview de PC.- El Caddy sí cablea el video/eventos WS (
/ws→:9099,/ws/video→:9100); el server de PC no proxya/ws(sólo/api/*y estáticos,atlas_preview_server.py:60-79). → En una PC sin productor, no hay video en vivo por WS (§4.5).- Caddy corre con
admin off(Caddyfile:3): no hay endpoint de admin, así que uncaddy reloadfalla → reiniciá el servicio (§6).
3.5 · Gotchas de LAN (reales)
- HTTP, no HTTPS. En LAN el nodo sirve HTTP plano (
:5177/:9097); no hay cert. Usáhttp://…, nohttps://…. Si la app mobile apuntahttps://a un nodo que sirvehttp, da "error sending request" (secure-relay/RUNBOOK.md:165). - El token que inyecta el proxy (502 sin token). El server de Atlas proxya
/api/*alnode-apicon un Bearer. Si no hay Bearer del usuario ni token configurado, responde502 "node-api token unavailable"(atlas_preview_server.py:176-178). El instalador siempre setea el token (install-node.sh:235-236), así que en tu nodo no debería pasar; si lo ves, revisá queembedda-atlastenga el env (§6). - Android no resuelve
.local. Los celulares Android no resuelven mDNS (<nombre>.local). Desde Android/iPhone usá la IP (http://<IP>:5177/…) o la URL remota HTTPS del relay (§4). (Memoria "edge plug-and-play": "Android no resuelve .local".) - Geolocalización del mapa necesita contexto seguro (HTTPS). En LAN por HTTP el navegador puede bloquear
navigator.geolocation→ serví Atlas por HTTPS (el relay lo da). (secure-relay/RUNBOOK.md:168.)
4 · Acceso REMOTO (por el relay ciego)
Requiere un VPS relay ya provisionado. Provisionarlo (una vez, en el VPS) es
deploy/secure-relay/provision-relay.sh+publish-registry.sh+enable-auto-enroll.sh— verdeploy/secure-relay/RUNBOOK.md §2. Acá asumimos que el relay (relay.embedda.com, wildcard*.analytics.embedda.com) ya existe, y documentamos enrolar tu nodo y entrar desde afuera.
4.1 · Qué es el relay (por qué es "ciego") SHIPPED
provision-relay.shlevanta en el VPS: (1) un hub WireGuard (los nodos marcan saliente hacia el VPS, UDP51820) y (2) nginxstreamconssl_prereadque enruta:443por SNI al nodo por la mesh, sin terminar TLS. El relay nunca descifra: es un caño. Fuente:provision-relay.sh:62-94.node-tls-up.shcorre en tu nodo (root): une el nodo a la mesh (WireGuardnx0) y levanta un Caddy propio que obtiene un cert real Let's Encrypt para tu dominio (TLS-ALPN-01 que entra por el passthrough) y proxya a tu server de Atlas (UPSTREAM=127.0.0.1:5177). La app confía sola → sin pinning. Fuente:node-tls-up.sh:14-15, 88-125.enroll-node.shcorre en el VPS (root): asigna mesh-IP, agrega el peer WG, computa el subdominio y enchufa la ruta SNI (dominio → nodo:8443) en nginx, y da de alta el nodo en el control-plane (INSERT conapi_url = https://<dominio>). Fuente:enroll-node.sh:26-31, 47-83.- El dominio es
<node-id>.<IP-del-VPS-con-guiones>.sslip.iopor defecto, o<node-id>.<RELAY_DOMAIN_BASE>si el relay define un dominio propio (p.ej.analytics.embedda.com). Fuente:enroll-node.sh:26-31.
4.2 · El "código de activación" remoto = la preauth key SHIPPED
El verdadero código de activación del nodo es la preauth que se mintea en el relay (estilo tailscale up --authkey). Se genera en el VPS:
# en el VPS relay (root), deploy/secure-relay/:
sudo ./enable-auto-enroll.sh --mint reusable # una key reutilizable
sudo ./enable-auto-enroll.sh --mint once # una key de un solo uso
sudo ./enable-auto-enroll.sh --mint reusable --tenant <tenant_id> # atada a un cliente
Fuente: enable-auto-enroll.sh:10-12, 26-38. Cada key es openssl rand -hex 24 (enable-auto-enroll.sh:26), guardada en /etc/embedda/preauth.keys (enable-auto-enroll.sh:17). Revocar = borrar su línea de ese archivo (enable-auto-enroll.sh:91). El primer enable-auto-enroll.sh sin args ya imprime una preauth reusable y el comando exacto de instalación (enable-auto-enroll.sh:86-90).
4.3 · Camino automático — enrolar el nodo al instalar SHIPPED
El instalador se auto-vincula al VPS si le pasás EMBEDDA_RELAY + EMBEDDA_PREAUTH. Fuente: install-node.sh:282-295.
cd /ruta/al/repo/node_nexus_v1
EMBEDDA_RELAY=https://relay.embedda.com \
EMBEDDA_PREAUTH=<preauth-key-de-4.2> \
EMBEDDA_NODE_TYPE=monitor \
bash tools/install-node.sh --name <tu-id> --allow-unsigned
Bajo el capó: llama node-tls-up.sh --auto con UPSTREAM=127.0.0.1:5177 (install-node.sh:287-289), que hace POST <relay>/enroll con tu preauth + la WG pubkey del nodo y recibe el bundle (DOMAIN/MESH_IP/RELAY_PUBKEY/…) → levanta WireGuard + Caddy con cert real. Fuente: node-tls-up.sh:37-50.
El túnel necesita root: el instalador eleva con
sudosi hace falta (install-node.sh:286). Si no haysudo, la vinculación falla pero el nodo igual queda vivo en la LAN (aviso honesto,install-node.sh:291-293).Qué deberías ver (
install-node.sh:298/node-tls-up.sh:127-130):Público (VPS): https://<tu-id>.analytics.embedda.com ← cert real, soberano, LAN + remotoEl primer pedido tarda ~10 s (emisión del cert). Verificá:
curl -sI https://<tu-id>.analytics.embedda.com(node-tls-up.sh:130).
4.4 · Camino manual (fallback / debug) SHIPPED
Tres pasos (en el nodo → en el relay → en el nodo). Fuente: secure-relay/RUNBOOK.md:86-101.
# (a) EN EL NODO: generar su identidad WireGuard → imprime la WG pubkey (FASE 1)
sudo deploy/secure-relay/node-tls-up.sh # node-tls-up.sh:52-57
# (b) EN EL RELAY (VPS, root): enrolar → imprime el BUNDLE (DOMAIN/MESH_IP/RELAY_PUBKEY/…)
RELAY_DOMAIN_BASE=analytics.embedda.com \
sudo ./enroll-node.sh <tu-id> <wg-pubkey-del-nodo> # enroll-node.sh:88-99
# (c) EN EL NODO: pegar el bundle en un archivo y aplicarlo → mesh + Caddy con cert real
sudo deploy/secure-relay/node-tls-up.sh --bundle node.bundle # node-tls-up.sh:59-125
Resultado: el nodo queda en https://<tu-id>.analytics.embedda.com (LAN + remoto), cert real Let's Encrypt (sin pinning), y el dato no sale del nodo (secure-relay/RUNBOOK.md:103-105).
4.5 · Paso a paso — entrar desde afuera y abrir ambas consolas
- Desde otra red o el celular con datos (no la LAN del nodo), abrí:
https://<tu-id>.analytics.embedda.com/monitor/→ Atlas ops remoto. (El relay reenvía por SNI → tu Caddy:8443→ server de Atlas:5177→/monitor/.) - Logueá con tu usuario admin (§2.3). El panel es el mismo; dejá URL vacía (estás sirviendo desde tu propio dominio, así que "este nodo" = tu nodo).
- Para el comercio remoto:
https://<tu-id>.analytics.embedda.com/apps/(Hub) → Minorista (/apps/embedda-retail/). Mismas rutas que en LAN, porque el upstream del Caddy remoto es el mismo server de Atlas:5177(node-tls-up.sh:15,107). - Instalar la PWA (opcional): en el navegador (Chrome Android / Safari iPhone), menú → "Agregar a pantalla de inicio". Atlas trae
manifest.webmanifest+ service worker (apps/nexus-atlas/manifest.webmanifest,apps/nexus-atlas/sw.js), así que se instala como app. Sirviéndola por HTTPS (el relay) el SW y la geoloc funcionan.
Multinodo (una consola, varios nodos): desde Atlas ops podés conectarte a otro nodo poniendo su URL + login en el panel (campo URL, placeholder
http://192.168.1.28:9097). Atlas rutea todas las llamadas a ese nodo con su token en memoria. Fuente:atlas.js:14093-14124(state.connectedNode = {apiUrl, token, …}) +atlas.js:9635-9652(switchToNode). Para cross-origin necesitás CORS+CSP (§6).
Nota honesta sobre el VIDEO remoto
- Datos (todas las
/api/v1/*) van bien por el relay: el Caddy remoto proxya al server de Atlas, que proxya alnode-api. Confirmado por el flujonode-tls-up.sh:107→atlas_preview_server.py:166-205. - Video en vivo por WebSocket (
/ws,/ws/video): el server de Atlas de PC NO proxya/ws— sólo/api/*y estáticos (atlas_preview_server.py:60-79). El video WS lo sirven productores aparte (:9099eventos,:9100H.264+KLV), y sólo el Caddy del appliance los cablea (Caddyfile:374-376, 395-402). → En una PC sin productor no hay video en vivo por WS aunque el nodo sea remoto. - Auth del WS de video: en cierre (ROADMAP). Atlas manda el bearer como subprotocolo WS (
nexus.ticket.v1+bearer.<token>,atlas.js:1089-1096), pero no encontré un endpoint de emisión de ticket ennode-api(búsqueda porws/ticket/video/ticket→ 0 hits). El descriptor sólo publica el path (streams.video_ws_path = "/ws/video",node_ops.rsatlas_descriptor). Es decir: el ticketing per-conexión del WS de video está plumbeado en el cliente pero sin emisor dedicado en el backend — coincide con la memoria "WS de video, auth hoy en cierre". No lo presentes como cerrado.
5 · Los dos Atlas: qué es cada uno y cuándo usar cada uno
| Atlas ops | Atlas Retail | |
|---|---|---|
| Ruta | /monitor/ | /apps/atlas-retail/ |
| Qué es | Consola de operaciones completa | Tablero del comerciante |
| Pantallas | mundo / enjambre / cámaras / cerebro / misiones (map-first) | Resumen · Embudo · Tienda · Mostrador · Cámaras · Cadena · Config |
| Para quién | operador / admin del sistema | dueño de comercio |
| Estado en TU nodo (main + installer) | SHIPPED (apps/nexus-atlas, atlas.js) | SHIPPED en el nodo-Caddy (/apps/atlas-retail/); en el preview server de PC, todavía no — ver abajo |
Cómo saltar entre consolas: el Hub (/apps/) es el lanzador con los tiles (embedda-hub/index.html:67-82). Cada consola maneja su propia sesión en el navegador (token en memoria, atlas.js:635). El "handoff con token" automático entre superficies no es un flujo del código de main que haya podido confirmar: en la práctica, logueás en cada consola.
Estado real de "Atlas Retail" en main (evidencia)
Corregido en
main(PR #35). Antes este runbook decía queatlas-retailno estaba enmainni la copiaba el instalador. Eso es falso hoy. La evidencia:
- La app
atlas-retailcon las 7 pantallas (Resumen/Embudo/…/Cadena/Config):- Sí está trackeada en
main:git ls-files | grep atlas-retaildevuelve sus archivos (índice + assets). El título es "Atlas Retail · Tablero del comerciante" (apps/atlas-retail/index.html). - Sí la copia el instalador:
install_atlas_companionscopiaapps/atlas-retaila$PREFIX/apps/atlas-retail(install-node.sh:141), junto aembedda-console,embedda-hub,embedda-retailyembedda-consorcio. - Sí la sirve el Caddy del nodo en
/apps/atlas-retail/*(deploy/caddy/Caddyfile:371-375); y el Caddy del portal alojado también (deploy/cloud/atlas/Caddyfile.operative:67-71+Dockerfile.operative:18). - Salvedad honesta (preview server de PC): el allowlist
VERTICAL_APPSdetools/atlas_preview_server.py:28todavía no incluyeatlas-retail, así que servida por el preview de PC (:5177) da 404 pese a estar copiada. Ampliar ese allowlist es el único paso pendiente para paridad total PC↔Caddy — ROADMAP acotado.
- Sí está trackeada en
- La otra app de comercio,
embedda-retail("Minorista",/apps/embedda-retail/,apps/embedda-retail/index.html→ "Embedda · Minorista"), sigue disponible en las tres capas (preview de PC, Caddy del nodo y portal). Es la vista de comercio más liviana. → Conclusión honesta: en un nodo actualizado servido por Caddy (appliance o portal), Atlas Retail (7 pantallas) ya se sirve en/apps/atlas-retail/, junto a Atlas ops (/monitor/); en la plataforma alojada (app.embedda.com/apps/atlas-retail/) también. El único hueco es el preview server de PC (allowlistVERTICAL_APPS).
Cuál usar para qué: operar el sistema (cámaras, cerebro, enjambre, misiones) → Atlas ops (/monitor/). Que el dueño del comercio mire su negocio → Atlas Retail (/apps/atlas-retail/, 7 pantallas) o Minorista (/apps/embedda-retail/, vista liviana) en tu nodo-Caddy, o cualquiera de las dos en la plataforma alojada.
6 · Troubleshooting (gotchas reales, con la causa y el fix)
| Síntoma | Causa (evidencia) | Fix |
|---|---|---|
502 "node-api token unavailable" al cargar Atlas | El proxy no tenía Bearer del usuario ni token configurado (atlas_preview_server.py:176-178) | Verificá que embedda-atlas tenga NEXUS_NODE_API_TOKEN/ADMIN_TOKEN (install-node.sh:235-236): systemctl --user show embedda-atlas -p Environment. Reiniciá: systemctl --user restart embedda-atlas |
install-node.sh aborta "falta el binario node-api" | El instalador no compila (install-node.sh:161) | cargo build --release --manifest-path apps/nexus-node-api/Cargo.toml, después reintentá |
install-node.sh aborta "este árbol no está firmado" | Corriendo desde el repo (dev) no hay MANIFEST.sha256 (install-node.sh:100-103) | Pasá --allow-unsigned |
| "node-api no respondió en :9097 tras ~30s" | El backend no ligó (install-node.sh:268-274) | Seguí el diagnóstico que imprime: systemctl --user status embedda-node-api + journalctl --user -u embedda-node-api -n 60 |
| La app mobile: "error sending request" | URL https:// contra un nodo que sirve http en LAN (secure-relay/RUNBOOK.md:165) | Usá http://<IP>:9097/api/v1 en LAN, o la URL HTTPS del relay para remoto |
<nombre>.local no abre (sobre todo Android) | Android no resuelve mDNS; falta zeroconf (install-node.sh:204-205) | Usá la IP (http://<IP>:5177/…) o la URL del relay. En Linux: pip3 install --user zeroconf |
Registro da 400 al crear cuenta | Password < 12 caracteres (auth.rs:266-267) | Usá 12+ caracteres |
"registro cerrado" (403) | Ya hay admin y NEXUS_OPEN_SIGNUP off (session.rs:277-282) | Entrá con tu usuario, o que un admin te cree la cuenta |
| Atlas no puede leer de otro nodo (multinodo) | Cross-origin: falta CORS del node-api destino y/o CSP connect-src no incluye ese origen (atlas.js:866 + CSP index.html:7) | El node-api responde preflight CORS (204 antes del gate de auth, main.rs); asegurate de conectar por la URL correcta con token y de que la CSP permita el origen (memoria "atlas-multinode": "CORS Y CSP connect-src", ambas destrabadas en el build vivo) |
| Video en vivo no aparece (incluso remoto) | El server de PC no proxya /ws; hace falta productor + Caddy appliance (atlas_preview_server.py:60-79 vs Caddyfile:374-376,395-402) | En PC no hay video WS; usá el appliance (Caddy :443) con productor, o mirá sólo datos /api |
| Cliente remoto da HTTP 000 / TLS al toque | El cert LE se está emitiendo (ventana ~10-30s) (secure-relay/RUNBOOK.md:163) | Reintentá a los ~10s; curl -sI https://<dominio> |
caddy reload falla (appliance) | Caddy corre con admin off (Caddyfile:3) | Reiniciá el servicio en vez de recargar (memoria "cerebro panel": Caddy reload falla → restart) |
unknown directive "stream" al provisionar el relay | El módulo stream no viene en el nginx base (provision-relay.sh:33-35) | apt install libnginx-mod-stream (ya lo hace provision-relay.sh) |
| Geoloc rota en Atlas por HTTP | navigator.geolocation exige contexto seguro (secure-relay/RUNBOOK.md:168) | Serví Atlas por HTTPS (el relay) |
7 · Este manual, servido desde el nodo SHIPPED
Este runbook también se publica como página web on-brand dentro del propio nodo, para tenerlo a mano sin salir de la consola. La página es documentación estática: no ejecuta comandos, no llama a la API y no afirma capacidades por su cuenta — sólo refleja lo que dice este manual (incluida la corrección de Atlas Retail de §5).
- Ruta:
/manual(alias/manual/). - Fuente de la página:
apps/embedda-console/manual.html(autocontenida, sin CDN: todo el CSS va inline y no carga recursos externos, así respeta la misma CSP del portal). - Servida en las tres capas:
- Caddy del nodo (
:443): handlerhandle /manualendeploy/caddy/Caddyfile(raíz/opt/nexus/apps/embedda-console). - Portal alojado: handler
handle @manualendeploy/cloud/atlas/Caddyfile.operative(raíz/srv/console). La página ya viaja en la imagen porqueDockerfile.operativecopiaapps/embedda-consolea/srv/console. - Preview server de PC (
:5177): ruta/manualentools/atlas_preview_server.py(sirveapps/embedda-console/manual.html).
- Caddy del nodo (
- Accesos desde la UI: link "📖 Manual · Levantar un nodo" en el footer y la topbar de la Console (
apps/embedda-console/index.html) y en el Hub (apps/embedda-hub/index.html, junto a "Cómo funciona cada app"). Abren/manualen una pestaña nueva.
Apéndice — Puertos y archivos de un vistazo
| Qué | Valor | Fuente |
|---|---|---|
node-api (backend) | 0.0.0.0:9097 | install-node.sh:38,213 |
| Server de Atlas (PC) | 0.0.0.0:5177 | install-node.sh:38,237 |
| Caddy del appliance | :443 | Caddyfile:4,459-462 |
| Cert real del nodo (remoto) | <id>.analytics.embedda.com (Caddy :8443 tras el relay) | node-tls-up.sh:66,106 |
| Relay: WireGuard hub | UDP 51820 | provision-relay.sh:15,50-56 |
| Relay: passthrough SNI | :443 (nginx ssl_preread) | provision-relay.sh:78-86 |
| Prefijo de instalación (PC) | ~/.local/share/embedda | install-node.sh:37 |
| Cuentas de usuario | ~/.local/share/embedda/data/users.json | install-node.sh:215 |
| Token admin (interno) | ~/.local/share/embedda/etc/admin-token | install-node.sh:185-186 |
| Servicios (PC) | embedda-node-api, embedda-atlas, embedda-mdns (systemctl --user) | install-node.sh:209-255 |
| Preauth remota (activation code) | /etc/embedda/preauth.keys (en el relay) | enable-auto-enroll.sh:17 |
Última verificación honesta: todo lo marcado SHIPPED sale del código de este checkout (
main). Lo marcado PLATAFORMA (app.embedda.com) y el estado en vivo del VPSrelay.embedda.comno se verifican desde este repo (infra externa). El video WS remoto con ticket per-conexión está plumbeado en el cliente pero sin emisor dedicado ennode-api(§4.5): trátalo como en cierre, no cerrado.