# ADR 0004 — Multi-tenant + registro/login propio (email/contraseña) - **Estado:** aceptada - **Fecha:** 2026-06-21 - **Supersede (parcialmente):** ADR 0002/0003 en lo relativo a "auth ligera sin cuentas". ## Contexto El contrato original definía la app como **una sola familia**, con auth ligera (PIN de padres) y datos globales. El usuario pide convertirla en **multi-familia (multi-tenant)** con **registro/login propio** y persistencia de preferencias. ## Decisión 1. **Tenant = `Family`** (cuenta con email único + contraseña BCrypt + PIN + prefs). Las entidades raíz (`child`, `activity`, `material_item`, `reward`) llevan `family_id`. El resto cuelga del niño. 2. **Aislamiento**: toda consulta raíz se filtra por la familia de la sesión; las operaciones por id verifican pertenencia y, si no, responden **404** (no 403, para no filtrar existencia). 3. **Sesión de familia ligada al dispositivo**, persistida en BD (`family_session`) para sobrevivir a reinicios (clave para el kiosko). Cabecera `X-Auth-Session`. Toda la API exige sesión válida; el niño NO se loguea (el adulto deja la sesión abierta en la tablet). 4. **Panel de padres**: además de la sesión, exige **desbloqueo con PIN** (`POST /api/parents/unlock`), que concede el rol PARENT durante 30 min. 5. **Sin Keycloak**: auth propia encapsulada en el paquete `security` (`AuthService`, `SessionAuthService`, `SessionAuthFilter`). 6. **Preferencia OpenDyslexic** pasa a campo por niño (`child.dyslexia_font`) + un default de cuenta (`family.default_dyslexia_font`). ## Consecuencias - Registro abierto (cualquiera crea una familia). Rate-limiting y verificación de email quedan como mejora futura (homelab). - La migración del esquema (`002-multitenant.yaml`) añade `family_id` NOT NULL; en BD ya poblada habría que hacer backfill (en este proyecto se parte de BD limpia con `docker compose down -v`). - La sesión de 30 días en el dispositivo es un compromiso UX/seguridad razonable para un kiosko doméstico; revocable borrando la fila de `family_session`. - Sustituir la auth por un IdP externo (Keycloak) solo afectaría al paquete `security`.