--- name: uniweb lang: go domain: infra version: 0.2.0 description: "Frontend web del bus unibus: SPA de chat (React+Mantine) con wallet por usuario (BIP39) + gateway Go (REST+SSE) que actúa de peer del bus para el navegador." tags: [service, messaging, web, frontend, e2e] uses_functions: - generate_identity_go_cybersecurity - seal_aead_go_cybersecurity - open_aead_go_cybersecurity - seal_key_box_go_cybersecurity - open_key_box_go_cybersecurity - sign_ed25519_go_cybersecurity - verify_ed25519_go_cybersecurity uses_types: [] framework: "react" entry_point: "cmd/webgw" dir_path: "projects/message_bus/apps/uniweb" repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/uniweb" icon: phosphor: "chats-circle" accent: "#6366f1" service: port: 8481 health_endpoint: null health_timeout_s: 3 systemd_unit: null systemd_scope: null restart_policy: always runtime: manual pc_targets: - lucas-linux is_local_only: false e2e_checks: - id: build cmd: "CGO_ENABLED=0 go build ./..." timeout_s: 180 - id: vet cmd: "CGO_ENABLED=0 go vet ./..." timeout_s: 120 - id: unit cmd: "CGO_ENABLED=0 go test ./..." timeout_s: 120 - id: web_build cmd: "cd web && pnpm install --frozen-lockfile && pnpm build" timeout_s: 180 --- ## Qué es `uniweb` es el frontend web del bus [unibus](../unibus/app.md): la interfaz que un humano usa desde el navegador para hablar por el bus. Se separó de `unibus` (v0.13.0) para que el plano del bus (membresía, claves, librería cliente) quede limpio y el frontend tenga su propia carpeta de servicio y su propio ciclo de release. Tiene dos mitades que viven juntas: - **SPA (`web/`)** — React 18 + Vite + Mantine v9. Pantallas de chat y onboarding wallet (join por invitación, login por passphrase local, recover por mnemónica). La identidad criptográfica de cada usuario se deriva de forma determinista de una frase BIP39 de 12 palabras y se cifra at-rest en el dispositivo (AES-256-GCM); la clave privada nunca viaja al servidor en claro. - **Gateway (`cmd/webgw`)** — binario Go (`package main`, REST + SSE) que actúa como peer del bus en nombre del navegador. Mantiene una sesión wallet por usuario, registra claves públicas por token de invitación, y traduce HTTP/SSE ↔ el protocolo del bus usando la librería cliente de unibus. ## Cómo se acopla a unibus `uniweb` consume `unibus` como **módulo Go**, no reimplementa nada del bus: ``` replace github.com/enmanuel/unibus => ../unibus # pkg/{busauth,client,frame,room} replace fn-registry => ../../../../ # functions/cybersecurity ``` Los `replace` no son transitivos en Go, así que `uniweb` (módulo principal) declara los dos: el de `unibus` (de donde importa la librería cliente) y el de `fn-registry` (de donde `pkg/client` toma las primitivas de cifrado). Compila con `CGO_ENABLED=0` igual que unibus. ## Ejemplo ```bash # 1. Backend: el control-plane del bus (en la carpeta de unibus) cd ../unibus && CGO_ENABLED=0 go run ./cmd/membershipd # :8470 # 2. Build de la SPA cd web && pnpm install && pnpm build # genera web/dist # 3. Gateway sirviendo la SPA + API contra el control-plane cd .. && CGO_ENABLED=0 go run ./cmd/webgw \ --port 8481 --ctrl-url http://127.0.0.1:8470 --web-dir web/dist # Navegador: http://127.0.0.1:8481 # Desarrollo de la SPA con hot-reload (gateway en modo API-only, sin --web-dir): cd web && pnpm dev # vite proxya /api + /stream al gateway ``` ## Cuándo usarla Cuando quieras que un humano hable por el bus desde un navegador, o cuando trabajes en la UI de chat / el onboarding wallet. Para la lógica del bus en sí (membresía, claves, peers programáticos) ve a `unibus`; `uniweb` solo es la capa web encima. ## Gotchas - El gateway necesita el control-plane de unibus vivo (`--ctrl-url`, por defecto `http://127.0.0.1:8470`); si no, las sesiones fallan al abrir el peer. - `--web-dir` es **opcional**: vacío = API-only (úsalo con el dev server de vite); apuntando a `web/dist` = sirve la SPA buildeada. Un path inválido degrada a API-only con un WARN, no peta. - Build cross-repo: `uniweb` no compila si `../unibus` no está presente en disco (el `replace` es local). Para deploy hay que llevar ambos repos, o vendorizar unibus. - La passphrase del wallet nunca se guarda ni se envía; perderla en un dispositivo sin la mnemónica BIP39 = identidad irrecuperable en ese dispositivo (recuperable en otro con las 12 palabras). ## Capability growth log - v0.2.0 (2026-06-13) — SDK del bus en TypeScript (`web/src/bus/`), issue 0001 Fase 1: el protocolo y el cifrado E2E del bus portados al navegador para que `uniweb` deje de depender del gateway Go. Módulos: `crypto.ts` (Ed25519, ChaCha20-Poly1305, sealed box con nonce BLAKE2b igual que Go), `frame.ts` (wire format = `encoding/json` de Go byte a byte), `room.ts` (Policy), `busauth.ts` (nkey NATS + firma de requests del control-plane), `client.ts` (envelope de room puro + `BusClient` sobre una interfaz de transporte + cliente HTTP firmado) y `wstransport.ts` (adaptador `nats.ws`). Paridad cross-language verificada contra vectores Go (`cmd/busvectors`): **19/19 tests verdes** — endpoint id, firma Ed25519, AEAD, sealed box, frame marshal/sign, nkey y canonical request. La clave privada del usuario nunca se serializa hacia la red. La conexión `nats.ws` + control-plane reales se validan en la Fase 3 (E2E) por requerir un unibus vivo con WebSocket. - v0.1.0 (2026-06-13) — scaffold inicial: extracción de la SPA (`web/`) y el gateway (`cmd/webgw`) desde `unibus` v0.13.0 a su propia app/sub-repo. Sin cambios de capacidad respecto a lo que ya vivía en unibus 0.12.0 (wallet BIP39 + sesiones por usuario); solo cambia la ubicación y el módulo Go. go build/vet/test + pnpm build verdes en la nueva ubicación con los `replace` cross-repo.