- app.md: frontmatter (service, port 8480, systemd unibus-admin.service on magnus),
architecture, capabilities, security, known gaps. uses_functions:
sign_ed25519_go_cybersecurity. e2e_checks (build/vet/web_build/smoke_mock).
- deploy/unibus-admin.service: systemd unit (Restart=always per the SIGTERM gotcha).
- deploy/README.md: reproducible deploy steps (no secrets), Caddy additive-site recipe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Panel web de administración de unibus: un binario Go que sirve una SPA Mantine embebida y expone una REST API. Tiene la identidad ADMIN del operador, firma cada petición al plano de control del bus, y gestiona rooms, miembros, claves, usuarios y el estado del cluster.
Panel web de administración del bus de mensajería unibus (NATS + JetStream con
cifrado E2E por room). Un único binario Go que:
Sirve la SPA Mantine compilada y embebida (embed.FS sobre web/dist), con
el mismo look índigo/oscuro que la web del cliente del bus.
Expone una REST API de administración bajo /api. El binario tiene la
identidad ADMIN del operador y media cada acción privilegiada contra el plano de
control de unibus, firmando cada petición. El navegador nunca firma, nunca
habla NATS y nunca ve una clave privada.
Arquitectura
navegador (SPA Mantine)
│ fetch /api/* (basic auth de Caddy + bind loopback)
▼
unibus_admin (gateway Go, identidad ADMIN del operador)
├── pkg/client (unibus) → CreateRoom / Invite / Kick / ListMyRooms (firma + cripto E2E)
├── GET firmado → /rooms/{id}/members (CanonicalRequest + SignEd25519, reusa la construcción del bus)
├── GET /healthz (CA-pinned)→ estado + posture de los 3 nodos del cluster
└── membership.Store (opc.) → users (allowlist) cuando hay acceso directo al store
▼
cluster unibus (magnus + homer + datardos, enforce + ACL + TLS + KV)
El gateway reutiliza el cliente del bus (github.com/enmanuel/unibus/pkg/client)
para todo lo que lleva criptografía (sellar la clave de room, firmar invite/rekey),
y construye GETs firmados con la única fuente de verdad de la firma del bus
(membership.CanonicalRequest + cs.SignEd25519) para las lecturas que el cliente
no expone. Nunca reimplementa firma ni cripto.
Capacidades
Pestaña
Qué hace
Vía
Cluster
up/down + posture (enforce/acl/tls/cluster/store) + latencia de cada nodo
GET /healthz (auth-exempt) de los nodos en --nodes, TLS pin a la CA del bus
Rooms
listar (rooms del admin), crear (subject + E2E/persist/firmado), ver miembros, invitar, expulsar+rekey
pkg/client (mutaciones) + GET firmado (miembros)
Users
listar/añadir/revocar la allowlist del bus
membership.Store directo — sólo con --db (single-node) o acceso KV admin
Cómo arrancar
# Mock (iterar la SPA sin bus):
./unibus_admin --mock --port 8480# Real contra un membershipd local (dev, sqlite, sin TLS):
./unibus_admin --port 8480\
--ctrl-url http://127.0.0.1:8470 --nats-url nats://127.0.0.1:4250 \
--db ./local_files/unibus.db \
--identity-pass unibus/operator-identity
# Producción (cluster magnus, enforce + TLS + nkey):
./unibus_admin --port 8480 --bind 127.0.0.1 \
--ctrl-url https://127.0.0.1:8470 --nats-url tls://127.0.0.1:4250 \
--ca /opt/unibus/tls/ca.crt \
--identity-file /opt/unibus_admin/identity.json \
--nodes "magnus=https://127.0.0.1:8470,homer=https://141.94.69.66:8470,datardos=https://51.91.100.142:8470"
Build
cd web && pnpm install && pnpm build # compila la SPA a web/dist (embebida)cd .. &&CGO_ENABLED=0 go build -o unibus_admin .
Seguridad
La identidad admin se carga de pass (unibus/operator-identity) o de un fichero
0600 (--identity-file); nunca va hardcodeada, ni a git, ni a argv.
El panel exige autenticación: en producción lo fronta Caddy con basic auth sobre
un subdominio ofuscado, y el gateway bindea sólo a loopback.
Acciones destructivas (revocar user, expulsar miembro + rekey) piden confirmación
explícita en la UI.
El operador debe estar en la allowlist del bus (rol admin) para que el gateway
pueda conectar bajo enforce.
Despliegue actual
Desplegado en magnus como unibus-admin.service (systemd system, Restart=always),
puerto 8480 en loopback, fronteado por Caddy con basic auth en un subdominio
ofuscado (admin-<hash>.organic-machine.com). Credenciales en pass
(unibus/admin-panel-password, unibus/admin-panel-url). Artefactos de deploy en
deploy/.
Gaps conocidos
Users en el cluster (KV): el plano de control no expone endpoint HTTP de users
— viven sólo en el store. Con el cluster en --store kv, el gateway no abre el KV
todavía, así que la pestaña Users queda degradada (estado informativo). Se habilita
con la vía de alta KV que añade la rama quick/0011-deploy-gaps del repo unibus, o
con --db en single-node.
meta-leader / tamaño de quórum del cluster: /healthz no los expone; requieren
el endpoint de monitoreo de NATS (varz/jsz). La pestaña Cluster muestra up/posture.
Invite a room E2E: requiere las claves públicas (sign_pub + kex_pub) del
invitado en hex, porque la clave de room se sella contra su X25519. La UI las pide
manualmente; no hay directorio de claves públicas todavía.