Rust · sin GC · un thread por shard · WAL persistente

Cache clínico
durable.

Habla RESP como Redis/Valkey, pero entiende salud: tipos nativos HL7 / FHIR, TTL que es una condición y no un número, invalidación por evento, colas de trabajo con reintentos y auditoría encadenada que sobrevive a la caída.

6+1
tipos clínicos
2×
protocolos: RESP + texto
kill -9
y la cola no pierde un HL7
Posicionamiento

Habla Redis. Piensa en clínica.

Klinex se ubica entre las capas de integración —API gateways, routers HL7/FHIR, motores conversacionales— y los servicios que consumen estado clínico. Cualquier cliente Redis se conecta; los que conocen Klinex aprovechan la semántica de dominio.

Redis/Valkey te da

  • Cache genérico de blobs opacos
  • TTL numérico: adivinar cuántos segundos
  • Expiración por tiempo, no por evento clínico
  • Colas: las armas tú, con sorted sets y scripts Lua
  • Auditoría: la pones tú, como middleware

Klinex además

  • Tipos clínicos nativos, indexados y consultables
  • TTL semántico: hasta que ocurra el alta
  • Invalidación por evento, sin polling
  • Colas con retardo, prioridad y reintentos, sin scripting
  • Auditoría encadenada por hash, en el camino de escritura
Tipos nativos

Estado clínico, no blobs

Cada tipo determina qué se indexa, qué eventos lo invalidan y cómo se lee su estado. Diseñado para cualquier clínica u hospital: el identificador de paciente admite RUT, MRN, FHIR, DNI extranjero o pasaporte.

PACIENTE

RUT, MRN, FHIR, DNI o pasaporte. Indexado para búsqueda directa.

SESION

Máquina de estados conversacional para motores LLM y WhatsApp.

HL7_MSG

Mensaje HL7 v2 crudo, con headers MSH, PID y PV1 indexados.

FHIR_RES

Recurso FHIR R4, consultable por resourceType e identifier.

SLOT_AGENDA

Unidad de agendamiento con transiciones de estado explícitas.

ALERTA

Alerta clínica con severidad; expira al confirmarse el evento.

TTL semántico

El tiempo es una condición, no un número

Un mensaje de admisión no "expira en 3600 segundos": deja de ser válido cuando llega el alta, o cuando el slot pasa a CONFIRMADO. Klinex modela la intención clínica real.

HASTA_EVENTO <evento>Se invalida cuando se publica el evento.
HASTA_ESTADO <tipo> <estado>Se invalida cuando el recurso alcanza ese estado.
HASTA_CONFIRMACIONSe invalida con un ACK explícito.
VENTANA_SESIONLigado al turno conversacional activo.
HASTA_TIMEOUT <seg>Fallback numérico, sólo como red de seguridad.
Superficie de query

Tu cliente Redis ya funciona

Comandos estándar sobre el namespace genérico; comandos K* para la semántica clínica con auditoría. La identidad se verifica contra el catálogo —no se afirma— y sin ella no hay escritura clínica.

redis-cli -p 6379 · klinex.gitweave.run
> AUTH hercules $TOKEN # el servicio lo pone el catálogo OK > KSET HL7_MSG hl7:1 "MSH|^~\&|REMI|…|ADT^A01|…" HASTA_EVENTO alta OK > KQUERY HL7_MSG paciente_id 12345678-9 1) "hl7:1" > KPUBLISH alta # el evento invalida la clave (integer) 1 > KGET hl7:1 (nil) > KAUDIT hl7:1 1) "… SET HL7_MSG actor=hercules servicio=api-hercules hash=b4662561…" 2) "… INVALIDATE HL7_MSG actor=hercules servicio=api-hercules prev=b4662561…"
Colas de trabajo

El HL7 aceptado se procesa, aunque el destino esté caído

Recibir un mensaje y ejecutar un procedimiento contra Oracle o SAP son dos cosas distintas. Si el destino está degradado, el mensaje no se pierde ni retiene el hilo: se encola con un retardo y se reintenta. Entrega at-least-once, deduplicada por MSH-10.

