Users tab gains the wallet-model account flow:
- "Crear usuario" button -> modal (handle + role + expiry) -> POST /api/invites
-> shows the copyable single-use join link (<client-base>/join?token=…). Warns
when the gateway has no client base URL configured (falls back to the panel's
own origin).
- Per-row "Eliminar" -> STRONG confirmation modal that requires typing the handle
and spells out the permanence and the difference from revoke -> DELETE
/api/users/{pub}.
- Pending invites card: handle, role, partial token, expiry, copy-link.
Includes the rebuilt embedded SPA bundle (web/dist) so the Go binary ships the
new UI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With user management now wired through the control-plane API, the Users
tab is always functional against a live gateway. Remove the "Gestión de
users no disponible" alert and the writable gating (button disabled,
revoke hidden) that were driven by the old users_backend === "none"
case. The backend badge now reads the wiring in use ("control-plane" or
"sqlite"). Add user (handle + 64-hex sign-pub + role) and revoke (with
explicit confirmation) consume the gateway REST unchanged. Includes the
rebuilt SPA bundle embedded by the binary.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SPA (React 19 + Vite 6 + Mantine v9, dark/indigo, @fn_library-style):
- AdminShell: AppShell nav (Cluster/Rooms/Users), operator endpoint badge
- ClusterPage: per-node up/down + posture badges (enforce/acl/tls/cluster/store),
10s auto-refresh
- RoomsPage: room table (E2E/cleartext, persist, signed, epoch, role), create
modal, members drawer with kick(+rekey) and invite modal
- UsersPage: allowlist table (handle/role/status/sign_pub), add modal, revoke
with confirmation, degraded state when no store backend
- api.ts: single repository layer hitting /api; gateway decides mock vs live
Verified end-to-end against a local membershipd in BOTH postures:
- auth-off: create room, list rooms, signed members GET, add/revoke user
- enforce + TLS + nkey (production posture): TLS-pinned healthz, nkey NATS
connect, signed control-plane requests verified by the server, 403 surfaced
for a non-member room
pnpm build green (tsc + vite); go build/vet green; dist embedded.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single Go binary: serves an embedded Mantine SPA and a small REST API over the
unibus control plane. Holds the operator ADMIN identity, signs every
control-plane request, never exposes a private key to the browser.
- internal/admin: Repo interface + mock + bus implementations, REST server
- repo_bus: rooms via pkg/client, members via signed GET (CanonicalRequest +
SignEd25519), cluster via /healthz (CA-pinned), users via membership.Store
- identity loaded from pass entry or 0600 file (operator-identity JSON)
- go build CGO_ENABLED=0 green; go vet clean
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>