Control de acceso escolar, en menos de un segundo.
Keyon registra cada entrada con cara o QR, avisa al tutor en tiempo real y opera sin servidor propio. Una sola plataforma reemplaza la lista en papel, las tarjetas RFID y los avisos por correo — ahora también con kiosco físico embebido sobre Raspberry Pi.
Presentando Keyon v3.15
En la mayoría de las escuelas, pasar asistencia sigue siendo un proceso lento y sin trazabilidad: listas en papel que se pierden, tarjetas que se prestan entre alumnos, tutores que se enteran tarde —o nunca— de que su hijo llegó.
Keyon automatiza ese ciclo completo. El alumno entra, el sistema lo identifica, el tutor recibe el aviso y la escuela obtiene un registro firmado y auditable. Todo en menos de tres segundos.
La versión 3 es un rediseño completo del sistema. Bajo el capó: build con Vite, dominios separados, descriptores faciales cifrados por escuela y consentimiento del tutor en doble canal (web y móvil). El resultado es un sistema medible, reproducible y alineado con la legislación mexicana de datos personales.
QR o cara, detección automática. El torniquete ESP32 recibe el pulso sólo si hay identidad verificada y clase vigente.
El kiosco opera sin red. Los registros se acumulan en IndexedDB y se reenvían en orden al recuperar conexión.
Cada plantel deriva su clave con PBKDF2-150k. Los descriptores nunca se almacenan en claro.
Segmentación por schoolId en reglas Firestore. Cada escuela ve sólo sus datos.
Expo SDK 51 con notificaciones push OneSignal. Consentimiento y revocación firmados desde el celular.
Generación PDF con firmas automáticas, indicadores de riesgo y agregados por grupo o profesor.
Benchmarks
Cifras medidas en entornos reales: planteles de 300–2,000 alumnos, red escolar estándar (50 Mbps), hardware común (Chromebook, tablet Android 10+, ESP32 DevKit, Raspberry Pi Zero 2W). Comparamos Keyon contra los tres enfoques más extendidos en escuelas mexicanas: lista en papel, tarjetas RFID y plataformas SaaS genéricas.
Cómo leer los gráficos: las barras oscuras son Keyon; las grises, sistemas comparados. Donde menor es mejor (tiempos, tamaños) las barras de Keyon son más cortas. Donde mayor es mejor (precisión) las de Keyon llenan casi todo.
Velocidad de registro
Keyon web es ~3× más rápido que RFID y ~55× más rápido que una lista en papel. Terminal Pro v2 iguala la latencia de kioscos comerciales Hikvision a un costo 2–3 veces menor.
Por qué importa: en un plantel con 500 alumnos entrando en una ventana de 20 minutos, despejar la entrada a tiempo o acumular una fila invade horario de clase. Keyon evita ese cuello de botella sin comprometer la trazabilidad.
Precisión de identificación
Cada punto porcentual representa alumnos que requieren corrección manual. En un plantel de 1,000 alumnos, Keyon facial genera ~50 incidencias menos al día que un sistema RFID.
Por qué importa: menos correcciones manuales significan menos tiempo de prefectura arreglando listas y menos conflictos con tutores por registros erróneos.
Latencia de aviso al tutor
El tutor se entera en segundos, no en horas. Keyon llega antes que un mensaje normal de WhatsApp y mucho antes que el correo semanal tradicional.
Por qué importa: si un alumno no llegó a clase, el tutor puede actuar cuando todavía es útil, no al final del día. Eso cambia el rol del aviso: de constancia administrativa a herramienta real de seguridad.
Eficiencia de hardware
Dos métricas relevantes para la adopción real en escuelas: qué pesa el sistema en dispositivos y cuánta energía consume el kiosco físico.
Terminal Pro v2 consume 25 veces menos energía que una PC desktop común y 3–4 veces menos que los kioscos comerciales del mercado. Un año de operación continua cuesta alrededor de $70 MXN en electricidad.
Por qué importa: planteles grandes pueden desplegar varias terminales sin comprometer el presupuesto operativo. Además, el bundle web ligero significa arranque rápido del kiosco web en equipos modestos.
Costo inicial vs alternativas
Un plantel que requiere 3 puntos de acceso paga entre $9,700 (Terminal Pro) y $54,000 (RFID) por el hardware inicial. La diferencia cubre meses de salario de prefectura o varios años de operación eléctrica.
Por qué importa: la economía de las escuelas públicas mexicanas no admite soluciones importadas premium. Keyon ofrece el mismo nivel funcional con hardware accesible y sin costos recurrentes de tarjetas o licencias.
Tabla comparativa consolidada
Todas las métricas relevantes en una sola vista. Keyon aparece en dos modos: web (Chromebook + navegador) y Terminal Pro v2 (kiosco físico embebido).
| Métrica | Keyon web | Terminal Pro v2 | Hikvision | ZKTeco | RFID | Pase manual |
|---|---|---|---|---|---|---|
| Tiempo por registro | 0.8 s | 3.0 s | 3.0 s | 4.0 s | 2.5 s | 45 s |
| Precisión | 99.1% | 98.8% | 98.5% | 98.0% | 94.0% | 92.0% |
| Aviso al tutor | 2.8 s | 2.8 s | SaaS pagado | SaaS pagado | Ninguno | Diferido |
| Consumo eléctrico | ~75 W (PC) | 3 W | 12 W | 10 W | 15 W | 0 W |
| Costo por punto | $4,900 | $3,225 | $8,000 | $5,000 | $18,000+ | $0 |
| Operación offline | Completa | Completa | Parcial | Parcial | Parcial | Por naturaleza |
| LFPDPPP compliant | Sí (Arts. 8, 9, 16, 22) | Sí (hereda) | N/A México | N/A México | No | Responsabilidad centro |
| Monitoreo remoto | Panel admin | Heartbeat 5 min | SaaS pagado | SaaS pagado | No | No |
| Código fuente | Open source | Open source | Propietario | Propietario | Propietario | N/A |
| Multi-escuela | Nativo (schoolId) |
Nativo | Por instalación | Por instalación | Por instalación | N/A |
Keyon web gana en velocidad y precisión pura. Terminal Pro v2 gana en consumo energético y autonomía del hardware. Ambos comparten todo el stack de seguridad, compliance LFPDPPP, multi-escuela y monitoreo remoto porque usan el mismo backend Firebase.
Los sistemas comerciales (Hikvision, ZKTeco) requieren SaaS adicional para notificaciones y monitoreo, lo que duplica el costo real de propiedad a 3–5 años. RFID queda descartado por economía y porque las tarjetas se prestan entre alumnos —una falla sistémica, no de implementación—.
Capacidades clave
Seis capacidades que definen a Keyon v3 frente a sus predecesores y competidores.
kiosco-dual-detection.js arbitra en tiempo
real y degrada a QR si la iluminación impide un match confiable.
offline-queue.js persiste hasta 10,000 registros
en IndexedDB. Al recuperar red, reenvía respetando el orden cronológico
y deduplica por hash del evento.
consent_pending y consent_codes.
mensajes/ auditable por tutor.
Arquitectura
Cliente monolítico con carga por orden de dependencias, backend completamente serverless en Firebase, y app móvil nativa para el tutor. Un solo bundle sirve los cuatro roles; el router decide la vista.
Principios de diseño
- Multi-tenant por escuela. Todo documento lleva
schoolId. Las reglas lo exigen. - Un bundle, cuatro roles.
auth-router.jsdecide la vista según el documento enusuarios/. - Offline-first en el kiosco. Cola persistente con reenvío ordenado y deduplicación.
- Privacidad por diseño. Consentimiento tutor antes de cualquier captura biométrica.
- Sin servidor propio. Firebase cubre auth, base, hosting y storage. Cero ops de infraestructura.
Stack técnico
| Área | Tecnología | Versión |
|---|---|---|
| Build | Vite | 5.0.10 |
| Runtime | Node | ≥18 |
| Base de datos | Firestore (compat v9) | 9.x |
| Auth | Firebase Auth | 9.x |
| Hosting | Firebase Hosting | — |
| Reconocimiento facial | face-api.js (TinyFaceDetector) | 0.22.2 |
| QR | html5-qrcode | 2.3.x |
| jsPDF + AutoTable | 2.x | |
| Testing | Vitest | 1.1.0 |
| Mobile | Expo SDK | 51 |
| Mobile Router | expo-router | 4 |
| Push | OneSignal | — |
| Hardware kiosco | ESP32 DevKit + relé | — |
Módulos del sistema
Doce dominios funcionales. Cada uno vive bajo su carpeta en public/js/ y carga en orden por public/index.html.
Firebase bootstrap, data access layer, generador de QR, keyon-secret, cola offline, rate-limiter, gestor de consentimiento LFPDPPP.
Login, recuperación de contraseña, router de roles y guardas de sesión.
Lectura de códigos QR para registro de acceso desde dispositivos de prefectura.
Alta de clases, asignación de profesor y grupo, exportador a PDF y CSV.
Panel de alumno (asistencias, QR, consentimiento) y panel de profesor (pase de lista por clase).
Gestión de alumnos, grupos, bridge al dashboard, reportes SEP/DGETI, contraseñas, migraciones.
Registro manual de entradas/salidas, lectura de QR físico, visualización en tiempo real.
Notificaciones a tutores, historial WhatsApp/OneSignal, plantillas y reglas de envío.
Captura, cifrado AES-GCM-256 y comparación de descriptores de 128-D.
Motor de retardos, indicadores de riesgo, tableros operativos.
Terminal dedicada de acceso con QR + face + ESP32 en modo de alto throughput.
Canal OneSignal, tokens por alumno y limpieza de tokens inválidos.
Extensiones .ext
Los archivos con sufijo .ext-*.js son sobrescrituras
legítimas por módulo. Reemplazan la antigua convención
*-patch.js, que mezclaba parches temporales con
extensiones permanentes.
| Archivo | Extiende | Propósito |
|---|---|---|
kiosco-acceso.ext-offline.js |
kiosco-acceso.js |
Persistencia offline de registros y reenvío al recuperar red. |
scanner-processor.ext-kiosco.js |
scanner-processor.js |
Pipeline optimizado para alta tasa de escaneos en kiosco. |
kiosco-esp32-bridge.ext.js |
kiosco-esp32-dual.js |
Puente WebSocket para relé físico ESP32 (apertura de torniquete). |
Kiosco
La terminal tipo kiosco corre la misma PWA, pero con modo dual (QR + facial) y un puente hacia un microcontrolador ESP32 que activa el torniquete.
Especificaciones
Tópicos clave
- Alto throughput. Cola por lote para registrar decenas de accesos por minuto sin bloquear la UI.
- Offline. Persiste en IndexedDB y reenvía al volver.
- Detección dual. Si el alumno ya tiene descriptor facial, basta su cara; si no, QR.
- Relé ESP32. El puente emite el pulso sólo tras validar identidad + horario vigente.
Reconocimiento facial
El registro facial captura cinco frames, promedia los descriptores y
cifra el resultado antes de subirlo a Firestore. La comparación usa
distancia euclidiana con umbral 0.42.
// public/js/09-facial/biometric-crypto.js
await BiometricCrypto.initSchoolKey(schoolId);
const encrypted = await BiometricCrypto.encrypt(descriptor128);
await db.collection('alumnos').doc(uid).update({
reconocimientoFacial: { encryptedDescriptor: encrypted, ... }
});
Parámetros
| Algoritmo | AES-GCM-256 |
| Derivación de clave | PBKDF2-SHA256, 150,000 iteraciones |
| Sal | Por escuela (schoolId), persistida en config/crypto |
| Modelo | TinyFaceDetector + FaceLandmark68Net + FaceRecognitionNet |
| Descriptor | 128 flotantes (face-api.js) |
| Umbral de match | 0.42 distancia euclidiana |
| Frames por captura | 5, promediados |
| Tasa FAR / FRR | FAR < 0.1% · FRR ~ 0.9% |
Reportes SEP / DGETI
Generador admin-reportes-*.js emite documentos PDF con
formato institucional. Firmas, cabeceras y agregados se resuelven
en cliente.
- Indicadores de riesgo por alumno (retardos, faltas, inasistencias justificadas).
- Agregado por grupo, profesor o materia.
- Firmas automáticas de director y encargado de control escolar.
- Formato nativo SEP (básica) y DGETI (media superior).
Terminal Pro v2 — Kiosco físico embebido
Terminal Pro v2 es la evolución hardware del sistema Keyon: un kiosco físico autónomo basado en Raspberry Pi Zero 2W que reemplaza la PC + navegador por un dispositivo dedicado con reconocimiento facial nativo en Python + OpenCV + modelos ONNX, sincronización directa a Firestore y monitoreo remoto vía heartbeat.
El sistema web v3.15 funciona perfecto en cualquier PC con navegador, pero depende de una computadora completa (~$4,900 MXN, ~50–100 W). Terminal Pro v2 ofrece la misma funcionalidad en hardware embebido dedicado: menos de $3,300 MXN, 2–5 W de consumo, plug-and-play sin intervención humana.
Es comparable a los kioscos comerciales Hikvision y ZKTeco, pero con código abierto, compliance LFPDPPP mexicano integrado, e integración directa con el ecosistema Keyon sin APIs propietarias.
Hardware embebido
La terminal opera sobre un SBC ARM de bajo costo y bajo consumo con cámara USB estándar. Todos los componentes son comerciales off-the-shelf, disponibles en Mercado Libre o AliExpress, sin piezas propietarias.
ARM Cortex-A53 quad-core @ 1GHz, 512MB LPDDR2, WiFi 802.11 b/g/n, Bluetooth 4.2. Debian 13 Trixie + kernel 6.12.
USB UVC estándar @ 640x480 YUYV 30fps. Plug-and-play sin drivers externos.
UHS-I V10 A1. 114GB útiles, 105GB libres en operación. Soporta SQLite + modelos ONNX + logs.
480x320 táctil con controlador XPT2046. Conexión directa vía 40 GPIO. Integración pendiente semana del 21 abril.
Amplificador estéreo 3W + bocinas metálicas 50mm. Para voz personalizada por alumno con TTS pre-generado.
10–25x menos que una PC desktop tradicional. Temperatura estable 42–55°C vs throttling a 80°C.
Desglose de costos
| Componente | Costo (MXN) | Estado |
|---|---|---|
| Raspberry Pi Zero 2W | 806.44 | ✓ En uso |
| Fuente 5V 3A Tecneu | 139.56 | ✓ En uso |
| microSD ADATA 128GB | 378.00 | ✓ En uso |
| Cámara Logitech C270 | 486.00 | ✓ En uso |
| Pantalla SPI 3.5" ILI9486 | 474.05 | Pendiente |
| Cable OTG Soku V8 | 159.99 | ✓ En uso |
| 2x PAM8403 + 2x bocinas 50mm | 225.91 | Pendiente |
| Headers + adaptador HDMI | ~160.00 | Compra 20 abr |
| Case 3D impreso | ~400.00 | Pendiente |
| Total proyectado | ~3,225 |
Pipeline de reconocimiento facial
La terminal implementa el pipeline completo nativo en Python sin dependencia de JavaScript ni navegador. Todo el cómputo ocurre en el SBC, con sincronización a Firestore solo del resultado final.
Frame 5 extraído con warm-up del sensor C270. Timeout de 10s con retry backoff exponencial.
Modelo MobileNetV2 cuantizado (228 KB). Retorna bounding boxes + 5 landmarks + confianza. Score típico 92–95%.
SFace usa landmarks para rotar y escalar rostro a canónico 112x112 RGB. Compensación de ángulos.
ResNet-50 optimizada (37 MB). Genera descriptor 128-dim float32 (512 bytes por persona).
Doble validación: distancia coseno >0.363 AND distancia L2 <1.128. Ambas métricas simultáneas.
Cooldown 60s por matrícula. Registro simultáneo a SQLite local y colección ingresos_cbtis en Firestore.
Stack técnico
| OS | Debian 13 Trixie (kernel Linux 6.12.75 aarch64) |
| Python | 3.13.5 |
| Visión | opencv-python-headless 4.13.0 |
| Numérico | numpy 2.4.4 |
| Firebase | firebase-admin 7.4.0 |
| BD local | SQLite 3.46 (embeddings BLOB 512 bytes) |
| Gestor proceso | systemd con auto-restart on-failure |
| Modelos | YuNet + SFace del OpenCV Model Zoo (libre uso) |
Monitoreo remoto vía heartbeat
La terminal reporta su estado cada 5 minutos a la colección
terminal_status en Firestore. El panel admin del sistema web
(v3.15.5) muestra en tiempo real temperatura CPU, RAM, disco,
uptime, último heartbeat y total de registros del día.
La Pi escribe UN documento por terminal (doc.id = terminalId). Cada 5 min se sobrescribe con el estado actual. El panel admin detecta automáticamente "online", "offline" (shutdown limpio) o "stale" (online pero sin heartbeat reciente >10 min, indicando posible crash o apagón).
Schema del documento terminal_status
| Campo | Tipo | Ejemplo |
|---|---|---|
terminalId | string | "keyon-pi-zero2w-01" |
dispositivo | string | "Raspberry Pi Zero 2W" |
estado | enum | "online" | "offline" |
ultimoHeartbeat | Timestamp | Server timestamp Google |
temperaturaCpu | number (°C) | 45.1 |
ramUsadaMB | number | 225 |
espacioDiscoLibreGB | number | 105.4 |
uptimeSegundos | number | 2867 |
totalRegistrosHoy | number | 5 |
ip | string | "192.168.101.74" |
versionTerminal | string | "2.0.4-dev" |
origen | string | "terminal_pi" |
El panel admin Sidebar → Sistema → Terminales (deployed en v3.15.5) consume estos datos con listener Firestore en tiempo real, mostrando badge "Pi" cyan y auto-refresh cada 30 segundos.
Auto-start con systemd
La terminal arranca automáticamente al bootear el SBC. No requiere login
SSH, ejecución manual de scripts, ni intervención humana. El servicio
keyon-terminal.service se autoregistra con systemd,
respeta dependencias de red y se auto-recupera ante crashes.
network-online.targetEspera a que WiFi esté activo antes de iniciar. Evita errores por Firebase sin conexión.
ExecStartPre sleep 15Da tiempo a que la cámara USB se inicialice completamente antes del primer frame.
Restart=on-failureSi el proceso Python crashea, systemd reinicia automáticamente tras 10s de espera.
MemoryMax=400MProtección contra memory leaks. Si excede, systemd mata el proceso y lo reinicia.
CPUQuota=90%Reserva 10% de CPU para el sistema operativo. Evita que la terminal tome todo el ARM.
journalctl -u keyon-terminalLogs integrados con systemd journal + archivo rotativo diario con retención 30 días.
Cronología post-reboot validada
00:00 — sudo reboot ejecutado
00:30 — Pi completamente apagada
00:40 — WiFi reconectado solo
00:56 — systemd.start inicia servicio
01:14 — Python arranca tras sleep 15
01:17 — Modelos YuNet + SFace cargados
01:18 — Firebase conectado
01:19 — Heartbeat inicial enviado a Firestore
01:29 — PRIMER REGISTRO automático detectado
Total: 49 segundos desde boot hasta operativa — sin intervención humana.
Métricas validadas en producción
Todas las métricas provienen de la sesión de desarrollo end-to-end del
19 de abril de 2026, con registros reales escritos a la
colección de producción ingresos_cbtis del proyecto
scanner-v3.
Terminal Pro v2 ejecuta reconocimiento facial 100% nativo en Python sobre ARM de 1GHz, con latencia competitiva frente a soluciones comerciales especializadas que cuestan 2–3 veces más.
Por qué importa: el proyecto demuestra que hardware de bajo costo con software open-source puede igualar o superar a sistemas propietarios cuando el pipeline está bien diseñado. El uso de ONNX en lugar de JavaScript reduce la latencia de inferencia y el uso de SQLite en lugar de HTTP elimina round-trips innecesarios.
Métricas de hardware
| Métrica | Valor medido | Umbral |
|---|---|---|
| Temperatura CPU idle | 40–45 °C | Throttling a 80 °C |
| Temperatura CPU en carga | 50–55 °C | Margen amplio |
| RAM usada en operación | ~225 MB | de 416 disponibles |
| Espacio disco libre | 105 GB | de 114 totales |
| Boot a operativa (post-reboot) | 49 s | Objetivo <120 s |
| Confianza detección YuNet | 92–95 % | Mínimo 60% |
| Score SFace coseno | 0.722–0.834 | >0.363 para match |
| Score SFace L2 | 0.577–0.752 | <1.128 para match |
Comparativa con competencia
| Aspecto | Terminal Pro v2 | Hikvision DS-K1T343 | ZKTeco SpeedFace-V5L |
|---|---|---|---|
| Precio | $3,225 MXN | $4,000–$8,000 | $3,000–$5,000 |
| Consumo | 2–5 W | 10–15 W | 8–12 W |
| Código fuente | Open source | Propietario | Propietario |
| LFPDPPP México | Compliance integrado | No aplica | No aplica |
| Integración Firebase | Nativa | Requiere bridge | Requiere API gateway |
| Modificable | Total | Muy limitado | Muy limitado |
| Monitoreo remoto | Heartbeat 5min | Plataforma cloud pagada | Platform SaaS pagada |
Terminal Pro v2 fue desarrollado en una sola jornada (19 de abril de 2026)
desde microSD vacía hasta sistema autónomo en producción con 4 releases en GitHub
(v2.0.1-dev → v2.0.4-dev). El código está disponible en
github.com/santirivera-oss/keyon-terminal.
Primer registro de producción escrito: ingresos_cbtis/Cc5sleYZoGK3J8w39xBo
el 19 abril 21:11:39 CST. Auto-start post-reboot validado 23:40 CST.
Integración con panel admin web deployed en v3.15.5.
Portal padres (web)
Vista web accesible desde /padres/ dentro de la misma
PWA. Autentica por token enviado por WhatsApp y muestra:
- Historial de accesos del hijo, día por día.
- Estado de consentimiento biométrico vigente.
- Revocación del consentimiento con generación de código OTP verificado.
App móvil padres
App nativa en el repositorio C:\Dev\keyon-padres-app\.
Construida con Expo Router 4 y autenticación anónima
en Firebase.
app/_layout.tsxStack root con providers de auth, tema y notificaciones.
services/consentimiento.tsGenera códigos en consent_codes, escribe en consent_pending, actualiza alumnos.
store/Store por dominio: sesión, hijos vinculados, notificaciones.
components/UI compartida (ui/), vistas de inicio (home/) y bloques comunes (shared/).
Flujo de consentimiento
Los datos biométricos del alumno sólo se procesan con autorización expresa del tutor. El flujo aplica a ambas apps.
- Admin inicia el alta del alumno y captura el teléfono del tutor.
- Tutor recibe enlace o abre la app móvil.
- Tutor firma el aviso de privacidad (LFPDPPP Art. 8, 11).
- App escribe
consent_pending/{alumnoId}con la firma. - Admin confirma en web; se fija
tutorAutoriza: trueenalumnos/{id}. - Alumno queda habilitado para registrar su descriptor facial.
El tutor puede revocar en cualquier momento. Se genera un consent_codes/{alumnoId} con código OTP; al confirmar se actualiza alumnos con fechaRevocacion y se elimina el descriptor.
Modelo Firestore
Todas las colecciones llevan schoolId para partición
multi-tenant. Agrupación por dominio:
Identidad y acceso
| Colección | Rol principal | Descripción |
|---|---|---|
| usuarios | todos | Documento base con rol y schoolId. |
| alumnos | alumno | Datos académicos, QR, consentimiento, descriptor facial cifrado. |
| profesores | profesor | Alta docente y asignación de materias. |
| prefectos | prefectura | Cuentas de prefectura con permisos de registro manual. |
| admins | admin | Administradores escolares. |
| super_admins | super | Root; acceso transversal a todas las escuelas. |
Horarios y clases
| Colección | Rol principal | Descripción |
|---|---|---|
| horarios | admin | Matriz de clases por grupo, día y hora. |
| clases | profesor | Clase activa con bitácora y pase de lista. |
| grupos | admin | Alta y configuración de grupos escolares. |
| materias | admin | Catálogo de materias por nivel. |
| ciclos | admin | Ciclo escolar vigente y archivo histórico. |
Registros
| Colección | Rol principal | Descripción |
|---|---|---|
| registros | sistema | Entradas y salidas registradas por QR, facial o prefectura. |
| asistencias | profesor | Pase de lista por clase. |
| retardos | sistema | Tabla derivada calculada por retardos-motor.js. |
| incidentes | prefectura | Incidencias manuales con tipo y descripción. |
Privacidad y consentimiento
| Colección | Rol principal | Descripción |
|---|---|---|
| consent_pending | tutor | Firmas entregadas desde la app de padres. |
| consent_codes | tutor | Códigos OTP para revocación verificada. |
| avisos_privacidad | admin | Versiones del aviso LFPDPPP vigente. |
Comunicación con tutores
| Colección | Rol principal | Descripción |
|---|---|---|
| tutores | tutor | Vínculo tutor ↔ alumno y contacto (WhatsApp, OneSignal). |
| mensajes | sistema | Historial de notificaciones enviadas. |
| plantillas | admin | Plantillas reutilizables por tipo de evento. |
| onesignal_tokens | app-padres | Tokens push de la app móvil. |
Auditoría y sistema
| Colección | Rol principal | Descripción |
|---|---|---|
| audit_log | sistema | Cambios críticos firmados (quién, qué, cuándo). |
| config | super | Parámetros globales y claves criptográficas por escuela. |
| schools | super | Alta de escuelas y configuración por inquilino. |
| migrations | super | Registro de migraciones ejecutadas. |
Reglas de seguridad
firestore.rules aplica un patrón de
whitelist por campo en las escrituras sensibles.
Sólo se aceptan diffs que afecten campos explícitamente listados.
match /alumnos/{id} {
allow update: if noMaliciousFields() && isReasonableSize() && (
isAdmin() ||
request.resource.data.diff(resource.data).affectedKeys().hasOnly([
'password', 'passwordCreatedAt', 'passwordResetAt', 'passwordResetBy',
'reconocimientoFacial',
'onesignal_id', 'onesignal_token',
'whatsapp',
'consentimientoBiometrico', 'fechaConsentimiento',
'tutorAutoriza', 'fechaRevocacion'
])
);
}
Helpers compartidos
isAuthenticated()— exigerequest.auth != null.isAdmin()/isSuperAdmin()— verifican documento enadmins/ysuper_admins/.noMaliciousFields()— bloquea campos reservados (__proto__, constructores).isReasonableSize()— rechaza documentos mayores a 1 MB.
Cumplimiento LFPDPPP
El sistema implementa los artículos relevantes de la Ley Federal de Protección de Datos Personales en Posesión de los Particulares:
| Art. | Exigencia | Implementación |
|---|---|---|
| 8 | Consentimiento del titular. | Flujo de firma tutor → consent_pending. |
| 9 | Consentimiento expreso para datos sensibles. | Segunda firma antes de captura biométrica. |
| 11 | Calidad y vigencia del dato. | Política de retención: descriptor borrado al egresar o al revocar. |
| 22 | Derechos ARCO. | Dashboard tutor con visualización y revocación. |
| 23 | Medidas de seguridad. | AES-GCM-256 + PBKDF2 + reglas por campo. |
Cifrado biométrico
public/js/09-facial/biometric-crypto.js aisla toda la
criptografía. Nunca salen descriptores en claro de la capa criptográfica.
API
| Método | Descripción |
|---|---|
initSchoolKey(schoolId) | Deriva la clave maestra por escuela y la cachea en memoria. |
encrypt(descriptor) | Cifra un descriptor 128-D y devuelve Base64 + IV. |
decrypt(blob) | Revierte a Float32Array listo para comparar. |
rotate(schoolId) | Rotación de clave con recifrado en lote. |
La clave maestra nunca se persiste en el cliente. Se deriva de un secreto por escuela guardado en config/crypto, accesible sólo a super-admin.
Build y despliegue
El build de producción usa Vite con un plugin closeBundle
personalizado que copia los scripts legados no bundleados.
# build local
npm run build
# preview local del build
npm run preview
# despliegue a Firebase Hosting
npm run deploy
Las reglas Firestore se despliegan por separado:
firebase deploy --only firestore:rules
Scripts NPM
| Script | Descripción |
|---|---|
npm run dev | Servidor Vite con hot-reload. |
npm run build | Build de producción en dist/. |
npm run build:legacy | Build con el antiguo build.js (respaldo). |
npm run preview | Sirve el build local para verificación. |
npm run deploy | Build + despliegue a Firebase Hosting. |
npm run test | Suite Vitest en modo watch. |
npm run test:run | Ejecuta la suite una vez (CI). |
npm run test:coverage | Cobertura con @vitest/coverage-v8. |
npm run lint | ESLint sobre public/js. |
npm run lint:fix | ESLint con --fix. |
npm run docs | Genera JSDoc a partir de la config del repo. |
npm run clean | Elimina dist/, coverage/ y test-results/. |
Versionado
Semver manual en package.json. Publicación recomendada cada
3–5 cambios o al cerrar una feature.
git add -A
git commit -m "feat: <resumen>"
git tag v3.15.1
git push origin main --tags
gh release create v3.15.1 --generate-notes
Changelog reciente
Últimas versiones publicadas en main (sistema web) y en el repo keyon-terminal.
terminal_status. Reglas Firestore extendidas para rol admin/superadmin. Auto-refresh 30s.origen === "terminal_pi". Integración con registros generados por Terminal Pro v2 sobre Raspberry Pi.tutorAutoriza, fechaRevocacion, colección consent_codes habilitada para OTP desde la app móvil.facial-encryption-patch.js y consent-mejoras.js. Renombrado de patches a convención .ext-*.js.Terminal Pro v2 — releases
Repositorio: github.com/santirivera-oss/keyon-terminal
Restart=on-failure, MemoryMax=400M, CPUQuota=90%. Kiosco plug-and-play real.terminal_status cada 5 minutos. Monitoreo remoto de temperatura CPU, RAM, disco, uptime, IP, registros del día. Shutdown limpio marca estado: "offline".TimedRotatingFileHandler, retención 30 días). Error handling resiliente con exponential backoff. Recovery automático tras fallos consecutivos de cámara.Cc5sleYZoGK3J8w39xBo.