Lo que salio de recorrer la app como alguien de la acampada:
- borrar (tribus y todo lo que usa renderContentActions) pide un segundo
toque; la X va la ultima de la fila
- Karvan: Llamar empieza solo con voz (si se negaba la camara no arrancaba
nada); botones con icono y nombre; textos traducidos; ayuda para invitar
- votaciones: tipo Si/No, semaforo o termometro (el servidor ya aceptaba
options); resultados en barras; aviso a quien la crea de que no puede votar
- workflow activistas con tribus, parlamento, votaciones y eventos
- seccion apagada: /modules dice cual era y la activa de un toque
- Modulos en el movil: agrupados como el lanzador, interruptores y Guardar fijo
- tribus en filas compactas; secciones de una tribu en tira de fichas + Mas
- lanzador y barra con iconos SVG en vez de letras vai
- Ajustes: idioma y aspecto primero; idiomas de un toque
- aviso de bienvenida solo en la portada y en una linea; Ahora no al final
- pie de una linea con el estado de sincronizacion en claro
- mobile_ux.css para los dos skins: un margen de 14px (eran 74 por lado en
las fichas), Anton solo en titulos y boton principal, buscador en una linea,
la barra de abajo se esconde con el teclado
Lanzador (iconos de 64px, etiquetas de 13px en negrita que solo parten entre
palabras), barra de abajo, fichas de filtro de 46px, iconos de las tarjetas
de 44px, reacciones del chat, formularios y textos de cabecera.
En Ajustes del movil, Tema y Flujos de trabajo dejan de ser desplegables:
cada skin es una tarjeta con su miniatura y cada workflow un boton grande,
y se aplican al tocarlos. Solo se ofrecen los skins que tienen maqueta de
movil (Acampada y Oasis Mobile); el servidor rechaza un tema de escritorio,
que dejaba la app sin maqueta. La bienvenida tambien ofrece elegir skin.
Aplicar un workflow ya no cambia el skin: se queda el que haya elegido cada
cual.
OasisMobile.css se carga con su propio <link> versionado antes de
Acampada.css, en vez de con @import: el @import sin ?v= se quedaba en la
cache del WebView y tras actualizar se veia la maqueta vieja.
Las ocho capturas, la portada y el esquema de la interfaz rehechos sobre la
1.3.3 con el tema Acampada. generar_portada.py compone la portada a partir
de las capturas y dibuja el esquema; el texto vive en el script. Novedades:
tema, fotos que no se pueden limpiar y DNS.
karvan.css trae su paleta amarilla sin !important: las salas activas salian
en negro con la letra oscura. Ahora tarjetas con marco negro, burbujas como
las del chat y los botones de llamada en blanco.
Si quitar los metadatos fallaba, blobHandler subia la foto original, con el
GPS. Ahora se para con un 400 y un aviso (imageMetadataUnsafe, en los 11
idiomas): HEIC/HEIF/AVIF y TIFF, que pueden llevar EXIF y aqui no se saben
limpiar, y JPEG/PNG/WebP que no se dejan recorrer. Un JPEG cortado ya no
sube como una imagen vacia. Formatos sin EXIF (BMP, SVG...) pasan igual.
Es el cambio del 10 de octubre, llevado a la copia de la limpieza que vive
ahora en blobHandler.js.
main.js preguntaba siempre a 8.8.8.8/1.1.1.1. Las VPN que evitan fugas de DNS
(Mullvad) y las redes que bloquean DNS externos los rechazan, y entonces no se
encontraba ningun pub por nombre: unirse a uno no hacia nada y no decia nada.
Ahora va primero getaddrinfo, que respeta la VPN y el DNS privado de Android;
a los 5 s, o si falla, tambien los publicos. Probado en Android 15 detras de
Mullvad: antes no resolvia hacklab.laenre.net y ahora si.
Lo del 10 de octubre que sigue valiendo sobre la base 1.3.3:
- con un tag v* el workflow publica la APK; a mano sigue en borrador
- el workflow trae el APK oficial de la misma version: de ahi salen
node_modules y libnode.so (prepare.sh ya lo usaba, pero el runner no lo tenia)
- versionName sale de package.json en vez de ir escrito a mano
- libnode.so comprimida, como en la oficial (unos 35 MB menos por descarga)
- release.sh: JAVA_HOME en el PATH, no deja salir una release debuggable,
exige CHANGE_WIFI_MULTICAST_STATE y avisa si la APK pasa de 80 MB
Se queda fuera la poda de prepare.sh y la sha256 de la APK 1.2.5: ahora el
node_modules viene ya podado del APK oficial.
El contenedor deja sitio a las barras del sistema y al teclado (Android 15
obliga con targetSdk 35). Las descargas van al gestor de Android. Fondo
blanco e iconos oscuros, como el tema Acampada.
El anuncio LAN de SSB va por UDP broadcast y Android lo filtra sin un
MulticastLock. La oficial declara ACCESS_WIFI_STATE y
CHANGE_WIFI_MULTICAST_STATE; esta no, asi que dos moviles en la misma red no
se encontraban.
Trae el tema del 10 de octubre (que se quedo fuera de PRUEBAS) y lo pone
por defecto en el movil. Acampada.css importa OasisMobile.css, asi que el
lanzador, la barra de 4 atajos y el workflow de activistas siguen igual;
hive_acampada.css va detras del tema y repinta lanzador, fichas de filtro,
hilos del foro, burbujas del chat, iconos de las tarjetas e identidades.
Quien ya tenia OasisMobile conserva su workflow reconocido.
Portada con cuatro pantallas, enlace de descarga y novedades arriba del
todo. Capturas nuevas de la interfaz y de Karvan, y el esquema de la
interfaz redibujado: ya no hay hexagonos ni Peers/Invites en la barra.
Las imagenes viven en docs/capturas, en el propio repo.
- Los filtros de chats, feed, foros, pads... heredaban el estilo de boton
grande (48px, ancho completo): en el foro las categorias llenaban la
pantalla. Ahora van en fila como los de Actividad; Crear sigue grande.
- La regla de tarjetas ([class*=-card]) pillaba tambien -card-header y
-card-hero-image, que salian como cajas con fondo y borde. Se limita a
las clases acabadas en -card.
- Acciones de la tarjeta (borrar, fijar, enlace, abrir) en una sola fila
de iconos redondos; '.main-column form' las ponia a ancho completo.
- Se ocultan las cajas de filtros vacias del foro.
Diferencia 1.3.1 -> 1.3.3 del codigo de la APK oficial aplicada a tres
bandas encima de la rama: karma en el perfil, salas con directo, fotos
selladas, visibilidad de menciones, telefono por stream en Android y
las traducciones nuevas. Sin dependencias nuevas.
OasisMobile.css sigue siendo el del fork; del oficial solo se traen al
final las reglas de las piezas nuevas (karma, directo a pantalla
completa, salas en vivo, graphos).
Con el workflow activistas caben todos los modulos en una pantalla: se
quitan el filtro Personal/Social y los accesos de usuario avanzado
(menciones, habitantes, perfil, CV, estadisticas, blockexplorer,
modulos). Peers y la edicion de la barra pasan a Herramientas; la barra
inferior se queda en los 4 atajos. La config de serie arranca ya con
los modulos de activistas.
La portada ordena los modulos por secciones en una rejilla de iconos, como el
cajon de apps del movil. Solo salen los modulos activos y las secciones que
tienen alguno; Personal y Social de la topbar parten el lanzador en dos.
Fuera los enlaces que ya no existen en 1.3.1 (/legacy, /public/latest/*) y
dentro los modulos nuevos: wiki, rooms, emergencias, mailing, campanas...
1.3.1 trae en mobile.css su propio chrome movil (cabecera fija y cajones); se
apaga desde el tema para que no se monte encima del de la rama.
Chats, Karvan, pads, foro, imagenes, videos, wiki y blogs, mas feed,
invitaciones y copia de seguridad. Karvan entra en la lista de modulos para que
los workflows lo enciendan y apaguen como al resto.
En movil un workflow cambia los modulos y no el tema: se queda OasisMobile.
Base del merge a tres bandas: git 0.9.5 de epsylon, esta rama y el src de la
APK oficial 1.3.1 (la variante movil: OASIS_MOBILE, regex sin \p{}, audio y
video de Android). Corre en el mismo nodejs-mobile 18.20.4 que ya usabamos.
Se conserva de la rama: Karvan, identidades y multicuenta, topbar y barra
inferior, borrado de EXIF sin sharp, filtro hot del foro, RTC sin STUN y los
modulos apagados en movil (wallet, banking, torrents).
Se toma de upstream lo que ya hace de serie: respuesta citada en chats, ssb-lan
en movil, configuracion en ~/.ssb/oasis (sobrevive a reinstalar el backend).
El asistente de bienvenida deja elegir workflow tambien en movil.
Al entrar en el repositorio se veia el aviso de rama experimental y, debajo,
el README entero de upstream copiado: 350 lineas contando lo que ya cuenta el
proyecto oficial, y mejor.
Ahora la portada ensena lo que hay aqui: la tabla de que anade esta rama frente
al oficial, Oasis explicado en cuatro dibujos, Karvan, las videollamadas, las
identidades y como empezar desde cero, todo con esquemas propios y capturas
reales.
El aviso de que esto NO es Oasis oficial se mantiene arriba del todo y completo.
Y al final, los enlaces al proyecto de epsylon, que es de donde sale todo.
Las imagenes se enlazan a la wiki en vez de duplicarse en el repositorio, que
es lo que hace tambien upstream con las suyas.
El fichero se sigue leyendo al arrancar el backend (readFileSync sin try/catch),
asi que lo importante es que exista: comprobado que arranca.
stripImageMetadata dependia de sharp, y cuando no esta devolvia la imagen
intacta. En Android sharp no se empaqueta —lleva binarios nativos y el bundle
del movil va sin ninguno— asi que las fotos subian con su EXIF entero: marca,
modelo, numero de serie, fecha y coordenadas GPS. Y sin sharp instalado,
tambien en escritorio.
Ahora hay un limpiador que no depende de nada: recorre el contenedor y tira los
bloques de metadatos sin decodificar ni recomprimir, asi que los pixeles quedan
byte a byte como estaban.
JPEG fuera APP1 (Exif y XMP), APP13 (IPTC), APP14, COM y demas.
Se quedan JFIF y el perfil de color, que afectan a como se ve.
PNG fuera tEXt, zTXt, iTXt, eXIf, tIME, dSIG.
WebP fuera los bloques EXIF y XMP del contenedor RIFF.
De la orientacion se guarda solo eso: sin sharp no se pueden girar los pixeles,
y tirar la etiqueta dejaria tumbadas las fotos verticales. Se reconstruye un
bloque EXIF de 32 bytes con esa unica etiqueta, que no dice nada de quien hizo
la foto ni donde. Si sharp esta, se sigue prefiriendo su camino, que ademas gira
la imagen de verdad.
Comprobado con una foto que llevaba GPS, marca, modelo, numero de serie y fecha:
- En el movil y en escritorio, el blob guardado queda con 32 bytes de EXIF
(solo la orientacion, que sobrevive) y cero rastros de lo demas.
- Los pixeles son identicos antes y despues, en JPEG, PNG y WebP.
- El perfil de color ICC se conserva.
- Sin metadatos, progresivo, GIF, fichero corrupto, fichero vacio y algo que
no es una imagen: se devuelven tal cual, sin excepciones.
Decia "Preparando la aplicacion" sin tilde y "puede tardar unos minutos", que
para un primer arranque de quince segundos suena a que algo va mal. Ahora dice
lo que hace y cuanto: descomprime el servidor, tarda un minuto, no hay que
hacer nada.
Upstream no usa ni un solo subdirectorio en src/views, src/backend, src/models,
src/client/assets/styles ni translations: todo plano. La rama habia creado dos
(views/fork/ y translations/fork/), que es lo que mas se separaba de su forma de
organizar el codigo. Se deshacen, y los ficheros pasan a llamarse como los suyos:
views/fork/hive_nav.js -> views/hive_views.js (como main_views.js)
views/fork/identities.js -> views/identities_view.js (como *_view.js)
translations/fork/i18n_fork.js -> translations/i18n_fork.js (junto a i18n.js)
backend/fork_routes.js -> backend/karvan_routes.js (familia karvan_*)
Ademas se retira una ruta duplicada: /legacy/export estaba declarada dos veces,
en backend.js (la del dev) y en el fichero de rutas de la rama, con el mismo
cuerpo salvo un && defensivo. Nacio cuando el formulario se paso a GET; al
devolverlo a POST quedo como copia exacta. Con ella se van legacyModel y
onboardingModel, que ya no hacia falta inyectar.
La cabecera del fichero de rutas explica ahora por que sigue existiendo, con los
datos medidos y no de memoria: backend.js concentra 625 rutas y se reescribe en
cada release (+833/-286 en la 0.9.2, +266/-31 en la 0.9.1), asi que una ruta
anadida en medio entra en conflicto cada vez.
Quedan diez ficheros propios, todos siguiendo la convencion de upstream, y
+247 lineas repartidas en 21 ficheros suyos (sin contar OasisMobile.css, que es
el tema de la rama).
Comprobado tras mover: arranque, las siete rutas principales, el selector de
identidades con sus siete entradas y su aviso, crear sala y enviar mensaje en
Karvan, y la exportacion de identidad por la ruta del dev.
Al girar el movil aparecia ERR_CONNECTION_REFUSED y ahi se quedaba. La lista de
configChanges estaba incompleta —faltaba smallestScreenSize, que es justo lo que
cambia al rotar—, asi que Android destruia y recreaba la Activity: el WebView se
perdia y volvia a cargar la URL, a veces antes de que el backend escuchara.
Con la lista completa, rotar ya no recrea nada y ademas no se pierde lo que
estuvieras escribiendo.
Y se anade una red de seguridad que no habia: onReceivedError vuelve a esperar
al backend y recarga la ultima pagina que si cargo, en vez de dejar el error de
Chromium en pantalla. Cubre tres casos mas, todos comprobados en el emulador:
- Matar el proceso :node con la app abierta (lo que hace Android cuando necesita
memoria): antes se quedaba muerta, ahora vuelve sola en unos 19 segundos.
- Cambiar de identidad, que reinicia el backend a proposito.
- Arranques lentos en moviles con poca memoria.
Un guard evita que se acumulen hilos esperando al mismo puerto, porque
onReceivedError se dispara una vez por recurso que falle.
Probado ademas: boton atras, modo avion, trim-memory RUNNING_CRITICAL y
am kill con reapertura. Todo eso ya iba bien.
El overlay pisaba menuNetwork, menuMedia y fediverse para dejarlos en Network,
Media y Fediverse. Se quitan: la rama no deberia separarse de upstream en algo
tan visible, y ahora movil y escritorio dicen lo mismo (Community, Library,
Multiverse).
Eso obliga a renombrar el filtro de la topbar: se llamaba Community y ahora ese
nombre es el de una categoria, asi que en la misma pantalla habria dos. Pasa a
ser Social, que es del fork y no choca con nada de epsylon.
Y las traducciones dejan de estar a medias: las 56 claves de la rama estan
completas en los 11 idiomas, ninguna cae al ingles. Antes solo el castellano
estaba entero y los otros nueve mostraban el texto en ingles.
El formulario de exportacion se habia pasado a GET, asi que la contraseña que
cifra la copia de la identidad acababa en el historial del navegador, en el
registro del servidor y en el Referer de lo siguiente que se cargara. Vuelve a
POST, como en upstream, y la ruta la lee del cuerpo.
Comprobado: la exportacion sigue descargando el fichero, y la direccion con la
contraseña ya no responde.
Resto del intento de subtemas propios que se descarto: se copiaba a cada
respuesta pero ninguna vista lo mira. Los subtemas de verdad son los de
upstream, con sus rutas y sus vistas.
Sacar o meter una identidad ya existente lo hace el modulo legacy, que es quien
sabe cifrar el secret con una contraseña. Se enlaza desde la lista para que
este donde se busca.
release.sh hace el ciclo entero y siempre igual, se lance a mano o desde un
runner: empaqueta el backend, compila, verifica y publica la release en gitea.
Las verificaciones no son decorativas, son las que han cazado fallos reales en
esta rama: una APK puede compilar, instalar y arrancar sin el backend dentro, o
haber perdido los permisos de camara y quedarse sin videollamadas. Se comprueba
que estan el backend y libnode.so, los dos permisos, y la firma.
El versionCode sale del numero de commits, que sube solo; el versionName, de
package.json, que es de donde lo saca tambien el backend, para que la version
que muestra la aplicacion no pueda discrepar de la de la APK. Los dos se pasan
por entorno a Gradle en vez de editarlos a mano.
La release se crea como borrador a proposito: publicar una APK firmada sin que
nadie la mire no deberia poder pasar por descuido.
El workflow de gitea no se dispara con cada commit (la APK son mas de 100 MB) y
necesita un runner con el SDK de Android; queda documentado en su cabecera.
La base de multicuenta ya estaba en el arranque (OASIS_ACCOUNT), pero solo se
podia elegir por linea de comandos. Ahora hay una seccion en los ajustes que
lista las identidades, marca cual esta en uso y permite crear una.
Como funciona:
- accounts.js encuentra las identidades buscando directorios ~/.<algo> con un
secret dentro, y lee de el su @id. En Android ese HOME es el almacenamiento
privado de la app, asi que quedan aisladas del resto del sistema.
- El selector anota la elegida en src/configs/active-account; ssb_config.js la
usa al arrancar si no hay OASIS_ACCOUNT (que sigue mandando).
- Crear una identidad solo anota el nombre: el secret lo genera ssb-config al
arrancar con esa cuenta.
En movil hay ademas un boton de reinicio, porque aqui cerrar la app no basta:
el backend vive en un servicio en primer plano que sobrevive. La ruta responde
la pagina y luego termina el proceso; el servicio se recrea y node arranca con
la identidad nueva. No se puede levantar el runtime dos veces en el mismo
proceso, de ahi salir en vez de reiniciar por dentro.
Comprobado en el emulador: al reiniciar, el feed pasa a ser el de la otra
identidad y la anterior sigue intacta.
Dos carpetas pueden tener el mismo secret copiado, y publicar desde las dos
parte el registro sin arreglo posible. La lista lo detecta comparando los @id
y lo avisa en rojo en las dos filas.
La pantalla de ajustes y el asistente de bienvenida piden ux-blocks.png,
ux-chats.png y ux-ainav.png para el selector de modo de interfaz. No se
copiaron al integrar 0.9.5, asi que el selector salia con tres imagenes rotas.
De paso se retira media-favorites.js con su JSON: es de una version anterior
de la rama y ya no lo requiere nadie, upstream lo resuelve con
content_favorites.js.
Cuatro arreglos del envoltorio, todos vistos al probarlo en el emulador:
- onShowFileChooser: sin el, el navegador interno no abre nada al pulsar
"Choose File", asi que no se podia subir ninguna imagen. El APK oficial si
lo implementa; era una regresion frente a el.
- La marca de re-extraccion del backend era el versionName. Al reconstruir la
APK sin subir version, la app se quedaba con el backend anterior y ningun
cambio se aplicaba. Pasa a ser lastUpdateTime del paquete, que cambia en
cada instalacion. (openFd sobre el asset no vale: va comprimido dentro del
APK y no admite descriptor.)
- La espera del backend sondeaba con HEAD, que el backend rechaza con 400.
Ahora abre un socket TCP contra 127.0.0.1:3000, que es lo unico que hay que
saber, y espera hasta cinco minutos en vez de uno: descomprimir 231 MB y
levantar SSB tarda mas en un movil lento. Mientras, pinta una pantalla de
estado con los segundos transcurridos, en vez de dejar el WebView en blanco.
- targetUrl/onNewIntent para poder abrir una ruta concreta desde un intent.
network_security_config.xml tenia base-config detras de domain-config, que el
esquema no admite. Corregido el orden y restaurada la referencia en el
manifiesto, que se habia sustituido por usesCleartextTraffic=true al depurar:
eso abria texto en claro hacia cualquier host, no solo hacia loopback.
Un comentario sin cerrar en karvan.css se comia la llave de la regla que
neutraliza el div{background:#222;padding:20px} global, y con ella todo el
bloque de la videollamada. El resultado eran dos cajas vacias enormes y unos
botones cuadrados en vez del panel.
Ademas:
- La hoja del modulo se cargaba antes que el tema, asi que el tema ganaba por
cascada. Pasa a cargarse despues, que es lo que decia su propio comentario.
- Las opciones de duracion (30m / 2h / 8h) se partian letra a letra en
escritorio; les faltaba white-space: nowrap.
- Clear-SNH usa !important en casi todo, asi que ninguna hoja posterior puede
ganarle. Se resuelve como lo resuelve Oasis: es el tema el que pinta el
modulo. Sin ese bloque las burbujas salian negras sobre blanco y el texto
secundario en amarillo palido, ilegible.
Comprobado con los cuatro temas en escritorio y en el emulador.
buildMessage no copiaba replyTo del contenido del mensaje, asi que la vista
nunca encontraba a quien se respondia y la burbuja salia sin cita. El campo
estaba puesto por error en buildChat, donde no significa nada: un chat no
responde a ningun mensaje.
Se ve al responder a un mensaje y recargar el hilo.
Diagnosticado en el emulador. El backend moria al cargar backend.js:
SyntaxError: Invalid regular expression: /^#([\p{L}\p{N}_-]+)/:
Invalid property name in character class
libnode.so de nodejs-mobile viene sin ICU completo, asi que las propiedades Unicode
en expresiones regulares no existen y el fichero ni siquiera compila. La rama ya lo
habia resuelto con un rango explicito; al integrar 0.9.5 se reintrodujo la forma de
upstream y con ella el fallo. Afectaba a backend.js y a data_model.js.
Tras el arreglo el emulador arranca: 'Backend started on port 3000', 991 modulos,
285 ms de warmup.
Y se devuelven al overlay los nombres de tres categorias. Upstream los renombro en
0.9.5 (Network -> Community, Media -> Library, Fediverse -> Multiverse) y al
restaurar los ficheros de traduccion se colaron los suyos, cambiando las etiquetas
de los hexagonos. Lo destapo una captura de pantalla del usuario, no el codigo.
ssb_config pasa a derivar la cuenta de OASIS_ACCOUNT. Como ssb-config construye la
ruta desde el appname y gui.js saca el socket de config.path, cambiar ese literal
arrastra de forma coherente el almacen, la base de datos, los blobs, el secret y el
socket. Sin la variable se usa 'ssb', asi que las instalaciones existentes siguen en
~/.ssb y no notan nada.
El nombre se valida contra [a-zA-Z0-9][a-zA-Z0-9_-]{0,31} y cae a 'ssb' si no encaja.
No es de adorno: acaba en rutas de fichero y hay operaciones destructivas detras.
Comprobado que OASIS_ACCOUNT=../../etc no sale de sitio.
statePath separa tambien por cuenta cuando se usa OASIS_STATE_DIR. Sin eso, dos
identidades compartirian el estado que 0.9.5 empezo a guardar ahi, que es justo el
camino a partir el registro.
Y el arreglo que importa: doce sitios construian la ruta como os.homedir() + '/.ssb'
en vez de usar la de la cuenta. El peor era panicmode_model, que con varias
identidades habria borrado la equivocada; tambien exportmode y legacy_model, que
habrian exportado o restaurado la identidad de otra cuenta. Los de lectura
—main_models, stats_model y backend.js con gossip.json y ebt— habrian mirado siempre
la cuenta por defecto.
Falta la parte visible: el selector de identidad y el reinicio del proceso al
cambiar. Esta base es la que hacia falta para que eso no sea peligroso.
Verificado: con OASIS_ACCOUNT sin definir la ruta sigue siendo ~/.ssb; con 'trabajo'
pasa a ~/.trabajo; con un valor con .. o espacios se rechaza. Diez rutas OK con la
app en marcha, incluidas /peers y /stats, que son las que leen esos ficheros.
El README abria con 'Oasis Mobile 0.9.1' y describia mejoras sobre 0.9.0, sin decir
en ningun sitio que esto no es el Oasis oficial. Ahora lo primero que se lee es que
es una rama experimental sin afiliacion, con enlace al repositorio de epsylon, la
indicacion de que los fallos se reportan aqui y el enlace a la wiki.
El package.json de la raiz seguia en 0.9.1 mientras el de src/server ya estaba en
0.9.5. Alineados.
Nota: backend.js sirve este README en la interfaz, asi que el aviso se ve tambien
desde dentro de la aplicacion.
renderMobileTopbar era la unica de las tres piezas de interfaz de la rama sin gate,
asi que en escritorio se dibujaba encima de la cabecera de upstream. Los hexagonos y
la barra inferior si lo tenian.
Detectado al arrancar el arbol sin OASIS_MOBILE para preparar la version de
escritorio.
Karvan solo existia en la rejilla de hexagonos, que es de movil. En escritorio no
habia forma de llegar salvo escribiendo la URL.
Se anade renderKarvanLink en main_views, calcado de renderPollsLink —misma forma,
mismo checkMod por getConfig().modules, mismo navLink— y se invoca en el grupo
social junto a renderChatsLink. Entra tambien en modules_view y en la lista de
/modules de backend.js, para poder apagarlo como cualquier otro modulo, con sus
etiquetas en el overlay.
Los estilos del modulo pasan a src/client/assets/styles/karvan.css. Estaban dentro
de OasisMobile.css, que en escritorio no se carga, asi que las salas se habrian
visto sin formato. Va como hoja propia siguiendo el patron de highlight.css, que
tampoco pertenece a ningun tema: asi el modulo se ve igual con cualquier tema y en
las dos plataformas.
Verificado: karvan.css sirve 200 y se carga en las dos plantillas, el enlace aparece
en el menu lateral, y en movil la rejilla de hexagonos sigue igual.
El cliente tenia stun.l.google.com fijo en el codigo. Un servidor STUN aprende la
IP publica y el momento de cada consulta, asi que tal cual estaba cada llamada le
avisaba a Google de que ese usuario esta llamando. En un proyecto con esta postura
sobre privacidad no se sostiene.
Por defecto ya no hay ningun servidor ICE: solo candidatos host, que funcionan en
la misma red y con NAT amable. Es mas honesto degradar a nada que degradar a un
tercero.
Se anade turnCredentials.js, siguiendo el estilo de los helpers de src/backend/:
genera credenciales efimeras con el mecanismo REST de coturn (use-auth-secret),
username = caducidad:identificador y credential = HMAC-SHA1 en base64. El feed id
hace de identificador, que es lo que distingue a quien llama en Oasis. Como coturn
valida el HMAC, las credenciales se emiten sin hablar con el servidor y caducan
solas.
La ruta GET /karvan/ice las sirve solo a loopback —la credencial va firmada con el
secreto del nodo y no debe salir de la maquina— y el cliente la pide ANTES de crear
ninguna RTCPeerConnection, porque la configuracion ICE de una conexion ya creada no
se puede cambiar de forma fiable. Con dos segundos de espera maxima y respaldo a
candidatos host.
La configuracion vive en oasis-config.json bajo rtc, vacia de fabrica, y admite tres
formas: STUN propio, TURN con secreto compartido, o credenciales estaticas. Con
relayOnly se fuerza iceTransportPolicy relay para que ningun par vea la IP del otro.
Verificado: sin configuracion devuelve iceServers vacio; con secreto emite credencial
reproducible y con caducidad; cero referencias a Google en el codigo.
Una revision a fondo del arbol contra 0.9.5 saco varios fallos, unos heredados y
otros que introdujo la propia integracion. Verificados uno a uno antes de tocar.
Recursos referenciados que no existian (los introduje al adoptar vistas de 0.9.5
que los usan, mientras la rama los habia borrado):
- Los cuatro temas *-SNH.css. settings_view ofrece Dark, Clear, Matrix y Purple,
pero solo estaba OasisMobile: elegir cualquiera daba 404 y la app se quedaba sin
estilos. Ahora los cuatro responden 200.
- pdf.min.mjs, pdf.worker.min.mjs y pdf-viewer.js, referenciados desde seis vistas.
Regresiones revertidas a 0.9.5 verbatim en catorce ficheros. Las de fondo:
- larp_model perdio getGoverningPeriodId, que backend.js llama: el anuncio de
gobierno no salia nunca, con el TypeError tragado por un catch.
- courts_view y search_view tenian selected: false, que hyperscript SI serializa
como atributo y HTML interpreta como seleccionado. La forma de upstream, el
spread condicional, es la correcta.
- pm_model perdio includeDeleted, projects_model la validacion de deadline pasado,
jobs_model el desempate por timestamp, middleware el flag de debug HTTP.
forum_view pasa a 0.9.5 conservando el filtro hot. Esto arregla un fallo activo: la
vista de la rama declaraba un cuarto parametro topic y backend.js llama con cinco
argumentos, asi que al abrir una respuesta el %clave del hilo se colaba como tema y
el hilo salia VACIO. Upstream pasa los mismos cinco argumentos a una firma de tres y
JS los ignora, que es lo correcto.
chats_view pasa a 0.9.5 y se implementa replyTo de verdad. Estaba muerto de punta a
punta: backend.js no leia ctx.query.replyTo ni pasaba el cuarto argumento a
sendMessage, asi que ningun mensaje llegaba a tener replyTo y la cita no se
renderizaba jamas. Ahora la cadena esta completa —modelo, GET, POST y vista— con
indice msgById armado antes de mezclar con las encuestas, y sin gate de movil:
responder a un mensaje no es una funcion de movil.
aria-label: encontrada la causa. No es que hyperaxe lo ignore, es que hyperscript
solo asigna como propiedad y html-element solo serializa atributos de su tabla, en
la que aria-* no esta; data-* si tiene rama propia, por eso las tablas responsive
funcionaban. Se arregla con el escape hatch attrs:{}, sin tocar node_modules.
prepare.sh copia README.md y falla si no esta: backend.js lo lee con readFileSync
sin try/catch, de modo que sin el fichero el backend muere con ENOENT al arrancar.
Verificado con la app en marcha: 17 rutas OK, los cuatro recursos que faltaban
sirviendo 200, aria-label emitido en el HTML, e interfaz intacta (10 categorias,
50 modulos, 7 accesos rapidos, 0 etiquetas vacias).
main.js resuelve el directorio de datos con process.env.HOME || resolve(__dirname,
'..','..'). En Android HOME suele llegar como "/", que no es escribible, asi que el
servicio lo fija explicitamente con Os.setenv al almacenamiento privado de la app:
ahi cuelga ~/.ssb. Tambien fija TMPDIR.
prepare.sh deja de pedir un npm install: saca el node_modules del APK base, que
viene podado —137 MB frente a los 1,4 GB de una instalacion de escritorio— y
sustituye solo el codigo. Es seguro porque las 98 dependencias son identicas entre
0.9.1 y 0.9.5: ninguna nueva, ninguna quitada, ninguna con version distinta.
El zip se comprime; sin comprimir el backend son 145 MB y la APK se iria por encima
de 200.
APK generada y verificada: 78 MB, net.laenre.oasis, versionName 0.9.5, targetSdk 35,
con libnode.so y el backend de 32 MB dentro.
Hasta ahora la APK salia re-firmando la de epsylon, y su classes.dex no implementa
WebChromeClient.onPermissionRequest. Sin ese metodo el WebView deniega por defecto
todo getUserMedia, asi que las videollamadas de Karvan no podian funcionar por mucho
que se parchease el manifest: la lista de permisos es estatica y se fija al compilar.
El wrapper cierra las dos puertas. Declara CAMERA, RECORD_AUDIO y
MODIFY_AUDIO_SETTINGS, pero no se conceden al instalar: son permisos peligrosos y se
piden en tiempo de ejecucion la primera vez que se pulsa Llamar. Si nadie llama, no
se pide nada. uses-feature required=false evita excluir dispositivos sin camara.
Reutiliza las tres librerias nativas tal cual: libnode.so es nodejs-mobile v18.20.4
oficial y libnative-lib.so son 6,8 KB con un unico simbolo JNI. De ahi que el
namespace sea com.solarnethub.oasis —JNI resuelve por nombre— mientras el
applicationId es net.laenre.oasis, que permite instalarla junto a la oficial. No
hace falta NDK ni compilar Node.
De paso corrige cosas del APK actual: targetSdk 35, trafico en claro permitido solo
hacia loopback en vez de usesCleartextTraffic global, allowBackup a false para que
adb backup no saque el .ssb, y el backend en un servicio en primer plano en el
proceso :node, que ademas es lo que permitira reiniciarlo limpio para el cambio de
identidad. El APK actual declara FOREGROUND_SERVICE pero no registra ningun servicio,
de modo que el backend muere al pasar a segundo plano.
Los binarios no se versionan: scripts/prepare.sh extrae las librerias y empaqueta el
backend. La keystore se pasa por entorno.
Verificado: ./gradlew assembleDebug construye una APK de 52 MB con las tres librerias,
targetSdk 35 y los permisos declarados; su DEX contiene onPermissionRequest,
PermissionRequest, CAMERA y RECORD_AUDIO, donde el APK actual tiene cero de los cuatro.
Falta probarla en un dispositivo real.
Sin JS el chat ya funcionaba —el formulario publica y el mensaje sale en el HTML—
pero no llegaban los mensajes de los demas hasta recargar a mano.
La pagina de sala anade <noscript><meta http-equiv=refresh content=10>: solo actua
cuando no hay JavaScript, asi que con JS el data-channel sigue mandando y no hay
recargas encima del chat en vivo. Es el mismo recurso que usa upstream en
indexing_view.
Comprobado ademas sobre el modelo, con TTL acortado: la sala se autodestruye al
vencer y no deja mensajes, el anillo respeta el tope de 250 conservando los mas
recientes, se rechaza la senalizacion de mas de 16 KiB, el tope de 100 salas se
aplica, y postear en una sala inexistente devuelve null en vez de reventar.
El identificador de sala son 72 bits de entropia: no es adivinable, y es la unica
credencial de acceso — quien tiene el enlace, entra. Es el modelo previsto, pero
conviene tenerlo escrito.
polls, blogs y data tenian modelo, vista y rutas desde la integracion de 0.9.5,
pero no aparecian en ningun sitio: habia que llegar por URL.
Entran en los hexagonos por categoria — blogs junto a menciones y publicaciones,
encuestas junto a votaciones, coincidencias en herramientas — y en la lista de
modulos fijables en la barra inferior. La rejilla se autogestiona, asi que no hace
falta tocar CSS.
Sus etiquetas ya existian en el i18n de upstream (pollsTitle, blogTitle, dataTitle),
de modo que salen traducidas sin anadir claves al overlay.
Verificado: 10 categorias y 50 modulos (antes 47), los tres enlaces presentes en la
home, 0 etiquetas vacias y las cuatro rutas respondiendo 200.