--- name: unibus lang: go domain: infra version: 0.1.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: - 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: "" entry_point: "cmd/membershipd" dir_path: "projects/message_bus/apps/unibus" repo_url: "" service: port: 8470 health_endpoint: /healthz health_timeout_s: 3 systemd_unit: null systemd_scope: null restart_policy: none runtime: manual pc_targets: - lucas-linux is_local_only: true 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: 180 - id: smoke cmd: "CGO_ENABLED=0 go build -o /tmp/unibus_membershipd ./cmd/membershipd && /tmp/unibus_membershipd --http-port 18470 --nats-port 14222 --db /tmp/unibus_smoke.db --store-dir /tmp/unibus_blobs --nats-store /tmp/unibus_js &" health: "http://127.0.0.1:18470/healthz" timeout_s: 30 --- ## Qué es `unibus` es un bus de mensajería unificado sobre NATS + JetStream. Una capa fina encima de NATS aporta lo que NATS no tiene: membresía de rooms, invitaciones con reparto de clave, y cifrado extremo a extremo (E2E) del payload por room, con rotación de clave activa (forward secrecy) al expulsar a un miembro. Todos los participantes —procesos/workers, interfaces de chat humanas, agentes LLM— son peers de primera clase que hablan el mismo protocolo y se diferencian solo por las rooms a las que se unen y por lo que hacen con lo que reciben. ### Piezas | Componente | Ruta | Rol | |---|---|---| | `membershipd` | `cmd/membershipd` | Service (control plane): metadata de rooms, directorio de miembros, reparto de claves selladas, object store de media. Arranca NATS embebido si no se pasa `--nats-url`. | | librería cliente | `pkg/client` | La API que consume cualquier peer. Crear/unirse a rooms, invitar, publicar/suscribir, request/reply, kick con rotación de clave, media cifrada. | | `worker` | `cmd/worker` | Peer demo: publica un contador incremental cada segundo a una room cleartext. | | `chat` | `cmd/chat` | Peer demo: suscriptor en vivo (modo simple) + demo de cifrado E2E y forward secrecy (`--demo-encrypted`). | ### Dos planos - **Control plane**: HTTP autoritativo en `membershipd`. Quién está en cada room, sus claves públicas, y la clave de room `K` sellada por epoch para cada miembro. - **Data plane**: NATS. Los mensajes (frames) viajan por subject; los blobs de media NO viajan por el bus, se cifran y se suben al object store, y por NATS solo viaja una referencia (hash + nonce). ### Criptografía (importada del registry, NO reescrita) El cifrado E2E se compone de las 7 primitivas del capability group `e2e-messaging` (`docs/capabilities/e2e-messaging.md`), importadas del paquete `fn-registry/functions/cybersecurity`. Esta app no reimplementa ninguna primitiva criptográfica. ## Ejemplo Demo end-to-end con `go run` (NATS embebido, nada que instalar): ```bash cd projects/message_bus/apps/unibus # 1. Service de membresía/claves (NATS embebido en :4250, HTTP en :8470) go run ./cmd/membershipd # 2. En otra terminal: peer publicador (proceso) — publica ticks cada 1s go run ./cmd/worker # 3. En otra terminal: peer suscriptor (chat humano) — imprime cada tick en vivo go run ./cmd/chat # 4. Demo de cifrado E2E + forward secrecy (contra el membershipd ya corriendo): # A crea room cifrada, invita a B, A publica (B descifra), A expulsa a B, # A publica de nuevo en el nuevo epoch (B ya NO puede descifrar). go run ./cmd/chat --demo-encrypted ``` Para apuntar a un NATS externo en producción: `--nats-url nats://host:4222` en `membershipd`, `worker` y `chat`. ## Cuando usarla - Cuando necesites un tejido de mensajería donde procesos, humanos y agentes LLM sean peers uniformes (mismo protocolo, distinta política por room). - Cuando quieras rooms cifradas E2E con forward secrecy (paridad con Matrix) sin montar un Synapse: `room.ModeMatrix`. - Cuando quieras fan-out cleartext rápido para telemetría/coordinación de procesos: `room.ModeNATS`. - Como sustituto de la capa de transporte Matrix de `agents_and_robots` (fase posterior; v1 valida el bus de forma autónoma). ## Gotchas - **El service NO está endurecido (v1).** No hay TLS, ni rate-limit, ni auth en las rutas GET de lectura. Confía en la red interna. Las rutas mutantes (`/rooms`, `/invite`, `/rekey`) sí exigen firma Ed25519 del owner sobre los bytes canónicos de la request. Endurecer es fase posterior. - **Identidad = secreto crítico.** El archivo de identidad (`worker.id`, `chat.id`) contiene las claves privadas (Ed25519 + X25519). Se escribe 0600. Perderlo = mensajes ilegibles, sin recuperación. Trátalo como una clave SSH. - **Las rooms reciben un ULID fresco al crearse.** No hay "crear o unirse por nombre": cada `CreateRoom` produce un room nuevo. Los peers demo cleartext comparten el *subject* (NATS enruta por subject), así que worker→chat funcionan aunque cada uno tenga su propio room id mapeado al mismo subject. - **La media no viaja por el bus.** `PublishMedia` cifra, sube al object store y publica solo un `BlobRef`. El receptor, si ve `Frame.Blob != nil`, descarga y descifra con `FetchMedia`. El frame de media NO lleva payload inline (su nonce vive en `BlobRef.Nonce`); `Subscribe` no intenta descifrar payloads vacíos. - **Forward secrecy depende del rekey.** `Kick` rota `K` a un epoch nuevo y la re-sella solo para los miembros restantes. El expulsado pierde acceso a los mensajes publicados después del kick, pero conserva los anteriores (las claves de epochs pasados no se borran: cifraban datos que ya podía leer). - **NATS embebido escribe JetStream en disco.** `--nats-store` apunta a `local_files/jetstream`; borrarlo resetea el historial persistido. - **Build sin CGO.** Usa el driver `modernc.org/sqlite` (pure-Go) y el paquete `cybersecurity` del registry compila limpio con `CGO_ENABLED=0`. NO requiere `fts5` ni `gcc`. ## Convención de subjects ``` proc.. telemetría/coordinación de procesos (proc.test.ticks) rpc. request/reply (rpc.indexer) room. chat humano/grupo (room.general) agent..{in,out} inbox/outbox de agente LLM (agent.scout.in) ```