KPUSH <cola> <payload> ID <msh-10>El mismo mensaje reenviado no se encola dos veces.
… DELAY <ms> · PRIO <0-9>Retardo y prioridad a la vez: son dos índices, no un score.
KPOP <cola> [COUNT <n>]Entrega con plazo de confirmación; sin ACK, vuelve a la cola.
KACK · KNACK · KREJECTConfirmar, reintentar con backoff, o descartar sin gastar intentos.
KQDEAD <cola>Lo que agotó sus reintentos, con el payload intacto.
redis-cli -p 6379 · cola hl7:sp
> KPUSH hl7:sp "MSH|^~\&|LIS||HIS||ADT^A01|MSH10-777|…" ID MSH10-777 "MSH10-777" > KPUSH hl7:sp "MSH|…|MSH10-777|…" ID MSH10-777 # el origen reenvía (nil) # no duplica: ya estaba > KPOP hl7:sp 1) 1) "MSH10-777" 2) "MSH|^~\&|LIS||HIS||ADT^A01|…" 3) (integer) 1 # intento > # el SP falla: reintentar en 30s en vez de perder el mensaje > KNACK hl7:sp MSH10-777 DELAY 30000 (integer) 1 > KQSTAT hl7:sp 1) "listos" 2) (integer) 0 3) "diferidos" 4) (integer) 1 5) "sin_confirmar" 6) (integer) 0 7) "muertos" 8) (integer) 0 9) "edad_mas_viejo_ms" 10) (integer) 412
🎯

Prioridad y retardo

La receta clásica sobre Redis mete ambas en un mismo score y obliga a elegir cuál se respeta. Aquí son dos índices con un paso de promoción: el diferido no sale antes de tiempo y el urgente no espera detrás.

⚛️

Atómico sin Lua

Sacar de la cola y marcar como no confirmado ocurren dentro de la tarea que posee la cola. No hay nada que hacer atómico: no se emula una cola sobre estructuras genéricas, la cola es del motor.

🪦

Nada desaparece en silencio

Al agotar los intentos el mensaje no se borra: pasa a la cola de muertos con su payload. Un HL7 que falló cinco veces y se evapora es justo el fallo que esto existe para evitar.

🧾

El ciclo queda auditado

Ingreso, duplicado detectado, entrega perdida, devolución y muerte entran al audit trail encadenado. Saber que el origen reenvió el mismo mensaje es exactamente lo que un canal opaco no te dice.

Política declarada

Un archivo decide quién puede qué

El catálogo es la única fuente de verdad, y el motor lo reconcilia: lo que está vivo y no está declarado se elimina. No hay forma de mutarlo por protocolo — KCATALOG sólo lee. La ausencia de declaración es denegación, no permiso.

klinex.yaml · política reconciliada cada 30s
actores: hercules: servicio: api-hercules # lo pone el catálogo, no el cliente escribe: [HL7_MSG, PACIENTE] lee: [HL7_MSG, PACIENTE] colas: [integracion_hl7] publica: [alta_paciente] suscribe: [alta_paciente, 'cola:integracion_hl7'] audita: false token_sha256: "93f7f70d…" # el hash, no el token
🔑

La identidad se verifica

El token se comprueba contra el token_sha256 declarado, y el servicio que llega al audit trail sale de la declaración, no de la conexión. Con mTLS es la huella del certificado y no viaja ningún secreto.

🚫

Denegado por defecto

Permisos separados para leer, escribir, usar colas, publicar, suscribirse y auditar. Un actor sin declaración no hace nada, y quitarlo del archivo revoca sus sesiones abiertas sin reiniciar.

♻️

La deriva se revierte

Cargar un archivo es una convención; reconciliarlo es una garantía. El loop compara lo declarado con lo vivo y elimina lo que sobra, así que el archivo no puede quedar mintiendo.

📓

La política también se audita

Cada cambio del catálogo entra al audit trail. Saber quién tocó una ficha sirve de poco si no consta quién cambió las reglas que decidían quién podía tocarla.

Garantías · modo Marte

Auditable por diseño. Durable por defecto.

🔗

Cadena por hash

Cada SET, DELETE e INVALIDATE encadena su hash SHA-256 con el de la operación previa. Alterar el pasado invalida todo lo posterior.

💾

WAL que sobrevive a kill -9

Audit trail y colas se persisten con framing CRC32 en logs separados; al arrancar se reproducen y toleran truncamiento por caída. Sin reinicios manuales.

El ingreso espera al disco

Un KPUSH no responde hasta que el mensaje está persistido: es la diferencia entre «acepté tu HL7» y «acepté tu HL7 y no lo voy a perder». Tras una caída dura, lo confirmado no revive y lo que estaba en vuelo se re-entrega conservando sus intentos.

📡

Invalidación por evento

Los sistemas publican eventos por el canal pub. Sin polling, sin escaneos lazy de expiración sobre tipos clínicos.

🧩

Sharding sin locks

Un thread por shard, modelo actor sobre Tokio. El estado no se comparte: se accede por mensajes. El log vive en un shard aparte.

Deja de tratar el estado clínico como un blob.

Un binario estático de ~2 MB. Habla RESP. Entiende salud. Y no pierde mensajes.