Capítulo identidad de la guía Neblla — login SSO, scopes, guests, save() y persistencia. El contrato base está en core.txt.

Identidad y persistencia

Guests

Sin login, cada pestaña de navegador recibe un id único y un sitio en el lobby — pero solo dura mientras la pestaña esté abierta. Si el usuario cierra y vuelve mañana, o abre la app en otro dispositivo, es alguien nuevo. Lo guardado con save() sin login va a localStorage (solo ese dispositivo).

SSO en dos capas

1. El usuario tiene una sola cuenta Neblla para todas las apps Neblla. Cada app recibe un ID estable para él — pero distinto en cada app: ninguna app puede correlacionarlo con otra a través de Neblla. Ese ID llega sin pantalla de consentimiento; es el mínimo que cualquier app recibe. 2. Para cualquier dato más allá del ID, la app pide autorización vía *scopes*. La primera vez, el usuario ve una pantalla de consentimiento con la lista exacta. Mientras no cambien los scopes, los logins siguientes son silenciosos. Si se añade un scope nuevo, el popup vuelve a salir.


neblla.login({ scopes: ['email', 'friends'] });

Scopes disponibles:

Evento 'login' (CLIENT) — { type, data, grantedScopes, email? }.

neblla.logout() cierra la sesión del usuario en tu app pero no cierra su sesión Neblla — sigue logueado para otras apps. Para cerrar sesión global, el usuario usa "Switch" en el popup SSO. Evento 'logout' — {}.

Eventos de sesión: 'sessionExpired' (CLIENT) {}; 'reconnected' (CLIENT) { room, isLobby }.

save() y neblla.me.data


neblla.save({ record: 1250, settings: { sonido: false } });

La misma línea de código en ambos casos; Neblla decide el camino según el estado de sesión. En cada reconexión, neblla.me.data está cargado y listo — no hay que pedirlo.

Por defecto save() reescribe el objeto entero (last-write-wins sobre todo el documento). Para actualizar un campo conservando el resto:


neblla.save({ ...neblla.me.data, record: 1250 });

…o con estrategia de merge (solo aplica con login; la copia en localStorage siempre se reescribe entera):


neblla.save({ record: 1250 }, { merge: 'shallow' }); // Object.assign: gana lo que mandas, lo demás se conserva
neblla.save({ settings: { sonido: false } }, { merge: 'deep' }); // merge recursivo de objetos
neblla.save({ record: 1250 }, { merge: 'crdt' }); // CRDT (LWW-Map): gana el valor más nuevo por campo,
                                                  // converge entre dispositivos aunque las escrituras
                                                  // lleguen desordenadas; escribir null borra ese campo

Tras un merge, el servidor devuelve el documento resultante: neblla.me.data se actualiza y se dispara 'dataMerged' ({ data, merge }). 'shallow'/'deep' aplican en orden de llegada; 'crdt' es order-independent (ideal para varias pestañas/dispositivos).

Cuidado con el reemplazo accidental:


// ❌ Mal — pierde todo lo demás de me.data:
neblla.save({ record: 1250 });

// ✅ Bien — mantiene el resto:
neblla.save({ ...neblla.me.data, record: 1250 });

Patrón: recordar nombre entre sesiones (client.js):


let info = neblla.me.data;
if (!info.name) {
  info.name = prompt('Tu nombre?') || 'Guest';
  neblla.save(info);
}
neblla.setPublicInfo({ name: info.name });

Privacidad: publicInfo vs me.data

setPublicInfo() se difunde a todos los miembros de la sala; neblla.me.data no llega a otros clientes (ni al peer que ejecuta room.js).


// ❌ Mal — difunde datos sensibles a toda la sala:
neblla.setPublicInfo({ name, email: neblla.me.data.email });

// ✅ Bien — solo lo que de verdad es público:
neblla.setPublicInfo({ name, avatar });
// El email se queda en neblla.me.data, no llega a otros clientes

BBDD propia del desarrollador (BYODB)

Los datos sincronizados consumen unidades de la cuota diaria (ver publicar.txt). Para más almacenamiento — o para que los datos vivan en BBDD propia — se aportan credenciales desde el dashboard. Engines soportados:


Engine     Proveedores compatibles (ejemplos)         Tier free típico
─────────  ─────────────────────────────────────────  ────────────────
MongoDB    Atlas, DocumentDB, Cosmos DB (Mongo API)   Atlas M0 = 512 MB
Postgres   Neon, Supabase, RDS, Aurora, Render        Neon = 3 GB
MySQL      PlanetScale, RDS, Cloud SQL, Aurora        varía
Turso      Turso.tech (libSQL distribuido)            9 GB

Qué va a tu BBDD: los blobs JSON que la app genera — save() (user.data), datos de salas (setRoomData) y comunicaciones dev↔user. Tres "tablas"/colecciones (user_data, room_data, user_communications) creadas automáticamente con sus índices la primera vez que Neblla escribe. Esquema estable y documentado.

Qué se queda en Neblla: identidad (auth, sesiones, longTokens), amistades cross-app, compras (Stripe), flags anti-cheat, configuración de la app, metadata de archivos hosting, cuotas, logs. Tu BBDD nunca ve emails ni identificadores externos del usuario; solo el userId opaco por app.

Migración transparente por fases (copia masiva → delta de cambios → swap final). El último paso bloquea la app unos segundos para asegurar zero data loss. La app puede manejar el evento 'app_maintenance' ({ reason: 'byodb_migration', retryInSeconds: 30 }): el socket se desconecta en ~2 segundos y el SDK reconecta solo — muestra un mensaje de mantenimiento mientras tanto. Tras la migración, save() y los demás blobs van directos a tu BBDD, sin consumir cuota de Neblla.