Files
agent c13d6baf60 feat: Android Compose client for the unibus bus
Thin Jetpack Compose UI over the real Go client (pkg/client) compiled to a
gomobile .aar, so the phone speaks NATS and runs the same end-to-end crypto as
any other peer. Connect, create room (nats/matrix), publish and live-receive.
2026-06-05 17:40:28 +02:00

112 lines
4.4 KiB
Markdown

---
name: unibus_android
domain: tools
version: 0.1.0
description: "Cliente Android (Jetpack Compose) del bus unibus: conecta, crea salas y chatea con el mismo cliente Go (pkg/client) compilado a un .aar via gomobile, conservando el cifrado E2E."
tags: [messaging, nats, e2e, android, compose, kotlin, gomobile, client]
lang: kt
framework: compose
entry_point: "app/src/main/kotlin/com/fnregistry/unibus/MainActivity.kt"
dir_path: "projects/message_bus/apps/unibus_android"
repo_url: "https://gitea.organic-machine.com/dataforge/unibus_android"
uses_functions:
- fn_theme_kt_ui
- fn_tokens_kt_ui
- fn_stack_kt_ui
- fn_group_kt_ui
- fn_card_kt_ui
- fn_badge_kt_ui
- fn_button_kt_ui
- fn_text_kt_ui
- fn_title_kt_ui
- fn_text_input_kt_ui
- fn_switch_kt_ui
uses_types: []
e2e_checks:
- id: bind
cmd: "cd ../unibus && gomobile bind -target=android -androidapi 24 -o ../unibus_android/app/libs/unibus.aar github.com/enmanuel/unibus/mobile"
timeout_s: 600
- id: build
cmd: "fn run gradle_assemble_debug_bash_infra projects/message_bus/apps/unibus_android"
timeout_s: 360
---
# unibus_android
Cliente Android del bus de mensajería `unibus`. La aplicación es una fina capa de
interfaz en Jetpack Compose sobre el cliente Go real del bus: `pkg/client` se compila
a una librería nativa Android (`unibus.aar`) con `gomobile bind`, de modo que el teléfono
habla el protocolo NATS y ejecuta el mismo cifrado end-to-end (ChaCha20-Poly1305 +
Ed25519 + X25519) que cualquier otro peer. No se reimplementa ni el protocolo ni la
criptografía: hay una única fuente de verdad compartida con el resto del ecosistema.
## Arquitectura
```
MainActivity.kt (Compose UI)
└── mobile.Session (Kotlin, generado por gomobile)
└── unibus.aar (libgojni.so: pkg/client + nats.go + crypto del registry)
├── data plane → NATS TCP (nats://host:4250)
└── control plane → HTTP REST (http://host:8470)
```
El wrapper Go que define la API plana del binding vive en
`projects/message_bus/apps/unibus/mobile/unibus.go` (paquete `mobile`): expone
`NewSession`, `CreateRoom`, `Join`, `Publish`, `Subscribe`, `Request` y la interfaz
`FrameListener` con tipos compatibles con gomobile (string, []byte, int, error,
interface).
## Regenerar el binding (.aar)
Tras tocar `pkg/client` o el wrapper `mobile/`:
```bash
cd projects/message_bus/apps/unibus
export ANDROID_HOME=$HOME/android-sdk
export ANDROID_NDK_HOME=$(ls -d $HOME/android-sdk/ndk/* | head -1)
export PATH="$PATH:$(go env GOPATH)/bin"
gomobile bind -target=android -androidapi 24 \
-o ../unibus_android/app/libs/unibus.aar \
github.com/enmanuel/unibus/mobile
```
## Build del APK
```bash
fn run gradle_assemble_debug_bash_infra projects/message_bus/apps/unibus_android
# o, directo:
cd projects/message_bus/apps/unibus_android && ./gradlew :app:assembleDebug
# salida: app/build/outputs/apk/debug/app-debug.apk
```
## Probarlo contra el bus
1. Levantar el control plane real (NATS embebido + REST de membresía):
```bash
cd projects/message_bus/apps/unibus
go run ./cmd/membershipd # HTTP :8470, NATS :4250
```
2. En el **emulador** (`Pixel_API34`), instalar el APK. La app usa `host = 10.0.2.2`,
que el emulador mapea al loopback del PC, así que conecta sin más configuración.
3. En la app: Conectar → Crear sala → escribir un mensaje. El mensaje viaja al bus por
NATS y vuelve al propio suscriptor (eco del subject), confirmando el camino completo
APK → bus → APK.
## Gotchas
- `cmd/membershipd` escucha en `127.0.0.1` (líneas `addr := "127.0.0.1:" + httpPort`).
Desde el emulador funciona (10.0.2.2 → loopback del host). Para un **teléfono real** en
la LAN o vía Tailscale hay que bindear el control plane y el NATS embebido a `0.0.0.0`
y usar la IP de red del PC/VPS en los campos Host de la app.
- El control plane v1 es HTTP sin TLS; el manifest declara `usesCleartextTraffic="true"`.
Para producción, exponer el bus con TLS y quitar ese flag.
- El `.aar` pesa ~24 MB (runtime Go embebido) y el APK debug ~54 MB. Es esperado: el
cliente Go completo viaja dentro.
- `onFrame` del `FrameListener` llega en una goroutine de entrega de NATS; la UI hace
`Handler(mainLooper).post { ... }` antes de tocar el estado de Compose.
## Capability growth log
- v0.1.0 (2026-06-05) — baseline: conectar, crear sala (modo nats/matrix), publicar y
recibir mensajes en vivo. Binding gomobile del `pkg/client` con cifrado E2E intacto.