docs(unibus_admin): bump 0.2.0 — account creation via invites + hard-delete

Document the wallet-model account flow in the Users tab: invite-link account
creation (with the configurable client base URL for the join link), permanent
hard-delete with strong confirmation, and the deploy gap (cluster still on bus
v0.11.0; this branch merges after the bus reaches master).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 22:29:51 +02:00
parent bfd4a99100
commit ea35c11f19
+50 -3
View File
@@ -2,8 +2,8 @@
name: unibus_admin name: unibus_admin
lang: go lang: go
domain: infra domain: infra
version: 0.1.0 version: 0.2.0
description: "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." description: "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 (alta por invitación + baja por hard-delete) y el estado del cluster."
tags: [service, messaging, admin, nats, e2e] tags: [service, messaging, admin, nats, e2e]
uses_functions: uses_functions:
- sign_ed25519_go_cybersecurity - sign_ed25519_go_cybersecurity
@@ -78,7 +78,7 @@ no expone. Nunca reimplementa firma ni cripto.
|---|---|---| |---|---|---|
| **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 | | **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) | | **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 | `pkg/client` (`ListUsers`/`AddUser`/`RevokeUser`) contra la API admin-only del plano de control, firmando como el operador. Funciona en cluster (los nodos escriben al mismo store que las rooms) sin acceso directo al store. `--db` queda como fallback single-node opcional | | **Users** | listar la allowlist; **crear usuario** por enlace de invitación (modelo wallet, sin manejar claves); añadir por clave conocida; **revocar** (status flip, auditable) y **eliminar** (hard-delete permanente, con confirmación fuerte) | `pkg/client` (`ListUsers`/`AddUser`/`RevokeUser`/`DeleteUser`/`CreateInvite`/`ListInvites`) contra la API admin-only del plano de control, firmando como el operador. Funciona en cluster sin acceso directo al store. `--db` queda como fallback single-node opcional |
## Cómo arrancar ## Cómo arrancar
@@ -127,8 +127,36 @@ ofuscado (`admin-<hash>.organic-machine.com`). Credenciales en `pass`
(`unibus/admin-panel-password`, `unibus/admin-panel-url`). Artefactos de deploy en (`unibus/admin-panel-password`, `unibus/admin-panel-url`). Artefactos de deploy en
`deploy/`. `deploy/`.
## Creación de usuarios (modelo wallet) y enlace de invitación
La pestaña Users crea usuarios SIN que el operador maneje claves: «Crear usuario»
acuña un enlace de invitación de un solo uso (`POST /api/invites` → bus
`POST /invites`, admin-only). El gateway construye el enlace
`<<APP_BASE>>/join?token=XXX`, donde `<<APP_BASE>>` es la URL del **cliente
final** (la página que hospeda `/join`), NO la del panel. Se configura en el
gateway con `--join-base-url https://chat.unibus.example` o la variable
`UNIBUS_JOIN_BASE_URL`; el valor se expone en `/api/me` (`join_base_url`). Si no
se configura, la SPA usa su propio origen como respaldo y avisa de configurarlo.
El usuario abre el enlace en su dispositivo, genera ahí su par de claves (la
privada nunca sale del equipo) y se registra (`POST /register`, lo consume la
página `/join` del cliente web — ver el contrato en el report del bus).
«Eliminar» es un **hard-delete permanente** (`DELETE /api/users/{pub}` → bus
`DELETE /users/{signpub}`), distinto de «Revocar» (status flip auditable). La UI
exige teclear el handle para confirmarlo (no se dispara por un clic accidental).
## Gaps conocidos ## Gaps conocidos
- **Cuentas (invites + hard-delete) contra el cluster desplegado**: el gateway y la
SPA ya consumen `POST/GET /api/invites` y `DELETE /api/users/{pub}`, verificados
end-to-end contra un `membershipd` v0.12.0 local (crear invite → registrar por
`/register` sin firma → aparece en `/users` → hard-delete → desaparece) y vía
`--mock`. El gap es de **despliegue**: el cluster corre hoy v0.11.0 (sin
`/invites`/`/register`/`DELETE /users`), así que la pestaña Users sólo creará/
eliminará cuentas en producción cuando el bus se actualice a v0.12.0 (rollout
pendiente). Verificado contra el cluster vivo: `/register` devuelve 401 en
magnus/homer/datardos (ruta aún no desplegada) frente a 400 en el nodo v0.12.0
local. El merge de esta rama del panel a master debe seguir al merge del bus.
- **Users contra el cluster desplegado**: el código del plano de control (unibus - **Users contra el cluster desplegado**: el código del plano de control (unibus
master, v0.10.0) ya expone la API admin-only de users (`GET/POST /users`, master, v0.10.0) ya expone la API admin-only de users (`GET/POST /users`,
`POST /users/{signpub}/revoke`) y el gateway la consume firmando como el operador. `POST /users/{signpub}/revoke`) y el gateway la consume firmando como el operador.
@@ -145,3 +173,22 @@ ofuscado (`admin-<hash>.organic-machine.com`). Credenciales en `pass`
- **Invite a room E2E**: requiere las claves públicas (sign_pub + kex_pub) del - **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 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. manualmente; no hay directorio de claves públicas todavía.
## Capability growth log
- v0.2.0 (2026-06-07) — capa de CUENTAS estilo WhatsApp sobre el bus v0.12.0. El
gateway gana `POST/GET /api/invites` (acuñar/listar invitaciones de un solo uso,
consumiendo `client.CreateInvite/ListInvites`) y `DELETE /api/users/{pub}`
(hard-delete, `client.DeleteUser`), con la misma doble vía que el resto de users
(plano de control firmado en cluster / store directo en single-node). El enlace de
invitación `<<APP_BASE>>/join?token=…` lo construye el gateway desde
`--join-base-url` / `UNIBUS_JOIN_BASE_URL` (URL del cliente final, no del panel),
expuesto en `/api/me`. La SPA añade a la pestaña Users: botón «Crear usuario»
(modal handle+rol+caducidad → enlace copiable), card de invitaciones pendientes
(handle, rol, token parcial, caducidad, copiar enlace), y botón «Eliminar» con
confirmación FUERTE (teclear el handle) que distingue el borrado permanente del
revoke. Verificado: flujo e2e contra `membershipd` v0.12.0 local (invite → register
por curl sin firma → aparece en /users → re-register 409 → hard-delete → desaparece)
y gateway vía `--mock` (las rutas nuevas + el SPA embebido sirven). El cluster vivo
sigue en v0.11.0 (rollout del bus pendiente del orquestador); esta rama del panel se
mergea a master tras el merge del bus a master. build/vet/web-build verdes.