diff --git a/app.md b/app.md index ba35038..01b38a8 100644 --- a/app.md +++ b/app.md @@ -2,8 +2,8 @@ name: unibus_admin lang: go domain: infra -version: 0.1.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." +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 (alta por invitación + baja por hard-delete) y el estado del cluster." tags: [service, messaging, admin, nats, e2e] uses_functions: - 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 | | **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 @@ -127,8 +127,36 @@ ofuscado (`admin-.organic-machine.com`). Credenciales en `pass` (`unibus/admin-panel-password`, `unibus/admin-panel-url`). Artefactos de deploy en `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 +`<>/join?token=XXX`, donde `<>` 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 +- **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 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. @@ -145,3 +173,22 @@ ofuscado (`admin-.organic-machine.com`). Credenciales en `pass` - **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. + +## 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 `<>/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.