Commit Graph

8 Commits

Author SHA1 Message Date
egutierrez bfd4a99100 feat(spa): create user via invite link, permanent delete, pending invites
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>
2026-06-07 22:28:44 +02:00
egutierrez f65271dc92 feat(gateway): invite and hard-delete REST endpoints + repo methods
Wire the bus's new account surface into the admin gateway:

- POST /api/invites, GET /api/invites: mint and list single-use registration
  invites (CreateInvite/ListInvites on the Repo). The gateway pre-builds the
  shareable join link (JoinURL) from a configurable end-user client base URL so
  the SPA does not need to know where the client lives.
- DELETE /api/users/{pub}: hard-delete (purge) a user, distinct from the existing
  revoke.
- Both backends covered: signed control-plane (cluster default) via the unibus
  client's CreateInvite/ListInvites/DeleteUser, and the direct membership store
  (single-node --db fallback). For the direct store, ListInvites filters to
  pending (the control plane already does so server-side).
- New --join-base-url flag / UNIBUS_JOIN_BASE_URL env feeds the join link base
  URL (the END-USER client, NOT the panel's own URL); surfaced on /api/me.
- Mock repo gains the same methods for UI iteration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:28:44 +02:00
egutierrez 1b19f8e60f docs: app.md reflects control-plane Users wiring and the deploy gap
Update the architecture diagram, capability table, run examples and known
gaps to describe user management via the signed control-plane API instead
of the old store-only/--db path. Record the remaining gap honestly: the
membershipd binaries currently deployed on the cluster predate the /users
route and return 404, so the Users tab becomes functional in production
once the bus is updated to v0.10.0; the gateway already connects and signs
correctly against those nodes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:10:41 +02:00
egutierrez c7631074cb feat: make the Users tab operational, drop the degraded empty state
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>
2026-06-07 21:10:35 +02:00
egutierrez c412941e4c feat: route Users management through the signed control-plane API
The gateway previously managed the bus allowlist only via a direct
membership store opened with --db, falling back to a "none" backend that
left the Users tab degraded in cluster (the control plane exposed no user
HTTP endpoint). The unibus control plane now exposes an admin-only user
API (GET/POST /users, POST /users/{signpub}/revoke), and pkg/client wraps
it with ListUsers/AddUser/RevokeUser that sign each request.

busRepo now drives those client methods whenever no direct store is
configured (the cluster default), so user management works in cluster
without KV/SQLite access — the bus verifies the operator's admin identity
with requireAdmin and writes to the same store the room handlers use. A
direct store (--db) is kept as an explicit single-node fallback. The
reported users_backend becomes "control-plane" (or "sqlite" with --db),
and ErrUsersUnavailable / the "none" path are removed since a connected
gateway can always reach the API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:10:27 +02:00
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
Egutierrez df1c03a0be feat: Mantine SPA (Cluster/Rooms/Users) + verified end-to-end
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>
2026-06-07 19:38:30 +02:00
Egutierrez 8d893d216b feat: scaffold unibus_admin gateway (Go REST + embed SPA placeholder)
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>
2026-06-07 19:27:49 +02:00