Files
unibus_admin/app.md
T
Egutierrez 93acc059f1 docs: app.md + deploy artifacts (systemd unit, deploy README)
- 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>
2026-06-07 19:44:38 +02:00

5.9 KiB

name, lang, domain, version, description, tags, uses_functions, uses_types, framework, entry_point, dir_path, repo_url, service, e2e_checks
name lang domain version description tags uses_functions uses_types framework entry_point dir_path repo_url service e2e_checks
unibus_admin go infra 0.1.0 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.
service
messaging
admin
nats
e2e
sign_ed25519_go_cybersecurity
main.go projects/message_bus/apps/unibus_admin https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/unibus_admin
port health_endpoint health_timeout_s systemd_unit systemd_scope restart_policy runtime pc_targets is_local_only
8480 /healthz 3 unibus-admin.service system always systemd-system
magnus
false
id cmd timeout_s
build CGO_ENABLED=0 go build ./... 180
id cmd timeout_s
vet CGO_ENABLED=0 go vet ./... 120
id cmd timeout_s
web_build cd web && pnpm install --frozen-lockfile && pnpm build 240
id cmd timeout_s
smoke_mock ./unibus_admin --mock --port 18490 & sleep 2 && curl -fsS http://127.0.0.1:18490/healthz && curl -fsS http://127.0.0.1:18490/api/cluster >/dev/null && kill %1 30

unibus_admin

Panel web de administración del bus de mensajería unibus (NATS + JetStream con cifrado E2E por room). Un único binario Go que:

  1. 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.
  2. 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.