Turnero, onboarding y configuración de plataforma
Tres piezas recientes que no tenían página técnica. El uso está en el manual de usuario, en Agenda → Turnero y Primeros pasos → Configuración guiada (otro sitio: no se enlaza desde aquí).
Turnero (kiosk)
Módulo kiosk, depende de scheduling. Manifiesto en backend/src/modules/manifests.ts
(kioskManifest), código en backend/src/modules/kiosk/ y frontend/src/sections/kiosk/.
Esquema en 20260922000001 (estados arrived / in_progress de la cita), 20260922000002
(tablas) y 20260922000003 (vinculación).
| Tabla | Qué guarda | Acceso |
|---|---|---|
kiosk_devices | Máquinas: tipo checkin / display, alcance tenant / workspace / sede, hash del token | Cerrada a anon y authenticated; solo backend |
kiosk_ticket_sequences | Contador de turnos por sede y día local de la sede | Solo backend |
kiosk_tickets | Turnos emitidos | Lectura por RLS (la cola), escritura por backend |
- Sin cuentas en la máquina. El kiosko autentica con
Authorization: Kiosk <token>; en base solo está su sha256, y revocar es poner el hash a NULL.KioskGuardes la única barrera entre un vestíbulo público y la agenda. - Vinculación por código en pantalla (patrón RFC 8628): la máquina muestra un código, el panel
lo reclama (
POST /api/kiosk/admin/devices/claim, permisokiosk.devices.manage) y la máquina recoge su credencial en el siguiente sondeo. - Numeración atómica con
kiosk_issue_ticket(SECURITY DEFINER, soloservice_role). - Privacidad por defecto: búsqueda con respuesta idéntica para documento desconocido y sin cita,
límite de intentos con bloqueo (
KioskAttemptLimiter), y nada de servicio, profesional ni nombre en papel o TV salvo que la política (tenant_modules.configdel módulo) lo active. - El turno nunca escribe el estado de la cita por su cuenta: pasa por
SchedulingService.updateStatus, dueño único de las transiciones y sus efectos. - Instalador:
frontend/public/instalar-kiosko.ps1configura Chrome (--kiosk,--kiosk-printing, perfil propio en%LOCALAPPDATA%\XenpiaKiosk) y crea el acceso directo de arranque, que abre/kiosk?instalador=1. Se ejecuta de dos formas: con el.cmdque descarga la propia pantalla del kiosko (frontend/src/sections/kiosk/installer.ts, generado en el navegador con el origen de la página) o con la línea de PowerShell del panel. - ¿Se abrió con el instalador? La pantalla guarda la marca
instalador=1ensessionStorage(la vinculación recarga/kiosksin ella) y la envía enPOST /api/kiosk/pair/start(via_installer). Al recoger la credencial se copia akiosk_devices.via_installer, y el panel avisa al vincular y marca la máquina como «Sin instalador». Es una señal que declara la máquina: informativa, nunca autoriza nada.NULL= vinculada antes de existir la señal. - En Windows, sin la marca, la pantalla ofrece descargar el instalador antes de pedir código:
la credencial vive en el
localStoragedel perfil de Chrome, así que vincular desde un Chrome cualquiera y después instalar obliga a vincular dos veces.
Onboarding (/api/onboarding)
Onboarding en 3 pasos (ADR 0007): negocio → asistente → WhatsApp. Backend en
backend/src/onboarding/, pantalla a pantalla completa en /onboarding
(frontend/src/sections/onboarding/; /dashboard/onboarding redirige). Migraciones
20260926000001 (tablas) y 20261022000003 (paso a 3 pasos).
industry_packs: catálogo global de sectores. Supayload(tipado enshared/src/onboarding/types.ts) define módulos, roles, servicios, horarios y la plantilla del bot (prompt_template,flow_templateopcional, compliance).tenant_onboarding: estado por tenant (pending,in_progress,completed,skipped), las respuestas de cada paso (answers.business,answers.assistant,answers.channel) y los pasos hechos, para retomar sin perder datos.- Rutas:
| Ruta | Qué hace |
|---|---|
POST business | Aplica el pack (módulos, roles, servicios, recordatorios), renombra el tenant, fija zona horaria y país, y crea la sede principal si hay agenda. Frena lo evidente con los patrones del guardián. |
POST prompt-draft | Prompt prediseñado (buildAssistantPrompt); con improve: true, reescritura con IA (6/min). |
POST preview-chat | Chat de prueba con el prompt en borrador + reglas de plataforma, sin herramientas (20/min, 150/día por tenant). |
POST assistant | Arma el flujo (buildSectorFlow), lo pasa por el guardián y crea o actualiza el bot con su versión de flujo. blocked no guarda el bot. |
POST channel | Crea la cuenta de canal, enciende WhatsApp y la ata al bot (20 msg/min si el tenant es nuevo). webchat solo en sandbox. |
POST complete / skip | Cierra el onboarding (con el modo y el número conectados). |
- El Embedded Signup del paso 3 lo hace el frontend con
captureEmbeddedSignup+completeWhatsAppOnboarding(ADR 0006) sobre la cuenta que devuelvePOST channel. OnboardingGate(layout del dashboard) lleva a/onboardinga quien tienependingoin_progress; "Hacerlo después" lo desactiva por la sesión y la tarjeta del inicio permite retomarlo.- Autorización: lecturas con pertenencia al tenant; escribir pasos, completar u omitir exige rol
admin/administrador/owner o
core.modules.manage/bot.create, porque el admin recién creado nace con permisos incompletos.POST /api/onboarding/preseedexige superadmin y lo usa el alta de tenant (deja el sector elegido y el estado enpending). - Preferencias de recorridos y ayudas por usuario (
GET|POST /api/onboarding/user-state).
Guardián de seguridad de los bots (/api/bot-safety)
Detalle y motivos en el ADR 0007. En corto:
backend/src/bot-safety/: patrones (safety-patterns.ts), combinación de capas (safety-verdict.ts), capas de OpenAI (openai-safety-layers.ts) yBotSafetyService.bots.safety_status(approved | pending | review | blocked) +safety_hash, y el historial enbot_safety_reviews(solo service-role). Migración20261022000001.- El registro de bots (
BotsRegistry) llama ashouldServeantes de cargar cada bot. BOT_SAFETY_MODE:observe(por defecto) oenforce.- Superadmin:
GET queue,POST :botId/decision(approved/blocked) yPOST tenants/:tenantId/verify(quita los límites de cuenta nueva). El tenant puede pedirPOST :botId/reviewtras editar su bot.
Límites de cuenta nueva
tenants.trust_level (new | verified, migración 20261022000002). TenantTrustService
(backend/src/common/tenant-trust/) se consulta en ChannelDispatcherRegistry.dispatch: un tenant
new tiene tope diario de mensajes y de plantillas, y pasa solo a verified tras
NEW_TENANT_TRUST_DAYS. Un trigger impide que el panel cambie su propio nivel.
Configuración de plataforma (/api/admin/platform-settings)
Interruptores globales de la plataforma, no por tenant. Backend en backend/src/platform-settings/,
tabla platform_settings (20260924000001), pantalla en /admin/settings/platform.
GETlista yPUT /:keyescribe; ambos detrás deSuperadminGuard.PlatformSettingsService.get(key, fallback)cachea 30 s y es fail-closed: si la fila falta o la consulta falla devuelve elfallbackde quien llama. Tras unPUTse invalida la caché.- Claves actuales:
copilot.write_actions(el copiloto puede ejecutar cambios, solo superadmin),flow_assistant.enabled(asistente del editor de flujos; nace apagado) yproduct_tours.enabled(tours, hints y centro de ayuda del dashboard; nace encendido). - La lectura RLS está abierta a
authenticated: no guardar secretos aquí.