EMA Lab expone 38 módulos API bajo el worker Hono, organizados
por dominio funcional. Cada ruta declara explícitamente su auth
layer y su allowlist de roles RBAC, lo que permite auditar el
contrato completo del producto sin inferir reglas globales. Cinco
layers de autenticación cubren los distintos contextos de acceso
—staff con JWT Supabase, paciente con token corto, hub on-premise
con bearer rotable, plataforma con header compartido, y rutas
estrictamente públicas— y seis roles RBAC distribuyen los permisos
sobre los handlers que requieren autenticación de staff.
Layer Cómo funciona Mounting publicSin credenciales rutas explícitas antes de authMiddleware supabaseJWT Supabase + tenantMiddleware + clientIsolationMiddleware app.use("/api/*", authMiddleware)portal-tokenToken base64 firmado in-app, exp 1h mount antes de authMiddleware hub-tokenBearer contra hub_registry.jwt_token activo mount antes de authMiddleware platform-keyHeader X-Platform-Key contra PLATFORM_API_KEY mount antes de authMiddleware
Rol Descripción lab_adminAdministración total lab_directorFirma clínica + políticas (TAT, SLA, routing, exclusions) lab_techTM/BQF — ingreso resultados, validación técnica, QC, micro lab_receptionAdmisión, órdenes, check-in, facturación lab_phlebotomistToma de muestras, check-in, etiquetas lab_viewerSolo lectura informes
Método Path Notas GET /api/healthLiveness probe GET /api/configBootstrap: Supabase URL + anon key GET /auth/me(supabase auth) user + tenants activos GET /auth/resolve-tenantRate limit 30/min — slug → tenant name GET /auth/callbackOAuth redirect POST /api/portal/verifyRate limit 5/min — RUT + birthdate → portal token GET /api/portal/verify/:codeRate limit 30/min — QR público sin PII
Método Path Notas GET /api/portal/resultsDR finales del paciente GET /api/portal/results/:idVerifica subject=Patient/{payload.patient_id} GET /api/portal/results/:id/pdfPendiente implementación
Método Path Notas POST /api/hub/heartbeatRate limit 60/min GET /api/hub/worklistsPull worklists para equipos on-prem POST /api/hub/resultsInbound + auto-consume inventario POST /api/hub/renew-tokenRotación token Hub
Método Path Notas POST /api/platform/tenantsProvisioning + seed RLS GET /api/platform/tenantsListado GET/POST /api/platform/tenants/:id/adminsListado + creación admin POST /api/platform/tenants/:id/admins/:uid/reactivateReactivar DELETE /api/platform/tenants/:id/admins/:uidDesactivar POST /api/platform/tenants/:id/admins/:uid/resendReenviar invitación PATCH /api/platform/tenants/:id/statusSuspender / reactivar tenant PATCH /api/platform/tenants/:id/slugRename slug + history POST /api/platform/tenants/:id/sync-orgRe-sync Organization FHIR
Módulo Path Roles permitidos Justificación Catálogo /api/catalog/*admin, director, tech, receptionRecepción selecciona tests Profiles /api/catalog/profiles/*idem Perfiles agrupan tests Specimens /api/catalog/specimens/*idem Tipos de muestra Containers /api/catalog/containers/*idem Tipos de tubo Lab sites /api/lab-sites/*adminSedes del tenant TAT /api/tat/*admin, directorPolíticas TAT por sección SLA /api/sla/*admin, directorSLA cliente × sección Routing /api/routing/*admin, directorReglas inter-sede Exclusions /api/exclusions/*admin, directorTests excluidos por cliente Barcode config /api/barcode-config/*adminEstrategia generación Sections /api/sections/*admin, directorEstructura organizacional Clientes /api/clients/*adminAdmisión clientes externos Equipment /api/equipment/*admin, director, techInventario equipos Maintenance /api/maintenance/*admin, director, techPlan + history mantenciones Transport /api/transport/*admin, directorRutas de transporte
Módulo Path Roles permitidos Notas Órdenes /api/orders/*admin, reception, techdirector no — su rol es firmaMuestras /api/samples/*admin, reception, phlebotomist, techToma + check-in Check-in /api/checkin/*idem Recepción de muestras Worklists /api/worklists/*admin, director, techCola técnica Resultados /api/results/*admin, director, techTM ingresa, director firma Validación /api/validation/*idem 4 stages QC /api/qc/*idem Westgard + Bull’s Reportes /api/reports/*admin, director, tech, reception, viewerviewer solo aquíNo conformidades /api/nonconformities/*admin, director, tech, receptionCLSI GP41 16 razones
Módulo Path Roles permitidos Notas Micro /api/micro/*admin, director, techCultures, antibiograma, MALDI Molecular /api/molecular/*idem Parser registry + sign Stewardship /api/micro/stewardship/*idem CLSI M39 trimestral Stewardship admin /api/admin/stewardship/*adminEmission failures, retry manual Inventario /api/inventory/*admin, director, techStock + auto-consume
Módulo Path Roles permitidos Notas Settings profile /api/settings/profileself GET / PATCH propio Settings org /api/settings/organizationadminWhitelabel, billing Settings users /api/settings/usersadminTenant roster product=lab Settings data /api/settings/data/*adminCatalog imports
Módulo Path Roles permitidos Notas Billing /api/billing/*admin, receptionListas de precios, invoices y facturación electrónica
Módulo Path Roles permitidos Notas Connectors /api/connectors/*adminHL7v2 + FHIR Subs admin
Módulo Path Roles permitidos Notas EMI /api/emi/{chat,access}authenticated Server-side proxy a EMI_BASE_URL, defense-in-depth tenant_emi_access
Módulo Path Roles permitidos Notas Dashboard /api/dashboard/*authenticated Read-only métricas
Endpoint Auth Fuente POST /api/billing/invoices/:id/dte-callbackX-API-Key per-tenant ema-dte worker (idempotente via invoice_id) POST /api/connectors/fhir/inboundX-Tenant-Id + handler validation Aidbox Subscription webhook POST /api/hub/resultshub-token Hub on-prem
app.use("/api/<modulo>/*", requireRole(...)) ANTES del
app.route en src/index.ts. No asumir herencia del prefix.
Endpoint mutante (POST/PUT/PATCH/DELETE) → rol más permisivo
debe ser lab_tech o más restringido. Nunca authenticated puro.
Webhook/callback → documentar auth aquí + agregar a “multi-auth”.
Endpoint público → debe justificarse (PII en endpoint público
= vulnerabilidad clase A).
Rate limiting pre-auth + endpoints de enumeración → vía
src/middleware/rate-limit.ts (L086).
# Listar todos los requireRole declarados
grep -n " app.use( \" /api/.*requireRole " src/index.ts
# Listar app.route — cualquiera sin requireRole precedente requiere revisión
grep -n " app.route " src/index.ts
Tests de RBAC: tests/security/rbac-matrix.test.ts — mutation testing
sobre cada endpoint mutante. 100% cobertura desde S7 (L087).