From 0b39e86ed684988538b8c60c4f339b85aff92018 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sun, 14 Jun 2026 15:32:00 +0200 Subject: [PATCH] docs(unibus): bump to 0.15.0; document directory + bot provisioning Add the 'Directorio de nombres (endpoint -> handle)' and 'Provisioning de bots / unibots' sections with an end-to-end snippet, and a capability growth log entry for v0.15.0. Co-Authored-By: Claude Opus 4.8 (1M context) --- app.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/app.md b/app.md index b957d002..9374a940 100644 --- a/app.md +++ b/app.md @@ -2,7 +2,7 @@ name: unibus lang: go domain: infra -version: 0.14.0 +version: 0.15.0 description: "Bus de mensajería unificado sobre NATS+JetStream con cifrado E2E por room (megolm/olm reducido): service de membresía/claves, librería cliente y peers demo." tags: [service, messaging, nats, e2e] uses_functions: @@ -158,6 +158,59 @@ Para apuntar a un NATS externo en producción: `--nats-url nats://host:4222` en `cybersecurity` del registry compila limpio con `CGO_ENABLED=0`. NO requiere `fts5` ni `gcc`. +## Directorio de nombres (endpoint → handle) + +Cada frame del bus lleva el **endpoint id** del remitente +(`base64url(sha256(signPub))`, sin padding — `frame.EndpointID`), no un nombre +legible. Para que un cliente muestre nombres en vez de hashes, el control-plane +expone `GET /api/directory`: + +- **Auth:** el mismo middleware de firma que el resto del control-plane + (cabeceras `X-Unibus-Pub/Ts/Nonce/Sig` sobre `CanonicalRequest`). NO es + admin-only: cualquier usuario activo del bus (member o admin) puede leerlo. En + modo `enforce`, una request sin firmar recibe 401 antes de llegar al handler. +- **Respuesta** `{ "members": [ { "sign_pub", "endpoint", "handle", "role" } ] }`, + solo usuarios `status=active`. El `endpoint` lo computa el servidor desde el + `sign_pub` con la misma derivación que el bus, así que casa byte a byte con el + sender id que el cliente ya tiene en cada mensaje. +- CORS: cubierto por la allowlist `--cors-origins` existente (mismas cabeceras + que el resto de rutas, sin caso especial). + +## Provisioning de bots / unibots + +Dar de alta una identidad para un proceso automatizado es **un solo comando**. +Antes había que derivar un keypair a mano y pasar el `sign_pub` a `user add`; +ahora `bot add` lo hace todo: mintea una identidad de bus fresca (Ed25519 + +X25519, la misma derivación `cs.GenerateIdentity` que usan `worker`/`chat`), +registra su `sign_pub` en el allowlist con `handle` y `role`, y escribe las +credenciales a un fichero 0600 que el proceso lee para conectar. + +```bash +# 1. Provisionar el bot (store sqlite local; usa --store kv contra un cluster vivo). +membershipd bot add --handle notifier --out ./local_files/notifier.id +# provisioned bot "notifier" role=member +# sign_pub: 97d5a903...b1d4 +# endpoint: HU85l2onjrK4EoTLoBfJVkGEXMw9LAjNEjPWiDS8YwM +# credentials: ./local_files/notifier.id (0600) + +# 2. El proceso arranca como ese usuario leyendo el --out (formato canónico +# pkg/client.LoadIdentity, sin conversión): el worker demo lo consume directo. +worker --id-file ./local_files/notifier.id --nats-url nats://127.0.0.1:4250 \ + --ctrl-url http://127.0.0.1:8470 + +# 3. (opcional) Verlo en el directorio / en user list. +membershipd user list +``` + +Las credenciales (`--out`) quedan en el fichero indicado, con permisos 0600. Es +el secreto del bot: contiene las claves privadas, trátalo como una clave SSH +(ver Gotcha "Identidad = secreto crítico"). `bot add` rehúsa sobrescribir un +`--out` existente, y registra al usuario ANTES de escribir el fichero, de modo +que un fallo nunca deja un bot a medias. + +Flags: `--handle` y `--out` obligatorios; `--role admin|member` (default member); +`--store sqlite|kv` y el resto de flags de conexión idénticos a `user add`. + ## Convención de subjects ``` @@ -169,6 +222,18 @@ agent..{in,out} inbox/outbox de agente LLM (agent.scout.in) ## Capability growth log +- v0.15.0 (2026-06-14) — nombres legibles + provisioning de bots de un comando. + (1) Nuevo `GET /api/directory` en el control-plane: cualquier usuario activo del + bus (member o admin), autenticado con la misma firma Ed25519 que el resto de + rutas, resuelve endpoint id → handle. Devuelve `{members:[{sign_pub, endpoint, + handle, role}]}` solo de usuarios activos; el endpoint lo deriva el servidor con + `frame.EndpointID`, casando byte a byte con el sender id de cada frame (paridad + verificada contra el vector de `cmd/busvectors`). (2) Nuevo `membershipd bot add + --handle --out [--role] [--store]`: mintea identidad, la registra en + el allowlist y escribe credenciales 0600 en formato `client.LoadIdentity`, de modo + que un proceso (worker/clientcheck) conecta como ese usuario sin pasos manuales. + Nuevo helper exportado `pkg/client.WriteNewIdentity` (no sobrescribe ficheros + existentes). Todo aditivo; build/vet/test verdes. - v0.14.0 (2026-06-13) — prep para el cliente browser-nativo `uniweb` (issue uniweb/0001, Fase 0), todo aditivo y opt-in: (1) el nats-server embebido puede exponer un listener WebSocket (`WebsocketConfig`) para que un navegador hable el