1 Introducción
Perfuse mueve mensajes clínicos entre sistemas que nunca fueron diseñados para comunicarse entre sí. Un laboratorio envía resultados en HL7 v2; el sistema de gestión de prácticas los espera con posiciones de campo diferentes, un identificador de paciente diferente y un acuse de recibo dentro de cinco segundos. Algo tiene que estar en el medio, y ese algo es un motor de integración.
Este manual describe lo que Perfuse hace, cada opción que acepta y —donde una decisión no es obvia— por qué funciona de esa manera.
1.1 Para qué sirve Perfuse
Una interfaz única entre dos sistemas clínicos suele costar decenas de miles, con un 15% al 20% más cada año en mantenimiento. Gran parte de ese costo no es el trabajo; es que el trabajo requiere alguien que haya trabajado con HL7 antes, y la mayoría de las prácticas no emplean a uno.
Perfuse está diseñado sobre la suposición de que la persona que lo configura tiene un problema que resolver y no tiene experiencia previa con HL7. Esa suposición se muestra en lugares específicos:
- Todo es configurable desde la interfaz web. Una característica accesible solo desde una terminal o un archivo editado a mano se considera un defecto, no una elección de diseño.
- Los mensajes se pueden rastrear paso a paso, mostrando qué leyó cada paso, qué cambió, qué omitió y por qué.
- Se puede generar tráfico de prueba que coincide con la forma de una fuente real sin contener ninguno de sus datos.
- Un canal se puede proponer a partir de un solo mensaje de muestra, con una declaración explícita de lo que el mensaje de muestra no puede decir.
- Antes de reemplazar un motor existente, su salida y la de Perfuse se pueden comparar mensaje por mensaje, con las diferencias agrupadas por campo.
1.2 Qué hay en él
Todo lo siguiente está en el mismo binario. Cada línea nombra el capítulo que lo cubre.
- Formatos. HL7 v2, FHIR R4, R4B y R5, DICOM, X12 (837, 835, 270/271, 276/277, 834, 278, 275), C-CDA y CDA, HL7 v3, NCPDP Telecommunication y SCRIPT, SMART Health Links y Cards, delimitados y sin procesar. Consulte formatos de mensaje.
- Conectores. 28 tipos de fuentes y 32 tipos de destinos, entre ellos MLLP, archivos, SFTP, bases de datos, DICOM, Kafka, AMQP 1.0, MQTT, IBM MQ y almacenamiento y mensajería de AWS, Azure y Google Cloud. Consulte la referencia de fuentes y la referencia de destinos.
- Cambiar mensajes. Filtros, once pasos de transformación declarativos, JavaScript (incluido el dialecto de Mirth) y Lua, tablas de asignación compartidas y conjuntos de códigos, y sugerencias de asignación. Consulte filtrado y transformación.
- Entregarlos. Una cola ordenada y duradera por destino, reintentos, cartas muertas que conservan el mensaje y acuse de recibo enviado después de la entrega. Consulte entrega y acuse de recibo.
- Verificar su funcionamiento. Paneles de control, paneles de control de equipo, un mapa de flujo, once tipos de alerta, métricas, seguimiento y un almacén de mensajes que encuentra los mensajes de un paciente en varios formatos. Consulte alertas, almacén de mensajes y depuración.
- Cambiar una interfaz en vivo de manera segura. Modo de sombra, contratos de alimentación, pruebas de canal y comparación de motor. Consulte pruebas.
- Dejar Mirth. Canales de Mirth, Open Integration Engine y BridgeLink explicados, traducidos y exportados de nuevo. Consulte migración.
- Pagadores y salud pública. Las cuatro API de CMS-0057, Da Vinci CRD, DTR, PAS, PDex y Plan-Net, CARIN Blue Button, un servicio de decisión opcional, eCR y ELR. Da Vinci CRD, DTR y PAS responden tanto a sus versiones 2.2.x como a sus versiones 2.1.0. Consulte CMS-0057.
- Quién puede hacer qué. SAML, OpenID Connect, LDAP, contraseñas, SCIM, TLS mutuo, seis roles, grupos de canales con un rol en cada uno, inquilinos, un servidor de autorización SMART, TEFCA y UDAP, y un registro de auditoría. Consulte seguridad.
- Ejecutarlo. Un binario estático para Linux, macOS y Windows, como un servicio, con una vista de flota de varios servidores. Consulte operaciones, la interfaz web, la línea de comandos y opciones de servidor.
1.3 Estándares y versiones
Qué versión de cada estándar implementa Perfuse y cómo se verificó. "Verificado" nombra la herramienta o referencia externa que juzgó el resultado; donde nada externo lo ha juzgado, dice así. Los resultados son de datos de prueba, no de un sitio de producción.
1.3.1 Mensajes y documentos
| Estándar | Versión | Verificado |
|---|---|---|
| HL7 v2 | cualquier versión: los mensajes se leen por sus propios separadores, no por un esquema de versión; los acuse de recibo predeterminados son 2.5.1 | Validador HL7 v2 de NIST; tráfico real de Mirth y OIE |
| HL7 v2 a FHIR | Asignación V2-FHIR de HL7 (ADT, ORU, SIU, MDM, VXU y los segmentos en ellos) | Validador FHIR de HL7 contra US Core, UK Core, FR Core, ISiK, nl-core y HL7 Europa |
| FHIR | R4 (4.0.1), con R4B y R5 leídos y escritos | Validador FHIR de HL7; Inferno |
| C-CDA y CDA | C-CDA 2.1 | ONC SITE (muestras C-CDA enviadas y recibidas a través de XDR); no por un esquema CDA |
| HL7 v3 | PIX y PDQ (IHE ITI-44, 45, 47) | Esquemas NE2008 de IHE y mensajes de ejemplo |
| DICOM y DICOMweb | DIMSE C-STORE, C-FIND, C-MOVE, C-GET, C-ECHO; QIDO-RS, WADO-RS, WADO-URI, STOW-RS | Orthanc |
| X12 (HIPAA) | 005010: 837, 835, 270/271 (X279A1), 276/277 (X212), 834, 278 solicitud (X217) e investigación (X215); 275 en 006020 (X314) | Guías de acompañamiento publicadas por los pagadores; muestras esMD de CMS |
| Conectividad CAQH CORE | vC2.2.0 SOAP y MIME, vC4.0.0 con MTOM | Un sistema UM de reemplazo escrito a partir de las reglas de CAQH |
| NCPDP | Telecomunicación D.0; SCRIPT 2023011 (y 2017071) con ePA; Beneficio de prescripción en tiempo real v13 | Escrito a partir de las guías de acompañamiento públicas de Surescripts y sus ejemplos; no contra los esquemas con licencia de NCPDP |
| UN/EDIFACT | Sintaxis 3 y 4, CONTRL | pydifact 0.1.8 |
| Compartir documentos IHE | XDS.b, XCA, XDR, MHD 4.2.2, PIXm, PDQm, XDM | Kit de herramientas XDS de NIST; ONC SITE |
| Mensajería segura directa | Declaración de aplicabilidad 1.2, XDR y XDM para Direct, DNS y descubrimiento de certificado LDAP | ONC SITE; Herramienta de descubrimiento de certificado de ONC (18 de 18) |
| AS2 | RFC 4130, compresión RFC 5402 | pyas2lib |
| Adjuntos firmados | HL7 DSDR, XAdES-X-L | xmlsec1, OpenSSL |
1.3.2 Guías de implementación de FHIR
| Guía | Versión | Verificado |
|---|---|---|
| US Core | conversiones a 9.0.0 (USCDI v6); servidor FHIR contra 6.1.0 y 7.0.0 | el validador HL7; Inferno US Core |
| Inicio de aplicación SMART | 2.2 | Inferno: 80 de 80 (aplicación pública) |
| Acceso a datos en bloque | 2.0.0 | Inferno: 48 de 48 |
| Suscripciones R5 Backport | para R4 | un receptor HTTP real; las pruebas de notificación del kit PAS |
| Da Vinci CRD | 2.2.1 y 2.1.0 | Inferno 2.2.1: 92 pasa, ninguno falla; 2.1.0 por los propios ejemplos de la guía |
| Da Vinci DTR | 2.2.0 y 2.1.0 | Inferno 2.2.0: 43 pasa, 2 fallan (un defecto del kit); 2.1.0 por los propios ejemplos de la guía |
| Da Vinci PAS | 2.2.1 y 2.1.0 | Inferno 2.2.1: 82 de 84; 2.1.0 por los propios ejemplos de la guía |
| Da Vinci PAS, envío (lado del proveedor) | 2.2.1 | la implementación de referencia de pago de Da Vinci PAS; no es un pagador de producción |
| Da Vinci PDex | 2.2.0 | Inferno PDex 2.0: pagador a pagador 19 de 19 |
Da Vinci HRex $member-match | 1.2.0 | Inferno PDex pagador a pagador |
| Da Vinci ATR | 2.1.0 | no verificado por una herramienta externa |
Da Vinci CDex $submit-attachment | 2.1 | no verificado por una herramienta externa |
| Da Vinci Plan-Net | 1.1.0 | Inferno: 199 pasa, 10 fallan (un defecto del kit) |
| Da Vinci US Drug Formulary | 2.1.0 | el validador HL7 |
| ISiK (hospitales alemanes), servidor FHIR | Stufe 5, módulo básico | conjunto de pruebas ISiK de gematik 3.0.9: 421 de 421 |
| PACIO Transiciones de Atención, Perfil de Medicación, Directivas Avanzadas, Funcionamiento | 1.0.0, 1.0.0, 2.0.0-balota, 2.0.0 | kits de Inferno de PACIO con datos de PACIO: cada error en sus datos o el kit. C-CDA a documento de TOC: el validador HL7 en ejemplos de HL7, un error restante, el propio |
| CARIN Blue Button | 2.2.0 | el validador HL7 |
| eCR | 2.1.2 | el validador HL7 |
| ELR | HL7 2.5.1 ELR Release 1 con sus erratas | herramienta ELR de ONC: 28 de 28 |
| Resumen de Paciente Internacional | 2.0.0 | el validador HL7 |
| DEQM | 5.0.0 | el validador HL7 |
| SQL en FHIR | v2 | conjunto de pruebas oficial: 144 de 144 |
| Tarjetas y Enlaces de Salud Inteligentes | Tarjetas 1.4, Enlaces STU 1 | la tarjeta de ejemplo de las especificaciones; una biblioteca de visor de Enlaces de Salud Inteligentes publicada |
| Seguridad UDAP | STU 1 | un servidor de autorización real y una comunidad de confianza |
| FHIR Facilitado por TEFCA | SOP v2.0 | no verificado contra un QHIN real |
1.3.3 Perfiles nacionales y europeos
Comprobado mediante la conversión de mensajes HL7 v2 publicados o inventados y la validación de cada recurso; los resultados completos y las razones de cada fallo se encuentran en la carpeta conformance/national del repositorio.
| Perfil | Versión | Recursos válidos |
|---|---|---|
| UK Core | STU2 2.0.2 | 185 de 187 |
| FR Core | 2.2.0 | 138 de 156 |
| ISiK Basismodul | 4.0.3 (Etapa 4) | 18 de 47; 42 de 47 con el script de paquete personalizado del hospital |
| Nictiz nl-core | 0.12.1-beta.1 (zib 2020) | 12 de 24; 18 de 24 con el script de paquete personalizado del hospital |
| Informe de laboratorio de HL7 Europa | 2.1.0 | 106 de 426 (la guía espera un documento, un resultado v2 no es) |
| Informe de alta hospitalaria de HL7 Europa | 1.0.0 | 68 de 69 |
Ninguno de esto es una certificación. Perfuse no ha sido certificado por ONC, CMS, gematik, Nictiz, NHS England o cualquier otra entidad.
1.4 Qué reemplaza
Perfuse cubre el terreno ocupado por Mirth Connect y puede importar exportaciones de canales de Mirth directamente. El capítulo sobre migración describe qué se traduce limpiamente, qué necesita una decisión y cómo probar el resultado antes de cambiar.
Las diferencias que importan en el uso diario son depuración y silencio. En Mirth, ver qué hizo un transformador con un mensaje generalmente significa volver a implementar el canal con registro agregado. En Perfuse, cualquier mensaje grabado se puede reproducir a través de la configuración actual y inspeccionar paso a paso, sin tocar el canal en ejecución. Y donde una alerta de Mirth sobre "no hay tráfico" es un umbral fijo que es inútil en cualquier fuente que esté en silencio por la noche, Perfuse aprende qué aspecto tiene una semana normal para cada fuente y alerta en consecuencia.
1.5 Cómo leer este manual
Los primeros capítulos son conceptuales y vale la pena leerlos en orden: canales, flujo de mensaje, luego transformación. Todo lo demás se puede leer según sea necesario.
Los capítulos de referencia cerca del final enumeran cada clave de configuración, su tipo, si es requerida y su valor predeterminado. Esos capítulos se generan a partir de la fuente, por lo que no pueden describir una opción que no exista ni omitir una que sí exista. Si una clave aparece en un archivo de configuración y no en el índice, no es una clave de Perfuse.
1.6 Convenciones
La configuración es YAML. La mayoría de las claves son snake_case en el cable — max_message_size, idle_timeout — y este manual siempre proporciona la forma del cable, porque eso es lo que se escribe. Hay una excepción de larga data, dataType en un canal, que es camelCase; se documenta de esa manera en la referencia del canal y se acepta solo con ese deletreo.
Las referencias de campo en HL7 utilizan la notación habitual: PID-5 es el quinto campo del segmento PID, PID-5.1 su primer componente y PID-3(2) la segunda repetición del tercer campo. Una ruta escrita sin una repetición se dirige a todas las repeticiones (ver paths).
Las duraciones se escriben como un número y una unidad — 30s, 5m, 2h. Un número sin unidad no es una duración y se rechazará en lugar de adivinarse.
2 Introducción
Perfuse es un binario único con una interfaz web integrada. No hay un servidor de aplicaciones que instalar, no hay una base de datos que provisionar y no hay una consola de administración separada.
2.1 Ejecución
perfuse serve -channels ./channels -db ./perfuse.db -addr 0.0.0.0:8443 \
-tls-cert ./cert.pem -tls-key ./key.pem
-channels es un directorio de archivos YAML, uno por canal. -db es un archivo SQLite que contiene mensajes grabados, usuarios y registros de auditoría; se crea si no existe.
Al iniciar por primera vez sin usuarios configurados, se crea una cuenta de administrador y su contraseña se escribe en el registro una vez. Esa contraseña no se puede recuperar después.
-tls-cert y -tls-key sirve HTTP plano. Eso es aceptable en una dirección de bucle de retorno para una primera vista y no es aceptable en ningún otro lugar: la interfaz web lleva credenciales y contenido de mensaje, ambos de los cuales son legibles en el cable sin TLS. Vea seguridad.2.2 El primer canal
Un canal necesita un nombre, una fuente y al menos un destino. El más pequeño y útil recibe HL7 sobre MLLP y escribe cada mensaje en un archivo:
name: first-feed
dataType: hl7
source:
type: mllp
listen: "0.0.0.0:6661"
destinations:
- name: to-disk
type: file
file:
root: /var/spool/perfuse
dir: incoming
Guarde eso como channels/first-feed.yaml y se recoge sin reiniciar.
Tres cosas sobre ese archivo son dignas de mención, porque son la forma de cada canal:
- El bloque de fuente es plano para MLLP.
listense sienta directamente ensource, no dentro de un bloquesource.mllp. Los transportes que necesitan más que una dirección — HTTP, SOAP, DICOM — tienen su propio sub-bloque, y esos se enumeran en la referencia de fuente. - Un destino de archivo necesita tanto
rootcomodir.rootes el límite que las rutas se resuelven contra y no pueden escapar;dires donde aterrizan los mensajes dentro de él. - No hay transformación y no hay filtro. Ambos son opcionales, y un canal con ninguno simplemente pasa los mensajes.
2.3 Construir un canal desde la interfaz
Todo lo anterior se puede hacer desde la interfaz web en su lugar, y para un primer canal, esa es la mejor ruta porque el formulario no le permitirá producir un archivo que no se cargue.
El constructor ofrece tres formas de entrada:
- Desde un mensaje que alguien le envió. Pegue la muestra que el laboratorio o el proveedor envió por correo electrónico. Perfuse lo lee y propone un canal, junto con tres listas separadas: lo que muestra la muestra, lo que se ha adivinado y lo que ninguna muestra puede decirle. Esto se describe en pruebas.
- Desde algo que ya funciona. Un conjunto de canales completos que solo necesitan sus direcciones llenas.
- Desde nada. Un formulario en blanco. Disponible, pero es el más lento de los tres y el más fácil de equivocar.
2.4 Verificar que funcione antes de conectar nada
Se pueden hacer dos cosas antes de que exista el sistema de envío.
Enviar un mensaje a mano desde la interfaz — el remitente de mensajes acepta texto pegado y informa qué le sucedió, incluyendo qué destinos lo aceptaron y qué hizo cada transformación.
O generar tráfico de prueba. Si el canal ha manejado algún mensaje en absoluto, Perfuse puede aprender su forma y producir más mensajes como ellos, en las mismas proporciones, sin contenido original. Vea pruebas.
2.5 Qué hacer a continuación
- Canales explica qué es un canal y cómo encajan sus partes.
- Flujo de mensaje sigue un mensaje desde su llegada hasta su entrega, lo que es la forma más rápida de entender dónde poner un cambio.
- Alertas vale la pena configurar antes de que un flujo se ponga en vivo en lugar de después del primer incidente.
3 La Interfaz Web
Perfuse está diseñado con la suposición de que todo es posible desde el navegador, y una función que solo se puede acceder editando un archivo se trata como un defecto. Este capítulo es un recorrido por lo que hay detrás de cada parte de la navegación, organizado de la misma manera que la interfaz: por la pregunta que estás haciendo.
La barra lleva Panel de control y Canales directamente, porque esos son donde la mayoría de las sesiones comienzan y terminan. Todo lo demás está agrupado, y los grupos están nombrados según preguntas en lugar de subsistemas — no vas al "subsistema de auditoría", vas y descubres quién cambió algo.
3.1 Leyendo el encabezado
Hay cinco cosas en el encabezado que son fáciles de pasar por alto.
Cada vista tiene una dirección. La barra de direcciones sigue a la vista — #/channels, #/messages, #/settings — por lo que una recarga vuelve a ella, un marcador la abre, un enlace enviado a un colega lo lleva a ella, y el botón de retroceso se mueve entre vistas en lugar de salir de la consola. Una dirección que nombra una vista por encima de su rol abre el panel de instrumentos en su lugar.
Búsqueda, o ⌘K. Busca en canales, ajustes, mensajes y el manual, lo que significa que es más rápido que navegar — particularmente para un ajuste cuyo nombre solo recuerdas a medias.
Tema. Tres de ellos: Medianoche, Oscuro y Claro. Medianoche es casi negro y es lo que una pantalla de pared en una sala de servidores suele querer; Oscuro es un gris neutral más suave; Claro es para una laptop junto a una ventana. La elección se almacena en el navegador en lugar de en la cuenta, porque un tema es una propiedad de dónde se sienta alguien, no de quién es — el mismo operador quiere Medianoche en la pared y Claro en un tren. La preferencia de la máquina se consulta una vez, para elegir un valor predeterminado sensato la primera vez.
Idioma. La consola está disponible en inglés (orden de fecha del Reino Unido o de EE. UU.), alemán, francés, holandés, español, italiano, portugués, polaco, sueco, danés, noruego o finlandés, con fechas y números escritos de la manera que lo hace esa región. La elección se mantiene en el navegador y se utiliza el idioma del propio navegador hasta que se realice una elección. Las traducciones fueron realizadas por máquina y aún no han sido revisadas por hablantes nativos; el inglés es la referencia y el selector lo indica. El código, los mensajes, las cargas, los nombres de los canales y cualquier cosa escrita nunca se traducen. El manual se publica en los mismos idiomas en https://perfuse.health/manual/<language>/ (el propio Manual de este servidor es el inglés).
Manual. El documento que estás leyendo, servido por el servidor en ejecución. Es el mismo archivo que el binario puede escribir en el disco, por lo que siempre describe la versión que tienes frente a ti en lugar de la última versión lanzada.
3.2 Monitor — si está funcionando y qué sucedió
3.2.1 Panel de control
El estado de cada canal, el rendimiento y cualquier problema actual. Esta es la pantalla que debes dejar abierta.
3.2.2 Mensajes
Cada mensaje que llegó, en qué se transformó, por destino, y qué le sucedió. Se puede buscar, incluyendo mediante una expresión de filtro, por lo que "muéstrame los que provienen de este remitente que fueron rechazados" es una consulta en lugar de una tarde.
Un mensaje se puede reproducir desde aquí y volver a procesar a través de la *configuración actual* en lugar de la que estaba en vivo en el momento — lo que es cómo se confirma que una solución funciona contra el tráfico que la rompió. Vea el almacén de mensajes para saber qué se conserva y durante cuánto tiempo.
3.2.3 Cola
Qué está esperando ser entregado y por qué. Una cola que no se está drenando es la advertencia temprana más útil que produce un motor de integración, y vale la pena emparejarla con una regla de alerta para que nadie tenga que estar vigilando. Vea entrega.
3.2.4 Alertas
Qué se ha activado y cuáles son las reglas. Las reglas se editan aquí en lugar de en un archivo. Vea alertas.
3.2.5 Métricas
Rendimiento, latencia y tasas de error con el tiempo. Útil para preguntas de capacidad y, con más frecuencia, para paquetes de junta — lo que explica por qué el logotipo del sitio aparece en él si se ha subido uno.
3.2.6 SQL en FHIR
SQL en vistas de FHIR: elija un ejemplo o una vista guardada, o escríbala, ejecútela sobre el almacén de FHIR de este servidor (todos los pacientes o uno) o sobre recursos pegados, vea las filas con sus tipos y descargue CSV, NDJSON o Parquet. Guardar la mantiene para el punto final de FHIR $sql-run. Véase formatos de mensaje.
3.2.7 Medidas de calidad
Medidas de calidad digitales: elija un ejemplo o una Medida en el almacén de FHIR, o escríbala con criterios de FHIRPath. Elija un período de medición y un informe (resumen, resumen con listas de pacientes o un paciente) y evalúelo sobre el almacén de FHIR o recursos pegados. La pantalla muestra el recuento y los pacientes de cada población, la puntuación y las estratificaciones. Descargar MeasureReport guarda el resultado de FHIR, y Guardar en el almacén de FHIR mantiene la Medida para Measure/$evaluate-measure. Las Medidas en CQL son rechazadas: Perfuse no tiene un motor de CQL. Véase medidas de calidad digitales.
3.2.8 Mapa de flujo
Una imagen de cómo se mueven los mensajes en realidad: qué fuentes alimentan qué canales y qué destinos alcanzan.
Su valor no es decorativo. Cada propiedad acumula un canal que nadie recuerda haber encargado y un destino que nadie se dio cuenta de que todavía estaba recibiendo, y ambos son visibles aquí de una manera que no lo son en una lista de archivos de canales.
3.2.9 Paneles de dashboards
Un panel de control para cada tipo de persona que vigila las interfaces: Operaciones, Conexiones, Analista de interfaz, Autorización previa, Imágenes, Ciclo de ingresos, Laboratorio, Privacidad y seguridad, y un resumen de Departamento en palabras llanas. Los azulejos muestran recuentos, estados y tiempos, nunca un mensaje o un paciente, por lo que un panel de control puede ir a un gerente o una pantalla de pared sin ampliar quién ve los datos del paciente.
Cada persona abre en el panel de dashboard su grupo de directorio o función asignada. -dashboards dashboards.yaml los asigna:
groups: {PACS-Admins: imaging, Network-Team: connections}
roles: {viewer: manager}
Un grupo gana sobre un rol, y cualquier persona no emparejada se abre en Operaciones. Organizar azulejos oculta y reordena los azulejos, el selector de canal los filtra, y Guardar como mi vista lo mantiene como su propio panel de control hasta Restablecer.
Exportar a Grafana descarga el panel de control como JSON de Grafana sobre /metrics, con una variable de fuente de datos y un filtro de canal, para cualquier persona que necesite más que una configuración ligera. Los azulejos sin métrica detrás de ellos se dejan fuera, y la descripción de la exportación los nombra.
El azulejo de Conexiones verifica cada destino en capas de red. Busca el nombre, abre una conexión TCP, completa TLS donde se utiliza, y lee la última entrega y falla del destino desde el tráfico real. Cada falla dice qué significa, por ejemplo "rechazado: el motor del socio probablemente está detenido", "tiempo de espera: un firewall o un túnel de VPN que está abajo", o "un certificado de una autoridad que este servidor no confía". Nada se envía al socio, con una excepción: un registro de inmunización que no ha tenido tráfico durante siete días se le pide su prueba de conectividad de CDC connectivityTest, que no envía nada. Con -vpn tunnels.yaml el azulejo de túneles de VPN junto a él lee el estado de cada túnel de socio de AWS Site-to-Site VPN, una conexión de Azure VPN Gateway o strongSwan en este host, para que se pueda distinguir un tiempo de espera de un túnel que está abajo. También compara la configuración de IKE y IPsec de ambos lados, ya que la nube informa la nuestra y el archivo enumera la de ellos, y dice dónde no pueden estar de acuerdo. Hoja de conexión descarga la configuración de un socio lado a lado como Markdown para enviarla; nunca mantiene una clave precompartida. Las cifras que un panel de control necesita que no se han registrado aún se enumeran bajo No medido aún en lugar de dibujarse como azulejos vacíos.
Los paneles de dashboard del departamento calculan sus propias cifras en el servidor, y cada uno dice qué ventana y fuente cubre:
- Autorización previa: solicitudes de PAS decididas dentro de las 72 horas (expedidas) o 7 días (estándar) de CMS-0057, decididas con retraso, en espera y vencidas ahora, con el tiempo medio para decidir. También cómo se respondieron los servicios solicitados (aprobados, modificados, denegados, pendientes), como cada uno se encuentra ahora, en un plazo de 30 días.
- Imágenes: objetos recibidos por C-STORE por modalidad, y aquellos que no se entregaron. También el retraso de enrutamiento por canal (la mediana y el percentil 95), consultas C-FIND y fallos por canal de consulta, e informes de imágenes (un ORU cuyo OBR-24 es una sección de imágenes, o un MDM) que no llegaron a cada destino.
- Ciclo de ingresos: conjuntos de transacciones 999 aceptados, aceptados con errores y rechazados, y reclamaciones 277CA aceptadas y rechazadas (A3, A4, A6, A7, A8). También reclamaciones 835 coincidentes con el número de cuenta de paciente con 837 enviados a través de este servidor, y las razones de rechazo más comunes.
- Laboratorio: tiempo de entrega de resultados, desde la recepción hasta el reconocimiento del último destino, y resultados críticos (OBX-8 HH, LL, AA, > o <) que no llegaron a cada destino.
- Privacidad y seguridad: quién abrió mensajes, buscó pacientes o leyó autorizaciones previas en la consola durante 7 días, desde el registro de auditoría (abrir un mensaje se audita como
message.read). También cada token de API en vivo y cuándo se utilizó por última vez; un token no utilizado durante 90 días se marca.
3.2.10 Compartir lo que un flujo realmente contiene
Perfil de este flujo informa qué llega en realidad: qué eventos de activación, qué campos están poblados y con qué frecuencia, qué formas toman los valores y qué códigos utiliza realmente el remitente. Esa última es generalmente el hecho más útil en él.
Un perfil se puede descargar y enviar a alguien más. Necesita un nombre y — obligatorio — qué sistema produjo el flujo, porque la pregunta que un desconocido hace de un perfil compartido es si describe su sistema, y un campo opcional haría que "Desconocido" sea la respuesta más común en cualquier colección compartida.
No lleva datos de paciente, y eso se verifica en lugar de asumirlo. Un perfil es estadístico: recuentos, tasas, longitudes, formas. Los valores se mantienen solo para campos que el estándar define como tablas de códigos, porque un valor de tabla de códigos no es identificable. Los valores de identificador nunca se enumeran.
Dos cosas se derivan de que sea una promesa en lugar de una esperanza. El servidor reconstruye el perfil a partir de los mensajes almacenados cuando lo exporta, en lugar de enviar el que se muestra en la pantalla — así que lo que sale del edificio fue ensamblado por el perfilador, que tiene una prueba que demuestra esta propiedad, y no por un navegador. Y la exportación se niega rotundamente si un perfil enumera valores para un campo que no es una tabla de códigos, nombrando el campo pero no los valores.
Un perfil que llega de otro lugar se verifica de la misma manera al entrar, por una razón diferente: fue producido por alguien cuyo cuidado es desconocido, y mostrar los identificadores de los pacientes sería una divulgación de este servidor. Importar uno no cambia nada — un perfil describe una fuente de alimentación, y qué hacer al respecto es una decisión separada.
3.3 Construir — cambiar lo que hace un canal y probarlo antes de que esté en vivo
3.3.1 Canales
El constructor de canales. Fuentes, destinos, filtros, transformaciones, scripts y pruebas, con el YAML generado mostrado junto al formulario mientras se trabaja — así que el archivo nunca es un misterio, y cualquier persona que prefiera el archivo puede leer a lo largo.
Consulte channels para saber qué son las partes y transformation para saber qué hacen los pasos.
3.3.2 Scripts
Un banco de trabajo para scripts, separado del constructor porque escribir un script y conectar un script son actividades diferentes.
Se ofrecen los siete ranuras — filtro, transformador, preprocesador, postprocesador, escritor, ciclo de vida y lector — y elegir la ranura correcta es importante, porque la ranura decide qué recibe el script y qué se espera que devuelva. Verificar un script como el tipo incorrecto no es una solución alternativa; compila algo diferente.
JavaScript, Lua y WebAssembly están disponibles. Consulte transformation.
3.3.3 Contratos
Expectativas sobre el aspecto que debe tener una fuente de datos, creadas y verificadas aquí, así como desde el perfuse contract. Consulte la línea de comandos.
3.3.4 Tablas
Tablas de búsqueda y conjuntos de códigos: los mapeos que convierten los códigos de un sistema en los de otro. Editables aquí para que agregar un código no sea una implementación.
3.3.5 Asistente de mapeo de inteligencia artificial
Propone asignaciones de campos con una puntuación de confianza para cada una, y se abstiene en lugar de adivinar. A pesar del nombre, no utiliza ningún modelo de lenguaje y no realiza ninguna llamada de red: las sugerencias provienen de reglas, nombres de campos y el contenido de la muestra, en este servidor. También propone un canal a partir de un mensaje de muestra único, y establece explícitamente qué no puede decirle la muestra. Esa segunda parte es la que lo hace confiable — una propuesta que pareciera completa invitaría a creer cosas que el mensaje nunca dijo.
3.3.6 Área de pruebas
Un bloc de notas para mensajes y transformaciones. Pegue un mensaje, aplique pasos, vea el resultado.
Nada se envía a ninguna parte una vez que la página se ha cargado, lo que es la razón por la que existe en esta forma: es seguro pegar algo real en ella, con la comprensión de que se queda en el navegador.
3.3.7 Sombra
Ejecuta tráfico a través de una configuración candidata junto con la configuración en vivo y informa las diferencias, para que un cambio pueda ser evidenciado antes de ser considerado confiable. La misma idea que perfuse compare, aplicada a un cambio en lugar de a una migración. Consulte pruebas.
3.4 Intercambio — los estándares que este servidor habla con otras organizaciones
3.4.1 Laboratorio de FHIR
Convierta HL7 v2 a FHIR, valide un recurso e inspeccione el resultado. Las muestras cubren admisiones, resultados de laboratorio, citas, notas clínicas, inmunizaciones y una visita informable. La validación incluye US Core, que es el perfil contra el que se escribe la regulación estadounidense, por lo que esto responde a "¿es lo que nos enviaron realmente conforme?" sin adoptar nada.
Informe de caso de salud pública construye el eCR eICR que un destino de informe de caso enviaría para el mensaje. Muestra qué código de activación hizo que la visita fuera informable, qué marcó el informe como faltante y el documento en sí. Un mensaje con nada informable obtiene la razón en su lugar. Nada se envía desde aquí.
3.4.2 Suscripciones
Suscripciones de FHIR y si sus notificaciones están llegando: eventos, aceptados, en espera y la última razón por la que uno fue rechazado. Las suscripciones se crean mediante clientes de FHIR a través de la API de FHIR, no desde esta página, y las credenciales que proporcionaron al servidor nunca se muestran. Consulte formatos de mensaje.
3.4.3 Documentos
Documentos clínicos — CDA y la forma imprimible de un documento. Útil cuando lo que se intercambia es un resumen de alta en lugar de un mensaje.
3.4.4 Compartir documentos
El registro y repositorio IHE XDS.b iniciado con -xds: cuántos documentos, pacientes y conjuntos de envío contiene y cuántos fueron reemplazados; las direcciones del repositorio, registro, puerta de enlace XCA y MHD; una búsqueda de los documentos de un paciente en este registro o en otro de la comunidad (un registro ITI-18 o una puerta de enlace ITI-38, con un token para ello); y la publicación de un documento CDA a mano, con el tipo de instalación y el entorno de práctica que un documento no especifica. Vea formatos de mensaje.
3.4.5 Direct
Mensajería segura directa. Las direcciones Direct de este servidor enumeran todas las direcciones que un canal envía o recibe, con su certificado, cuando expira, los anclajes que confía y cualquier problema (un archivo que falta, un certificado expirado). El Libro de direcciones mantiene a los corresponsales y los certificados a los que se cifra su correo: pegue uno de su HISP, o Guarde con el certificado desde DNS; un certificado que no es para la dirección o su dominio es rechazado. Buscar un certificado en DNS pregunta sin guardar. Vea formatos de mensaje.
3.4.6 Enlaces de salud
Enlaces y tarjetas de salud SMART, para *Matar el portapapeles* de CMS. Recibir un enlace de salud SMART lee el enlace o código QR que un paciente muestra al llegar, verifica las tarjetas de salud dentro y lo envía a un canal. Compartir un registro de visita crea un enlace que este servidor hospeda, opcionalmente con un código de acceso, y un código QR para el paciente; Usar su resumen de paciente lo llena con el resumen de paciente internacional (ver formatos de mensaje) de la tienda FHIR de este servidor. Enlaces que hospeda este servidor los enumera con la frecuencia con la que cada uno se recuperó, cuándo caduca y los intentos de código de acceso restantes, y revoca uno. Verificar una tarjeta de salud SMART verifica la firma y el emisor de una tarjeta por sí solo. Consulte CMS-0057 para las opciones del servidor que necesita.
3.4.7 Imágenes
El archivo que mantiene este servidor cuando se ejecuta con -dicomweb-archive: recuentos de estudios, series, instancias y espacio utilizado, la dirección DICOMweb y el bloque de fuente de datos para un visor OHIF, y una búsqueda de estudio por nombre de paciente, ID, acceso y fecha realizada con la misma solicitud QIDO-RS que hace un visor. Un estudio se abre a sus series e instancias, y una instancia se descarga como un archivo DICOM. Nada se representa: Perfuse mueve las imágenes y un visor las muestra. Sin un archivo, la pantalla indica cómo activar one. Vea DICOMweb.
3.4.8 TEFCA
El registro de auditoría para el intercambio a través de una Red de Información de Salud Calificada, y las reglas de propósito de uso que lo rigen.
Dos transportes, en etapas genuinamente diferentes, y la pantalla dice cuál es cuál. Una bandera que cubra ambos estaría mal en cualquier dirección en la que se redondeara.
Se implementa FHIR facilitado y nunca ha hablado con un QHIN real. Sigue el Procedimiento Operativo Estándar de Sequoia Project vigente desde el 8 de marzo de 2026: descubrir los metadatos de UDAP del socio, verificarlos, registrar dinámicamente, obtener un token de acceso que lleva el propósito de uso y consultar. UDAP es una infraestructura de clave pública sobre OAuth 2.0 — una comunidad de confianza emite certificados X.509 a sus miembros, y un miembro se autentica firmando JWT con la clave privada y adjuntando la cadena de certificados.
La capa de seguridad se verifica contra la implementación de alguien más, que es la única verificación que vale la pena aquí. Un registro firmado por esta compilación se envió a un servidor de referencia en vivo y se rechazó con unapproved_software_statement — Untrusted: Certificate is not a member of community. Para producir esa respuesta, el servidor decodificó la solicitud, analizó la declaración de software como un JWT, verificó su firma, recorrió la cadena de certificados y tomó una decisión sobre la membresía. Es la diferencia entre ser rechazado en la puerta y recibir una carta ilegible.
Lo que queda no se puede hacer desde aquí. El certificado que prueba la membresía de la comunidad sale del proceso de incorporación de QHIN y no es algo que el software produzca. Hasta que exista uno, ningún intercambio con un socio real se completa.
Cómo pregunta un canal. Un destino de tipo tefca pregunta al conjunto de socios FHIR facilitado en Configuración, TEFCA (tefca.partnerFHIRBase) para cada mensaje de paciente: por identificador cuando el mensaje tiene uno (PID-3, o un identificador de paciente FHIR), de lo contrario por apellido con fecha de nacimiento, y nunca solo por nombre, porque una mala coincidencia con un nombre devuelve el registro de otra persona sin error. El destino establece el propósito de uso (tratamiento, pago, operaciones, salud pública, acceso individual); un propósito que el participante no declara se rechaza antes de que se envíe nada. Los propósitos se envían como códigos de propósito de intercambio TEFCA (T-TRTMNT, T-PYMNT, T-HCO, T-PH, T-IAS, sistema urn:oid:2.16.840.1.113883.3.7204.1.5.2.1), o con tefca.purposeCodes: hl7 como códigos de propósito de uso HL7 para comunidades que utilizan esos. El paquete encontrado es la respuesta del destino y se puede entregar a otro canal con results_to. Cada consulta, incluidas las rechazadas, está en el registro en esta pantalla, que nombra al socio.
Todo el camino, desde el descubrimiento hasta un token que el servidor FHIR del socio acepta, se prueba contra el servidor de referencia udap-dotnet (ver verificación).
No se implementa el intercambio de QHIN a QHIN más antiguo basado en los perfiles de IHE. Se rechaza un intento con un error que lo dice, y el rechazo se audita como un fallo. Eso es deliberado en lugar de estar pendiente de una limpieza: un intercambio simulado que informa de éxito pondría entradas en el registro de auditoría para intercambios que nunca sucedieron, y un registro que no está de acuerdo con lo que sucedió es peor que ninguno, porque el registro es lo que se cree.
En todo, los puntos de conexión utilizados para el intercambio provienen de la parte firmada de los metadatos de un socio, nunca del cuerpo del documento. Verificar una firma y luego leer los puntos de conexión de la copia no firmada sería una forma elaborada de confiar en la red: un atacante capaz de reescribir la respuesta deja el elemento firmado solo, apunta el punto de conexión del token a su propio servidor y recopila aserciones de cliente de todos los que verificaron la firma y luego ignoraron lo que cubría.
Se requiere un paquete de anclaje de confianza y su ausencia se rechaza al inicio. Comprobar los metadatos firmados de un socio contra el certificado que llegó dentro de él solo demuestra que una parte realizó ambos — un error que esta base de código ha cometido antes, en SAML, donde el verificador prefirió el certificado incrustado y autenticó a un atacante como administrador mientras todas las pruebas pasaban. Incluya los intermediarios en el paquete: un servidor UDAP solo necesita enviar su certificado hoja, y el servidor de referencia público hace exactamente eso.
3.5 Pagadores — reclamaciones, autorización previa, medicamentos y reglas del pagador de CMS
3.5.1 Reclamaciones y autenticación
Construya un anexo de reclamaciones X12 275 en el formulario 006020 que adopta el CMS-0053-F, a partir de un C-CDA pegado o un PDF, TIFF o JPEG subido, y véalo leer de nuevo. Lea un 275 y descargue el documento interior. Convierta una decisión X12 278 en una respuesta de reclamación Da Vinci PAS ClaimResponse. Nada en esta página se almacena o envía. Vea formatos de mensaje para saber de qué se construye el 275 y qué no hace.
3.5.2 Medicamentos
El formulario de medicamentos cargado con -formulary, y las dos normas NCPDP respondidas desde él. Formulario enumera los planes y los medicamentos con su nivel y requisitos, y dónde se encuentran la API de formulario de medicamentos Da Vinci US pública y los puntos de conexión NCPDP. Verificación de precios (RTPB) asigna un precio a un medicamento para un miembro como lo haría una solicitud de beneficio de prescripción en tiempo real de un prescriptor: el estado de cobertura, las restricciones en palabras con sus códigos, la participación del miembro y la del plan, y las alternativas más baratas. Autorización previa (ePA) envía una iniciación de SCRIPT al punto de conexión propio de este servidor, muestra el conjunto de preguntas como un formulario, envía las respuestas y muestra la decisión; a continuación, cada caso con su estado y si se ha notificado al prescriptor, las respuestas del caso, un botón para enviar nuevamente una decisión no enviada y las cifras de medicamentos que propone el CMS-0062-P. Véase CMS-0057.
3.5.3 CMS-0057
El lado del pagador de la regla de interoperabilidad y autorización previa de CMS, como cinco herramientas:
- Las cuatro API: si Patient Access, Provider Access, Payer-to-Payer y Prior Authorization están listas en esta instancia, y qué es lo que cada una todavía necesita.
- Cola de revisor: las solicitudes de PAS que esperan a una persona, primero las más próximas, con los documentos enviados para cada una; aprobar, denegar o certificar menos unidades. Consulte la cola de revisor.
- Reclamaciones a CARIN BB: un 837 y su 835 convertidos en paquetes de botón azul CARIN.
- Autorización previa a PDex: una respuesta de PAS convertida en una autorización previa de PDex.
- Métricas de autorización previa: la página de métricas de autorización previa pública anual, vista previa a partir de un CSV de decisiones.
Las conversiones no almacenan nada y no envían nada.
3.5.4 Red alineada con CMS
Preparación para el Marco de Interoperabilidad de CMS: cada uno de sus 26 criterios, agrupados como los agrupa CMS, con lo que proporciona Perfuse, whether se encuentra aquí, la configuración que lo activa y qué debe hacer la organización. Los recuentos en la parte superior filtran la lista. Vea CMS-0057.
3.6 Administrar — quién puede hacer qué, y cómo se configura este servidor
3.6.1 Usuarios
Cuentas, roles (monitor, viewer, operator, editor, admin) y grupos de canales. Los grupos de canales de cada persona se editan en el lugar en la lista; vacío significa todos los canales, y lab=editor, adt da un rol diferente en un grupo. Un administrador limitado a algunos grupos solo ve a las personas de esos grupos. El encabezado muestra sus propios grupos junto a su rol cuando tiene alguno. Vea seguridad.
Credenciales de máquina también están aquí. Un token es para una máquina en lugar de una persona: no caduca y no se ve afectado por nadie que cierre la sesión, lo que es lo que un servidor que sondea a su vecino necesita. Solo se almacena un hash, por lo que un token se muestra una vez cuando se crea y no se puede recuperar — una base de datos robada no produce un token utilizable. Darle el rol más bajo que haga el trabajo.
Con MCP activado, la sección también muestra la dirección a la que se conecta un agente de inteligencia artificial, una configuración de cliente para copiar y cada herramienta con el rol que necesita; emitir un token con Dejar que un agente de inteligencia artificial actúe con él le da el ámbito mcp:write. Vea seguridad.
3.6.2 Inquilinos
En un servidor compartido, la lista de organizaciones del operador de la plataforma: cada una con su recuento de canales y tamaño almacenado junto a sus límites (canales, mensajes por minuto, almacenamiento), que se establecen en el lugar. Solo se muestra al rol de la plataforma. Vea seguridad para saber qué hace cada límite.
3.6.3 Certificados
Los certificados TLS en uso y cuándo caducan. Las fechas de caducidad son el punto: un certificado que caducó un domingo es una clase de interrupción que es completamente prevenible y rutinariamente no se previene.
3.6.4 Actividad
Quién cambió qué, y cuándo. Cada cambio de configuración se registra, lo que es lo que hace que "empezó a fallar el martes" sea respondible.
3.6.5 Protección de datos
Las solicitudes de una persona bajo el GDPR y el GDPR del Reino Unido, las retenciones legales, el registro de eliminación, la retención y el registro de actividades de procesamiento; solo administradores. Consulte protección de datos.
3.6.6 Flota
Cada instancia de Perfuse en una página.
Mirth vende esto como un producto separado; aquí es un archivo de configuración y un token de solo lectura. La razón por la que vale la pena tenerlo es estrecha e importante: un servidor muerto no se parece a uno silencioso. El panel de un instancia única no puede decirle que un segundo sitio dejó de informar, y una vista de flota puede.
3.6.7 Migrar
Coloque una exportación de canal de Mirth u OIE y vea cuántos de sus canales funcionarían aquí sin cambios. El mismo análisis que perfuse explain y perfuse translate, sin necesidad de una terminal. Consulte migración.
3.6.8 Configuraciones
Cada configuración que tiene el servidor, con un control para cada una — incluyendo aquellas que tradicionalmente solo viven en un archivo. Cada una indica si tiene efecto de inmediato o necesita un reinicio, y si se ha establecido explícitamente o está en su valor predeterminado.
La configuración cubre cada opción de perfuse serve que activa una función: el punto de conexión FHIR y su versión, las API de pagadores, PAS y las reglas CRD, el puente X12 278, eCR, el servidor de autorización SMART, TLS y firma, métricas, flota y VPN. Están agrupados como FHIR, Pagadores, Salud pública, SMART, Seguridad, etc. Cada uno también se muestra con la bandera que corresponde.
Reiniciar desde la consola. Una configuración marcada como *Requiere reinicio* se guarda de inmediato, pero solo se lee cuando el servidor se inicia. Mientras alguna espera, un banner en la parte superior de la configuración los enumera con un botón Reiniciar el servidor (el editor de inicio de sesión y los archivos de configuración ofrecen el mismo botón después de guardar). Pide confirmación primero, porque cada canal se detiene durante unos segundos: cada uno finaliza el mensaje que está manejando, como lo hace cuando el servicio se detiene, y los remitentes vuelven a enviar lo que llega mientras tanto. La consola espera a que el servidor responda de nuevo y lee la configuración de nuevo. Solo un administrador puede reiniciar, y cada reinicio se registra en el registro de actividad como server.restart.
En Linux y macOS, el proceso se reemplaza con una copia fresca del mismo programa y argumentos, manteniendo su ID de proceso, por lo que systemd, launchd o un tiempo de ejecución de contenedor no ve nada suceder. En Windows, el servidor se inicia de nuevo dentro del mismo proceso, que mantiene un servicio de Windows bajo el administrador de servicios. Si el servidor no ha respondido después de dos minutos, la consola lo indica; la razón está en el registro del servidor.
Bandera y archivo de configuración. La primera vez que el servidor se inicia, las banderas que se le dieron se escriben en el archivo de configuración. A partir de entonces, el archivo tiene prioridad sobre las banderas, por lo que cambiar una configuración se hace en la consola (o en el archivo), no editando la línea de comandos del servicio. Una bandera que el archivo no menciona aún se registra cuando el servidor se inicia con ella. Quitar una bandera de la línea de comandos más tarde no desactiva su función: desactívala en la configuración.
SNMP tiene un panel debajo de la configuración: si el agente está respondiendo y en qué dirección, las redes que responde, cuántas trampas de alerta se enviaron o fallaron, y Descargar el MIB para el OID base en uso. Los valores se establecen en Monitoreo, SNMP.
Marcas también están aquí: un sitio puede establecer el nombre del producto, un eslogan, un color de acento y cargar un logotipo. El logotipo luego aparece en el encabezado, en la página de inicio de sesión, en el panel y en las pantallas de informes. Si no se carga ninguno, nada se sustituye — una instalación sin marca permanece sin marca en lugar de adquirir decoración.
3.7 Qué significan los colores
Cuatro registros, y cada uno dice algo diferente. La distinción vale la pena porque es la diferencia entre una pantalla que se puede leer a primera vista y una donde todo compite por la atención.
Texto plano es un hecho sobre cómo funciona Perfuse. La mayoría de la interfaz. Nada se colorea para indicar que es importante.
Verde significa que algo tuvo éxito hace un momento, porque de algo que usted hizo. Aparece después de una acción y no persiste.
Ámbar es una advertencia: vale la pena saber antes de actuar, y no es un error. Una función que no está activada, una configuración cuyos cambios se auditan, una conversión que aproxima en lugar de reproducir, un transporte que Perfuse maneja pero no puede verificar completamente. Ámbar es el color de "lea esto antes de continuar".
Rojo significa que algo está mal en este momento. Un valor que fue rechazado, una solicitud que falló, un certificado que ha caducado, una alerta crítica que se está disparando. Nada más lo usa.
Esa última regla es por qué una pantalla no se abre en rojo. Rojo es el único color que lleva una instrucción — detener, y puede ser algo que usted hizo — y una pantalla que lo gasta en decoración no tiene nada que decir cuando la entrada realmente está mal. Esto no siempre fue cierto: la pantalla de usuarios solía abrir con "el servidor envió una respuesta que no se podía leer" en cada instalación que no había configurado claves de paso, y la pantalla de configuración marcaba los valores auditados en el mismo color que uno rechazado. Una prueba en web/src/RedMeansWrong.test.ts ahora falla si una frase que describe algo ausente o aproximado se le da un estilo rojo.
Dentro de una caja de código, el vocabulario es diferente. Allí, rojo es un tipo de token — un nombre de segmento de HL7, un elemento de XML — y no lleva ningún juicio en absoluto.
4 Canales
Un canal es un flujo de mensajes: somewhere they come from, cambios opcionales y uno o más lugares a donde van. Es la unidad de configuración, la unidad de monitoreo y la unidad de implementación. Todo lo demás en Perfuse existe para servir a los canales.
4.1 Anatomía
Un archivo de canal tiene estas partes, en el orden en que surten efecto:
| Parte | Clave | Requerido | Qué hace |
|---|---|---|---|
| Identidad | name | sí | Nombra el canal. Debe ser único. |
| Tipo de datos | dataType | no | Cómo analizar los mensajes. Por defecto es HL7 v2. |
| Origen | source | sí | De dónde provienen los mensajes. Exactamente uno. |
| Filtro | filter | no | Decide qué mensajes aceptar. |
| Transformaciones | transformations | no | Cambios ordenados al mensaje. |
| Destinos | destinations | sí | A dónde van los mensajes. Uno o más. |
Además de varios que se aplican en todo el canal: scripts, shadow, contract, tables, attachments, group y enabled. Cada uno se encuentra en la referencia del canal.
4.2 Un origen, muchos destinos
Esta asimetría es deliberada y vale la pena entenderla desde el principio, porque es la fuente de confusión más común al venir de un motor diferente.
Un canal tiene exactamente un origen. Si los mensajes provienen de dos lugares, eso son dos canales. Intentar expresarlo como un canal produce una configuración en la que una falla en cualquier transporte se ve igual desde afuera, y donde el tráfico de las dos fuentes se mezcla en las estadísticas — así que una caída en uno se oculta por el otro.
Un canal tiene cualquier número de destinos, y son independientes. Una falla en uno no detiene a los demás, y cada uno tiene su propia cola, su propia política de reintento y su propio registro de entrega. Un mensaje que llegó a tres de cuatro destinos se registra como exactamente eso, no como un éxito o una falla.
4.3 Los destinos no son una cadena
Cada destino recibe el mensaje tal como estaba después de las transformaciones del canal. Un destino no recibe la salida del destino anterior.
Esto importa cuando un destino tiene sus propias transformaciones, lo que puede suceder. Esas se aplican solo a la copia de ese destino. Un error común es poner un cambio en el primer destino y esperar que el segundo lo vea.
Si genuinamente necesita una cadena — la salida de un flujo alimentando a otro — use un destino de canal, que envía a otro canal por nombre. Eso es explícito, aparece en el historial del mensaje como dos canales y se puede monitorear como dos cosas, lo que una cadena oculta no puede.
4.4 Nombramiento
El nombre es la identidad del canal en todas partes: en el archivo, en el almacén de mensajes, en las alertas, en el registro de auditoría y en la URL. Cambiarlo no es un cambio de nombre; es un nuevo canal, y la historia del antiguo canal se mantiene bajo el nombre antiguo.
group es separado y existe para organizar la interfaz. Un grupo no tiene efecto en el comportamiento, y agrupar canales no hace que compartan nada.
adt-inbound.4.5 Habilitar y deshabilitar
enabled: false detiene un canal sin eliminarlo. Su configuración, historia y estadísticas se mantienen.
Un canal deshabilitado no es uno pausado. Su origen no está escuchando, así que un remitente que intenta conectarse obtiene una conexión rechazada en lugar de una conexión colgada. Para MLLP, ese es el comportamiento correcto — el remitente se pondrá en cola y reintenta — pero significa que el registro de errores del remitente se llenará, así que un canal deshabilitado no es un sustituto para organizar una interrupción con quien está enviando.
4.6 Dónde viven los canales
Un archivo YAML por canal, en el directorio dado por -channels. El nombre del archivo no es el nombre del canal; la clave name es. Se recomienda encarecidamente mantenerlos iguales, y nada los obliga.
Los archivos se vuelven a leer sin reiniciar. Un archivo que no se analiza se informa y la versión previamente cargada sigue funcionando, así que un error de sintaxis no puede sacar un flujo de trabajo del aire.
4.7 Cifrar el propio oyente de un canal
Un canal que escucha en un puerto es su propio servidor, y su TLS es separado del TLS en la interfaz web de Perfuse. Configurar uno no configura el otro. Para una fuente HTTP, los ajustes están bajo TLS y límites en la fuente.
Activar TLS solicita un certificado y una clave privada. Requerir un certificado de cliente lo convierte en TLS mutuo: el remitente debe presentar uno firmado por una autoridad que usted nombre, lo que es autenticación, por lo que un oyente con ella activada no necesita un token compartido — aunque tener ambos no es incorrecto.
La autoridad se solicita solo una vez que se solicita un certificado de cliente, y se omite del archivo hasta entonces. Un canal que nombra una autoridad que nunca se consulta se lee como si los remitentes estuvieran siendo verificados cuando no lo están.
Un oyente MLLP tiene los mismos ajustes bajo Cifrar este oyente, con una diferencia digna de mención: en MLLP, un certificado de cliente es la única autenticación disponible. No hay token ni encabezado, por lo que un oyente sin uno acepta mensajes de cualquier host que pueda llegar al puerto.
Dos límites se encuentran junto a él. Dejar de leer después de una duración, porque un remitente que abre una conexión y deja de escribir de otra manera mantiene la conexión abierta. Y mensaje más grande aceptado, en bytes, porque cero significa sin límite y el límite es entonces la memoria — un remitente que publica un gigabyte es rechazado en lugar de absorbido.
4.8 Validación
Un canal se verifica cuando se carga, y las verificaciones son rechazos en lugar de advertencias donde la alternativa sería un canal que se ejecuta y hace algo incorrecto.
Ejemplos de cosas rechazadas directamente: un destino sin tipo, una transformación que se dirige a una ruta que no puede existir en el tipo de datos declarado, un modo de reconocimiento que el transporte de origen no puede entregar, y una receta de la lista II con recargas.
La distinción que Perfuse intenta mantener es que cualquier cosa que produzca un resultado incorrecto plausible se rechaza, y cualquier cosa que sea inusual se permite y se informa. Un canal que no puede estar incorrecto es más valioso que un canal que comienza.
5 Flujo de mensaje
Este capítulo sigue un mensaje desde el momento en que llega hasta el momento en que se entrega o se abandona. Conocer este orden es la forma más rápida de determinar dónde pertenece un cambio y por qué algo no está sucediendo.
5.1 El orden de los eventos
- Llegada. La fuente acepta los bytes. Para un transporte de flujo, esto incluye la desenmarcación — encontrar dónde termina un mensaje y comienza el siguiente.
- Analizar. Los bytes se convierten en un árbol según el tipo de datos (
dataType) del canal. Un mensaje que no se puede analizar se detiene aquí y se registra como un error de análisis. - Registrar. El mensaje se escribe en el almacén de mensajes tal como se recibió, antes de que algo lo modifique. Esto es lo que hace posible la reproducción y el procesamiento nuevamente.
- Filtrar. Si el canal tiene un filtro (
filter), decide si aceptar el mensaje. Un mensaje rechazado se registra como filtrado, lo que es un resultado normal y no un error. - Transformar. Las transformaciones (
transformations) del canal se ejecutan en orden, cada una contra el resultado de la última. - Distribuir. Cada destino recibe una copia del mensaje transformado, aplica sus propias transformaciones si las tiene, y entrega.
- Registrar el resultado. Por destino: entregado, en cola, fallido o filtrado.
- Confirmar. Si la fuente espera una confirmación, se genera y se envía. Por defecto, esto sucede aquí, después de la entrega — véase a continuación.
5.2 Cuándo se envía el reconocimiento
Este es el ajuste más importante en un canal, porque decide qué se le promete al remitente. Es ack.when, y tiene dos valores.
on_delivery es el valor predeterminado. El reconocimiento se envía solo después de que todos los destinos habilitados hayan aceptado el mensaje. Un reconocimiento positivo significa, por lo tanto, que los datos realmente llegaron a algún lugar, lo que es la afirmación más fuerte que un motor en el medio puede hacer honestamente.
on_receipt reconoce tan pronto como el mensaje se analiza y se coloca en cola, antes de que se haya escrito ningún destino.
Perfuse utiliza on_delivery de forma predeterminada porque prometer la entrega y luego perder el mensaje es peor que ser lento. Bajo on_receipt, un mensaje que se ha reconocido aún puede perderse si el proceso muere con trabajo en cola — el remitente cree que está entregado, nada upstream lo volverá a enviar, y el único registro de que existió está en el propio almacén de Perfuse.
5.3 Cuándo elegir en recepción
El caso para ello es real, y se trata de los tiempos de espera. Un remitente MLLP que no recibe un reconocimiento en unos pocos segundos normalmente lo vuelve a enviar, por lo que un destino lento bajo on_delivery produce duplicados en lugar de retrasos. Si un destino es un archivo lento o un sistema que no está disponible regularmente, on_delivery acopla el tiempo de espera del remitente al peor caso de ese destino.
El intercambio es explícito: on_receipt transfiere el riesgo de duplicados a pérdida. Elegirlo significa aceptar que un mensaje confirmado puede perderse en caso de fallo, y es recomendable emparejarlo con alertas de profundidad de cola para que se note si una cola ha dejado de drenar. Consulte alertas.
5.4 Dónde se registra el mensaje
Antes del filtro, y por lo tanto antes de cualquier transformación. Tres consecuencias:
- Un mensaje que el filtro rechazó sigue estando en el almacén. Puedes ver qué llegó y confirmar que el filtro estaba en lo correcto.
- La reproducción y el procesamiento nuevamente siempre comienzan desde lo que realmente llegó, no desde una versión parcialmente transformada.
- Un cambio en las transformaciones puede probarse contra tráfico histórico real, porque el original sigue estando allí. Consulta testing.
La forma transformada también se registra, por destino, junto con el resultado. Así que la tienda contiene lo que llegó, lo que se envió y qué sucedió — que es lo que una investigación necesita.
5.5 Falla en cada etapa
| Etapa | Cómo se ve el fallo | Qué sucede |
|---|---|---|
| Llegada | Error de delimitación, conexión interrumpida en medio de un mensaje | Registrado, conexión cerrada, remitente vuelve a intentarlo |
| Análisis | No es válido para el tipo declarado | Registrado como un fallo de análisis, acuse de recibo negativo si la *fuente* espera uno |
| Filtro | El filtro en sí genera un error | Tratado como un rechazo, registrado con el error |
| Transformación | Un paso falla | Depende del on_error del paso; véase transformación |
| Entrega | Destino no accesible o rechaza | Encolado y vuelto a intentar según la política del destino; véase entrega |
La asimetría importante: una falla de análisis es permanente y una falla de entrega generalmente no lo es. Reintentar un mensaje que no se puede analizar nunca ayudará, así que no se reintenta. Reintentar un destino no accesible generalmente sí ayudará, así que se reintenta.
5.6 Orden
Dentro de un destino, los mensajes se entregan en el orden en que llegaron, y un mensaje que falla y entra en la cola mantiene su lugar — los mensajes posteriores esperan detrás de él en lugar de adelantarlo.
Este es el valor predeterminado correcto para los datos clínicos, donde una admisión seguida de un alta entregada en el orden incorrecto es peor que si ambos se retrasan. También significa que un mensaje dañado puede bloquear una cola, lo que explica por qué la profundidad de la cola vale la pena alertar sobre ella.
Entre destinos no hay garantía de orden, porque son independientes. Dos destinos no necesariamente recibirán el mismo mensaje al mismo tiempo, y uno rápido puede estar varios mensajes por delante de uno lento.
5.7 Concurrencia
Una fuente puede manejar varias conexiones al mismo tiempo, limitadas por max_connections. Los mensajes de diferentes conexiones se procesan de forma concurrente.
Esto significa que el orden de llegada de dos mensajes enviados simultáneamente a través de dos conexiones no está definido y no puede estarlo — llegaron genuinamente al mismo tiempo. Un remitente que necesita orden debe usar una conexión, lo que es lo que la mayoría de ellos hace.
6 Transformación
Las transformaciones cambian un mensaje. Son una lista, se ejecutan en orden y cada una ve el resultado de la anterior.
Las transformaciones de Perfuse son declarativas: cada paso nombra una operación y el campo al que se aplica. No hay lenguaje de scripting en la ruta ordinaria. Esta es una limitación deliberada y la justificación se encuentra en why not a script a continuación.
6.1 Los pasos
| Paso | Qué hace |
|---|---|
set | Escribe un valor literal en una ruta, creándola si está ausente. |
copy | Copia una ruta a otra, con un valor predeterminado opcional cuando la fuente está ausente. |
clear | Vacía un campo, dejándolo presente. |
remove | Elimina un campo por completo. |
map | Busca el valor actual en una tabla y lo reemplaza. |
replace | Sustitución de expresión regular dentro de un campo. |
pad | Rellena un valor a un ancho fijo. |
date | Convierte una fecha o marca de tiempo entre formatos. |
trim | Elimina el espacio en blanco circundante. |
case | Convierte a mayúsculas o minúsculas. |
Cada paso también acepta description, que aparece en la traza y en la interfaz, y when, que hace que el paso sea condicional.
Un paso se escribe como el nombre de la operación con su propio bloque:
transformations:
- description: Our record number, not theirs
set:
path: PID-3.4
value: RIVERSIDE
- copy:
from: PID-19
to: PID-3.1
default: UNKNOWN
- map:
path: PID-8
table:
M: Male
F: Female
set y value no son hermanos. set toma un bloque que contiene path y value. Escribir set: PID-3.4 con value: al lado es el error más común en un canal escrito a mano y produce un error de carga en lugar de una configuración incorrecta silenciosa.6.2 Borrar, eliminar y la diferencia
clear deja el campo presente y vacío. remove lo elimina por completo.
Para HL7 esta distinción es real y llega al receptor. Un campo vacío dice "tengo este campo y no tiene valor". Un campo ausente no dice nada en absoluto. Algunos sistemas receptores tratan el primero como una instrucción para borrar su valor almacenado y el segundo como una instrucción para dejarlo solo — lo que es la diferencia entre eliminar una alergia registrada de un paciente y no mencionarla.
Si no sabes qué es lo que el receptor quiere, pregunta. Adivinar tiene un cincuenta por ciento de posibilidades de destruir silenciosamente los datos en el lado lejano.
6.3 Condiciones
when toma una expresión y el paso se ejecuta solo si es verdadera:
- when: MSH-9.2 == "A08"
set:
path: EVN-1
value: A08
Un paso cuya condición es falsa se registra como omitido, y esto se informa por separado de un paso que se ejecutó y no cambió nada. Esa distinción es el propósito del depurador paso a paso y se cubre en depuración: una condición falsa y un campo ausente parecen idénticos desde afuera y significan cosas opuestas.
6.4 Rutas
Una ruta se dirige a un campo: PID-5 es el quinto campo de PID, PID-5.1 su primer componente, PID-3(2) la segunda repetición de PID-3 y OBX(3)-5 el quinto campo del tercer segmento OBX.
Dos comportamientos dignos de conocer:
setcrea la ruta que se dirige, incluida la estructura intermedia. Esto es conveniente y significa quesetnunca puede demostrar que un campo estaba ausente, ya que después del paso existe. Para probar qué sucede cuando un campo está ausente, usecopydesde una ruta que no esté allí.- Una ruta sin índice se dirige a todas las repeticiones y ocurrencias, al igual que en un filtro (donde
PID-3.5 == "SS"es verdadero cuando alguna repetición es). En un mensaje con cinco segmentos OBX,setenOBX-5cambia los cinco, ysetenPID-3.4cambia cada identificador en PID-3, incluido el número nacional. Para cambiar uno, proporcione su índice:PID-3(1).4,OBX(2)-5.
6.5 Errores
La mayoría de los pasos no tienen nada que falle. Establecer un valor, borrar un campo o copiar desde una ruta ausente con un valor predeterminado están definidos para todas las entradas.
date es la excepción, porque un valor que no es una fecha que reconoce no tiene una conversión correcta. Toma on_error:
fail— el valor predeterminado. El mensaje se detiene y se registra como un fallo de transformación.keep— deja el valor original solo.clear— vacía el campo.
El valor predeterminado es fail porque una marca de tiempo que no se convierte silenciosamente es peor que un mensaje que no llegó. Una fecha en el formato incorrecto generalmente todavía parece una fecha, por lo que pasa la validación del receptor y aterriza en el registro como el día incorrecto.
keep es legítimo cuando el receptor tolera ambos formatos, y cuando se usa, la traza marca el cambio como deliberado para que no se lea como un paso que falló silenciosamente.
6.6 El orden es importante, y la traza lo muestra
Cada paso ve la salida del paso anterior. Dos consecuencias que atrapan a las personas:
- Un
mapdespués de uncaseve el valor convertido por el caso, por lo que las claves de su tabla deben coincidir con la forma convertida. Una tabla deM/Fdespués de un paso en minúsculas no coincide con nada, silenciosamente, porque un valor que no está en la tabla se deja solo. - Un
copydesde un campo que un paso posterior elimina todavía funciona, porque se ejecutó primero. El inverso no.
Ambos son visibles en la vista paso a paso, que muestra el mensaje completo después de cada paso en lugar de solo el cambio. Vea depuración.
6.7 Sugiriendo asignaciones y qué hacer con una sugerencia
El Asignador de inteligencia artificial propone asignaciones entre un conjunto de campos de origen y un conjunto de objetivos. Su comportamiento definitorio es que se abstiene: por debajo de su umbral de confianza, se abstiene en lugar de adivinar, porque una asignación equivocada confiada en un sistema clínico es peor que ninguna asignación en absoluto.
6.7.1 Dale algo de qué estar seguro
Un nombre por sí solo es una evidencia débil, incluso cuando coincide exactamente. PatientMRN contra PatientMRN puntúa 68, que se abstiene en el umbral predeterminado de 70.
The engine knows what an HL7 path means — it will tell you PID-7 is Date/Time of Birth and score your field name against that rather than against the string PID-7, which shares no characters with anything. A locally defined Z segment means whatever your site decided, so nothing is claimed about it.
Eso obtiene una asignación la mayor parte del camino. Para terminarla, proporcione ambas mitades:
- Valores de ejemplo para un campo de origen, después de un signo igual:
ZPI-1 = MRN00412, MRN00998 - El tipo de valor que un objetivo mantiene, de la misma manera:
PID-3.1 = mrn. Los tipos sonmrn,npi,ssn,phone,dateyemail.
Con esos, el motor puede ver que los valores coinciden con el tipo del objetivo, y una sugerencia se basa en más que un nombre.
6.7.2 La aprobación es una asignación a la vez
Cada sugerencia tiene su propia casilla de aprobación. No hay una opción para "aceptar todo lo que supere el 80%", deliberadamente: el motor ya ha utilizado la confianza para decidir si ofrece la asignación y lo que queda es un juicio sobre este campo en esta fuente. Un número no es ese juicio.
Una abstención no se puede aprobar en absoluto. No tiene casilla, ni siquiera deshabilitada, y la pantalla dice que el motor no lo sabe. Si usted conoce la respuesta, asígnela manualmente — eso es un acto diferente al de estar de acuerdo con una sugerencia.
Una sugerencia por debajo de su umbral se muestra sin casilla tampoco, marcada como por debajo de él. Vale la pena leer — un casi acierto es a menudo la asignación que se deseaba, y ocultarlo dejaría preguntándose si el motor lo consideró — pero no es aprobable, porque su umbral es la línea que dibujó.
6.7.3 Dos cosas que hacer con lo que aprobó
Agregarlos a un canal convierte cada asignación en un paso de copia. Esto solo funciona donde la fuente es una ruta en el mensaje, como ZPI-1, porque un paso copia de un lugar en un mensaje a otro. Los pasos van al archivo del canal y surten efecto cuando se vuelve a cargar — nada cambia en el tráfico hasta entonces. La descripción de cada paso registra la confianza con la que se sugirió y que una persona lo aprobó, lo que lo hace responsable seis meses después.
Descargar como receta es la respuesta correcta para el caso más común: una asignación desde un nombre de columna, una clave JSON o un nombre de campo propio de un proveedor. Esas son asignaciones reales, y un canal no puede leer ese campo directamente, por lo que un paso no puede expresarlo. La receta registra las asignaciones aprobadas y se puede compartir.
6.8 ¿Por qué no un script
Mirth coloca un transformador de JavaScript en el centro de cada canal, y es el diseño más flexible disponible. Perfuse ofrece scripting — véase la clave scripts en la referencia del canal — pero no lo convierte en el camino ordinario, por tres razones que provienen de lo que sale mal en la práctica.
Un paso declarativo se puede inspeccionar. Perfuse puede decirle que un paso lee PID-5 y escribe PID-8, antes de ejecutarlo, y por lo tanto puede mostrar una traza a nivel de campo, advertir que un paso se dirige a una ruta que el tipo de datos declarado no puede contener y generar una reproducción por paso.
Un paso declarativo se puede razonar mediante la interfaz. El constructor puede ofrecer los campos que existen en el tráfico de este canal y un cambio en un paso se puede verificar contra mensajes históricos reales antes de guardarlo.
Y un paso declarativo no puede hacer algo no relacionado con el mensaje. Un script puede llamar a la red, leer el sistema de archivos o depender del momento del día. Cuando un canal que funcionó durante dos años comienza a producir una salida incorrecta, la causa que está fuera del mensaje es el caso más difícil de diagnosticar, y eliminar la posibilidad vale más que la flexibilidad que cuesta.
Donde una transformación genuina no se puede expresar de forma declarativa, use un script y acepte que la traza lo mostrará como un paso opaco.
6.9 Qué se permite que haga un script
Un script se le niega todo lo que esté fuera del mensaje a menos que se le conceda, y las concesiones están en la sección Scripts del constructor. Esto es deliberadamente diferente a Mirth, que da a cada script todos ellos — así que allí, un transformador puede comenzar a leer el sistema de archivos después de una copia y pegado.
Hay tres.
Lectura y escritura de archivos requiere que nombre los directorios que puede usar, y un canal que otorga acceso a archivos sin ellos se rechaza al cargar. Ese requisito es toda la diferencia entre el acceso a archivos y el acceso a archivos sin restricciones: otorgar acceso a archivos sin directorios una vez alcanzó todo el sistema de archivos, incluida la base de datos de Perfuse, que contiene hashes de contraseñas, la contraseña de la cuenta de servicio LDAP y el secreto del cliente OIDC. Crear un canal solo requiere el rol de editor, por lo que la escalada se completó en el momento en que un administrador inició el canal.
Consulta a una base de datos utiliza las conexiones configuradas en el canal. Un script con esto puede leer cualquier cosa que esas credenciales puedan.
Enrutamiento de mensajes a otros canales permite que un script envíe un mensaje a otro canal, lo que es cómo se hace posible un bucle entre dos canales.
Otorgar una capacidad a un canal sin scripts no hace nada, y el archivo generado omite el bloque completo en ese caso.
6.10 Manejo de una fecha que no coincide con su formato
Un paso de fecha convierte de un formato a otro, y la pregunta interesante es qué sucede cuando un valor no coincide con el formato que afirma.
El valor predeterminado es detener el mensaje, y es el valor predeterminado correcto: una marca de tiempo que no se convierte silenciosamente es aceptada en la corriente descendente y luego se lee incorrectamente, lo que es peor que un mensaje que falló ruidosamente y está sentado donde alguien puede verlo.
Dejar el valor como está lo pasa en su formato original. La corriente descendente ve entonces dos formatos en un campo, lo que es el fallo que el paso existía para prevenir — pero es la elección correcta donde el campo es opcional y un socio lo envía de forma inconsistente.
Vaciar el campo es honesto y mejor que un formato incorrecto, pero un receptor que requiere el campo rechaza el mensaje en su lugar.
6.11 Los lenguajes de scripting
Tres son aceptados, y cuál elegir depende en gran medida de si se está migrando. El valor se coloca en la clave language de un script:
| Valor | Qué es |
|---|---|
javascript | El predeterminado. Un script sin marca es JavaScript. |
lua | La mejor opción cuando nadie está migrando. |
wasm | Los bytes de un módulo WebAssembly compilado, no el código fuente. |
JavaScript es el predeterminado, y un script sin marca se trata como JavaScript. Está aquí como una obligación de compatibilidad más que como una preferencia: los scripts de Mirth son JavaScript y un sitio que se mueve a través tiene miles de líneas de código. Esto incluye el soporte para E4X, una extensión obsoleta del lenguaje que los scripts de Mirth utilizan para XML y que debe traducirse antes de que algo se pueda analizar.
Lua (lua) es la mejor opción para aquellos que no están migrando. Un tiempo de ejecución más pequeño, sin cadena de prototipos para malversar y tablas con índices enteros que coinciden con la dirección de segmentos y campos de una manera más natural que la de JavaScript. Su biblioteca estándar es lo suficientemente pequeña como para enumerarla, lo que hace que su sandbox sea más fácil de confiar.
WebAssembly (wasm) no es un lenguaje, sino un módulo compilado, y el campo quiere los bytes de un archivo .wasm en lugar del código fuente. Úselo cuando la transformación ya existe en un lenguaje compilado o cuando es lo suficientemente pesado como para que el costo de tiempo de ejecución sea importante.
Su contrato es deliberadamente el más simple disponible: un módulo lee el mensaje en la entrada estándar y escribe su resultado en la salida estándar. Bytes de entrada, bytes de salida — lo que hace cada cadena de herramientas y lo que alguien que prueba un módulo en su propia línea de comandos ya tiene. La alternativa era una función exportada sobre memoria lineal, que es más rápida y requiere que cada autor de módulo esté de acuerdo sobre la asignación, la codificación de cadena y quién libera qué; para algo que se ejecuta una vez por mensaje, ese intercambio no vale la pena.
Cualquier cosa que un módulo escriba en el error estándar se convierte en líneas de registro, por lo que puede explicarse a sí mismo de la misma manera que un script que llama a logger.
true o false en la salida estándar. Confundir los dos haría que un módulo que se estrella sea indistinguible de uno que deliberadamente descarta tráfico.6.11.1 Qué significa la salida
Las reglas son las mismas para los tres lenguajes en lugar de nuevas reglas por tiempo de ejecución, por lo que el comportamiento observable de un script — qué puede ver, qué puede hacer, cuánto tiempo puede tomar, qué significa un veredicto de filtro — se define una vez.
Para un transformador, preprocesador o escritor, una salida vacía significa que el mensaje no ha cambiado. Caer al final de un script deja el mensaje solo; tratar una salida vacía como "el mensaje ahora está vacío" descartaría tráfico por una cuestión técnica.
Los módulos WebAssembly se compilan cuando se carga el canal, no en el primer mensaje. La compilación no es gratuita — un módulo construido a partir de un lenguaje que trae su propio tiempo de ejecución puede tomar cientos de milisegundos — y pagar ese costo en el momento de la carga significa que el primer mensaje a través de un canal no es misteriosamente más lento que el resto.
6.12 Tablas de mapeo compartidas
La otra mitad del mismo problema: los códigos de sexo y las clases de pacientes de un sitio se traducen de manera idéntica en cada canal que los toca, y una tabla dentro de un canal es una tabla que solo ese canal puede usar.
# codes.codeset.yaml
tables:
- name: sex-to-lab
describes: the hospital's sex codes into the numeric codes the lab expects
decided_by: Dave, during the 2019 migration
decided_on: 2019-04-11
source: the lab's interface specification, version 3
entries:
- from: M
to: "1"
- from: U
to: "9"
why: the lab has no 'unknown', and 9 is its 'not stated'
# in the channel
tables:
- codes.codeset.yaml
transformations:
- map:
path: PID-8
use: sex-to-lab
La procedencia es el punto. La mayoría de lo que hace que una interfaz sea difícil de mantener es que nadie sabe *por qué* un mapeo es de una determinada manera, y la persona que lo sabía se ha ido. Un mapeo con una razón puede ser debatido; uno sin razón se convierte en algo que nadie se atreve a cambiar y nadie se atreve a eliminar.
Las tablas se crean y modifican en Build → Tablas así como en el archivo. Guardar una tabla que está en vigor reinicia los canales en ejecución que cargan su archivo, por lo que el siguiente mensaje lo utiliza; un borrador no reinicia nada.
6.12.1 Versiones y fechas efectivas
Una tabla puede mantener varias ediciones bajo un mismo nombre, cada una con su propia version. La edición que utiliza un mensaje se elige cuando el mensaje llega: de las ediciones aprobadas cuyas fechas cubren ese día (UTC), la que comenzó más recientemente. Así, un cambio de código acordado para el primer día del próximo mes se ingresa ahora y se aplica automáticamente.
tables:
- name: sex-to-lab
version: "3"
effective_from: 2026-11-01 # optional; empty means from the start
effective_until: 2027-10-31 # optional; the last day it applies
source_system: urn:local:hospital-sex
target_system: http://terminology.hl7.org/CodeSystem/v2-0001
entries: [...]
statusesapproved(el predeterminado),draft(utilizado por nada) oretired
(conservado para el registro, utilizado por nada).
- Dos ediciones de un nombre con la misma versión, o dos ediciones aprobadas
que comienzan el mismo día, son rechazadas cuando se carga el archivo.
- Guardar una nueva versión sin fecha de inicio mientras otra está aprobada la fecha
desde hoy, por lo que reemplaza la que está en vigor. Una que solo había comenzado hoy es retirada, ya que dos ediciones que comienzan el mismo día dejan sin regla.
- Un paso de mapa utiliza su propio
defaultystrictcuando el paso los establece;
de lo contrario, se utilizan los de la tabla.
- En la pantalla de Tablas, Mostrar versiones enumera cada edición con su estado,
quién la guardó y quién la aprobó, y Comparar con la versión en vigor muestra qué agrega, elimina y cambia una edición.
6.12.2 Firma
Con Configuración → Datos → Tablas de asignación "Los cambios en una tabla de asignación requieren la aprobación de una segunda persona" (data.tableApproval) activado, cada guardar es un borrador, y la persona que lo guardó no puede aprobarlo: alguna otra persona lo hace, opcionalmente dando el día en que entra en vigor. Una edición aprobada no se puede editar en el lugar; Nueva versión desde esta copia en la siguiente versión. Aprobar, retirar y eliminar un borrador se escriben en el registro de auditoría, y aprobar reinicia los canales en ejecución que cargan el archivo.
6.12.3 Valores sin tabla de mapeo
Cada valor que una paso de mapa buscó y no encontró se cuenta, por tabla, con los canales por los que pasó y cuándo se vio por primera y última vez. La pantalla de Tablas los enumera, en orden de frecuencia. Map it abre la tabla con ese valor ya agregado como fila; Dismiss lo elimina hasta que se vuelva a ver. Los valores más largos de 64 caracteres no se registran (no son códigos), y la lista mantiene lo que se vio dentro del período de retención de mensajes. La lista se mantiene en terminology.db junto a la base de datos principal.
6.12.4 Sistemas de códigos
Los sistemas de códigos publicados se pueden importar desde los archivos de sus propios editores, bajo Sistemas de códigos en la pantalla de Tablas (administradores) o con POST /api/terminology/import?format=… y el archivo como cuerpo:
| Formato | Archivo | Sistema |
|---|---|---|
loinc | Loinc.csv desde la tabla de lanzamiento de LOINC | http://loinc.org |
icd10cm | el archivo de pedidos de CMS (icd10cm_order_*.txt) o archivo de códigos | http://hl7.org/fhir/sid/icd-10-cm |
rxnorm | RXNCONSO.RRF desde el lanzamiento completo de RxNorm | http://www.nlm.nih.gov/research/umls/rxnorm |
snomed | la instantánea de descripción RF2 (sct2_Description_Snapshot-en_*.txt) | http://snomed.info/sct |
snomed-rf2 | un paquete de lanzamiento completo de SNOMED CT RF2 (el zip que publica un centro de lanzamiento nacional) | http://snomed.info/sct, versión http://snomed.info/sct/{module}/version/{date} |
claml | un archivo ClaML: WHO ICD-10, ICD-10-GM y OPS (BfArM), CIM-10 FR (ATIH) | desde el título del archivo, o el system dado |
icd11 | la tabulación simple MMS de la OMS ICD-11 (tabulada, con columnas Code y Title) | http://id.who.int/icd/release/11/mms |
csv | cualquier archivo delimitado, con system, codeColumn y displayColumn (CPT, HCPCS, códigos locales, tablas nacionales) | el system dado |
Ninguno viene con Perfuse: LOINC, SNOMED CT, RxNorm y CPT cada uno tienen su propia licencia, y la pantalla pide al importador que confirme que la organización la posee (registrada con la importación). Los códigos importados se utilizan de tres maneras:
- una tabla que nombra
source_systemotarget_systemtiene sus entradas verificadas cuando
se guarda, y los códigos que no están en el sistema se enumeran en la edición;
- Buscar un código busca por código o por palabras en el nombre;
- el servidor FHIR responde
CodeSystem/$lookupyCodeSystem/$validate-code
para ellos (ver las opciones del servidor FHIR). Los códigos ICD-10-CM se aceptan con o sin el punto.
Ediciones y clasificaciones nacionales. Una edición de SNOMED CT se importa desde su paquete RF2 completo: conceptos con su estado real, cada descripción activa, y los conjuntos de referencia de lenguaje. La edición se encuentra desde el paquete (el módulo nacional con la mayoría de los conceptos; edition lo nombra en su lugar) y se convierte en la URI de versión. language elige el término preferido de cada concepto (sv, da, nl, de, fr, en-GB, en-US ...; inglés de lo contrario), y se mantienen como designaciones el término preferido y el nombre completamente especificado de cada lenguaje: $lookup los devuelve, y su displayLanguage elige uno (de-CH se retrotrae a de). Las ediciones que Perfuse nombra (Internacional, Reino Unido, Alemania, Países Bajos, Suecia, Dinamarca, Bélgica, Irlanda, Noruega, Suiza, Estonia, España) se enumeran en GET /api/terminology/editions, los IDs de módulo no verificados contra una fuente publicada marcada como tal; cualquier otra edición se importa también, nombrada por su módulo.
Los archivos ClaML se leen en UTF-8, ISO-8859-1, ISO-8859-15 o Windows-1252, según su declaración XML. El título de cada categoría es su rúbrica preferida (términos anidados incluidos); un modificador (un quinto carácter para el sitio de una fractura, por ejemplo) hace que los códigos que agrega, con el título del modificador después del de la categoría, a menos que la categoría lo excluya; blocks=true importa capítulos y bloques también. Las columnas de la tabulación ICD-11 se encuentran por nombre, ya que su orden ha cambiado entre lanzamientos, y los guiones que muestran la profundidad se eliminan de los títulos. Un archivo delimitado toma delimiter (, ; | o tab), encoding (utf-8, iso-8859-1, iso-8859-15, windows-1252) y noHeader=true, con columnas numeradas desde 1. La consola ofrece ajustes para los archivos de la 5ª edición del ICD-10 del Reino Unido y OPCS-4 (TRUD), la tabla de etiquetas CIM-10 FR de Francia (ATIH) y una exportación ICD-10-BE; no todos los editores documentan sus columnas, así que compruébelas contra el encabezado del archivo.
Fechas efectivas. effective=YYYY-MM-DD importa una versión ahora y la utiliza a partir de ese día (ICD-10-GM, por ejemplo, cada 1 de enero): hasta entonces, una búsqueda que no nombra versión se responde con la versión ya en vigor, y la pantalla muestra la fecha.
ICD-11 de la OMS. Con un cliente registrado en icd.who.int (Configuración → Datos → Sistemas de códigos, serve -icd11-client-id y -icd11-client-secret; -icd11-release, 2025-01 por defecto), los códigos ICD-11 que no se importan se buscan en la API ICD de la OMS (codeinfo, luego el título de la entidad) y se mantienen durante un día. Un código postcoordinado (NC72.2&XK9K) se verifica completo, por lo que una combinación ICD-11 que no se permite se rechaza, y sus nombres de visualización cada parte ("Fractura del cuello del fémur (Derecha)").
ICD-11 sin enviar códigos. La OMS publica la misma API como un contenedor (whoicd/icd-api, gratuito, Intel y Arm). Ejecútelo en su propia red y proporcione su dirección (serve -icd11-url http://icd-api.local, o Dirección de la API de ICD en la misma configuración); no se necesita cliente y ningún código sale de su red. Probado contra ese contenedor (version 2.6.0, lanzamiento 2025-01 en inglés y francés).
7 Filtrado
Un filtro decide qué mensajes acepta un canal. Es una expresión en el canal, evaluada después de que el mensaje se haya analizado y registrado, antes de cualquier transformación.
filter: MSH-9.1 == "ADT" && MSH-9.2 in ("A01", "A03", "A08")
Un mensaje que el filtro rechaza se registra con el resultado filtrado. Eso es un resultado normal, no un error, y no genera una alerta ni produce un acuse de recibo negativo.
7.1 Filtrado no es lo mismo que fallido
Esta distinción es importante porque confundirlos puede ocultar un problema real.
Un mensaje filtrado fue correctamente rechazado: no era para este canal. Un mensaje fallido fue uno que este canal debería haber manejado y no pudo. Si ambos se registraran de la misma manera, un canal que comienza a fallar en todos los mensajes parecería exactamente como un canal que está filtrando la mayoría de su tráfico, lo que es el estado normal de muchos canales.
Por lo tanto, las estadísticas los cuentan por separado, las reglas de alertas los tratan de manera diferente y la interfaz los muestra en diferentes colores. Un aumento en los mensajes filtrados puede significar que el remitente ha comenzado a enviar algo nuevo; un aumento en los fallos significa que algo está roto.
7.2 Qué puede leer un filtro
Cualquier campo en el mensaje analizado, por los mismos caminos que utilizan las transformaciones. El filtro se ejecuta antes de las transformaciones, por lo que ve el mensaje tal como llegó — no como se enviará.
Ese orden es deliberado. Un filtro que lea valores post-transformación estaría decidiendo si aceptar un mensaje en función de los cambios realizados sobre la suposición de que fue aceptado, lo que es circular y produce canales cuyo comportamiento depende del orden de dos cosas que parecen independientes.
7.3 Expresiones
Comparaciones: ==, !=, y para números <, <=, >, >=.
Pertenencia: in ("A01", "A03"), que es más claro que una cadena de || y es la forma que produce el constructor.
Combinación: &&, ||, y paréntesis. Negación con !.
Ausencia: una ruta que no está presente se compara desigualmente con cualquier valor, incluida la cadena vacía. Para probar la ausencia explícitamente, compárela con absent. Esto es importante porque un campo ausente y un campo vacío son diferentes, como se describe en transformation, y un filtro que los trata de la misma manera aceptará mensajes que se escribió para excluir.
7.4 Un error en el filtro es una negativa
Si el filtro en sí mismo no puede evaluarse — una expresión malformada que pasó la validación, o una comparación que no se puede realizar — el mensaje se rechaza y el error se registra en su contra.
Rechazar en lugar de aceptar es la opción más segura de las dos, pero ninguna de las dos es buena, y el resultado se registra de manera diferente a una negativa de filtro ordinaria para que pueda activar una alerta. Un filtro que está generando errores en todos los mensajes está silenciosamente descartando toda la fuente de datos, y no debe parecer un filtro que esté funcionando.
7.5 Filtrar en la fuente cuando se pueda
Un filtro se ejecuta después de que el mensaje ha cruzado la red y sido analizado. Algunas fuentes pueden filtrar antes y más barato:
- Una fuente de corredor toma un
selector, que filtra en el corredor. Los mensajes que este canal no quiere nunca cruzan la red en absoluto. En una cola compartida, esa es la diferencia entre leer cien mensajes al día y cien mil. - Una fuente de base de datos decide qué devuelve su consulta.
- Un patrón de fuente de archivo decide qué archivos recoge.
Filtrar en la fuente es una decisión de rendimiento, no de corrección, y tiene un costo: un mensaje filtrado en el corredor nunca se registra en Perfuse, por lo que no aparece en el almacén de mensajes y no se puede inspeccionar más tarde. Si necesita poder demostrar qué envió el remitente, filtre aquí en lugar de allí.
7.6 Probar un filtro
Un filtro se puede verificar con tráfico histórico real antes de guardarlo. La interfaz informa cuántos de los últimos N mensajes registrados el filtro aceptaría, rechazaría y generaría errores — lo que es la forma más rápida de descubrir que una expresión que parece correcta no acepta nada.
La traza también muestra, por mensaje, qué campos leyó el filtro y qué valores encontró. Un filtro que rechaza todo de manera inesperada generalmente está leyendo un camino que no existe en esta fuente de datos, y la traza lo nombra.
8 Formatos de mensaje
El dataType de un canal decide cómo los bytes se convierten en un mensaje. Afecta el análisis, qué significan los caminos, cómo se escribe el mensaje nuevamente y qué transformaciones son válidas.
| Valor | Formato |
|---|---|
hl7 | HL7 v2, el estándar delimitado por tuberías. El valor predeterminado. |
hl7v3 | Documentos HL7 v3 y CDA, que son XML. |
x12 | ASC X12, utilizado para la elegibilidad y las reclamaciones. |
ncpdp | NCPDP Telecommunication Standard D.0 para reclamaciones de farmacia. |
script | NCPDP SCRIPT, recetas electrónicas. XML. |
dicom | Objetos de imagen DICOM. |
delimited | CSV y otros textos delimitados. |
raw | No se realiza análisis. Bytes de entrada, bytes de salida. |
8.1 HL7 v2
El valor predeterminado, y lo que la mayoría de los ejemplos de este manual utilizan. Segmentos terminados por retorno de carro, campos por |, componentes por ^, repeticiones por ~.
Dos cosas sobre la codificación son dignas de conocer porque causan fallos silenciosos.
Los delimitadores no son fijos. Se declaran en MSH-1 y MSH-2 de cada mensaje, y Perfuse los lee desde allí en lugar de asumir tuberías. Un remitente que utiliza un separador de campo diferente se analiza correctamente.
La consecuencia es que un mensaje cuyos separadores han sido reemplazados — la mayoría de las veces por un procesador de texto o cliente de correo electrónico que sustituye por caracteres con apariencia tipográfica similar — se analiza perfectamente en campos que están todos mal. No se produce ningún error. Esta es la razón por la que el lector de ejemplo descrito en testing informa sobre el carácter de separador que encontró y se niega a expresar confianza cuando es inusual.
8.2 HL7 v3 y CDA
XML en lugar de delimitado. Las rutas son rutas de elementos y atributos en lugar de posiciones de campo.
En v3, un valor es casi siempre un atributo en lugar de texto de elemento. Una comparación que solo lee el texto de elemento informa que dos documentos son idénticos cuando la fecha de nacimiento, el género y todos los identificadores difieren. La comparación de Perfuse lee ambos, y esta es la razón: la alternativa tiene éxito silenciosamente.
Las opciones de hl7v3 están en la referencia de formato.
8.3 X12
Segmentos terminados por ~ convencionalmente, con delimitadores declarados en el segmento ISA. Al igual que HL7, se leen desde el mensaje en lugar de asumirlos.
La estructura de X12 es sobres dentro de sobres —intercambio, grupo funcional, conjunto de transacciones— y un solo intercambio puede contener muchas transacciones.
Por defecto, un intercambio es un mensaje. Todo el archivo obtiene un registro, un resultado y una fila en la interfaz, sin importar cuántas transacciones contenga.
8.3.1 El X12 XML de Mirth
Mirth Connect y Open Integration Engine muestran X12 como XML: una raíz X12Transaction (o EDIMessage cuando la entrada no comienza con ISA) que transporta los delimitadores como atributos, y un elemento por segmento y elemento llamado ISA.01, NM1.03.1, etc. Los canales y receptores construidos para Mirth leen y escriben esa forma. Perfuse lee y escribe la misma:
x12: { arrives_as: xml }en un canal X12 toma ese XML y lo maneja como el X12 que representa (la elección Llega como del constructor);x12_as: xmlen un destino envía el X12 como ese XML (la elección Enviar como X12 XML de Mirth del constructor);perfuse x12 xml [file]yperfuse x12 from-xml [file]convierten en la línea de comandos.
El formato se escribió a partir de la salida observada y el formato publicado de Open Integration Engine 4.6, y se verifica contra él: 25 intercambios generados y 4 ejemplos conservados salen byte por byte como los escribe OIE, y viceversa (ver verificación). Un DOCTYPE se rechaza, como en todos los XML que lee Perfuse.
8.3.2 Dividir un intercambio
Establecer split hace que cada conjunto de transacciones sea un mensaje por derecho propio, con su propio registro, sus propias métricas y su propia fila. Un operador que busca una reclamación encuentra una reclamación en lugar de un archivo de cuatrocientas.
Está desactivado por defecto porque activarlo multiplica todo lo que está aguas abajo —recuentos de mensajes, filas, umbrales de alerta— y un umbral ajustado para archivos que de repente cuenta reclamaciones no significa lo que solía significar. Eso debería ser una decisión que alguien tomó en lugar de un valor predeterminado que heredó.
split y acknowledge no pueden establecerse ambos, y un canal con ambos se rechaza al cargar. Un reconocimiento es una declaración sobre todo un intercambio, por lo que la división enviaría uno por conjunto de transacciones, y un socio que recibe varios 999 que nombran el mismo grupo funcional no tiene forma de reconciliarlos.8.3.3 Cuando los recuentos no suman
X12 declara sus propios recuentos —cuántos segmentos, cuántas transacciones— y la clave envelope dice qué hacer cuando no coinciden con lo que realmente está presente. Esos recuentos son el único mecanismo que tiene X12 para detectar que la mitad de un archivo de reclamaciones ha llegado.
| Valor | Comportamiento |
|---|---|
require | Rechazar el intercambio. El valor predeterminado. |
warn | Procesarlo y registrar la falla contra el mensaje. |
ignore | No verificar en absoluto. |
warn existe para una situación real: el software de algunos socios ha generado recuentos incorrectos durante años en archivos de lo contrario completos, y un sitio que tiene que procesar esos necesita una forma de decirlo en voz alta en la configuración en lugar de desactivar la validación en general.
ignore se nombra para ser incómodo. Establecerlo elimina el único medio que X12 te da para notar un archivo truncado, y un archivo de reclamaciones truncado es indistinguible de uno pequeño.8.3.4 Transacciones que Perfuse lee como datos estructurados
Cualquier intercambio X12 puede ser enrutado, filtrado y dirigido por ruta. Estas transacciones se analizan además en campos con nombre, por lo que un canal puede preguntar qué decidió un pagador en lugar de qué elemento se encuentra en la posición cuatro.
| Transacción | Lo que lleva |
|---|---|
835 | Asesoramiento de pago: lo que se pagó, lo que se denegó y los motivos de ajuste. |
275 | Documentos adjuntos de reclamaciones: documentación clínica que respalda una reclamación. |
277 | Estado de la reclamación, incluida la solicitud del pagador de documentación adicional. |
278 | Autorización previa: la solicitud de revisión y la decisión del pagador. |
999 | Reconocimiento funcional, que Perfuse también genera. |
271 | Elegibilidad: si un paciente está cubierto, y sus copagos, coseguros, deducibles y montos fuera de bolsillo, dentro y fuera de la red. |
|---|---|
834 | Inscripción: miembros agregados, cambiados y terminados, con cada línea de cobertura y sus fechas. |
837 | Reclamaciones, profesionales e institucionales (leer para la conversión CARIN de CMS-0057). |
Perfuse también construye la consulta de elegibilidad 270 y la solicitud de estado de reclamación 276, bajo Pagadores → Reclamaciones y autenticación o a través de POST /api/x12/eligibility/build y /api/x12/claimstatus/build. Ambas son las versiones HIPAA 5010 (005010X279A1, 005010X212), leídas y verificadas antes de ser mostradas, y ninguna se envía desde esa página: entregar una es el trabajo de un canal, donde se encuentran el socio comercial y sus credenciales.
8.3.5 Elegibilidad, y la regla de contenido de datos CAQH CORE
Un 271 es una lista de segmentos EB, cada uno un solo hecho: cobertura activa, o un copago de $25 para el tipo de servicio 30 en la red, o $800 del deducible individual restante. El lector los ensambla en la respuesta que quiere una recepción - "cobertura activa bajo Gold PPO 2000; copago en la red $25; deducible individual restante en la red $800" - y nombra un rechazo (un segmento AAA) en palabras, con lo que el pagador dice que hacer a continuación.
También verifica la respuesta contra la regla de contenido de datos de elegibilidad y beneficios CAQH CORE: para una consulta sobre cobertura de plan de beneficios de salud (servicio tipo 30), una respuesta activa debe llevar fechas del plan, el nombre del plan, copago, coseguro, deducible y deducible restante tanto dentro como fuera de la red, y los tipos de servicios básicos (1, 33, 35, 47, 48, 50, 86, 88, 98, AL, MH, UC). Cada uno se enumera como cumplido o faltante. Esta es la interpretación de Perfuse de la regla, útil para "¿por qué este pagador nunca nos dice el deducible?" - no es una certificación CORE, que los proveedores de pruebas autorizados por CAQH llevan a cabo.
8.3.6 Autorización previa
Perfuse implementa la autorización previa dos veces, a propósito.
El lado FHIR es lo que CMS-0057-F requiere que los pagadores expongan a partir del 1 de enero de 2027. El 278 es lo que los pagadores aceptan hoy, y la regla no lo reemplaza — Da Vinci PAS es en la práctica un mapeo sobre esta transacción, porque un 278 es lo que un sistema de gestión de utilidad habla. Un motor que solo lee la mitad FHIR puede hablar con los sistemas que existirán y no con los que existen.
Una respuesta 278 lleva el número de autorización, y ese es el campo para el que existe toda la transacción. Sin él, un servicio certificado no es facturable: la reclamación se envía sin número de autorización y se deniega por la falta de uno, lo que se lee aguas abajo como una denegación clínica.
Una denegación y una negativa a considerar no son lo mismo
Esta es la confusión más costosa en la transacción, y Perfuse informa sobre las dos por separado.
| Lo que llegó | Lo que significa | Lo que hacer |
|---|---|---|
HCR con acción A3 | El pagador consideró la solicitud y no la certificará. | Apelar, o cambiar el plan de atención. |
Un segmento AAA | El pagador no la consideraría — paciente no encontrado, solicitante no reconocido, revisión no cubierta. Nada se decidió. | Corregir la solicitud y enviarla nuevamente. |
Ambos se leen como "no aprobado" para cualquiera que escanee, y las acciones correctas son opuestas. Apelar algo que nadie dictaminó no logra nada; volver a presentar un rechazo produce el mismo rechazo. El resultado analizado nombra qué sucedió — denied contra not-considered — y AAA04 lleva el propio consejo del pagador sobre si la solicitud es recuperable en absoluto.
La certificación parcial se informa como partially-certified en lugar de integrarse en la aprobación. Hay un número de autorización para facturar, así que cuenta como aprobado, pero la cantidad certificada no es la cantidad solicitada — y una práctica que reserva seis sesiones contra tres aprobadas lo descubre en la cuarta reclamación.
Un código de acción que Perfuse no reconoce se informa como unknown, no como pendiente. Pendiente se lee como "esperar", y esperar es la acción incorrecta para la mayoría de lo que podría ser un código no reconocido.
Un canal puede enrutarse en la decisión directamente:
filter: HCR-1 == "A3"
Converting entre los dos
Perfuse convierte un 278 analizado en una respuesta de reclamación Da Vinci PAS ClaimResponse, e informa sobre cada juicio que la conversión necesitó en lugar de solo el resultado. La conversión sigue tres reglas:
- No se actualiza ninguna decisión. La certificación parcial permanece parcial, y una solicitud que el pagador se negó a considerar no se convierte en una denegación. Ambas reducciones están disponibles en el mapeo obvio y ambas son incorrectas en la dirección costosa.
- No se inventa nada para llenar un campo requerido. Un evento certificado sin número de autorización se informa como tal, no se le da uno plausible — un número fabricado produce una reclamación que se deniega más tarde por una razón que nadie puede rastrear.
- Se utiliza una decisión llevada solo al nivel de línea. Un pagador que responde por servicio puede dejar el evento sin una decisión propia; leer solo el nivel de evento informaría que está pendiente, y una práctica espera una respuesta que ya tiene mientras la aprobación caduca. Donde las líneas no están de acuerdo, gana la menos favorable, porque un evento con una línea rechazada no es una aprobación.
Reducir a un vocabulario de tres estados — aprobado, denegado, pendiente — está disponible y es explícito sobre lo que descarta. Reducir una negativa a considerar produce "denegado" y dice en palabras que la acción correcta es una resubmisión corregida en lugar de una apelación.
Verificado contra PAS 2.2.1, no solo contra sí mismo
El ClaimResponse está diseñado según el perfil Da Vinci PAS 2.2.1 y se verificó con el validador oficial de HL7 cargado con el paquete PAS publicado. La primera versión solo había sido verificada por las propias pruebas de Perfuse, y el validador la rechazó con seis u ocho errores por recurso. Cada uno de ellos es algo que el punto de conexión PAS de un pagador también habría rechazado:
| Qué estaba mal | Dónde PAS lo pone |
|---|---|
La decisión estaba en adjudication.category y reason. | La extensión reviewAction, en una adjudicación cuya categoría es submitted. |
| El código de razón nombrado elemento X12 1034. | Lista de códigos externos X12 886. |
Una solicitud pendiente respondió outcome: queued. | outcome se refiere al procesamiento y PAS solo permite complete, error y partial. Un pendiente es complete, con acción de revisión A4. |
processNote.type era un CodeableConcept. | R4 tiene un código: print. |
patient, created, insurer y request faltaban. | Los cuatro son requeridos. Se proporcionan como referencias de identificador, porque la respuesta se mantiene sola en lugar de en un paquete. |
La salida corregida se valida sin errores para una certificación, una denegación y un pendiente. Las advertencias que permanecen son el validador diciendo que no puede cargar los sistemas de códigos X12, que X12 licencia. El número de autorización se lleva en reviewAction.number, donde el ejemplo propio de la guía lo pone, y en preAuthRef cuando la respuesta solo tiene uno.
El validador no es parte de make check, porque necesita Java y una descarga de la guía y sus dependencias. Lo que encontró se mantiene en una prueba en su lugar: cada regla que aplicó se afirma, por lo que un cambio que vuelve a introducir uno de esos errores falla.
La vista Reclamaciones y autenticación convierte una 278 pegada y muestra las decisiones, las notas y el recurso.
8.3.7 Adjuntos, y por qué necesitan un manejo especial
CMS-0053-F hace que el 275 sea un estándar HIPAA para adjuntos de reclamaciones, con cumplimiento requerido para el 26 de mayo de 2028. Lleva un documento — una nota operativa, un informe de imágenes, un formulario escaneado — vinculado a la reclamación que respalda.
El 277 es la otra mitad de la misma conversación. Un pagador recibe una reclamación, decide que necesita documentación, y envía un 277 solicitándola; el proveedor responde con un 275.
Un canal puede enrutarse en el código de estado directamente, porque STC01 es un compuesto y su segundo componente es el estado. Escrito en forma canónica, porque un componente se dirige con un punto y las dos notaciones no se pueden mezclar — STC-1.2 es elemento 1, componente 2:
filter: STC-1.2 in ("227", "233", "252", "287")
Esos son los códigos que inequívocamente significan que se quiere un documento. La categoría categoría en STC-1.1 no se utiliza deliberadamente por sí sola: R4 significa una solicitud de documentación en algunas guías de implementación y "reenviado a otro lugar" en otras, por lo que un filtro en la categoría sola pondría reclamaciones finalizadas en la cola para que alguien las persiga.
Dos identificadores deciden si un adjunto se asocia alguna vez con su reclamación, y ambos se informan: el número de control de adjunto, que el proveedor puso en la reclamación original para decir que se proporcionaría documentación, y el número de seguimiento, que coincide con la solicitud del pagador. Un adjunto que llega con ninguno de los dos se archiva y nunca se conecta a nada, mientras la reclamación envejece.
275 no se interpreta. Perfuse informa sus bytes y lo que el remitente dice que son. Un adjunto puede ser un PDF, un TIFF, un documento CDA o un mensaje HL7, y un canal que necesita el contenido puede entregar esos bytes al formato que lo lee.El formulario 006020, que es el que adopta la regla
CMS-0053-F adopta la versión 006020, y su 275 difiere del formulario 005010 de dos maneras que importan.
- La carga útil está en
BDS, no enBIN.BDS01nombra un filtro,ASCoB64.BDS02es la longitud yBDS03los datos. - La carga útil es una entidad MIME, con encabezados que dicen qué es el documento y un cuerpo base64. Con
B64la entidad en sí es base64 también.
Perfuse desenvuelve la entidad y informa del documento que contiene, con su tipo de contenido y nombre de archivo. Los bytes que informa son exactamente los que el remitente envolvió. Una entidad que no se puede desenvolver deja la carga útil sin procesar, con una nota que explica por qué.
Construir un 275. Perfuse también construye adjuntos 006020X314: uno no solicitado para acompañar una reclamación, o una respuesta a una solicitud 277 de un pagador. La diferencia está en BGN01 02 frente a 11, y si TRN02 lleva el PWK06 de la reclamación o el TRN02 de la 277. Cada intercambio construido se analiza nuevamente antes de devolverlo, y la respuesta indica si el documento que contiene es el mismo que se introdujo.
La estructura proviene de las dos guías de acompañamiento publicadas para 006020X314: la guía esMD de CMS y la de UnitedHealthcare. No proviene del informe técnico X12, que X12 licencia y que este proyecto no posee.
- Donde las dos guías están de acuerdo, Perfuse las sigue.
- Donde difieren, satisface ambas si puede.
CAT02esHLpara un documento HL7, que esMD utiliza, yIApara una imagen, que UnitedHealthcare utiliza. - De lo contrario, envía el segmento opcional solo cuando se le da el valor: el identificador de reclamación
REF*X1de esMD y la fecha de servicioDTP*472de UnitedHealthcare.
No hay validación contra el informe técnico. La guía de acompañamiento del socio gobierna.
8.3.8 Firmas electrónicas en adjuntos
CMS-0053-F también adopta un estándar de firma, en 45 CFR 162.2002(e): la Guía de implementación de HL7 para CDA Release 2, Firmas digitales y delegación de derechos, Release 1. Se aplica a las firmas que se ponen en la información de adjunto cuando se envía en una transacción de adjunto de HIPAA, a partir del 26 de mayo de 2028. Un pagador no puede pedir más de lo que establece la guía.
Perfuse firma un C-CDA de esa manera en su camino hacia un 275, y verifica las firmas de cada C-CDA en un 275 que lee.
- Qué se firma. Todo el documento excepto sus participantes
legalAuthenticatoryauthenticator, para que cada firmante posterior deje intactas las firmas anteriores. La firma indica esto con una transformación de filtro XPath 2.0 que cualquier biblioteca de firma XML puede evaluar. - Dónde va. En Base64 en
sdtc:signatureTexten el participante que firma, con una descripción de texto plano de una línea. El firmante es el autenticador legal del documento si tiene uno no firmado, de lo contrario es un nuevoauthenticator. - Qué establece. El certificado del firmante, la hora en UTC, un rol de la taxonomía de proveedores NUCC y un propósito de ASTM E1762. El propósito predeterminado es *Firma del autor* para un autenticador legal y *Firma de coautor* de lo contrario, como en el ejemplo de la guía.
- Cuánto dura. La guía pide XAdES-X-L, que lleva todo lo que un pagador necesita para verificar la firma años después sin llegar a la red. Eso es la ruta de certificación, una respuesta OCSP o CRL del CA, y dos sellos de tiempo de una autoridad de sellado de tiempo. Perfuse obtiene los datos de revocación de las direcciones en el certificado y los sellos de tiempo de
-tsa-url.
Si algo de eso no está disponible, la firma todavía se crea y es válida. El resultado nombra la forma XAdES que alcanzó y dice qué lo detuvo antes de llegar a X-L: por ejemplo, un certificado auto-firmado (no hay CA para preguntar) o no hay autoridad de sellado de tiempo.
La clave es -signing-cert y -signing-key. Coloque los certificados CA después del certificado del firmante en el mismo archivo para que la ruta viaje con la firma. Para una firma que signifique lo que significa DSDR, el certificado debe nombrar al clínico, con su NPI, en lugar del servidor. La firma es una acción de administrador, como firmar cualquier documento.
La guía no nombra un espacio de nombres para sus propios elementos. Perfuse coloca digitalSignature, authorizedSigner y SignaturePurpose en urn:hl7-org:sdtc. También establece el propósito como un tipo de compromiso XAdES con el OID ASTM, que las herramientas XAdES entienden. La delegación de derechos, donde un delegado firma con una aserción SAML del firmante autorizado, no se produce, y una recibida se informa como no verificada.
El NPI del proveedor se verifica contra su dígito de verificación. Un pagador coincide un adjunto con su reclamación en parte con ese número, y un número mal escrito deja el adjunto sin coincidencia mientras la reclamación se deniega por falta de documentación.
La vista Reclamaciones y autenticación construye un 275 a partir de un C-CDA pegado o un archivo cargado, opcionalmente firmando el C-CDA, y lee uno con los hallazgos de cada firma. Nada en esa página envía nada: el intercambio se devuelve para ser entregado a través de un canal, que es donde pertenece el socio, las credenciales y el registro de auditoría.
8.3.9 Segmentos binarios
Un segmento BIN lleva bytes sin procesar, y los bytes sin procesar contienen delimitadores. Cada segmento X12 ordinario termina en el terminador de segmento, por lo que un analizador ordinario encuentra segmentos buscando un byte; un adjunto no se puede encontrar de esa manera. Un PDF que contiene un ~ se cortaría en piezas, y cada pieza es un segmento estructuralmente válido — por lo que el documento se trunca, cada segmento después de él está desalineado, y nada informa un error.
Perfuse lee el recuento de bytes que el segmento declara y toma exactamente esos bytes. Dos consecuencias dignas de conocer:
- Una longitud declarada que no coincide con los datos es rechazada, nombrando ambos números. La pregunta útil es si el recuento del remitente era incorrecto o si el archivo se truncó durante el tránsito, y solo los dos números responden a ello.
BDS, el segmento binario006020, mantiene su recuento enBDS02y sus datos enBDS03. Fue rechazado hasta que se pudo confirmar a partir de una fuente publicada en lugar de recordarlo, y dos fuentes ahora lo confirman: la tablaBDSde la guía de esMD de CMS y la guía de UnitedHealthcare.
La guía esMD propia muestra un intercambio que declara 16 bytes para la misma carga útil de 53 bytes que su tabla cuenta correctamente como 53. Perfuse rechaza esa muestra, como debería. Una prueba mantiene tanto el ejemplo de la tabla como el error tipográfico de la muestra, por lo que un cambio en cualquier dirección es notado.
8.4 FHIR
8.4.1 En lo que se convierte un mensaje HL7 v2
El tipo de mensaje decide qué se produce, y cada conversión informa los juicios que necesitó como notas junto al paquete.
| v2 | FHIR |
|---|---|
ADT | Paciente, una Encuesta cuando hay una visita, una Condición por diagnóstico DG1 (también enumerada en la Encuesta diagnosis, una de admisión como AD), un Procedimiento por PR1 y una AlergiaIntolerancia por AL1. A28, A31, A29 y A24 se refieren a la persona, por lo que no producen Encuesta ni diagnósticos, pero sí llevan alergias. |
ORU | Informe de Diagnóstico por OBR, con sus Observaciones y Especimen, y la orden que responde como una Solicitud de Servicio (su solicitante OBR-16 o ORC-12, su estado ORC-5); OBR-32 es el intérprete de resultados, OBR-34 y OBR-35 son los realizadores. Los resultados se agrupan bajo el OBR anterior según su posición, porque OBX-4 no es confiable. |
ORM, OML, OMG | Solicitud de Servicio. |
SIU | Cita, con el paciente, los profesionales de la salud de AIP y las ubicaciones de AIL. |
MDM | Referencia de Documento que lleva el documento. |
VXU | Inmunización, una por RXA. |
Cualquier otra cosa produce el Paciente y una advertencia que nombra el tipo que no se asignó.
Programación. Una reserva, una reprogramación, una cancelación y un no-show aterrizan en la misma Cita. Su identidad proviene de los números de la cita en SCH-1 y SCH-2, nunca del mensaje, por lo que una cancelación actualiza la reserva. La alternativa sería una segunda cita cancelada junto a una cita activa, lo que muestra al paciente reservado en un espacio que ya no existe.
- Estado. El estado de relleno en
SCH-25decide, y el evento de activación se utiliza solo cuando está vacío. - Espacios bloqueados. Un espacio bloqueado no es una cita con un paciente y no produce ninguna.
- Hora de inicio. Se lee desde
AIS,AIG,AILoAIPprimero, luegoSCH-11, porque la versión 2.5 lo movió y las fuentes no se movieron todas con ella. - Duraciones. Una duración en una unidad que Perfuse no reconoce se deja fuera, no se adivina.
Documentos. Las notas de texto (TX, FT, ST) mantienen sus saltos de línea, incluidas las líneas en blanco, porque una repetición en blanco en una nota es un salto de párrafo.
- Documentos incrustados. Un PDF, TIFF o JPEG (
ED) incrustado se transporta byte por byte. El base64 no válido se rechaza en lugar de pasarse. - Tipos. El tipo de documento en
TXA-2se mantiene como se envió y, donde hay un equivalente claro, se asigna el código LOINC que utiliza US Core. - Firma. Una nota que nadie ha firmado es
preliminary, yT11hace que el documento seaentered-in-error. - Sin contenido. Una notificación sin contenido (
T01,T03y otros eventos con números impares) no produce un DocumentReference. FHIR requiere contenido, y un archivo adjunto vacío parecería una nota mientras no dice nada.
Esto es lo que pide el criterio 14 del Marco de interoperabilidad de CMS: notas de gráficos y documentos clínicos devueltos como archivos adjuntos de FHIR.
Inmunizaciones. Una dosis nunca se informa como administrada cuando el mensaje dice lo contrario.
- Dosis rechazadas. Una dosis rechazada o no administrada (
RXA-20REoNA) esnot-done, con la razón deRXA-18. - Eliminaciones. Una eliminada (
RXA-21D) esentered-in-error. - Registros históricos. Un registro transcrito desde otro lugar (
RXA-9diferente a00) no es una fuente primaria. - Cantidades desconocidas. Una cantidad de
999significa desconocida, no 999. - Rutas y sitios. Cada dosis toma la ruta y el sitio del
RXRdespués de ella, por lo que un mensaje con dos dosis no da a la segunda la ruta de la primera.
Entrada incómoda. Cada una de estas se informa en las notas en lugar de decidirse silenciosamente:
- Identificadores.
PID-2yPID-4, retirados después de la v2.7 pero aún enviados, se mantienen comoPID-3.CX.7yCX.8se convierten en
Identifier.period. Una autoridad de asignación que lleva un OID ISO (AUSHIC&2.16.840.1.113883.3.879&ISO) se convierte en urn:oid:. Un código de tipo fuera de la tabla HL7 0203 se mantiene como texto, no etiquetado como tal.
- Fusiones.
MRGen un evento de fusión (A40y sus similares) envía también el registro retirado, inactivo, conPatient.linken ambos sentidos. En cualquier
otro evento, como un A08, no se fusiona nada y una advertencia indica que el identificador anterior sigue estando activo.
- Tiempos. Una hora de nacimiento en
PID-7se envía a la extensiónpatient-birthTime. Una marca de tiempo sin desplazamiento toma la zona horaria configurada,
de lo contrario, el desplazamiento en MSH-7 (v2 supone la hora local del remitente), de lo contrario, UTC. Una hora de nacimiento es la excepción: sin desplazamiento de cualquiera de los tres, se omite en lugar de etiquetarse como UTC, porque un desplazamiento incorrecto puede mover un nacimiento a otro día. birthDate se mantiene, y una advertencia indica que la hora se ha eliminado y que establecer una zona horaria para la fuente la mantiene.
- Resultados. Las repeticiones de un resultado de texto se unen con saltos de línea. Un resultado
ED, o uno codificado cuyo valor es claramente Base64, se convierte en un
DocumentReference que la Observación señala en derivedFrom, ya que una Observación R4 no tiene un valor de adjunto. Un comparador SN de = se elimina como redundante; <> mantiene el texto, porque ninguna cantidad puede decir "no igual".
- Visitas. Un
A08con una fecha de alta (PV1-45) es un encuentro terminado. Una clase de paciente que falta o es desconocida es el sabor nuloUNK,
porque R4 requiere una clase.
- Segmentos Z se nombran en una advertencia, ya que no hay un mapa estándar para ellos.
- Names. When the sender splits a family name (
XPN.1,XCN.2) into its parts - a surname prefix and a surname, the person's
own and a partner's, as Dutch and Flemish names are sent - family is the whole name and the parts go in HL7's family-name extensions (humanname-own-prefix, -own-name, -partner-prefix, -partner-name).
- Procedures.
PR1becomes a Procedure with statusunknown, since an ADT does not say whether it was done or planned; the
anaesthetist (PR1-8) and surgeon (PR1-11) are performers with SNOMED CT roles. An OPS code also gets its SNOMED CT category from its chapter, by HL7 Deutschland's OPS-SNOMED-Category map, which ISiK requires.
8.4.2 Lo que agrega un sitio: el script de paquete
Un perfil nacional a menudo quiere lo que un mensaje HL7 v2 no lleva: el número IK de un hospital o el código ODS, qué nivel de contacto es una visita (ISiK), si un nombre dado es un nombre o una inicial (el nl-core holandés). Perfuse no adivina esos. El destino fhir tiene un bundle_script donde un sitio escribe ellos: JavaScript ejecutado en cada Bundle convertido, como bundle, antes de que se valide y envíe. Lo que el script deja es lo que se envía, comprobado como cualquier conversión, y un script que lanza una excepción falla el mensaje como cualquier transformador. La consola lo tiene debajo de *Agregar lo que sus sistemas saben* del destino.
destinations:
- name: kis
type: fhir
fhir:
url: https://fhir.klinikum.example/fhir
version: "4.0.1"
identifier_systems: {KLINIKUM-MUSTERSTADT: "https://fhir.klinikum-musterstadt.example/sid/patientennummer"}
bundle_script: |
bundle.entry.forEach(function (e) {
if (e.resource.resourceType === "Organization" && e.resource.name === "KLINIKUM-MUSTERSTADT") {
e.resource.identifier = [{system: "http://fhir.de/sid/arge-ik/iknr", value: "260326822"}];
}
});
perfuse fhir convert -bundle-script FILE ejecuta el mismo script, por lo que un sitio puede probarlo en sus propios mensajes primero. Dos ejemplos que funcionan están en el repositorio: examples/isik/bundle-script.js (IKNR, Kontaktebene, número de paciente de tipo MR, Diagnosetyp) y examples/nictiz/bundle-script.js (nombres y letras iniciales). Con ellos, los mensajes alemanes e holandeses inventados en conformance/national van de 18 a 42 de 47 recursos válidos contra ISiK y de 12 a 18 de 24 contra nl-core; lo que queda está en los mensajes o en el servidor de terminología, y se enumera allí.
El paquete pasa por el modelo de Perfuse en su camino de regreso, por lo que los cambios de un script se verifican y se escriben en la versión FHIR del destino. Los miembros que el modelo no tiene campo para se conservan como el script los escribió, dentro de los tipos de datos también.
8.4.3 Núcleo de EE. UU. 9.0.0 y USCDI v6
-us-core (y claim_us_core en un destino fhir) coloca un perfil US Core en meta.profile, pero solo en un recurso que ha sido verificado contra ese perfil y se ha encontrado que cumple. Uno que no cumple es aún convertido y enviado sin la reclamación, y una nota da la razón, como "US Core requiere un tipo de encuentro, y este Encuentro no tiene ninguno". Las verificaciones se transcriben desde US Core 9.0.0, la versión que el proceso de avance de normas 2026 de ONC aprueba para USCDI v6. Cubren Paciente, Encuentro, la observación y el informe de diagnóstico de laboratorio, Specimen, Practicante, Organización, Ubicación, Inmunización, DocumentReference, la condición y la alergia de diagnóstico de encuentro y la intolerancia.
Lo que el convertidor lleva, por clase de datos de USCDI v6:
| USCDI v6 | De | Como |
|---|---|---|
| Demografía del Paciente | PID | Nombre, fecha de nacimiento, sexo, dirección, teléfono y correo electrónico, identificadores. Raza y etnia (PID-10, PID-22) en extensiones de US Core cuando se codifican en CDC Raza y Etnia. Idioma preferido (PID-15) como BCP 47 |
| Información de Encuentro | PV1 | Encuentro con tipo, clase, período, ubicación y participantes |
| Información de Instalación | PV1-3 | Ubicación, con la instalación como su Organización de gestión |
| Problemas, Fecha de Inicio | DG1 | Condición (diagnóstico de encuentro). DG1-5 es el inicio, como lo tiene el mapa HL7 v2-a-FHIR |
| Alergias y Intolerancias, Reacción | AL1 | AlergiaIntolerancia con categoría, criticidad y reacción |
| Laboratorio | ORU | Informe de Diagnóstico, Observaciones, Especimen (de SPM), Solicitud de Servicio |
| Órdenes | ORM, OML | Solicitud de Servicio |
| Inmunizaciones | VXU | Inmunización |
| Notas Clínicas | MDM | Referencia de Documento |
| Miembros del Equipo de Cuidado | PV1, TXA, DG1 | Profesional de la Salud |
No se transportan porque los mensajes v2 de estos tipos no los contienen o Perfuse no los asigna: Medicamentos, Procedimientos, Metas y Preferencias, Evaluaciones de estado de salud, Plan de atención, Historia de salud familiar, Dispositivos médicos (UDI), Información de seguro de salud, Signos vitales como un perfil distinto y Procedencia.
8.4.4 R4 en ambos sentidos
Un servidor FHIR que declara R4 acepta R4. Eso no era cierto de los Encuentros hasta esta versión. El servidor convirtió lo que sirvió a R4, pero leyó lo que recibió como si ya estuviera en su forma interna, por lo que un Encuentro R4 se rechazó con un 400. Lo mismo sucedió con cada paquete que la propia conversión de ADT de Perfuse produjo, porque cada uno lleva Encounter.class, que R4 escribe como un solo codificado.
Se descubrió la primera vez que algo publicó una ADT convertida en el propio servidor R4 de Perfuse. Una prueba ahora lo hace para cada tipo de mensaje asignado, y otra verifica que todo lo escrito como R4 se lea sin cambios.
8.4.5 Resumen Internacional de Paciente
GET /fhir/Patient/{id}/$summary, o GET /fhir/Patient/$summary?identifier=system|value, devuelve un Resumen Internacional de Paciente (HL7 IPS 2.0.0): un paquete de documento FHIR document cuya Composición enumera los problemas activos del paciente, alergias, medicamentos, inmunizaciones, resultados, signos vitales, procedimientos, dispositivos, enfermedades pasadas, historial social y planes de atención, cada uno con una narrativa legible. Es el registro breve que un clínico que nunca ha conocido al paciente lee primero, y el resumen que el Espacio Europeo de Datos de Salud intercambia entre países a partir de 2029.
Se construye a partir de lo que almacena la tienda FHIR, y nada se inventa:
- Los registros marcados como
entered-in-errorse omiten. - Una condición resuelta se coloca bajo enfermedad pasada, no problemas.
- Una orden de medicamento que se detuvo o completó es historia, no un medicamento actual.
- Las tres secciones que requiere el IPS (problemas, alergias, medicamentos) siempre están presentes. Cuando una de ellas no tiene nada, la sección
lleva el motivo codificado unavailable y dice que el servidor no tiene información, "lo que no significa que no la haya". Una lista de alergias vacía nunca debe leerse como "no se conocen alergias".
- Los profesionales, organizaciones, medicamentos y ubicaciones a los que se refieren las entradas se incluyen, para que el documento se pueda leer por sí solo.
Access follows $everything: a token limited to one patient gets a 403 for another, and an identifier that matches two patients is refused rather than guessed. Only the IPS profile is built; asking for another profile is refused. The document Perfuse produces from its test records validates against the IPS 2.0.0 Bundle profile with the HL7 validator, with no errors. A record built from real feeds can still fail where the feed's own codes do (a display name the code system does not use, for example), because the summary passes the data on as it was received.
Intercambio → Enlaces de salud → Compartir llena el contenido con un resumen de paciente: escriba el id de FHIR del paciente y elija Usar su resumen de paciente, luego léalo y cree el enlace. Construirlo se audita como fhir.summary.
8.4.6 Resúmenes de transferencia para cuidados postagudos (PACIO)
When a patient leaves hospital for a nursing home, a rehabilitation unit or home health, the hospital's EHR sends a C-CDA: a transfer summary, a discharge summary or a CCD. Perfuse turns that document into the transfer summary PACIO's Transitions of Care guide describes (hl7.fhir.us.pacio-toc 1.0.0): one FHIR R4 document Bundle, US Core 8.0.1 inside, that the receiving facility's system can file without anyone reading XML.
- Cada una de las quince secciones de la guía está presente, cada una con su propio texto, en el orden de la guía: directivas anticipadas, alergias, salud conductual, estado funcional, inmunizaciones, instrucciones de alta, dispositivos médicos, medicamentos, plan de atención, problemas, procedimientos, motivo de derivación, resultados, historia social y signos vitales.
- Una sección que el documento no tenía se dice que está faltante, nunca se llenó. Sale vacía, con el motivo codificado
unavailabley la oración "El documento de envío no tenía sección ...". Una sección de alergias vacía no debe leerse como "no se conocen alergias". - Una sección que se transporta como texto solo mantiene su texto y no obtiene entradas. La prosa no se convierte en datos codificados. Estado funcional, salud conductual (la sección de estado mental de C-CDA), directivas anticipadas, dispositivos médicos, plan de atención, instrucciones de alta, motivo de derivación e historia social se envían de esta manera hoy; las notas lo indican para cada una.
- Las entradas codificadas se convierten en recursos por las mismas reglas que la conversión ordinaria: alergias (US Core AllergyIntolerance), problemas y diagnósticos de alta (US Core Condition, lista de problemas o diagnóstico de encuentro), procedimientos, inmunizaciones, resultados, signos vitales y medicamentos como una lista de medicamentos PACIO de MedicationStatements (
104204-3, lista de medicamentos de alta, de una sección de medicamentos de alta;104202-7, lista de medicamentos activos, de lo contrario). - Signos vitales en la forma de FHIR. Una presión sistólica y una presión diastólica tomadas juntas se convierten en un panel de presión arterial (
85354-9) con dos componentes, y un código más específico (peso corporal medido,3141-9) también lleva el código de los perfiles de signos vitales de FHIR (29463-7), además del original. - Quién y de dónde. El autor del documento se convierte en el autor de la Composición (un Practitioner, con el NPI cuando su dígito de verificación es válido), su custodio el custodio de la Organización, su encuentro que abarca la estancia que resume el resumen. Un documento sin persona como autor, o sin custodio, es rechazado: la guía requiere ambos, y Perfuse no pondrá un nombre en un registro clínico que nadie dio.
- La negación mantiene su significado. "No se conocen alergias" (una alergia negada a cualquier sustancia) se envía como SNOMED CT
716186003; una alergia negada a un medicamento es rechazada, porque FHIR no puede decirlo y el recurso diría lo contrario; un problema negado esrefuted.
Tres formas de hacer uno:
- A channel: a
cdadestination withwrite: transferposts each document's transfer summary to the FHIR server's
Bundle endpoint (url), or writes <document>.transfer.json to dir. In the channel builder: Emit › A PACIO transfer summary.
- The command line:
perfuse fhir transfer [-authority OID=URI] [-out dir] <document.xml>.... - The console: Documents, turn on Also make a PACIO transfer summary, and read the document.
Verificado con el validador FHIR oficial contra el perfil de paquete TOC 1.0.0, en los ejemplos de C-CDA de HL7 (un resumen de transferencia, un resumen de alta y un CCD). El único error que informa está en la guía: cada signo vital coincide con ambas entradas de rebanada simple-observation y vital-signs del paquete, por lo que no puede evitarse mediante ningún remitente. Todavía no se ha realizado: convertir las entradas de las secciones de solo texto (estado funcional, estado mental, plan de cuidado, dispositivos) en recursos codificados, y la referencia de documento TOC de la guía que indexa un resumen de transferencia almacenado.
8.4.7 Suscripciones
Con -fhir-subscriptions, el punto de conexión FHIR envía notificaciones cuando se crea o cambia un Encuentro o una Cita. Sigue la guía de trabajo de suscripciones R5 Backport IG para R4, que es lo que utiliza el marco de interoperabilidad de CMS y la hoja de ruta de FHIR de TEFCA para notificaciones de citas y encuentros.
Un cliente crea una Subscription con el nombre de un tema y, opcionalmente, un filtro como Encounter?patient=Patient/123. Los temas se enumeran en la declaración de capacidad:
| Tema | Se dispara en | Filtros |
|---|---|---|
https://perfuse.health/fhir/SubscriptionTopic/encounter | Encuentro creado o actualizado | patient, status, class |
https://perfuse.health/fhir/SubscriptionTopic/appointment | Cita creada o actualizada | patient, status |
Un filtro que nombra cualquier otra cosa es rechazado, no ignorado. Un filtro ignorado es una suscripción que notifica sobre todos los pacientes cuando el cliente solicitó uno.
Las propiedades que rodean la implementación:
- An acknowledged change is never a lost notification. The notification is recorded in the same database transaction as the change it describes, and delivered from there, so it survives a restart. A test writes an Encounter, starts a new server on the same database, and checks the notification arrives.
- Notifications arrive in order, and gaps are visible. Event 2 is not sent before event 1 is accepted, and each notification carries the running count, so a receiver can tell when it has missed one.
- The server decides when a subscription is active. A new subscription is
requested, and becomesactiveonly when the endpoint accepts a handshake. A client that asks foractivegetsrequested. A refused handshake is tried again four times over about fifteen seconds before the subscription is anerror: it goes out the moment the subscription is stored, often before the client that created it is listening. - A full-resource notification carries the version that changed. A discharge notification carries the discharge, not whatever the Encounter had become by the time delivery succeeded.
- Failure is bounded. A refused delivery is retried with a growing delay. After ten consecutive failures the subscription goes to
error: later changes are counted but not queued, and updating the subscription resumes delivery from the oldest held notification. The count then shows how many were missed.
Lo que un cliente puede hacer que haga el servidor está limitado deliberadamente:
- Los puntos de conexión deben ser
httpsa menos que el operador pase-fhir-subscriptions-allow-http, que es para un receptor de prueba. - La política de salida se aplica cuando se crea una suscripción, de nuevo en cada entrega y en la dirección que cada conexión alcanza realmente (por lo que un nombre que cambia lo que resuelve entre la comprobación y la conexión sigue siendo rechazado); no se siguen las redirecciones.
- La carga útil predeterminada es
id-only, que dice qué cambió y permite al receptor recuperarlo con sus propias credenciales. - Nada de lo que un receptor envía de vuelta se muestra a nadie, solo su código de estado.
Está desactivado a menos que se solicite, porque una vez activado, un cliente FHIR puede hacer que este servidor envíe datos de paciente a una URL elegida por el cliente. Necesita un punto de conexión R4 que acepte escrituras y se niega a iniciar de lo contrario.
No implementado, para que nadie tenga que descubrirlo por intentarlo:
- canales distintos de
rest-hook - latidos cardíacos
- notificaciones de eliminación
- las operaciones
$statusy$events
La vista Suscripciones muestra el tema, filtro y punto de conexión de cada suscripción, cuántos eventos ha tenido, cuántos se han aceptado y cuántos están esperando, y el último motivo por el que uno fue rechazado. Los valores de encabezado y la cadena de consulta del punto de conexión se retienen, porque ahí es donde viven las credenciales del suscriptor.
8.5 Estándar de telecomunicaciones NCPDP
Reclamaciones de farmacia. Encabezado de ancho fijo seguido de segmentos variables.
Dos detalles que importan, ambos fáciles de equivocar y producir resultados plausibles:
El encabezado tiene exactamente 56 bytes y un ancho fijo. Un encabezado corto no se puede detectar solo por la longitud, porque un encabezado truncado más los segmentos siguientes supera los 56 bytes. La detección se realiza porque un encabezado correcto nunca contiene un byte separador, por lo que se escanea el encabezado en busca de uno y se rechaza un mensaje que lo contenga con el número de bytes que le falta.
0x1E y 0x1C son marcadores de inicio de grupo y campo, no separadores. Tratarlos como separadores desplaza cada campo un lugar a la izquierda, y la mayoría de los valores resultantes siguen siendo plausibles: una cantidad aterriza donde se esperaba un suministro diario y ambos son números.
Los canales NCPDP y SCRIPT están excluidos del encuadre MLLP, y esto se aplica en lugar de aconsejarse. Una reclamación legítima contiene 0x1C, que es el byte de fin de bloque de MLLP, por lo que enmarcar una reclamación la truncaría en el primer marcador de campo.
8.6 NCPDP SCRIPT
Recetas electrónicas, en XML. El paquete interno se llama eprescribe porque script estaba tomado por el motor de JavaScript.
La principal preocupación de seguridad es la bandera de sustitución, y se lee al revés de la intuición: en el cable, 0 significa que la sustitución está permitida y 1 significa dispensar según lo escrito.
Una bandera de sustitución ausente se rechaza en lugar de ser valorada por defecto. Ambos valores posibles por defecto llegan al paciente: uno puede dispensar un genérico donde el prescriptor requirió la marca, el otro evita una sustitución que el prescriptor permitió, por lo que no hay una suposición segura y el mensaje se detiene.
Una receta de la Lista II con recargas se rechaza, y el constructor no produce una.
Los rechazos y advertencias se mantienen como listas separadas en lugar de una lista con etiquetas de gravedad, porque un lector que tenga que verificar un campo de gravedad para saber si se envió el mensaje eventualmente no lo verificará.
La autorización previa electrónica (PAInitiationRequest, PARequest, PAAppealRequest, PACancelRequest) y el beneficio de prescripción en tiempo real se responden en el lado del pagador en /ncpdp/script y /ncpdp/rtpb, a partir del formulario de medicamentos; véase CMS-0057.
8.7 DICOM
Imágenes. Perfuse puede recibir objetos DICOM, consultar un archivo remoto y almacenar en uno, a través de los servicios de red clásicos de DICOM o a través de DICOMweb, y puede mantener un archivo propio para los visores.
DICOM no es un formato de mensaje en el mismo sentido que los demás: los objetos son grandes, y el apretón de manos de la asociación es más importante que el análisis de la carga útil. Los títulos called_ae y calling_ae son la razón más común por la que falla una primera conexión, y el fallo parece un problema de red en lugar de uno de configuración.
8.7.1 Lo que acepta un oyente C-STORE
Dejar abierto, un oyente DICOM acepta cualquier cosa de cualquier persona que pueda llegar al puerto. Cuatro configuraciones bajo Lo que acepta este oyente reducen eso, y la primera es la que vale la pena entender.
Solo aceptar estos títulos AE que llaman es la restricción. El título AE *llamado* —el que está junto a la dirección— es el nombre que un remitente marca, por lo que identifica a este oyente en lugar del remitente, y no es una restricción. Cualquiera que pueda llegar al puerto puede usar el título AE llamado correcto, porque se publica para ellos. Sin una lista de llamadores permitidos, cualquier host en la red puede empujar imágenes a un canal clínico.
Solo aceptar estas clases SOP y solo aceptar estas sintaxis de transferencia toman UID separados por comas. Vacío acepta todo tipo de objeto y negocia cualquier compresión que ofrezca el remitente, lo que incluye tipos que los destinos de este canal no pueden manejar. Rechazar en la asociación es más claro para el remitente que aceptar el objeto y fallar después.
Objeto más grande aceptado está en bytes, y cero significa sin límite. Las imágenes de toda la diapositiva y las series largas pueden alcanzar gigabytes, por lo que el límite es de otro modo la memoria.
8.7.2 DICOMweb
DICOMweb es DICOM sobre HTTP: QIDO-RS busca, WADO-RS y WADO-URI recuperan, STOW-RS almacena. Los visores del navegador como OHIF solo hablan DICOMweb, al igual que los servicios de imágenes en la nube (Google Cloud Healthcare, Azure Health Data Services, AWS HealthImaging). Perfuse lo utiliza en tres lugares.
El archivo de imágenes. Inicie el servidor con -dicomweb-archive DIR (Configuración › Datos › Archivo de imágenes) y mantendrá los objetos en esa carpeta, indexados en SQLite, y los servirá en /dicomweb en la dirección del propio consola: QIDO-RS en los niveles de estudio, série y instancia, WADO-RS para objetos, metadatos, fotogramas y datos bulk, WADO-URI en /dicomweb/wado, y STOW-RS. La lectura toma el rol de visor y el almacenamiento el rol de operador, con una sesión o un token de API; cada lectura y almacenamiento se registra en Actividad como dicomweb.read o dicomweb.store. Una cuenta limitada a un inquilino o a grupos de canales es rechazada, porque el archivo pertenece a todo el servidor. Los objetos se mantienen exactamente como se reciben, por lo que lo que un visor recupera es byte a byte lo que se almacenó. Los fotogramas se devuelven en la sintaxis de transferencia en la que se almacenaron; una solicitud que necesitaría transcoding responde con 406 en lugar de una imagen convertida.
Una búsqueda en un atributo que el archivo no indexa responde 400 en lugar de ignorar la condición y devolver más estudios de los solicitados. Indexado: nombre del paciente (comodines y coincidencia difusa con fuzzymatching=true), ID del paciente, fecha de nacimiento, sexo, fecha y hora del estudio (rangos), número de acceso, ID del estudio, descripción, médico remitente, estudio, series y UID de instancia SOP (listas), modalidad, parte del cuerpo, número y descripción de la serie, clase SOP, número de instancia, filas, columnas y número de fotogramas.
La fuente. type: dicomweb escucha STOW-RS, en /dicom-web/studies por defecto (la ruta que utilizan Orthanc y dcm4chee), y envía cada objeto a través del canal como un mensaje DICOM. El canal necesita dataType: dicom. Establezca un token de portador o un nombre de usuario y contraseña: sin uno, cualquier persona que pueda llegar al puerto puede enviar imágenes al canal. La respuesta enumera cada objeto como almacenado o fallido, con la razón de falla DICOM, para que el remitente sepa qué debe volver a enviar.
name: imaging-from-viewers
dataType: dicom
source:
type: dicomweb
dicomweb:
listen: 0.0.0.0:8043
token: ${STOW_TOKEN}
tls: { enabled: true, cert_file: /etc/perfuse/imaging.crt, key_file: /etc/perfuse/imaging.key }
destinations:
- name: archive
type: dicomweb
dicomweb:
url: https://perfuse.example/dicomweb
token: ${ARCHIVE_TOKEN}
El destino se encuentra en entrega, bajo archivos DICOMweb.
Apuntar OHIF al archivo
Utilice una fuente de datos DICOMweb con qidoRoot y wadoRoot establecidos en la dirección del archivo (la pantalla Imágenes la muestra, con el bloque de fuente de datos completo para copiar), wadoUriRoot a esa dirección seguida de /wado, imageRendering y thumbnailRendering a wadors, y supportsFuzzyMatching en true. Un visor en otro origen necesita ese origen en -dicomweb-cors (Orígenes de navegador permitidos para llamarlo), y sus solicitudes necesitan un token de API en el encabezado Authorization, porque un navegador en otro origen no envía la cookie de sesión de la consola. Servir OHIF desde el mismo origen a través de un proxy inverso evita ambos.
Probado contra Orthanc con su complemento DICOMweb, en ambos sentidos: el destino almacena en Orthanc, y el cliente DICOMweb de Orthanc busca en el archivo de Perfuse y recupera un estudio idéntico al almacenado. OHIF 3 enumera los estudios del archivo y muestra sus fotogramas con la configuración anterior. OHIF y varios otros clientes envían type= sin comillas en los encabezados Accept y Content-Type, lo que RFC 2045 no permite; Perfuse lo acepta.
8.8 Compartir documentos IHE: XDS.b, XCA, XDR, MHD, PIXm y PDQm
Documentos clínicos compartidos entre organizaciones de la manera en que los intercambios de información de salud, Carequality y eHealth Exchange lo hacen: un repositorio los mantiene, un registro indexa sus metadatos y una comunidad responde a las consultas de otras comunidades a través de un portal. Inicie el servidor con -xds REPOSITORY-OID (Configuración › Datos › Compartir documentos) y será las tres cosas, en la base de datos de este servidor, en estas direcciones, cada una aceptando un token de API como token de portador (el rol de visor para consultar y recuperar, el rol de operador para enviar; la mayoría de los dominios de afinidad agregan TLS mutuo al frente):
| Dirección | Actor | Transacciones |
|---|---|---|
/xds/repository | Repositorio de documentos | ITI-41 Proporcionar y registrar conjunto de documentos-b, ITI-43 Recuperar conjunto de documentos |
/xds/registry | Registro de documentos | ITI-18 Consulta de almacenamiento de registro (las trece consultas de almacenamiento), ITI-42 Registrar conjunto de documentos-b |
/xca/gateway | Puerta de enlace de respuesta XCA | ITI-38 Consulta entre puertas de enlace, ITI-39 Recuperar entre puertas de enlace, con -xds-home-community urn:oid:... |
/mhd | Recipiente y respondedor de documentos MHD | ITI-65 Proporcionar conjunto de documentos, ITI-66 Encontrar listas, ITI-67 Encontrar referencias de documentos, ITI-68 Recuperar documento |
/mhd/Patient | Proveedor de PDQm y administrador de PIXm | ITI-78 Consulta de datos demográficos del paciente, ITI-83 Patient/$ihe-pix, desde el almacenamiento de FHIR de Pacientes |
/pixv3 | Administrador de PIX (HL7 v3) | ITI-44 Alimentación de identidad de paciente HL7 V3, ITI-45 Consulta de PIXV3, desde el índice de pacientes (-mpi) |
/pdqv3 | Proveedor de PDQ (HL7 v3) | ITI-47 Consulta de PDQV3, con continuación y cancelación, desde el índice de pacientes |
Las transacciones SOAP son SOAP 1.2 con WS-Addressing, y MTOM/XOP donde viajan los documentos (un documento en línea como base64 también es aceptado). Una transacción fallida se responde con un RegistryResponse que lleva los códigos de error de IHE, no una falla SOAP: XDSDuplicateUniqueIdInRegistry, XDSNonIdenticalHash, XDSPatientIdDoesNotMatch, XDSMissingDocument, XDSRegistryMetadataError (con el campo que falta), XDSStoredQueryMissingParam, XDSUnknownStoredQuery, XDSUnknownRepositoryId, XDSUnknownCommunity y UnresolvedReferenceException. Un reemplazo (RPLC, XFRM_RPLC) deprecia el documento que reemplaza, que luego solo responde a las consultas que solicitan documentos obsoletos; APND, XFRM y signs se mantienen como asociaciones. El hash y el tamaño se calculan en el repositorio y se verifican contra lo que dice la presentación. Los documentos y metadatos bajo demanda (ITI-57) no están soportados, y una presentación que los solicita es rechazada con la razón.
MHD es el mismo registro en FHIR: un documento proporcionado a través de MHD se encuentra mediante ITI-18 y a través de XCA, y uno proporcionado a través de XDS.b se encuentra mediante ITI-67. El id de un DocumentReference es el UUID de la entrada, su masterIdentifier el uniqueId, y la url de su adjunto la dirección ITI-68 del documento. Los pacientes se resuelven al identificador del dominio de afinidad desde la tienda FHIR: el primer identificador cuyo sistema es urn:oid:, o el Patient que se lleva en el Bundle. PIXm responde con cada otro identificador del Patient con el dado (sourceIdentifier=system|value, estrecho por targetSystem), y 404 para uno que no conoce; PDQm busca por identificador, nombre, fecha de nacimiento, género, dirección y telecom.
Publicación. Un destino xds de un canal publica cada documento clínico que recibe, un documento CDA o uno transportado en un mensaje HL7 v2, en cualquier repositorio XDS.b con ITI-41. Los metadatos se leen del documento: el paciente (el identificador en patient_domain, la autoridad de asignación del dominio de afinidad), título, tipo (como typeCode y, a menos que esté establecido, classCode), tiempos de creación y servicio, autor, confidencialidad, idioma y formato (cuerpo estructurado C-CDA 2.1 o 1.1, XDS-SD, o mimeTypeSufficient, desde sus plantillas); y completado desde lo que un documento no dice: el source_id del remitente, el facility_type y practice_setting que cada registro requiere. La respuesta del Registro del repositorio decide la entrega: un error en un PartialSuccess es una entrega fallida con los códigos de error del registro.
destinations:
- name: hie
type: xds
xds:
url: https://hie.example/xds/repository
source_id: 1.2.840.114350.1.13.99
patient_domain: 1.2.840.114350.1.13.99.2.7.5.737384.14
facility_type: { code: "225732001", scheme: 2.16.840.1.113883.6.96, display: Community hospital }
practice_setting: { code: "394802001", scheme: 2.16.840.1.113883.6.96, display: General medicine }
tls: { enabled: true, cert_file: /etc/perfuse/hie.crt, key_file: /etc/perfuse/hie.key }
XDR. Agrega direct_from (tu dirección Direct) y direct_to (las de los destinatarios) y el mismo destino envía XDR como transporte de Direct, como lo describe el proyecto Direct *XDR y XDM para mensajería Direct*: un bloque de direcciones Direct (urn:direct:addressing) en el encabezado SOAP nombra al remitente y destinatarios, y el intendedRecipient de la presentación nombra a cada destinatario (||^^Internet^address). Apunta url al punto de conexión XDR del destinatario o un HISP que acepte XDR. XDR viaja sobre TLS, usualmente con un certificado de cliente (tls), y no es S/MIME. Una fuente direct también acepta XDR (ver Direct a continuación).
xds:
url: https://xdr.hisp.example/xdr
source_id: 1.2.840.114350.1.13.99
patient_domain: 1.2.840.114350.1.13.99.2.7.5.737384.14
facility_type: { code: "225732001", scheme: 2.16.840.1.113883.6.96, display: Community hospital }
practice_setting: { code: "394802001", scheme: 2.16.840.1.113883.6.96, display: General medicine }
direct_from: referrals@direct.hospital.example
direct_to: ["intake@direct.clinic.example"]
tls: { enabled: true, cert_file: /etc/perfuse/xdr.crt, key_file: /etc/perfuse/xdr.key }
PIX y PDQ en HL7 versión 3. Con el índice de pacientes en (-mpi), /pixv3 es un administrador de PIX y /pdqv3 un proveedor de PDQ sobre SOAP 1.2, para los hospitales y centros de intercambio que aún utilizan los perfiles de v3 de IHE. Una fuente alimenta a los pacientes con ITI-44 (registro agregado PRPA_IN201301UV02, revisado PRPA_IN201302UV02, duplicados resueltos PRPA_IN201304UV02): el primer identificador del paciente es su registro, los demás y los de asOtherIDs son identificadores que lleva, y una fusión combina el identificador del registro anterior con el que sobrevive, como lo hace un A40 de HL7 v2. ITI-45 (PRPA_IN201309UV02) responde con los demás identificadores de la persona que posee uno, limitado por dataSource, en Patient.id; el identificador consultado solo se devuelve en la consulta repetida. ITI-47 (PRPA_IN201305UV02) coincide con los datos demográficos (nombre, género, hora de nacimiento, identificador, dirección, telecom) con el comparador de índice propio, minimumDegreeMatch como puntuación sobre 100, otherIDsScopingOrganization limitando los dominios, y initialQuantity paginando la respuesta: el resto se proporciona con QUQI_IN000003UV01 durante diez minutos, o se descarta con su cancelación. Las respuestas siguen los casos de las transacciones: un paciente desconocido o dominio es AE con error 204 y la ubicación del parámetro, un paciente conocido con nada en los dominios solicitados es NF. v3 nombra una autoridad solo por OID, por lo que un identificador cuyo sistema es una URL sin OID detrás (algunos números nacionales) se omite en las respuestas v3 y se proporciona a través de FHIR; establezca el sistema de identificador de empresa en un OID (urn:oid:..., en la configuración del índice) para que aparezca el EUID y el custodio. El rol de visualizador consulta y el rol de operador alimenta; cada transacción se audita con un identificador, nunca con un nombre. Un mensaje v3 sin procesar publicado como application/xml se responde del mismo modo, lo cual es útil para pruebas manuales.
Intercambio › Compartir documentos muestra lo que mantiene el registro y dónde se puede acceder, encuentra los documentos de un paciente en este registro o en otro (el ITI-18 de un registro o el ITI-38 de una puerta de enlace), descarga uno y publica un documento CDA a mano con los metadatos que un destino le daría.
Verificado contra la herramienta XDS 7.13.0 de NIST, la implementación de referencia de IHE: ITI-41, ITI-18 y ITI-43 de Perfuse contra su simulador de Registro/Repositorio (aceptado sin errores de validación, el documento recuperado es idéntico byte a byte), y su simulador de Puerta de Enlace de Inicio que consulta y recupera de la Puerta de Enlace de Respuesta de Perfuse a través de XCA, sin errores en su registro. Cada mensaje SOAP que escribe Perfuse es válido contra los esquemas ebXML Registry 3.0 y XDS.b, y el DocumentReference y SubmissionSet de MHD contra IHE MHD 4.2.2 con el validador FHIR de HL7. XDR desde un destino xds con un bloque de direccionamiento Direct fue aceptado por el simulador de Recipiente de Documentos de la herramienta, cuya validación de metadatos encontró el único defecto descrito en docs/verification.md, y por el validador XDR de ONC SITE, la lista de verificación XDR Direct (h)(2) la prueba de certificación aplica. Las respuestas PIX y PDQ v3 son válidas contra los esquemas HL7 V3 de IHE (NE2008 multicacheschemas), y las respuestas de muestra de IHE (el feed ITI-44, combinar, consulta ITI-45, consulta ITI-47, continuación y cancelación) se responden como muestran sus respuestas publicadas. Todavía no se ha verificado contra una implementación independiente de PIX o PDQ v3, Gazelle o un Connectathon.
8.9 Mensajería segura directa
Direct es la forma en que los proveedores de EE. UU. se envían mutuamente referencias, resúmenes y resultados por correo electrónico seguro: cada mensaje está firmado con el certificado del remitente y cifrado para el certificado del destinatario (S/MIME), y viaja entre los proveedores de servicios de información de salud (HISPs) que emiten las direcciones y responden de ellas. Perfuse es el sistema de borde junto a su HISP, no un HISP: el HISP emite su dirección, su certificado y su clave, y usted mantiene su paquete de confianza (como el de DirectTrust). Sigue la Declaración de aplicabilidad de Direct para el transporte de salud seguro 1.2, la Guía de implementación para la notificación de entrega, IHE XDM (Anexo 15 de ITI TF-1) para documentos con metadatos, la Guía de implementación de descubrimiento de certificados para Direct Project (DNS y LDAP), y XDR y XDM para el mensajería Direct.
Sending. A direct destination signs each message as your address (SHA-256, RSA), encrypts it to every recipient's certificate and your own (AES-256-CBC, RSA key transport), and submits it to the HISP by SMTP with STARTTLS required. The message travels as an attachment named by its content (document.xml, message.hl7, resource.json), or, with xdm: true, a CDA document travels as an IHE XDM package (IHE_XDM/SUBSET01/METADATA.XML with the document and its hash and size, plus INDEX.HTM and README.TXT) that receiving EHRs import with its metadata. Recipient certificates come from the Direct address book (below), then from DNS, where Direct publishes them as CERT records (RFC 4398): under the address with its @ made a dot, else under its domain; then from the recipient domain's LDAP directory, which its _ldap._tcp SRV record names (or ldap: host:port; ldap: off never asks one): each naming context is searched anonymously for an entry whose mail is the address, then the domain, and its userSMIMECertificate or userCertificate read. A certificate is used only when it is for the address or its domain, has not expired, and chains to one of your anchors, fetching any intermediate issuer its Authority Information Access extension names; and it is refused when its issuer's revocation list (from its CRL distribution point, signed by that issuer) lists it. A list that cannot be fetched does not stop delivery. A recipient with no such certificate is a failed delivery, never plain e-mail. to and subject take field references such as {PV1-8.1}, and request_mdn asks the recipient for a delivery notification.
destinations:
- name: referrals
type: direct
direct:
address: referrals@direct.hospital.example
cert_file: /etc/perfuse/direct/referrals.crt # with its chain after it
key_file: /etc/perfuse/direct/referrals.key
anchors_file: /etc/perfuse/direct/anchors.pem
to: ["intake@direct.clinic.example"]
subject: "Referral for {PID-5.1}"
text: "Please see the attached summary."
xdm: true
xdm_source_id: 1.2.840.114350.1.13.99
smtp_host: smtp.hisp.example:587
username: referrals
password: ${DIRECT_HISP_PASSWORD}
request_mdn: true
Recepción. Una fuente direct es el servidor SMTP al que el HISP entrega el correo de su dirección. Acepta correo solo para esa dirección (cualquier otra cosa se rechaza en RCPT), requiere STARTTLS cuando se proporciona un certificado, y abre cada mensaje antes de que nada lo lea: lo descifra con su clave, verifica la firma contra sus anclajes y comprueba que el certificado de firma es para la dirección From o su dominio. Un mensaje que no está cifrado S/MIME, está firmado por un remitente en el que no confía o está firmado por un certificado para alguien más es rechazado en DATA con el motivo, por lo que el HISP lo mantiene y lo informa al remitente; Perfuse nunca acepta lo que luego descarta. Cada documento de un paquete XDM se convierte en un mensaje (comprobado contra los hashes en sus metadatos); de lo contrario, cada archivo adjunto se convierte en uno, o el texto cuando no hay ninguno. Con smtp_host establecido, cada mensaje que se recibe se responde con una notificación firmada y cifrada de "procesado" (MDN), y las notificaciones que llegan para lo que envió se registran.
source:
type: direct
direct:
address: intake@direct.clinic.example
cert_file: /etc/perfuse/direct/intake.crt
key_file: /etc/perfuse/direct/intake.key
anchors_file: /etc/perfuse/direct/anchors.pem
listen: 0.0.0.0:2525
tls_cert: /etc/perfuse/tls/server.crt
tls_key: /etc/perfuse/tls/server.key
smtp_host: smtp.hisp.example:587
Desde la bandeja de entrada del HISP. Un HISP que mantiene el correo en lugar de entregarlo da a la dirección una bandeja de entrada IMAP: imap la lee (TLS o STARTTLS, cada sondeo una sesión fresca), abre cada mensaje como arriba, mueve lo que se tomó a move_to (o lo elimina) y lo que no se pudo abrir a error_folder, por lo que nada se lee dos veces. listen puede entonces dejarse fuera. Un HISP cuyo servidor de correo tiene un certificado privado se confía con tls.ca_file (su certificado, o el de su autoridad) y tls.server_name; la verificación no se puede desactivar.
imap:
host: imap.hisp.example
username: intake@direct.clinic.example
password: ${DIRECT_MAILBOX_PASSWORD}
move_to: Processed
XDR. xdr acepta XDR en /xdr en su propia dirección HTTPS, como un HISP envía Direct que se convierte a SOAP o un socio envía punto a punto: una solicitud ITI-41 Proporcionar y registrar cuyos documentos se convierten en un mensaje. Una presentación cuyo bloque de direcciones Direct nombra otra dirección es rechazada, y una que el canal no acepta se responde con una respuesta de registro de fallo que indica por qué. client_ca_file requiere un certificado de cliente de esas autoridades. XDR está protegido por TLS, no por S/MIME, por lo que una fuente que solo acepta XDR necesita la dirección pero no su certificado, clave o anclajes. Cada documento se convierte en un mensaje del tipo de datos del canal dataType: los documentos CDA necesitan raw (pasados tal como están) o hl7v3, ya que el valor predeterminado, hl7, espera HL7 v2 y los rechaza.
xdr:
listen: 0.0.0.0:8445
tls_cert: /etc/perfuse/tls/server.crt
tls_key: /etc/perfuse/tls/server.key
client_ca_file: /etc/perfuse/direct/hisp-clients.pem
Exchange › Direct enumera las direcciones Direct que sus canales envían y reciben, con sus certificados, fechas de vencimiento y anclajes; mantiene el libro de direcciones de los corresponsales (nombre, organización, notas y certificado, pegado desde su HISP o encontrado en DNS o LDAP, y rechazado cuando no es para esa dirección o dominio); y busca los certificados de una dirección en DNS o LDAP sin guardarlos. El libro es el directorio direct-certs junto a la base de datos, un archivo PEM por dirección o dominio, que un destino lee a menos que especifique su propio recipient_certs_dir.
Verificado contra OpenSSL 4.0's cms y smime en ambos sentidos: lo que Perfuse firma y cifra OpenSSL verifica y descifra (PKCS#1 v1.5 y transporte de claves OAEP), y Perfuse abre lo que OpenSSL firma y cifra (AES-128 y AES-256). Un mensaje creado con openssl smime y entregado a través de SMTP es recibido, abierto y almacenado por un canal, y uno que Perfuse envía un mensaje a otro a través de un sustituto de HISP, que responde con un MDN que el remitente abre. Los certificados se encuentran en OpenLDAP 2.4 (vinculados a la dirección y al dominio, escritos por el propio ldapadd de OpenLDAP); un mensaje Direct se lee desde una bandeja de correo IMAP de GreenMail y se abre, y uno que no es Direct se mueve a la carpeta de errores; XDR desde un destino xds es tomado por una fuente direct y se rechaza cuando está dirigido a alguien más, y es aceptado por el kit de herramientas de Document Recipient de NIST XDS y el validador de XDR de ONC SITE; la herramienta de prueba de borde de XDR Direct de SITE envió ambos mensajes de metadatos mínimos y completos a una fuente direct, que los tomó. Contra la herramienta de descubrimiento de certificados Direct de ONC, un destino Direct elige el certificado correcto, o ninguno, en todos los 18 casos de descubrimiento (D1 a D18: certificados caducados, revocados y emitidos por AIA, prioridad y peso de SRV, un directorio inaccesible). Una fuente Direct lee un mensaje desde el propio HISP de SITE (Apache James) a través de IMAP con STARTTLS. Todavía no se ha probado: intercambio de SMTP con el HISP de SITE (AWS retiene el correo saliente en el puerto 25 para nuevas cuentas) o con un HISP acreditado.
8.10 SQL en vistas FHIR
Una ViewDefinition (HL7 SQL en FHIR v2) convierte cada recurso FHIR de un tipo en filas de columnas con nombre, cada columna una expresión FHIRPath, para que los datos FHIR puedan leerse como tablas por DuckDB, Athena, Spark, un almacén de datos o una hoja de cálculo. Perfuse ejecuta estas vistas con su propio motor FHIRPath: column, select anidado, forEach, forEachOrNull, repeat y unionAll, where, constant (cada tipo de valor[x]), columnas collection, %rowIndex, y las claves de unión getResourceKey() y getReferenceKey(type).
{
"resourceType": "ViewDefinition",
"name": "blood_pressure",
"resource": "Observation",
"constant": [{ "name": "systolic", "valueCode": "8480-6" }],
"where": [{ "path": "code.coding.exists(system = 'http://loinc.org' and code = '85354-9')" }],
"select": [{ "column": [
{ "name": "id", "path": "getResourceKey()", "type": "id" },
{ "name": "patient_id", "path": "subject.getReferenceKey(Patient)", "type": "id" },
{ "name": "systolic", "path": "component.where(code.coding.code = %systolic).value.ofType(Quantity).value.first()", "type": "decimal" }
]}]
}
Monitorizar › SQL en FHIR ejecuta una vista sobre el almacén FHIR de este servidor (un paciente o todos) o sobre recursos pegados (NDJSON desde una exportación masiva, un Bundle, una matriz JSON), muestra las filas y las descarga como CSV, NDJSON o Parquet. Comienza con cinco vistas de ejemplo (demografía del paciente, observaciones con un valor, presión arterial, condiciones, encuentros) y guarda vistas en el directorio views junto a la base de datos, un archivo JSON cada uno.
El punto de conexión FHIR responde a la especificación $sql-run a nivel de sistema, mediante GET o POST, para una ViewDefinition sujeto dada en línea (subjectResource), por subjectReference (ViewDefinition/<name>, una vista guardada) o por subjectCanonical (la url de una vista guardada), con _format (csv, ndjson (el valor predeterminado), json o parquet), header, patient, _since, _limit y resource para ejecutar sobre recursos enviados con la solicitud en lugar del almacén. Es una lectura: un token SMART necesita ámbito de lectura para el tipo de recurso de la vista, un token vinculado a un paciente obtiene las filas de ese paciente solo, y los ámbitos granulares reducen los recursos leídos, como lo hacen en una búsqueda. Más de un millón de filas necesita _limit, patient o _since.
perfuse sof -view v.json [-format csv|ndjson|json|parquet] [-o out] files... ejecuta una vista sobre archivos FHIR sin un servidor: los archivos NDJSON de una exportación masiva, Bundles o matrices.
perfuse sof -view blood_pressure.json -format parquet -o bp.parquet export/Observation.ndjson
duckdb -c "SELECT patient_id, avg(systolic) FROM 'bp.parquet' GROUP BY 1"
Los archivos Parquet tienen un grupo de filas y páginas PLAIN sin comprimir; los booleanos son BOOLEAN, los tipos de enteros INT64, los decimales DOUBLE y todo lo demás cadenas UTF-8 (una columna de colección como su JSON).
Verificado con la suite de pruebas oficial SQL en FHIR (las 144 pruebas del repositorio sql-on-fhir.js tests/, ejecutadas en cada compilación: todas pasan), y la salida Parquet leída de nuevo por DuckDB 1.5.6 con los tipos y valores correctos. No compatible: Bibliotecas SQLQuery y SQLView (SQL sobre las tablas de las vistas), $sql-export, _format=fhir y los parámetros source y group; cada uno se rechaza con el motivo.
8.11 Medidas de calidad digitales
Una medida de calidad digital cuenta los pacientes en cada población de una FHIR Measure sobre un período de medición (población inicial, denominador, exclusiones y excepciones del denominador, numerador, exclusiones y excepciones del numerador) y puntuación del resultado. La respuesta es un FHIR MeasureReport, con forma como la guía Da Vinci Data Exchange para Medidas de Calidad (DEQM) solicita. Este es el camino que siguen los programas eCQM de CMS y los programas de HEDIS digitales de NCQA.
En Perfuse, los criterios de población son FHIRPath ("language": "text/fhirpath"), ejecutados por el mismo motor que SQL en FHIR. Cada criterio se evalúa una vez por paciente. Su enfoque es un conjunto de registros del paciente (el compartimiento del paciente), y tiene estas variables: %subject (el paciente), %periodStart y %periodEnd (el período, como fechas) y %"Measurement Period". Un criterio da como resultado verdadero o falso; un solo valor que existe cuenta como verdadero.
{
"code": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/measure-population", "code": "numerator" }] },
"criteria": {
"language": "text/fhirpath",
"expression": "entry.resource.ofType(Observation).where(code.coding.where(system = 'http://loinc.org' and code = '4548-4').exists() and effective.ofType(dateTime) >= %periodStart and effective.ofType(dateTime) <= %periodEnd).exists()"
}
}
Las poblaciones se anidan como especifica la especificación de Medida. Un paciente está en el denominador solo si está en la población inicial. Una exclusión del denominador saca al paciente antes de que se juzgue el numerador. Una excepción del denominador cuenta solo para un paciente que no esté en el numerador. La puntuación de una proporción es (numerador - exclusiones del numerador) / (denominador - exclusiones - excepciones); la de una razón es numerador / denominador; un grupo de cohortes tiene recuentos y no tiene puntuación. Los estratificadores de un solo criterio dividen cada población por su valor (sexo, por ejemplo).
Perfuse no tiene un motor CQL. Las medidas eCQMs de CMS y las medidas digitales HEDIS publicadas están escritas en CQL, por lo que no se pueden ejecutar tal como están. Una Medida cuyos criterios son CQL se rechaza con ese motivo. Escriba la lógica como FHIRPath en su lugar. La puntuación de variables continuas, los estratificadores multicomponentes y los sujetos de Grupo o Practicante se rechazan de la misma manera.
Monitorizar › Medidas de calidad evalúa una medida sobre el almacén de FHIR de este servidor o sobre recursos pegados, para un período. Ofrece tres informes: un resumen, un resumen con los pacientes en cada población, o uno por paciente. La pantalla muestra las poblaciones, la puntuación y las estratificaciones, y descarga el MeasureReport. Comienza con tres ejemplos: presión arterial bajo control, diabetes con una prueba de HbA1c y pacientes vistos en el período. Son ilustrativos, escritos para este manual, y no son medidas eCQMs de CMS ni HEDIS. Guardar en el almacén de FHIR verifica la Medida y la almacena como un recurso Measure.
El punto de conexión de FHIR responde a Measure/[id]/$evaluate-measure y Measure/$evaluate-measure?measure=<url>, mediante GET o mediante POST de un recurso Parameters. Parámetros:
periodStartyperiodEnd: obligatorios. Una fecha significa todo el día.reportType:summary,subjectosubject-list.subject:Patient/<id>.reporter:Organization/<id>. Cuando está ausente, el informe contiene una Organización nombrada porserve -measure-reporter.measure: una Medida enviada en línea, como parámetro de POST.
El informe se devuelve, no se almacena. La operación es una lectura, por lo que un token SMART necesita acceso de lectura a Medida y a Paciente. Un token vinculado a un paciente obtiene un informe individual sobre ese paciente y no sobre otro.
perfuse measure -measure m.json -period-start 2026-01-01 -period-end 2026-12-31 [-type summary|subject|subject-list] [-subject Patient/ID] [-o out] files... evalúa una medida sobre archivos FHIR (un NDJSON de exportación masiva, paquetes, matrices) sin un servidor. -measure example:example-bp-control ejecuta un ejemplo enviado, y -examples los enumera.
Verificado. El validador HL7 FHIR verifica las Medidas enviadas contra FHIR R4, sin errores. Verifica un resumen, un individual y un informe de Medida de tipo subject-list contra los perfiles de resumen, individual y subject-list DEQM 5.0.0, también sin errores. No verificado: equivalencia con cualquier eCQM publicado, ya que ninguno se puede ejecutar aquí sin CQL.
8.12 El índice de pacientes (MPI)
Un índice de pacientes maestros enlaza los registros que cada sistema mantiene para una persona (el MRN del hospital, el de la clínica, el del laboratorio) en un identificador de empresa único, el EUID. Está desactivado hasta que alguien decida mantener uno, porque es una tabla de nombres de pacientes, fechas de nacimiento e identificadores: active Configuración → Datos → Índice de pacientes (o serve -mpi) y reinicie. Vive en mpi.db al lado de la base de datos principal.
Alimentarlo. Agregue un destino de tipo mpi (El índice de pacientes en el constructor) a los canales que transportan registros. Lee el PID de cada mensaje HL7 v2, o un paciente FHIR en un canal sin procesar. El identificador del registro es la repetición PID-3 de tipo MR (id_type lo cambia); los demás, y PID-19, se convierten en sus otros identificadores. Una autoridad de asignación con un identificador universal (PID-3.4.2 con tipo ISO) se convierte en urn:oid:...; una con solo un espacio de nombres se mapea mediante authorities, o se convierte en urn:perfuse:aa:<namespace>. Un A40, A34, A18 o A39 combina la persona que posee el identificador de MRG-1 con la persona que posee PID-3 (no_merge los ignora).
destinations:
- name: index
type: mpi
mpi:
authorities: { HOSP: "urn:oid:1.2.840.114350.1.13.1" }
Coincidencia. Un nuevo registro se vincula en este orden:
- Identificadores de confianza. Un registro que comparte un identificador de un sistema listado como de confianza (un identificador nacional, un número de empresa) es la misma persona, sin puntuación. Nunca se liste un sistema que una familia comparta, como el número de suscriptor de un pagador.
- Puntuación. De lo contrario, las personas cuyos registros podrían coincidir se encuentran mediante bloqueo (el mismo apellido sonoro y año de nacimiento, la misma fecha de nacimiento o el mismo número de teléfono) y se puntúan con pesos de Fellegi-Sunter sobre el nombre de familia y el nombre dado (Jaro-Winkler y Soundex, acentos y puntuación ignorados, nombres intercambiados detectados), fecha de nacimiento (un día y mes intercambiados es un acuerdo parcial), sexo, código postal, teléfono y dirección. Solo los campos que ambos registros tienen cuentan. La puntuación es la evidencia encontrada como una parte de la evidencia posible. El bloqueo utiliza cinco claves, por lo que un error no oculta a una persona: sonido del apellido y año de nacimiento, fecha de nacimiento, teléfono, sonidos de ambos nombres (en cualquier orden) y código postal con el sonido del nombre dado.
- Decisión. Por defecto, nada se vincula solo por la puntuación. Un registro que obtiene una puntuación igual o superior al umbral de revisión (60%) se convierte en una nueva persona y la posible coincidencia espera en la cola de revisión; aquellos que están en o por encima del umbral automático (90%) con el apellido de familia y la fecha de nacimiento que coinciden ambos se marcan como *seguros*. Vincular a dos personas en una (una superposición) coloca los resultados de un paciente en la tabla de otro, por lo que el vinculación automática es un interruptor que activa un administrador (Vincular coincidencias seguras sin preguntar a nadie) una vez que la cola ha mostrado que los seguros son correctos. Con él activado, una coincidencia segura se vincula cuando no hay una segunda persona que obtenga una puntuación tan alta. Por debajo del umbral de revisión, el registro es una nueva persona.
Números nacionales. El número de la NHS (y el CHI de Escocia y el número de Salud y Cuidado de Irlanda del Norte), el PPSN y el IHI de Irlanda, el KVNR de Alemania, el SVNR de Austria, el número de la AHV de Suiza, el BSN de los Países Bajos, el número de registro nacional de Bélgica, el matrícula de Luxemburgo, el INS (NIR y NIA) de Francia, el código fiscal de Italia, el DNI/NIE y el CIP-SNS de España, el número del SNS de Portugal, los números personales nórdicos (Suecia, Dinamarca, Noruega con su número D, Finlandia, Islandia), los códigos bálticos, el PESEL de Polonia, los números de nacimiento checo y eslovaco, y los números húngaro, rumano, búlgaro, croata, esloveno y griego son conocidos por el índice. Cada uno se almacena de una manera: bajo su sistema de esquema, espacios y puntos eliminados, por lo que el número de la NHS enviado como urn:oid:2.16.840.1.113883.2.1.4.1|999 000 0018 por un sistema y como https://fhir.nhs.uk/Id/nhs-number|9990000018 por otro es un identificador. Una repetición de HL7 v2 PID-3 cuyo tipo de identificador es NN y un código de país de tres letras (NNDEU, NNNLD) se lee como el número principal de ese país, y el tipo NH de la autoridad NHS como el número de la NHS. Se prueba el dígito de verificación de cada número: uno que falla se mantiene en su registro, se informa (enmascarado) en la respuesta y nunca se utiliza para vincular o encontrar a alguien, ya que un dígito mal escrito generalmente hace que el número de otra persona sea incorrecto. El CPR de Dinamarca se verifica solo para su fecha, porque los números emitidos desde 2007 no necesitan pasar el módulo 11 antiguo.
La lista de configuración muestra cada esquema con su formato, su verificación y el sistema en el que se almacena. Los sistemas tomados de la guía de FHIR del país (UK Core, fhir.de, Nictiz, CH Core, ANS, HL7 Italia, MedCom, los registros OID sueco, noruego y finlandés) se marcan como tales; los demás son valores predeterminados que se pueden cambiar al valor nacional, lo que un sitio puede hacer por esquema (el antiguo aún se reconoce). Confiar en un esquema es un tick. Los números nacionales se muestran completos a los editores y, de forma predeterminada, enmascarados (los últimos caracteres) a todos los demás; Quién ve los números completos cambia eso, hasta los espectadores o hasta los administradores solamente. Donde la ley de un país limita el uso de un número (Référentiel INS de Francia, PPSN de Irlanda, número de registro nacional de Bélgica, §290 SGB V de Alemania), la configuración lo indica. GET /api/mpi/schemes enumera los esquemas y POST /api/mpi/check verifica un número sin almacenarlo. FHIR $match y PIXm responden a los clientes autorizados con números completos.
Una fecha escrita con el día o el mes primero (08/15/1972) se mantiene como se escribió y no coincide con una fecha con el año primero: los pedidos de EE. UU. y Europa no están de acuerdo, y adivinarlo vincularía a las personas equivocadas.
La pantalla del índice de pacientes (Build) muestra los recuentos, encuentra personas por nombre, número, EUID o fecha de nacimiento, y abre cada persona: sus registros, cada identificador y el historial de lo que les sucedió y quién lo hizo. Los editores deciden las revisiones (La misma persona las vincula; Personas diferentes las mantiene separadas para siempre), mueven un registro a otra persona, sacan un registro (No esta persona), y fusionan dos personas. Los administradores establecen los umbrales, los sistemas de identificadores de confianza, los números nacionales y el sistema de EUID. Cada búsqueda y cada persona leída se escribe en el registro de auditoría.
Responder a otros. POST /fhir/Patient/$match (la operación FHIR, como IHE PDQm ITI-119 la utiliza) toma un Paciente y devuelve las personas que podrían serlo, primero las mejores, cada una un Paciente cuyo id es el EUID, con cada identificador, search.score, y la extensión de grado de coincidencia (certain, probable, possible); onlyCertainMatches y count son honrados. PIXm (/mhd/Patient/$ihe-pix, ITI-83) responde desde el índice: cada identificador de cada registro vinculado a la persona, y el EUID. Lo mismo hacen PIX y PDQ en HL7 v3 (/pixv3, /pdqv3; véase el documento de intercambio de IHE), que también lo alimentan.
La API es /api/mpi (resumen y búsqueda), /api/mpi/persons/{euid}, /api/mpi/find, /api/mpi/reviews, /api/mpi/records (registro, enlace, desenlace), /api/mpi/merge y /api/mpi/settings.
Pesos. Los pesos Fellegi-Sunter predeterminados provienen de la literatura de vinculación de registros para datos de registro hospitalario. Estimar desde nuestros registros (en la configuración, administradores; POST /api/mpi/estimate) los ajusta a su propio índice del sitio con maximización de expectativa, sin necesidad de pares revisados: m (acuerdo entre coincidencias verdaderas) de los pares que bloquean, u (acuerdo por casualidad) de 20.000 pares extraídos al azar, ya que los pares bloqueados coinciden en nombres y fechas por construcción. La estimación se muestra junto a los pesos en uso y no cambia nada hasta que se utiliza y se guarda. Un campo comparado en demasiados pares mantiene su valor predeterminado. La estimación supone que los campos son independientes dado la clase, lo que no es del todo cierto, por lo que se debe tratar un gran cambio con sospecha.
Medido. En el conjunto de datos FEBRL 3 (5.000 registros sintéticos de 2.000 personas, los duplicados con errores de escritura, campos intercambiados, valores perdidos y apodos; PERFUSE_TEST_FEBRL), registrado en orden de archivo con un revisor que responde cada par en cola correctamente:
| Vinculados por sí mismos | Puestos frente a un revisor con la persona correcta | Perdidos | Enlaces incorrectos | |
|---|---|---|---|---|
| Vinculación automática apagada (predeterminada) | 0 | 2.649 de 3.000 | 351 | 0 |
| Activada, pesos predeterminados | 1.637 | 1.012 | 351 | 0 |
| Activada, pesos estimados desde los registros | 1.622 | 1.070 | 308 | 0 |
Nunca se vinculó un registro a la persona equivocada, y ninguna de las 2.000 personas nuevas se puso en cola innecesariamente. Los que se perdieron difieren en casi todos los campos (una fecha de nacimiento y una dirección ambas mal escritas, por ejemplo) y tienen una puntuación por debajo del umbral de revisión. FEBRL es sintético y tiene pocos nombres iguales; un índice real tiene más, por lo que su cola de revisión será más larga, y estos números son un piso de cuidado, no una promesa.
8.13 Delimitado
CSV y sus parientes. Las opciones están en la fuente en el constructor, y la referencia de formato enumera las claves de archivo.
Las rutas dirigen columnas. Una columna con nombre está disponible cuando se declara una fila de encabezado o se dan los nombres; de lo contrario, las columnas son posicionales.
8.13.1 Nombrar las columnas
Hay dos formas, y son exclusivas. O la primera fila del archivo las nombra — la primera fila nombra las columnas — o las enumera usted mismo, en orden. Declarar un encabezado significa que la fila no se trata como datos.
Si ninguna de las opciones se establece, las columnas solo se pueden dirigir por posición, por lo que un filtro escrito contra un nombre no tiene nada que coincida. Activar el encabezado elimina la lista de nombres en lugar de mantener ambas: con un encabezado, los nombres provienen del archivo, por lo que una lista que queda atrás sería ignorada, y un canal que lee como si hubiera elegido los nombres cuando el archivo los eligió es más difícil de depurar que uno que se niega.
8.13.2 Lo que ve un filtro o un script
Un documento delimitado se convierte en muchos mensajes, por lo que esto debe establecerse antes de escribir cualquiera de los dos.
Con división activada, todo se ejecuta por fila. Los pasos declarativos, el filtro y el script de transformación ven una fila, y una ruta significa *la columna de esta* fila. Un filtro que rechaza una fila rechaza solo esa fila, y el recuento recibido es de filas en lugar de documentos.
Con la división desactivada, el mensaje es el documento completo y una escritura que cruza filas se rechaza en lugar de aplicarse a la primera. Ese rechazo es deliberado: escribir silenciosamente en la fila uno es el comportamiento que produce un archivo que parece transformado y no lo es.
8.13.3 Los tres que cambian lo que cuenta como un error
Aceptar filas con el número incorrecto de columnas está desactivado por defecto. Una fila con demasiadas o demasiado pocas columnas es entonces un error, lo que es lo que atrapa un archivo cuya forma cambió sin previo aviso. Activarlo acepta la fila y deja las columnas perdidas vacías.
Recortar espacios alrededor de cada valor arregla el caso común de un valor relleno a un ancho de columna. Deje esta opción desactivada donde los espacios finales son significativos, lo que ocasionalmente sucede en un identificador.
Mantener líneas en blanco como filas está desactivado por defecto y generalmente debe estarlo. Activarlo, una línea en blanco se convierte en una fila de columnas vacías, lo que solo importa cuando un recuento debe coincidir exactamente con el archivo.
8.14 Crudo
No se realiza ningún análisis. El mensaje es una secuencia de bytes.
Utilice esto cuando el contenido no es algo que Perfuse comprenda y no necesita serlo: un PDF que se mueve entre sistemas, un formato propietario, un documento ya formado que se reenvía sin cambios.
El costo es que nada que dependa de la estructura funciona — ninguna transformación a nivel de campo, ningún filtro en un campo, ninguna comparación a nivel de campo en el verificador de paridad. Un canal crudo es una tubería, y es honesto al respecto.
8.15 Elegir
Si el mensaje es HL7 v2, use hl7, incluso si solo tiene la intención de reenviarlo sin cambios. El análisis le proporciona la traza, las estadísticas a nivel de campo, la capacidad de filtrar más adelante y la capacidad de comparar. raw en contenido analizable no aporta nada y pierde todo eso.
Use raw cuando el análisis fallaría o sería una mentira sobre el contenido.
8.16 Enlaces y tarjetas de salud SMART
El *Kill the Clipboard* de CMS pide a los proveedores que acepten los registros de un paciente desde un código QR en el check-in, y que devuelvan el registro de la visita de la misma manera. El código QR contiene un enlace de salud SMART (shlink:/...): una dirección, una clave de 256 bits y, opcionalmente, una etiqueta, una fecha de caducidad y una bandera de contraseña. Intercambio → Enlaces de salud lee uno.
Recepción. Pegue el enlace o escanéelo con la cámara donde el navegador pueda (Chrome y Edge). Perfuse recupera el manifiesto, descifra cada archivo con la clave del enlace, resume cualquier paquete FHIR - quién es el paciente, cuántos recursos de cada tipo - y verifica cualquier *tarjeta de salud inteligente* dentro contra la clave publicada de su emisor. *Entregar al canal* entrega el contenido FHIR a un canal como un mensaje, que es cómo llega al registro electrónico de salud. La recuperación se mantiene en la política de salida en el momento de la conexión, se rechaza el HTTP plano y la respuesta de un servidor que no es un servidor de manifiesto nunca se muestra, porque la dirección provino de un código QR de un desconocido.
Compartir. *Compartir* cifra un paquete FHIR, lo hospeda y muestra el enlace y su código QR una vez. El servidor mantiene el texto cifrado y no la clave - no puede abrir lo que hospeda, y no puede mostrar el enlace nuevamente. Un código de acceso se hash; diez intentos incorrectos deshabilitan el enlace. Los enlaces caducan después de 30 días por defecto, se pueden revocar y enumeran con qué frecuencia y por quién se recuperaron. Inicie el servidor con -public-url configurado en la dirección https que los teléfonos de los pacientes alcanzan; la URL del manifiesto que construye debe ajustarse a los 128 caracteres de la especificación.
Verificar una tarjeta. *Verificar una tarjeta* toma un código QR numérico shc:/ (un bloque o varios), un archivo .smart-health-card o un JWS, y dice si la firma coincide con la clave del emisor.
8.17 UN/EDIFACT
dataType: edifact lee intercambios UN/EDIFACT (ISO 9735): ORDERS, INVOIC, DESADV, IFTSTA y el resto, incluidos los mensajes de reclamaciones de atención médica y laboratorio europeos. El consejo de cadena de servicio (UNA) se respeta, con el carácter de liberación y, para la versión de sintaxis 4, el separador de repetición; los grupos funcionales (UNG/UNE) son opcionales. El intercambio se entrega exactamente como lo escribió el remitente.
name: supplier-orders
dataType: edifact
edifact:
envelope: require # require (default), warn, or ignore
split: false # true: one interchange per message
acknowledge: contrl # none (default) or contrl
filter: 'UNB-2.1 == "5412345000013"'
source:
type: as2 # or http, sftp, file, email ...
# ...
destinations:
- name: orders
type: file
dir: /data/orders
filter: 'UNH-2.1 == "ORDERS"'
El sobre. El recuento de segmentos y la referencia de UNT, el recuento de mensajes y la referencia de UNE, y el recuento y la referencia de UNZ se comparan con lo que contiene el intercambio. Con require, el valor predeterminado, una discrepancia rechaza el intercambio (es cómo muestra una transferencia cortada); warn lo acepta y registra una advertencia; ignore no lo verifica.
Rutas, para los filtros de canal y destino, cuentan elementos y componentes de 1 en el orden en que se escribe el segmento, como lo numeran las guías de implementación: BGM-2 (el número de documento), BGM-1.1, NAD(2)-2.1 (la parte del segundo NAD), LIN-3(2).1 (una repetición), UNB-2.1 (el remitente), UNH-2.1 (el tipo de mensaje). Una ruta sin ocurrencia se dirige a todos.
División entrega un intercambio por mensaje, cada uno con el UNB original (y encabezado de grupo) y tráileres que cuentan como uno, por lo que un pedido puede ser filtrado, buscado y reintento por su cuenta. Los filtros de destino ven cada pieza. Un destino de archivo nombra archivos por tipo de mensaje (ORDERS_INVOIC-20261017.edi), y ${control_id} es la referencia de intercambio.
CONTRL. Con acknowledge: contrl, un remitente que publica sobre HTTP recibe una respuesta con sintaxis y informe de servicio CONTRL: acción UCI 7 cuando el intercambio fue leído y entregado, 4 cuando fue rechazado o no pudo ser deliverado (HTTP 400 o 502 entonces). No se puede combinar con split, ya que un CONTRL responde a todo el intercambio. A través de AS2, el recibo del socio es el acuse de recibo.
No se ofrece: los directorios de mensajes (que segmentos ORDERS D.96A permite), por lo que se verifica la estructura, no el contenido; las transformaciones, que reescribirían un documento de un socio; y los scripts de filtro o transformador (un preprocesador se ejecuta, antes de analizar). Verificado contra pydifact 0.1.8, un analizador independiente, en ambos sentidos.
9 Reconocimientos
Un reconocimiento informa al remitente qué sucedió. Para HL7 v2 sobre MLLP es un mensaje ACK; el remitente no enviará el siguiente mensaje hasta que llegue, y generalmente lo reenviará si no lo hace.
Equivocarse en los reconocimientos es la forma más común en que una integración pierde silenciosamente datos, porque un remitente que está siendo reconocido no tiene razón para quejarse.
9.1 Temporización
Cubierto en flujo de mensaje, y el resumen es: ack.when es on_delivery por defecto, lo que significa que el reconocimiento se envía solo después de que todos los destinos habilitados hayan aceptado el mensaje. on_receipt lo envía tan pronto como el mensaje se analiza y se coloca en cola.
9.2 Lo que envía Perfuse
Un AA cuando el mensaje se aceptó, un AE cuando se rechazó por una razón que el remitente podría actuar, y un AR cuando se rechazó por una razón que el remitente no puede.
La distinción entre AE y AR es quién tiene que hacer algo. AE significa que el mensaje tenía un problema - un campo que no se podría analizar como una fecha, un segmento requerido que falta - y el remitente puede corregirlo y reenviarlo. AR significa que Perfuse no podría aceptarlo independientemente del contenido, lo que generalmente significa un problema de configuración o disponibilidad en este extremo. Un remitente que reenvía después de un AR obtendrá la misma respuesta.
9.3 Identificar este motor
ack.application y ack.facility poblán MSH-3 y MSH-4 del acuse de recibo.
Cuando ambos están vacíos, el acuse de recibo refleja el receptor original: utiliza lo que el mensaje entrante tenía en MSH-5 y MSH-6. Eso es lo que la mayoría de los remitentes esperan, y es el valor predeterminado por esa razón.
Establézcalos explícitamente cuando el remitente los valide, o cuando varias instancias de Perfuse manejen la misma fuente y necesite saber cuál respondió.
9.4 El evento de activación
ack.include_trigger_event envía ACK^A01 en lugar de un simple ACK.
Algunos receptores lo requieren y otros lo rechazan, lo que explica por qué es una opción explícita sin un valor predeterminado inteligente. Si un remitente se niega a aceptar sus acuses de recibo y el contenido parece correcto, esta es la primera cosa que debe intentar.
9.5 Los acuses de recibo negativos no son fallos que ocultar
Un NACK tratado como un éxito es una de las configuraciones incorrectas más dañinas posibles, y es lo suficientemente común como para merecer ser nombrada. Ocurre cuando un destino envía un acuse de recibo negativo y el lado emisor registra la *transmisión* como exitosa porque los bytes se enviaron y algo regresó.
Perfuse lee el acuse de recibo que recibe de un destino MLLP y registra el resultado en consecuencia. Un AE o AR de un sistema downstream es un fallo de entrega, se reintenta según la política del destino y cuenta hacia la tasa de fallos que activa las alertas.
9.6 Tiempo de espera
Esperar un acuse de recibo está limitado. Cuando no llega a tiempo, la entrega es un fallo y se coloca en cola para reintento.
Un tiempo de espera no es lo mismo que un rechazo y se registra de manera diferente. Un rechazo significa que el lado lejano consideró el mensaje y lo rechazó; un tiempo de espera significa que no sabe si se procesó. Esa distinción decide si un reintento es seguro: reintenta después de un rechazo es inútil, y reintenta después de un tiempo de espera arriesga un duplicado. Perfuse reintenta después de un tiempo de espera, porque un mensaje clínico perdido es generalmente peor que un duplicado, y el sistema receptor es generalmente capaz de detectar un ID de control de mensaje repetido.
Si ese intercambio es correcto para una fuente determinada es un juicio, y vale la pena hacerlo deliberadamente para cualquier cosa donde un duplicado tenga una consecuencia clínica — una orden, una administración de medicación — en lugar de aceptar el valor predeterminado silenciosamente.
9.7 Fuentes que no reconocen
No todos los transportes tienen el concepto. Una fuente de archivo no tiene dónde enviar un acuse de recibo; una fuente de corredor reconoce al corredor en lugar del remitente original; una fuente HTTP responde con un código de estado.
Donde la fuente no puede entregar el modo de acuse de recibo que un canal solicita, el canal se rechaza en la carga en lugar de iniciar y no reconocer silenciosamente. Un canal que cree que está reconociendo y no lo está es peor que uno que no inicia.
9.8 Acuses de recibo X12
Todo lo anterior se refiere a HL7 v2. X12 tiene sus propios acuses de recibo, y a diferencia de v2, están desactivados por defecto: un ajuste vacío significa ninguno.
Ese valor predeterminado es el seguro, porque un socio comercial que no espera un acuse de recibo y lo recibe puede tratarlo como un intercambio no solicitado. La clave acknowledge en un canal X12 puede tener uno de cuatro valores:
| Valor | Qué se envía |
|---|---|
none | Nada. El valor predeterminado. |
999 | Acuse de recibo de implementación. Sustituye al 997 para transacciones HIPAA. |
997 | Acuse de recibo funcional. Más antiguo, y todavía lo que muchas conexiones de pagador esperan. |
ta1 | Acuse de recibo de intercambio, sobre el sobre rather que su contenido. |
Qué acuse de recibo enviar es una propiedad de la relación de socio comercial en lugar del mensaje, por lo que debe configurarse y no puede inferirse. Un 999 sustituye a un 997 para transacciones HIPAA, pero muchos socios — especialmente conexiones de pagador más antiguas — están configurados para esperar un 997 y tratarán un 999 como un archivo no reconocido.
9.9 Acuses de recibo HL7 v3
Un canal HL7 v3 envía un acuse de recibo de forma predeterminada, lo que es lo opuesto a X12 y vale la pena saber.
La razón es el transporte más que el estándar: una interacción v3 generalmente llega a través de una conexión síncrona con la aplicación de envío esperando una respuesta, y un remitente que no recibe nada generalmente lo intentará de nuevo. Esa es la forma en que un paciente se registra tres veces.
Desactivarlo es posible y debe hacerse explícitamente — "no establecido" y "establecido en falso" son deliberadamente diferentes, por lo que el valor predeterminado se mantiene a menos que alguien haya dicho lo contrario.
10 Entrega, Cola y Reintento
Un destino que no se puede alcanzar es el caso normal, no la excepción. Los sistemas se reinician, las redes se caen y un motor de interfaz de hospital no está disponible durante su propia ventana de mantenimiento. La entrega se basa en eso.
10.1 Cola por destino
Cada destino tiene su propia cola. Un destino que está fallando no afecta a los demás, y cada uno tiene su propia profundidad, edad y estado de reintento.
La cola es por destino más que por canal porque la falla es por destino. Una cola a nivel de canal retendría la entrega a tres sistemas que funcionan porque un cuarto está caído, y las dos situaciones — un destino no disponible, los cuatro destinos no disponibles — se verían iguales en las estadísticas.
10.2 Configuración de la cola
queue on a destination:
| Clave | Qué hace |
|---|---|
enabled | Si los mensajes fallidos se colocan en cola. |
max_attempts | Cuántas veces intentar antes de rendirse. |
backoff | Esperar antes del primer reintento. |
max_backoff | Límite en la espera creciente. |
max_depth | La mayoría de los mensajes que se pueden mantener. No establecido significa sin límite. |
retain_hours | Cuánto tiempo se mantiene un mensaje entregado. No establecido lo mantiene indefinidamente. |
Las dos últimas estaban faltando en esta tabla y en el constructor, y son la pareja que evita que se llene un disco. Una cola sin ninguna de las dos crece hasta que el sistema de archivos está lleno, y esa falla llega como algo no relacionado que se rompe — una escritura que falla en algún lugar completamente diferente — en lugar de como un problema de cola.
El retardo crece entre intentos hasta max_backoff. Creciente en lugar de fijo, porque un destino que está caído generalmente está caído durante minutos más que segundos, y reintenta cada segundo durante diez minutos produce varias centenas de entradas de registro que entierran la que importa.
10.3 Se mantiene el orden
Dentro de un destino, los mensajes se entregan en orden de llegada, y un mensaje que falla mantiene su lugar. Los mensajes posteriores esperan detrás de él en lugar de adelantarlo.
Este es el valor predeterminado correcto para los datos clínicos — una admisión y una alta aplicadas en el orden incorrecto es peor que ambas siendo tardías — y tiene una consecuencia digna de comprender: un mensaje que siempre falla mantendrá todo detrás de él.
Esa es la razón por la que la profundidad de la cola y la edad de la cola son condiciones de alerta separadas. Una cola profunda que se está drenando es un sistema ocupado. Una cola cuyo mensaje más antiguo sigue envejeciendo está atascada, y los tipos de alerta queue-depth, queue-age y queue-stuck las distinguen.
max_attempts es lo que limita el daño: después de eso, el mensaje se registra como fallido y la cola continúa. Establecer max_attempts muy alto con la esperanza de un eventual éxito convierte un mensaje fallido en una fuente detenida.10.4 Cuando la cola está deshabilitada
Con queue.enabled falso, una entrega fallida es una entrega fallida. Se registra y no se reintenta.
Esa es la elección correcta cuando los datos tardíos son peores que no tener datos — una pantalla en tiempo real, un sistema de paging, cualquier cosa donde un mensaje entregado cuarenta minutos después del evento sería engañoso más que simplemente retrasado.
Es la elección incorrecta para cualquier cosa que vaya a un registro, donde el mensaje es igual de válido una hora después.
10.5 Reintento versus cola
Hay dos mecanismos y operan a diferentes niveles.
retry en un destino gobierna los intentos inmediatos dentro de una entrega — una conexión que falla se intenta de nuevo después de una breve pausa, sin salir del camino de entrega. Esto maneja fallos transitorios: una conexión caída, un rechazo momentáneo.
queue gobierna lo que sucede una vez que los intentos inmediatos se agotan. El mensaje sale del camino de entrega y se reintenta más tarde en un horario más largo.
Ambos existen porque los dos fallos son diferentes. Un restablecimiento de conexión en el primer intento generalmente desaparece en el segundo, y pasar por la cola para ello agregaría minutos de latencia a un fallo que duró milisegundos. Un destino que ha sido apagado para mantenimiento no volverá dentro de tres intentos inmediatos, y golpearlo es inútil.
10.6 Qué cuenta un punto final HTTP como entregado
Un destino HTTP tiene tres configuraciones que deciden si un mensaje se trata como enviado, y las tres están en el destino en el constructor.
Status codes that mean delivered. Empty means any 2xx. Some endpoints answer 202 Accepted for something they have not processed yet, and some answer 200 with an error in the body — the second is what *Treat as failed if the reply contains* is for. Setting an explicit list is worth doing when a partner has told you which code means what, because the alternative is discovering the difference from a message that was never delivered and never queued.
Seguir redirecciones. Desactivado por defecto, y deliberadamente. Una redirección puede apuntar a un host diferente, por lo que seguir una redirección envía el mensaje a algún lugar que nadie ha configurado y informa de éxito.
Token de portador. Enviado como un encabezado Authorization. Se almacena con el canal y nunca se muestra de nuevo, por lo que dejar la casilla vacía al editar un canal existente mantiene el token ya guardado en lugar de borrarlo.
10.7 Nombrar y podar una carpeta de archivo
Un destino de archivo agrega a un archivo por día y tipo de mensaje por defecto (20261012-ADT_A01.hl7), enmarcado en MLLP para que el archivo se reproduzca con perfuse send. Cuatro configuraciones en el destino cambian eso.
Nombre de archivo (file_name) hace un archivo por mensaje en su lugar, nombrado desde ${date}, ${timestamp}, ${control_id}, ${message_type} y ${destination}. Un nombre que ya está tomado (el mismo ID de control dos veces) obtiene un sufijo único en lugar de sobrescribir el mensaje allí.
Agregar (append: true) escribe cada mensaje en el archivo que Nombre de archivo nombra, por lo que ${date}.hl7 es un archivo por día para cada tipo.
Sufijo al escribir (temp_suffix, un archivo por mensaje solo) predeterminado es .part y se elimina mediante un cambio de nombre una vez que el archivo esté completo. Quien recolecta estos archivos no debe recoger uno escrito a medias, y un cambio de nombre dentro de una carpeta es atómico. Cambie solo si el recolector filtra en una extensión diferente.
Eliminar después (retain_hours) poda archivos en la carpeta que tengan más de la cantidad de horas dada, verificada como máximo cada diez minutos mientras llegan mensajes. Solo se eliminan archivos simples directamente en la carpeta, nunca una subcarpeta. Vacío o cero los mantiene para siempre, que es el valor predeterminado y es una decisión en lugar de una omisión — pero un archivo que nadie poda llena el disco, y un disco lleno detiene el canal en lugar del archivo.
10.8 Enviar a un servidor FHIR
Tres configuraciones deciden cuánto se comprueba antes de que un paquete salga.
Reclamar la conformidad de US Core agrega el perfil a cada recurso. Solo reclame cuando el recurso lleve lo que el perfil requiere, porque un receptor puede validar contra la reclamación y rechazar un paquete que de otra manera habría sido aceptado.
Validar antes de enviar comprueba cada recurso aquí en lugar de aprender del receptor. Un rechazo en el extremo lejano le dice menos, más tarde, y a menudo solo en un registro que no puede leer.
Tratar advertencias como rechazos solo tiene efecto cuando la validación está activada, y el control no está disponible hasta que lo esté. Es lo suficientemente estricto como para detener un paquete que un receptor aceptaría, por lo que pertenece a una fuente que se está implementando en lugar de una en servicio.
10.8.1 Servicios FHIR alojados
Un destino fhir tiene un bloque auth que inicia sesión en los servicios alojados, donde un token de portador pegado deja de funcionar dentro de una hora:
fhir:
url: https://healthlake.us-east-1.amazonaws.com/datastore/<id>/r4/
auth: {type: aws, access_key_id: ${AWS_ACCESS_KEY_ID}, secret_access_key: ${AWS_SECRET_ACCESS_KEY}}
aws firma cada solicitud para AWS HealthLake con Signature Version 4, con la región leída desde la URL. azure obtiene un token de Microsoft Entra ID para Azure Health Data Services desde tenant_id, client_id y client_secret - un registro de aplicación que tiene el rol de contribuyente de datos FHIR - con la URL del servicio FHIR como su audiencia. client_credentials hace lo mismo contra cualquier URL de token token_url de OAuth 2.0. Los tokens se reutilizan hasta un minuto antes de que expiren. Las mismas opciones están bajo el Inicio de sesión del servicio alojado del destino en el constructor de canales.
10.8.2 Informes de casos de salud pública
Active Informar condiciones informables a salud pública en un destino de FHIR y enviará informes de casos en lugar de paquetes. Cada mensaje se verifica para un código de activación de condición informable: un diagnóstico, una prueba de laboratorio o un resultado codificado. Si lo tiene, el mensaje se convierte en un eICR 2.1.2 de HL7 eCR, envuelto en un mensaje eCR y publicado en la URL $process-message. Si no lo tiene, no se envía nada y no falla; el destino cuenta como no informable.
fhir:
url: https://ecr.agency.example/fhir
ecr:
rctc: /etc/perfuse/rctc.json
source: https://fhir.springfield-general.example/fhir
facility: {name: Springfield General Hospital, phone: +1-217-555-0100, city: Springfield, state: IL}
rctc es el conjunto de códigos de activación de condiciones informables, como un conjunto de valores de FHIR o el paquete eRSD. Necesita una licencia de UMLS, por lo que no está incluido; sin ella, se utiliza una muestra de prueba integrada corta, solo para pruebas. source es donde la agencia envía su respuesta de informabilidad. La instalación es necesaria porque eCR solicita su teléfono y dirección, y un mensaje v2 no tiene ninguno.
Un informe dice qué es lo que falta en el mensaje en lugar de inventarlo, y cada brecha se registra. Intente un mensaje en el panel Informe de caso de salud pública del laboratorio de FHIR antes de apuntar una fuente en vivo a una agencia.
10.8.3 Resultados de laboratorio a salud pública: ELR
perfuse elr -config elr.yaml results.hl7 convierte los resultados de un laboratorio en mensajes ELR HL7 2.5.1 que reciben los departamentos de salud estatales, uno para cada mensaje con un resultado informable, y solo las órdenes que llevan uno: un recuento sanguíneo completo junto a una prueba de SARS-CoV-2 positiva no es asunto de salud pública. Los códigos de activación son los mismos que los de informes de casos, el RCTC que carga o el ejemplo integrado.
ELR nombra a todos por OID o número CLIA, que ninguna fuente de laboratorio lleva para el remitente y el receptor, por lo que provienen del archivo:
sending_application: {namespace: Perfuse, id: 2.16.840.1.113883.19.5.9, type: ISO}
sending_facility: {namespace: Springfield Lab, id: 2.16.840.1.113883.19.5, type: ISO}
receiving_application: {namespace: ELR, id: 2.16.840.1.113883.19.6.1, type: ISO}
receiving_facility: {namespace: State DOH, id: 2.16.840.1.113883.19.6, type: ISO}
processing: T
ordering_facility: {name: Springfield Clinic, phone: "+1 217 555 0100", line: 1 Main St, city: Springfield, state: IL, postalCode: "62701"}
performing_lab: {name: Springfield Lab, line: 2 Lab Rd, city: Springfield, state: IL, postalCode: "62701"}
performing_lab_clia: 14D0000000
placer_authority: {namespace: SPRINGFIELD-EHR, id: 2.16.840.1.113883.19.5.1, type: ISO}
filler_authority: {namespace: SPRINGFIELD-LAB, id: 2.16.840.1.113883.19.5.2, type: ISO}
La instalación que ordena, el laboratorio que realiza y las autoridades que asignan se utilizan solo donde el mensaje del laboratorio los deja fuera, y cada uso es una nota. Lo que solo sabe el laboratorio nunca se rellena: un resultado sin tipo de especimen va con SPM-4 vacío y una nota que lo dice, porque un especimen adivinado en un informe a salud pública es peor que una brecha que el estado puede preguntar. Comience con processing: T mientras el equipo de incorporación del estado prueba la fuente.
Lo que el laboratorio hizo bien se mantiene como se envió. Una orden secundaria (un panel de susceptibilidad o una prueba de reflejo cuyo OBR-29 nombra a su padre y que no tiene ORC propio) se envía tal como se recibió, sin ORC ni especimen inventado, y una orden secundaria sin resultado informable tiene el de su padre, ya que una susceptibilidad no significa nada sin la cultura a la que pertenece. Una nota después del PID se mantiene con el paciente, y ORC-14, el número de teléfono de llamada, se copia de OBR-17 cuando el laboratorio solo lo puso allí. Todos los 28 mensajes de ejemplo que ONC publica para su herramienta de certificación ELR salen válidos sin errores.
MSH-2 se escribe como ^~\&#, los cinco caracteres que requiere la errata de ELR Release 1 y que verifica la herramienta de certificación de ONC. Si la guía de su estado muestra los cuatro anteriores, agregue encoding_characters: "^~\\&" al archivo.
Para enviarlos desde un canal en lugar de desde la línea de comandos, dé al destino que alcanza el estado un bloque elr que nombra el mismo archivo. Cada resultado de laboratorio se reorganiza en el camino, y uno con nada informable no se envía en absoluto, lo que se cuenta como delivered, no fallido: el estado solicitó resultados informables solo.
destinations:
- name: state-elr
type: mllp # or http, sftp, file: whatever the state's onboarding team gives you
address: elr.health.state.example:6661
tls: {enabled: true}
elr:
config: elr.yaml # beside this channel file
rctc: rctc.json # optional; the built-in sample is for testing only
El bloque se aplica en un canal de HL7 v2, a destinos que entregan el mensaje en sí (mllp, http, tcp, archivo, sftp, ftp, S3, Azure Blob, soap, los corredores de mensajes o otro canal); el canal se niega a cargar de lo contrario, y cuando el archivo está faltante o carece de la instalación de envío, la aplicación receptora o la instalación receptora. Las notas se escriben en el registro con el ID de control del mensaje.
El archivo ELR es el compañero del canal, no un canal: perfuse check y la pantalla de canales lo omiten en lugar de informar un Canal que no se puede cargar. En el constructor de canales, las mismas dos configuraciones están bajo el destino, en Informe de laboratorio (ELR 2.5.1); el campo de códigos de activación se abre una vez que se nombra un archivo ELR, y guardar se niega a un archivo que no esté junto al canal.
10.8.4 Archivos DICOMweb
type: dicomweb almacena cada objeto con STOW-RS: Orthanc, dcm4chee, los servicios de imágenes en la nube o el propio archivo de un servidor Perfuse. Proporcione la raíz del servicio (https://pacs.example/dicom-web); /studies se agrega, y una URL que ya termina en /studies se rechaza en lugar de publicar en /studies/studies. Autentíquese con token (un token de portador) o username y password; headers agrega otros, como el encabezado de proyecto de un servicio en la nube. El canal necesita dataType: dicom.
La respuesta del servidor decide el resultado, no solo su código de estado. Un objeto que la respuesta enumera como fallido es una entrega fallida con la razón de falla de DICOM, y también lo es un 202 que no enumera nada como almacenado: el servidor tomó la solicitud y no mantuvo nada de ella. El tiempo de espera predeterminado es de dos minutos, porque una serie grande es lenta.
10.8.5 Repositorios de documentos (IHE XDS.b)
type: xds publica cada documento clínico en un repositorio XDS.b con ITI-41 Provide and Register: un documento CDA, o uno que lleva un mensaje HL7 v2 (MDM, ORU). Los metadatos provienen del documento y de la configuración del destino (source_id, patient_domain, facility_type, practice_setting), y la respuesta del registro del repositorio decide la entrega. Vea Compartir documentos de IHE.
10.8.6 Registros de inmunización
Cada registro de inmunización estatal (IIS) de EE. UU. acepta mensajes de HL7 2.5.1 en la misma interfaz de SOAP, la publicada por los CDC en 2011 (namespace urn:cdc:iisb:2011). Tiene dos operaciones: connectivityTest, que prueba el punto final, y submitSingleMessage, que lleva el mensaje. Un VXU^V04 informa un disparo y regresa como un ACK; un QBP^Q11 solicita el historial y el pronóstico de un paciente y regresa como un RSP^K11.
Elija Registro de inmunización (Servicio web IIS CDC) como el servicio de un destino SOAP, o escriba profile: cdc-iis:
destinations:
- name: state-registry
type: soap
soap:
profile: cdc-iis
url: https://iis.state.example/iis-ws/cdc
username: clinic-submitter # the registry account for this submitter
password: "..."
facility_id: FAC-1234 # optional in the WSDL; many registries require it
queue: {enabled: true}
Perfuse construye el sobre, por lo que no hay cuerpo que escribir. El nombre de usuario y la contraseña van dentro de la solicitud, donde los CDC WSDL los colocan, no en la autenticación HTTP. La versión es SOAP 1.2, la vinculación de WSDL, a menos que establezca version: "1.1" para un registro cuya guía solicita eso. Donde un registro requiere un certificado de cliente, configure la configuración de tls del destino como para cualquier llamada de HTTPS.
La confirmación del registro decide si el mensaje se entregó, no el estado de HTTP. Un registro responde a un disparo rechazado con HTTP 200 y un AR dentro, por lo que un destino que confiaba en el estado informaría una inmunización perdida como entregada.
| El registro responde | Registros de Perfuse |
|---|---|
| AA (o CA) | Entregado. |
AE cuyos segmentos ERR son todos advertencias (ERR-4 W o I) | Entregado. Las advertencias están en la respuesta. |
AE con algún ERR-4 E, o un ERR sin gravedad | Fallido, con la ubicación, código y texto de cada ERR en el error. |
| AR o CR | Fallido, de la misma manera. |
| Una falla de SOAP (contraseña incorrecta, mensaje demasiado grande) | Fallido, con la razón del registro. |
| Algo que no es un mensaje HL7 | Fallido. |
warnings_are_errors: true (Tratar advertencias como fallos) hace que cada AE espere a una persona. La respuesta, que los scripts de respuesta y un llamador HTTP que espera en el canal leen, es el mensaje HL7 del registro: el ACK, o el RSP con el historial y el pronóstico. examples/iis contiene tres canales para empezar: VXU desde un EHR sobre MLLP, consultas QBP sobre HTTP y un sustituto local para un registro.
El panel de Conexiones le pide a un registro su connectivityTest cuando no se ha enviado nada durante siete días. Esa operación existe para este propósito y no envía nada, así que es la única comprobación de nivel de aplicación que realiza Perfuse. Demuestra la dirección, TLS y el enlace SOAP, no el nombre de usuario y la contraseña; solo una solicitud real de envío prueba esas.
Un origen SOAP puede responder de la misma manera: Responder como un registro de inmunización, o profile: cdc-iis en el origen. Repite connectivityTest, toma el mensaje de submitSingleMessage, devuelve el acuse de recibo del canal en return y sirve el WSDL de los CDC en ?wsdl. Un nombre de usuario y una contraseña, cuando se establecen, se verifican contra los de la solicitud, y un par incorrecto obtiene la SecurityFault del WSDL. Un mensaje que el canal rechaza se devuelve como un NAK, nunca como una falla, como dice la guía de los CDC. Úselo como un doble de prueba, o para recibir envíos de la manera en que lo hace un registro.
No cubierto: las partes específicas del estado de la incorporación. Cada registro tiene su propio punto de conexión, proceso de cuenta y perfil local de la guía 2.5.1 (campos RXA y OBX requeridos, elegibilidad VFC, códigos de rechazo). Esos son el contenido de los mensajes, que las transformaciones del canal dan forma; ningún punto de conexión del estado está integrado. Perfuse se ha probado contra sí mismo y contra respuestas de registro grabadas, no contra un registro del estado en vivo.
10.9 Cuando el transporte dice sí y el mensaje no llegó
"¿Llegó esto?" a menudo no es una pregunta que el transporte pueda responder. Un receptor MLLP devuelve un acuse de recibo de aplicación cuyo significado está en su texto. Un receptor HTTP devuelve 200 con un documento de error. En ambos casos, el transporte tuvo éxito y el mensaje no llegó.
Inspeccionar la respuesta en un destino toma un script dado la respuesta del receptor. Devuelve falso, o lanza, y la entrega se marca como fallida — lo que significa que se reintenta, se coloca en cola y aparece en el recuento de fallos en lugar de ser contado silenciosamente como entregado.
Se ofrece solo en destinos que reciben una respuesta. En uno que no puede, se rechaza en la carga en lugar de dejarlo nunca ejecutarse.
10.10 Qué significa el resultado de una entrega
| Resultado | Significado |
|---|---|
Delivered | El destino aceptó el mensaje. Para MLLP, se recibió un acuse de recibo positivo. |
Queued | La entrega falló y el mensaje se volverá a intentar. |
Failed | La entrega falló y no se volverá a intentar, ya sea porque la cola está desactivada o porque se han agotado los intentos. |
Filtered | El destino rechazó el mensaje por su propio filtro. No es un error. |
Estos se registran por destino por mensaje, así que un mensaje que llegó a tres de cuatro destinos se registra como exactamente eso. No hay un "éxito" a nivel de canal, porque no significaría nada.
10.11 Entrega lenta
Un destino que está aceptando mensajes pero tarda mucho en hacerlo es un problema distinto de uno que está fallando, y es fácil perderlo porque cada resultado es un éxito.
El tipo de alerta slow-delivery existe para esto. Vale la pena configurarlo en cualquier cosa donde el acuse de recibo del remitente espere la entrega, porque un destino cuyo tiempo de respuesta ha aumentado silenciosamente eventualmente cruzará el tiempo de espera del remitente y comenzará a producir reenvíos — y en ese punto el síntoma aparece en el sistema aguas arriba, en un sistema que puede que no administre.
10.12 Escribir en una base de datos
type: database ejecuta una instrucción por mensaje con valores enlazados desde el mensaje.
Los marcadores de posición son del propio controlador — $1 para postgres, ? para mysql y sqlite, @p1 para sqlserver — y Perfuse no los reescribe. Eso es deliberado: una capa de traducción que obtuviera un marcador de posición incorrecto enlazaría el nombre de un paciente con la columna incorrecta, y el resultado parecería datos válidos en lugar de un error.
Una instrucción escrita en el dialecto incorrecto se rechaza cuando se guarda, nombrando el estilo que el controlador quiere. Antes de que existiera esta comprobación, el error surgía como syntax error at or near "," del servidor cuando llegaba un mensaje, que nombra una instrucción que escribió y así lo envía a buscar un error tipográfico en lugar de al dialecto.
destinations:
- name: warehouse
type: database
database:
driver: postgres
dsn: ${WAREHOUSE_DSN}
statement: INSERT INTO messages (mrn, family_name, birth_date) VALUES ($1, $2, $3)
params:
- PID-3.1
- PID-5.1
- PID-7
Un parámetro entre comillas es una constante en lugar de una ruta, que es cómo un canal escribe su propio nombre o un código de sistema de origen en una fila junto con los datos del mensaje.
Los valores siempre se enlazan como parámetros. Una referencia ${...} dentro de la instrucción se rechaza: un nombre que contiene una comilla simple rompería la instrucción, y un valor hostil la reescribiría.
10.13 Enviar a un socket sin procesar
type: tcp escribe en un socket con un marco que elige, que es lo que normalmente quiere un dispositivo o un instrumento de laboratorio. MLLP es uno de los marcos entre varios; este destino ofrece los demás.
destinations:
- name: instrument
type: tcp
tcp:
address: instrument.lab:9100
framing: delimited
delimiter: \r
expect_reply: true
timeout: 30s
El marco no tiene un valor predeterminado, deliberadamente. Escribir con el marco incorrecto no falla. El extremo lejano lee mensajes divididos en los lugares incorrectos, o espera un terminador que nunca llega, y el síntoma aparece en el sistema de recepción en lugar de aquí.
framing | Lo que escribe |
|---|---|
mllp | 0x0b antes, 0x1c 0x0d después. El mismo marco que utiliza un destino MLLP. |
delimited | Un delimiter después de cada mensaje, y un start_block opcional antes de él. |
fixed | Registros con relleno o truncados a record_length. |
length | Un prefijo length_bytes. big_endian y length_includes_header tienen dos convenciones en la naturaleza y la diferencia es silenciosa. |
whole | Un mensaje por conexión, cerrado para marcar el final. |
Solo se aceptan los ajustes que utiliza el marco elegido. Una longitud de registro en una secuencia delimitada se rechaza en lugar de ignorarse, porque un ajuste que se ignora silenciosamente es uno que alguien cree que está en efecto.
expect_reply cambia lo que significa entregado. Sin él, el éxito significa que los bytes alcanzaron el búfer de envío del sistema operativo — lo que un peer que se estrelló un momento después nunca leyó.
10.14 Amazon S3, SQS y SNS
Un canal puede leer desde una cola SQS o un prefijo de bucket S3, y entregar a SQS, SNS o S3. Los cinco son HTTP firmados con ninguna SDK de AWS en el binario, y los cinco toman los mismos ajustes de acceso: region, access_key_id, secret_access_key (escribir ${AWS_SECRET_ACCESS_KEY} para leer la variable de entorno), un session_token opcional, y endpoint para LocalStack, MinIO o un punto final de VPC. Bajo Canales → Nuevo canal, elija *Leemos desde una cola Amazon SQS*, *Recopilamos objetos desde un bucket Amazon S3* , o los tipos de destino SQS, SNS y S3.
Nada se confirma a AWS hasta que el canal lo ha manejado. Un mensaje SQS se elimina después y de lo contrario vuelve a aparecer cuando tiempo de espera de visibilidad termina, así que una política de reenvío de una cola es lo que mueve uno que sigue fallando a una cola de carta muerta. Un objeto S3 se mueve a processed/ después, o a error/ si no se pudo manejar, así que un archivo malo no detiene al resto.
En una cola o tema FIFO, los mensajes se agrupan por paciente de forma predeterminada - SQS mantiene el orden dentro de un grupo y en ninguna otra parte - y desduplicados por contenido, así que un reintento no se entrega dos veces.
Un archivo S3 se puede escribir directamente en Glacier con storage_class, y como líneas JSON para Amazon Athena con format: ndjson; perfuse athena imprime la tabla. La referencia tiene todos los ajustes.
10.15 AMQP 1.0 y Azure
AMQP 1.0 es lo que hablan Azure Service Bus y Event Hubs, y RabbitMQ 4, ActiveMQ y Artemis además de sus otros protocolos. Un origen amqp recibe de una cola o una suscripción y acepta cada mensaje solo una vez que el canal lo ha procesado; uno que no se pudo procesar se libera como una entrega fallida, por lo que el broker lo vuelve a entregar y, pasado el límite de la cola, lo envía a la carpeta de mensajes muertos. Un destino amqp informa de éxito solo cuando el broker acepta el mensaje. Para Service Bus: addr es <namespace>.servicebus.windows.net:5671 con tls, username es el nombre de la política de acceso compartido, password es su clave y address es el nombre de la cola, o <topic>/subscriptions/<name> para recibir.
Azure Blob Storage: un destino azure_blob escribe un blob por mensaje en el nivel de acceso elegido - Hot, Cool, Cold o Archive - y un origen azure_blob recoge blobs de un prefijo, moviéndolos a processed/ o error/ como el origen S3 hace. Prefiera un token SAS tlimitado al contenedor sobre la clave de la cuenta.
10.16 MQTT
Un origen mqtt se suscribe a un filtro de tema (+ coincide con un nivel, # con todos los niveles debajo) en cualquier broker MQTT 3.1.1: Mosquitto, EMQX, HiveMQ, AWS IoT Core, Azure Event Grid. En QoS 1, el predeterminado, cada mensaje se confirma solo una vez que el canal lo ha manejado. MQTT no tiene forma de rechazar un solo mensaje, así que uno que no se pudo manejar se deja sin confirmar y la conexión se hace nuevamente; el broker mantiene la sesión bajo la id de cliente (perfuse-<channel> a menos que client_id diga lo contrario) y lo envía nuevamente, primero en la cola. QoS 0 es como máximo una vez: cualquier cosa enviada mientras Perfuse está abajo se pierde. Un destino mqtt publica y, en QoS 1, informa de éxito cuando el broker tiene el mensaje.
source:
type: mqtt
mqtt:
addr: broker.hospital.local:8883
topic: hl7/+/adt
username: perfuse
password: ${MQTT_PASSWORD}
tls: {enabled: true, ca_file: /etc/perfuse/broker-ca.pem}
AWS IoT Core quiere un certificado de cliente: proporcione tls.cert_file y tls.key_file.
10.17 IBM MQ
ibm_mq lee y escribe colas de IBM MQ a través de la API de mensajería REST del administrador de colas (mqweb), que IBM MQ 9.1.5 y posteriores envían en cada plataforma, incluyendo z/OS, por lo que no se instala ningún cliente de MQ junto con Perfuse. El usuario necesita el rol MQWebUser y la autoridad de poner, obtener y examinar la cola. Un origen examina el primer mensaje, se lo pasa al canal y lo elimina por su id de mensaje solo una vez que se procesó; un mensaje que falla se mantiene al principio de la cola. Ejecute un lector por cola. Un destino coloca mensajes persistentes (a menos que non_persistent), con el id de control de HL7 como el id de correlación.
destinations:
- name: to-mainframe
type: ibm_mq
ibm_mq:
url: https://mq.hospital.local:9443
queue_manager: QM1
queue: HL7.ADT.OUT
username: perfuse
password: ${IBM_MQ_PASSWORD}
tls: {enabled: true, ca_file: /etc/perfuse/mq-ca.pem}
api_version es v3 para MQ 9.3 y posteriores (el valor predeterminado), v2 para 9.1.5 a 9.2. Un administrador de colas con un canal AMQP también se puede alcanzar con el conector amqp.
10.18 Google Cloud Pub/Sub y Cloud Storage
Un origen pubsub extrae de una suscripción y reconoce cada mensaje una vez procesado; uno que falla se devuelve de inmediato (su plazo de reconocimiento establecido en cero), con el resto de su lote, por lo que Pub/Sub lo entrega de nuevo y, pasado la política de carta muerta de la suscripción, lo envía a la carpeta de mensajes muertos. Un destino pubsub publica con el tipo de mensaje de HL7 y el id de control como atributos; order_by: patient establece la clave de ordenación a partir de PID-3.1, por lo que una suscripción con ordenación de mensajes mantiene los mensajes de cada paciente en orden.
Un destino gcs escribe un objeto por mensaje, con un nombre como una clave de S3, en la clase de almacenamiento elegida (STANDARD, NEARLINE, COLDLINE, ARCHIVE). Un origen gcs recoge objetos de un prefijo y los mueve a processed/ o error/ como lo hace el origen de S3.
Ambos se conectan como una cuenta de servicio: credentials_file es su clave JSON. Déjelo vacío en Google Cloud (una VM, GKE o Cloud Run) para usar la cuenta de servicio adjunta allí. endpoint nombra un emulador (el emulador de Pub/Sub, fake-gcs-server), que no requiere credenciales.
source:
type: pubsub
pubsub:
project: hospital-integration
subscription: adt-to-perfuse
credentials_file: /etc/perfuse/gcp-key.json
10.19 Leyendo una bandeja de entrada (IMAP y POP3)
Una fuente de email lee una bandeja de entrada y convierte lo que encuentra en mensajes: cada archivo adjunto (take: attachments, el valor predeterminado, restringido por patrones attachment_names), el texto del correo electrónico (take: body), o el correo electrónico completo (take: raw) para que un script pueda desmontarlo. Los laboratorios, centros de imagen y pequeñas prácticas aún envían resultados de esta manera.
Cada comprobación se conecta, lee el correo no leído más antiguo primero y se desconecta. A través de IMAP, un correo electrónico se recupera sin marcarlo como leído, se le pasa al canal y solo una vez que todos los mensajes de él se aceptaron se mueve a move_to (Procesado), se marca como leído (after_read: flag) o se elimina. El correo que falló o que no se tomó (un remitente fuera de allowed_senders, sin adjunto que coincida con attachment_names, más grande que max_message_size), se mueve a error_folder (Error) con la razón en el registro. Ambas carpetas se crean cuando faltan. POP3 no tiene carpetas ni marcas, por lo que elimina: las eliminaciones se confirman con el QUIT al final de la comprobación, por lo que una sesión que se interrumpe no elimina nada y un correo electrónico procesado puede leerse de nuevo (nunca se pierde). El correo de POP3 que no se toma permanece en el servidor y se informa una vez.
La conexión es una contraseña (${MAIL_PASSWORD}; una contraseña de aplicación para Gmail) u OAuth 2.0 a través de SASL XOAUTH2, que Microsoft 365 requiere (ha desactivado la conexión con contraseña) y Gmail prefiere:
source:
type: email
email:
host: outlook.office365.com # imap.gmail.com, pop.gmail.com, or your server
username: results-feed@hospital.example
oauth:
provider: microsoft # microsoft, google or custom (with token_url)
tenant: hospital.onmicrosoft.com
client_id: 00000000-0000-0000-0000-000000000000
client_secret: ${MAIL_CLIENT_SECRET}
attachment_names: ["*.hl7"]
allowed_senders: [lab.example, results@partner.example]
Con client_secret y sin refresh_token, se utiliza el acceso de aplicación de Microsoft 365 (la concesión de credenciales de cliente, ámbito https://outlook.office365.com/.default): un administrador registra la aplicación, le otorga acceso a IMAP.AccessAsApp y la limita a la única bandeja de entrada con un principal de servicio de Exchange. Con un refresh_token (el consentimiento de una persona), se utiliza el ámbito delegado IMAP.AccessAsUser.All o POP.AccessAsUser.All y se mantiene un token de actualización rotado durante la vida del proceso. Gmail necesita un refresh_token: Google no ofrece acceso de aplicación a una bandeja de entrada.
allowed_senders acepta direcciones y dominios. La línea De un correo electrónico se puede falsificar, por lo que mantiene alejados los accidentes y el correo electrónico extraviado, no a un atacante; una fuente que debe ser de confianza pertenece a Direct (S/MIME firmado), SFTP o una VPN. security es tls (993 o 995, el valor predeterminado), starttls (143 o 110) o none, que solo se permite a localhost. Las comprobaciones están separadas por al menos 10 segundos (60 por defecto), porque los servicios de correo electrónico limitan las cuentas que verifican más rápido.
Probado contra GreenMail 2.1.3 (IMAPS, IMAP, POP3S y POP3: se deniega la conexión con una contraseña incorrecta, recuperación sin marcar como leído, MOVER, marca, eliminar, un correo electrónico demasiado grande se rechaza antes de que se lea) y contra el IMAP de Gmail con una contraseña de aplicación (crear una carpeta, agregar, recuperar, MOVER, eliminar). Se probó OAuth contra un punto final de token de reemplazo solo: no contra Microsoft o Google.
10.20 AS2
AS2 (RFC 4130) es cómo muchos pagadores, casas de compensación y proveedores intercambian EDI: un POST de HTTP cuyo cuerpo está firmado con el certificado del remitente y cifrado para el receptor, respondido con un recibo (un MDN) que nombra un resumen de lo que llegó. El S/MIME es el mismo código que utiliza el mensajería Direct. Ambos extremos están en el constructor de canales: Los socios comerciales nos envían EDI a través de AS2 como un origen, Un socio comercial a través de AS2 como un destino.
# receive: partners post here
source:
type: as2
as2:
listen: ":4080" # path /as2 by default
id: HOSPITAL-AS2 # our AS2 identifier (AS2-To)
cert_file: as2/ours.crt # partners encrypt to this
key_file: as2/ours.key # and receipts are signed with it
tls: { enabled: true, cert_file: tls.crt, key_file: tls.key }
partners:
- id: PAYER-AS2
cert_file: as2/payer.crt
dataType: x12
# send
destinations:
- name: payer
type: as2
as2:
url: https://as2.payer.example/as2
from: HOSPITAL-AS2
to: PAYER-AS2
cert_file: as2/ours.crt
key_file: as2/ours.key
partner_cert_file: as2/payer.crt
file_name: "${control_id}.x12"
Recepción. Un mensaje a cualquier otro AS2-To, o de un remitente que no esté en partners, es rechazado con 403 y sin recibo. El mensaje de un partner debe estar firmado y cifrado a menos que su entrada establezca allow_unsigned o allow_unencrypted. La carga útil se descifra, se verifica su firma contra el certificado del partner (los partners suelen intercambiar certificados auto-firmados, lo que está bien: el certificado es la confianza), y se le entrega al canal según lo que dice su formato de mensaje. El partner recibe el recibo que solicitó: en la respuesta, o publicado en su URL Receipt-Delivery-Option; firmado cuando lo solicitó. Un mensaje que falla recibe un recibo que nombra el motivo (decryption-failed, authentication-failed, insufficient-message-security, unexpected-processing-error).
Envío. Cada mensaje está firmado (SHA-256) y cifrado (AES-256-CBC). Con receipt: sync, el valor predeterminado, se considera entregado solo cuando el recibo verifica contra partner_cert_file, dice processed, y lleva el mismo resumen (MIC) que lo que se envió; anything else es una entrega fallida y se reintenta. receipt: async solicita el recibo en receipt_url, que debe ser una fuente AS2 en este servidor que enumere al partner: el mensaje se considera entregado una vez que el partner acepta la solicitud, y un recibo que posteriormente no esté de acuerdo se registra como un error. receipt: none solicita nada. unsigned, unencrypted y unsigned_receipt relajan cada parte para un acuerdo que lo diga; username y password agregan autenticación básica HTTP para pasarelas que lo requieren. El tipo de contenido sigue el formato del canal (application/edi-x12 para X12) a menos que content_type establezca uno.
Triple DES, AES-128 y AES-192 se leen así como AES-256, ya que los partners todavía los envían, y también se lee el streaming BER (longitudes indefinidas) que escriben las pasarelas de Java.
Compresión (RFC 5402). Un mensaje comprimido se lee de cualquier manera que se haya construido: comprimido antes de firmar o cifrar, o un mensaje firmado comprimido en su totalidad. El resumen (MIC) del recibo sigue el RFC: sobre lo que se firmó cuando está firmado, sobre el contenido sin comprimir cuando no lo está. Un mensaje que se expandiría más allá de diez veces el max_message_size de la fuente se rechaza con decompression-failed, para que unos pocos kilobytes no puedan reclamar gigabytes de memoria. Un destino se comprime con compress: true, antes de firmar y cifrar, como lo hacen la mayoría de las pasarelas; pregúntele al partner primero.
Un mensaje enviado dos veces. Un partner que nunca recibió nuestro recibo envía el mensaje nuevamente con el mismo Message-ID (RFC 4130 sección 7.5). La fuente recuerda cada Message-ID de los partners para duplicate_window (predeterminado 720h, 30 días; al menos una hora) y responde a una reenvío con un recibo processed sin pasarla nuevamente. El registro se mantiene en la base de datos, para que un reinicio no lo olvide. Un mensaje cuyo procesamiento falló no se recuerda, para que el reintento del partner se realice. Las copias que llegan al mismo momento esperan una a la otra, y solo la primera se pasa. Si el registro no se puede leer, el mensaje se rechaza con unexpected-processing-error y el partner reintenta más tarde, en lugar de arriesgarse a una copia adicional. Un mensaje sin Message-ID no se puede reconocer y se registra como tal.
La interoperabilidad se prueba contra pyas2lib 1.4.3 en ambos sentidos, con Triple DES y AES-256, comprimidos y no, con recibos sincrónicos firmados incluidos.
10.21 OpenPGP
Los pagadores, las casas de compensación y las agencias estatales a menudo toman y envían archivos solo bajo OpenPGP. Un bloque pgp en una fuente abre lo que llega; uno en un destino protege lo que sale. Ambos están en el constructor de canales: Mensajes cifrados con Open PGP bajo la fuente, Cifrar con OpenPGP en cada destino.
source:
type: sftp
sftp: { host: sftp.payer.example, user: hospital, key_file: keys/id_ed25519, dir: outbound }
pgp:
decrypt_with: keys/our-secret.asc # our secret key, armored or binary
passphrase: ${PGP_PASSPHRASE}
verify_with: [keys/payer.asc] # the senders' public keys
require_signature: true
destinations:
- name: claims
type: sftp
sftp: { host: sftp.payer.example, user: hospital, key_file: keys/id_ed25519, dir: inbound,
file_name: "${date}-${control_id}.x12.pgp" }
pgp:
encrypt_to: [keys/payer.asc]
sign_with: keys/our-secret.asc
passphrase: ${PGP_PASSPHRASE}
Los archivos de claves se leen relativos al archivo del canal y se leen y desbloquean cuando se carga el canal: una clave faltante o una frase de contraseña incorrecta detiene el canal allí, no en el primer archivo.
- En una fuente, todo el archivo, objeto, archivo adjunto de correo electrónico o cuerpo de la solicitud se
abre primero y luego se divide en mensajes, para que un lote cifrado como un archivo funcione. Uno que no se puede abrir se rechaza: una fuente de archivo lo mueve a su carpeta de error, un remitente HTTP responde 400 con la razón. require_signature rechaza un mensaje que no esté firmado por uno de verify_with; allow_unencrypted acepta uno que solo esté firmado; pass_plain deja que un mensaje que no sea PGP en absoluto pase sin cambios, para una carpeta que recibe ambos.
- En un destino, lo que se habría entregado se cifra (con cualquiera de
encrypt_to) y se firma (con sign_with), después de cada otro paso. Un destino de archivo, SFTP, FTP, S3, Azure Blob o GCS todavía nombra el archivo a partir del mensaje sin formato, para que ${control_id} funcione, y cualquier estructura está dentro del cifrado. armor: true escribe texto .asc en lugar de binario; no_compress desactiva la compresión; file_name es el nombre que se transporta dentro del mensaje.
Los mensajes se escriben con AES-256 en el formato RFC 4880 que los receptores más antiguos leen, y el formato RFC 9580 más nuevo se lee también. La criptografía es ProtonMail/go-crypto; Perfuse no implementa primitivos. La interoperabilidad se prueba contra GnuPG en ambos sentidos: gpg descifra y verifica lo que escribe Perfuse, y Perfuse abre y verifica lo que escribe gpg.
10.22 Entrega a otro canal
Un destino de canal entrega el mensaje a otro canal por nombre. El canal receptor lo trata como cualquier otra llegada: lo registra, lo filtra, lo transforma y lo distribuye.
Esta es la forma compatible de construir una cadena. Está deliberadamente visible — dos canales, dos conjuntos de estadísticas, dos entradas en el historial de mensajes — porque una cadena oculta dentro de un canal es una cadena que nadie puede supervisar.
El mensaje se entrega en memoria en lugar de a través de una red, por lo que no hay estructura, no hay reconocimiento y no hay tiempo de espera entre los dos.
10.23 Qué sucede cuando se mata el proceso
La pregunta que cualquier persona que haya ejecutado un motor de interfaz hace primero, y la que rara vez se responde, porque la respuesta honesta suele ser vergonzosa.
Se respondió matando el proceso con SIGKILL a mitad de un lote — lo que es lo que una falla de energía, una matanza por falta de memoria y un reinicio de hipervisor parecen desde adentro — y contando lo que sobrevivió contra lo que se había prometido.
La propiedad que se prueba no es "nada se pierde". Eso no es alcanzable y afirmarlo sería una mentira. Es la promesa más estrecha que un remitente realmente confía en: un mensaje que fue positivamente confirmado es un mensaje que llegó.
Con ack.when: on_delivery, que es el valor predeterminado, cada destino se escribe antes de que se envíe el acuse de recibo, por lo que un AA es una declaración sobre el destino en lugar de sobre una cola. Un mensaje eliminado antes de su acuse de recibo puede perderse, y eso es correcto: el remitente nunca se le prometió nada y lo enviará de nuevo.
10.23.1 Confirmación de entrega
Cinco ejecuciones, asesinadas después de 5, 25, 40, 75 y 110 mensajes de 120.
| Confirmado | Presente en la corriente de abajo después | Confirmado pero faltante |
|---|---|---|
| 255 | 255 | 0 |
La promesa se mantuvo en cada punto de eliminación, y nada se duplicó. Una duplicación habría sido aceptable y una pérdida no: un A08 reenviado es una molestia que los receptores absorben, y un resultado de laboratorio que nunca llegó silenciosamente es un evento de seguridad del paciente.
10.23.2 Confirmación de recepción
on_receipt responde tan pronto como el mensaje se coloca en la cola, antes de que se haya escrito algún destino. Este capítulo siempre ha dicho que un mensaje confirmado aún puede perderse de esta manera. Aquí está el tamaño de ello.
| Asesinado después | Confirmado | Presente en la corriente de abajo | Perdido |
|---|---|---|---|
| 5 | 5 | 1 | 4 |
| 25 | 25 | 1 | 24 |
| 75 | 75 | 2 | 73 |
| 110 | 110 | 2 | 108 |
Esencialmente todo lo que está en vuelo. La configuración no es un defecto y hay fuentes donde es la elección correcta, pero debe elegirse sabiendo que un bloqueo descarta lo que ha sido confirmado y no escrito, y que esto es casi todo de un estallido.
Nada se recupera al reiniciar, porque perfuse run no tiene un almacenamiento duradero. Un destino que no debe perder mensajes durante una interrupción necesita queue.enabled, que necesita la base de datos que proporciona perfuse serve — y perfuse run ahora rechaza ese canal en lugar de iniciarlo sin la cola.
10.23.3 Un remitente que muere a mitad de camino de un mensaje
El más peligroso de estos errores, porque un mensaje HL7 truncado suele seguir analizando. Los segmentos antes del corte son completos y bien formados, por lo que un receptor no tiene forma de saber que llegaron los segmentos PID y PV1 y no llegaron los segmentos OBX que transportan los resultados.
Se abrió un marco, se envió seis décimas partes de un mensaje y la conexión se restableció en lugar de cerrarse. El mensaje parcial no se entregó, el oyente sobrevivió y un mensaje bien formado enviado inmediatamente después se aceptó de manera normal, por lo que el fragmento no se llevó al siguiente mensaje tampoco.
El registro de conexión ahora informa cuántos bytes de un marco sin terminar se abandonaron, para cada modo de falla en lugar de solo un cierre ordenado. Anteriormente, un remitente bloqueado, un proceso asesinado y un cable arrancado todos registraron messages=0 y nada más, lo que no puede distinguir una comprobación de salud vacía de un resultado de laboratorio tirado nueve décimas partes del camino.
10.23.4 Un disco lleno
Se detecta y registra el agotamiento de espacio en lugar de tragarse. Hay dos cosas sobre ello que vale la pena saber.
El remitente no recibe un rechazo inmediato. La entrega se reintenta según el horario habitual — un segundo, dos, cuatro, ocho — y el acuse de recibo negativo llega unos quince segundos después. Un remitente cuyo propio tiempo de espera es más corto que ese ve una conexión bloqueada en lugar de un AE, y concluirá que la red es la culpable en lugar del disco.
Y un volumen que informa de cero bytes libres aún puede aceptar pequeños apéndices durante un tiempo, porque agregar un par de cientos de bytes a un archivo cuyo último bloque tiene espacio no necesita una nueva asignación. El primer fallo de escritura es posterior al momento en que el disco se llenó.
10.23.5 Lo que no se probó
Un directorio de destino que pierde el permiso de escritura. No se puede inyectar con chmod: el motor mantiene el archivo de salida abierto y cambiar el modo de un archivo no afecta a un descriptor que ya se ha abierto contra él. Probarlo adecuadamente requiere que el volumen se vuelva a montar de solo lectura o se elimine debajo del proceso, lo que no se ha hecho. Se nombra aquí en lugar de omitirlo, porque una brecha que nadie menciona se lee como una brecha que nadie buscó.
11 Alertas
Nada fuera de Perfuse le dirá que una fuente ha dejado de funcionar. El remitente está siendo confirmado y está satisfecho; el receptor no tiene forma de saber que se envió un mensaje. Las alertas son lo único que se encuentra entre una interfaz rota y alguien que se da cuenta semanas después.
11.1 Los tipos de alertas
| Tipo | Se dispara cuando |
|---|---|
error-rate | Los fallos superan una proporción del tráfico. |
queue-depth | Una cola tiene más de N mensajes esperando. |
queue-age | El mensaje en cola más antiguo es más antiguo que una duración. |
queue-stuck | Una cola no se está drenando en absoluto. |
no-traffic | Nada ha llegado durante un período fijo. |
below-rhythm | El tráfico está muy por debajo de lo que este feed normalmente hace *en este momento de la semana*. |
channel-down | Un canal que debería estar en ejecución no lo está. |
script-errors | Un script está fallando. |
rows-quarantined | Una fuente de base de datos está poniendo filas a un lado. |
slow-delivery | Las entregas están teniendo éxito pero están tardando demasiado. |
contract | Un mensaje viola el contrato declarado del canal. |
phi-volume | Un canal transporta varias veces su tasa habitual para la hora. |
phi-after-hours | Un canal que solo transporta tráfico diurno transporta algo por la noche. |
phi-new-destination | Un canal entrega a una dirección a la que no ha enviado antes. |
phi-bulk-reads | Una persona lee más de N registros de pacientes diferentes dentro de un minuto. |
Los últimos cuatro vigilan el acceso a los datos de los pacientes y se describen a continuación.
11.2 El silencio es lo más difícil de detectar
Un canal que ha dejado de recibir mensajes no produce errores. No hay nada que contar. Es por esto que existe no-traffic y por lo que no es suficiente por sí solo.
no-traffic es un umbral fijo: alertar si nada ha llegado durante, por ejemplo, dos horas. En un flujo que genuinamente nunca se detiene, funciona. En un flujo de laboratorio de una clínica es inútil — el flujo está en silencio todas las noches y todos los fines de semana, por lo que un umbral de dos horas envía una página a alguien a la 1 am todas las noches, y un umbral lo suficientemente largo como para sobrevivir al fin de semana no notará una interrupción el martes por la mañana hasta el martes por la tarde.
No hay un solo número que sea ambos. Eso no es un problema de ajuste; es que la pregunta "¿ha detenido este flujo" no puede ser respondida por una constante cuando el flujo normal varía por un factor de cien a lo largo de la semana.
11.3 Detección consciente del ritmo
below-rhythm compara el tráfico actual con lo que este canal normalmente hace en esta hora de este día de la semana.
Perfuse aprende la semana de cada canal a partir de su propia historia registrada: 168 cubos horarios, comenzando el lunes para que una semana laboral sea contigua. Para cada cubo, mantiene una mediana, un valor bajo y un valor alto, un piso y cuántas observaciones se basa.
El umbral es una parte del normal, que por defecto es 0.8 (80 por ciento en la consola) — alertar cuando el tráfico cae por debajo de cuatro quintos de lo que es normal para esta hora.
11.3.1 Mediana, no media
Un solo flujo de retraso de cuarenta mil mensajes contra un normal de trescientos mueve la media lo suficiente como para que el flujo pueda ir completamente en silencio después sin caer nunca por debajo de ella. La mediana no se ve afectada por una hora extraordinaria, que es exactamente la propiedad necesaria.
11.3.2 El piso es un percentil, no el mínimo
Si el ritmo de un canal es lo suficientemente confiable como para alertar se decide por su extremo bajo, no por su extremo alto. Una alerta de escasez se dispara cuando el tráfico es *bajo*, por lo que solo el extremo bajo puede decir si producirá falsos positivos. Una versión temprana de esto comparó el alto con la mediana y se equivocó en ambos sentidos al mismo tiempo: descalificó un flujo perfectamente estable durante un mes después de un retraso y aprobó un flujo genuinamente errático que oscilaba entre dos y novecientos mensajes en la misma hora porque su alto era solo tres veces su mediana.
El piso es el percentil vigésimo en lugar del mínimo, porque el mínimo es arrastrado a cero por un solo feriado bancario. Y el percentil no se interpola — cada número en una alerta a las dos de la mañana debe ser una cuenta que el flujo realmente produjo, no un promedio de dos cuentas que no produjo.
11.3.3 Dirá cuándo no puede decir
Un canal con ninguna historia en absoluto se omite por completo en lugar de ser tratado como uno que se ha detenido. Un nuevo canal no debe parecer uno roto.
Donde la historia es demasiado delgada o demasiado errática para respaldar un juicio, la regla se niega a disparar en lugar de adivinar. "No puedo decir honestamente" es una respuesta de primera clase aquí, y se prefiere a una alerta que está en lo correcto ligeramente más a menudo que la casualidad.
11.4 Configuración de alertas
Desde la interfaz, bajo Alertas, por canal o en todos ellos. Cada regla tiene un tipo, un umbral y dónde enviar la alerta. Las reglas se guardan en alerts.yaml junto a la base de datos (o el archivo -alerts nombra) y surten efecto de inmediato; hasta el primer guardado, las reglas integradas se aplican. El archivo también se puede editar manualmente: el cambio se recoge dentro de treinta segundos, y un archivo que ya no se carga se informa en el registro y se mantienen las reglas en vigor.
Vale la pena hacer antes de que un flujo se ponga en vivo en lugar de después del primer incidente. El conjunto específico que vale la pena tener en cualquier flujo clínico:
below-rhythm— el flujo se ha detenido cuando no debería estar en silencio.error-rate— los mensajes están llegando y fallando.queue-age— algo está atascado en lugar de simplemente estar ocupado.channel-down— el canal no se está ejecutando en absoluto.
El primero es el que atrapa el fallo que nadie más informará.
11.5 Vigilando el acceso a los datos de los pacientes
Una violación rara vez se anuncia. Parece un flujo que lleva más de lo habitual, tráfico a las tres de la mañana en un canal que solo funciona durante las horas de oficina, un destino que nadie configuró, o una persona que abre paciente tras paciente. Perfuse vigila estos cuatro, activados por defecto (serve -watch-access=false los desactiva), e informa lo que ve a través de las reglas de alerta anteriores, por lo que está configurado, confirmado y entregado como cualquier otra alerta.
- Explosiones (
phi-volume). La tasa de cada canal se compara con su propia historia en la misma hora del día, por lo que una mañana ocupada no es noticia. El umbral es un múltiplo de la tasa habitual, tres por defecto. Una explosión suele ser un retraso que se libera después de un tiempo de inactividad, y también es lo que los registros extraídos a través de un flujo parecen. - Tráfico nocturno en un canal diurno (
phi-after-hours). Un canal se considera diurno cuando menos del 2 por ciento de lo que ha transportado cayó fuera de las 07:00 a 19:00, en la zona horaria del servidor. - Un nuevo destino (
phi-new-destination). La dirección es lo que el destino nombra: un host y un puerto, una URL, una carpeta. Renombrar un destino no es noticia; apuntar a un nuevo host es. Las direcciones a las que cada canal ha enviado se mantienen enaccess-watch.jsonjunto a la base de datos, por lo que un destino cambiado mientras el servidor estaba inactivo aún se informa. - Lecturas masivas (
phi-bulk-reads). Contadas desde el registro de auditoría: mensajes abiertos, pacientes buscados o consultados (PDQm, PIXm, búsqueda de identidad), documentos e imágenes leídos (XDS, MHD, DICOMweb), autorizaciones previas leídas, resúmenes y vistas ejecutadas. Leer el mismo registro nuevamente no cuenta dos veces. El umbral es cuántos registros diferentes en un minuto, diez por defecto, por una cuenta desde una dirección.
Nada se juzga para un canal hasta que haya transportado 200 mensajes, porque "inusual" no significa nada sin un patrón. El patrón se aprende de las últimas dos semanas de la tienda de mensajes cuando el servidor comienza, por lo que un reinicio no hace que cada canal sea nuevo de nuevo. Cualquier cosa vista se mantiene durante quince minutos, luego la alerta se borra.
Solo vigila e informa. Nunca retiene un mensaje ni rechaza una lectura: estas son heurísticas, y un flujo detenido porque una suposición estaba mal es un problema clínico. Cada alerta dice qué se vio y qué podría ser inocentemente, porque la mayoría de ellas serán inocentes.
Los administradores ven lo que el monitor ha aprendido en la pantalla de alertas, bajo Vigilando el acceso a los datos de los pacientes: el patrón de cada canal (todavía aprendiendo, solo diurno o diurno y nocturno), cuántos destinos conoce y todo lo visto en los últimos quince minutos, incluyendo lo que está por debajo del umbral de una regla.
11.6 Dónde van las alertas
Las alertas se pueden enviar como JSON a un webhook (-alert-webhook: Slack, Teams, Mattermost, PagerDuty, Alertmanager o cualquier cosa que acepte un POST), se pueden enviar como trampas SNMPv2 a administradores de SNMP (-snmp-traps, véase SNMP), o se pueden escribir en el registro para que un colector lo recopile. No hay entrega de correo electrónico propia: apunte el webhook a un relé si las alertas deben llegar como correo electrónico.
Una alerta que solo se escribe en un registro que nadie lee no está alertando. Si Perfuse es la única cosa que notará un flujo detenido, la alerta tiene que llegar a una persona.
12 La Tienda de Mensajes
Cada mensaje que Perfuse recibe se registra, tal como se recibió, antes de que nada lo modifique. Lo mismo sucede con lo que se envió a cada destino y lo que le sucedió. La tienda es un archivo SQLite dado por -db.
12.1 Qué se mantiene
Por mensaje: los bytes brutos tal como llegaron, cuándo llegaron, qué canal, el tipo de mensaje analizado y el evento de activación, y el resultado del filtro.
Por destino por mensaje: los bytes que se enviaron, el resultado, el tiempo que se tardó y cualquier error o acuse de recibo negativo recibido.
Esta es la forma que necesita una investigación. "¿Qué nos enviaron", "¿qué les enviamos" y "¿qué dijeron sobre ello" son tres preguntas diferentes y cada una de ellas se hace.
12.2 Búsqueda
Por canal, por rango de tiempo, por resultado, por tipo de mensaje y evento de activación, y por valor de campo.
La búsqueda de campos funciona en el mensaje analizado, por lo que buscar un número de registro lo encuentra dondequiera que viva ese campo en el mensaje, en lugar de coincidir con los dígitos en cualquier lugar del texto. Una búsqueda de texto para 12345 en un mensaje que contiene una cantidad de 12345 encuentra un mensaje que no tiene nada que ver con el paciente que está buscando.
12.3 Repetición y reprocesamiento
Debido a que se mantiene el original, un mensaje se puede ejecutar a través de la configuración actual nuevamente.
Seguimiento toma un mensaje grabado y muestra qué haría el canal con él ahora, sin enviar nada. Cubierto en depuración.
Reprocesar lo vuelve a ejecutar realmente, incluyendo la entrega. Esto es lo que corrige un lote de mensajes que fallaron porque un destino estaba mal configurado: corrija la configuración, seleccione los mensajes afectados, reprocese.
12.4 Caducidad
Un seguimiento de un mensaje antiguo contra la configuración actual está respondiendo a una hipótesis: ¿qué *sucedería* ahora. Eso es lo que normalmente se desea, pero no siempre — si está investigando qué salió mal el martes pasado, el canal puede haber cambiado desde entonces.
Perfuse detecta esto y lo indica. Un seguimiento de un mensaje que llegó antes de que el canal se editara por última vez se marca como tal, para que la respuesta no se confunda con una reconstrucción de lo que realmente sucedió.
12.5 Retención
La tienda crece. cleanup.periodDays controla cuánto tiempo se mantienen los mensajes.
Dos cosas que sopesar. Los mensajes clínicos pueden estar sujetos a requisitos de retención que son más largos que cualquier cosa que elegiría por razones operativas, y la tienda no es el sistema de registro — el sistema receptor es. La copia de Perfuse existe para diagnosticar y reprocesar, y ambas necesidades disminuyen bruscamente después de unas semanas.
La retención también decide cuánta historia tiene el aprendiz de ritmo para trabajar. Vea alertas: una ventana de retención más corta que unas semanas deja que no pueda distinguir un domingo tranquilo de una alimentación detenida.
12.6 La tienda contiene datos clínicos
Esta es la parte de Perfuse que contiene información de pacientes, y debe tratarse como tal: en almacenamiento cifrado, respaldado como datos clínicos y controlado de acceso. Vea seguridad.
Los mensajes de prueba generados descritos en pruebas existen en parte para que estos datos no tengan que salir del entorno para reproducir un problema en otro lugar.
13 Depuración
La pregunta es casi siempre la misma: este mensaje llegó, algo salió mal, ¿dónde cambió? Perfuse responde mostrando cada paso al reproducir un mensaje grabado a través de la configuración actual.
Nada se envía mientras se realiza un seguimiento. El canal sigue funcionando sin cambios y no se necesita volver a implementar.
13.1 Seguimiento de un mensaje
Seleccione cualquier mensaje grabado y realice un seguimiento. El resultado muestra, en orden:
- Qué leyó el filtro. Qué campos examinó y qué valores encontró. Un filtro que rechaza todo generalmente lee una ruta que no existe en esta fuente, y esto lo nombra.
- Cada transformación. Qué cambió, de qué a qué.
- Qué decidió cada destino. Si aceptaría el mensaje y por qué no si no lo haría.
- La salida. El mensaje tal como cada destino lo enviaría.
13.2 Paso a paso
La vista paso a paso ejecuta cada transformación por separado y captura una instantánea del mensaje completo después de cada una. Una barra numerada de pasos, coloreada según lo que sucedió, y el mensaje tal como estaba en cada punto.
El mensaje completo en lugar de solo el cambio, porque el cambio a menudo no está donde está el problema — un paso que modificó correctamente el campo al que apuntaba, en un mensaje donde un paso anterior ya había colocado lo incorrecto allí, parece perfectamente correcto en aislamiento.
13.3 Cuatro resultados y la distinción que importa
| Resultado | Significado |
|---|---|
changed | El paso se ejecutó y modificó el mensaje. |
no-effect | El paso se ejecutó y el mensaje no cambió. |
skipped | La condición when del paso era falsa, por lo que no se ejecutó. |
failed | El paso falló. |
no-effect y skipped se informan por separado, y este es el punto de toda la característica.
Desde afuera son idénticos: el mensaje es el mismo después de cualquiera de los dos. Significan cosas opuestas. skipped significa que la condición funcionó como se escribió y este mensaje no era uno al que se aplicaba — generalmente correcto. no-effect significa que el paso se ejecutó y no encontró nada que hacer, lo que generalmente significa que está dirigido a un campo que no está allí.
Un motor que informa ambos como "nada sucedió" te deja sin poder distinguir una condición funcional de una ruta rota, y esa es la falla de transformación más común que existe.
no-effect en cada mensaje es casi siempre un camino incorrecto. El encabezado de paso a paso cuenta cuántos pasos no encontraron nada, lo que es la forma más rápida de detectarlo: un canal con tres pasos y tres resultados no-effect no está transformando nada en absoluto.13.4 Prueba de un cambio antes de guardarlo
El constructor puede ejecutar una configuración candidata contra tráfico grabado y informar qué diferiría. Esta es la forma más segura de cambiar un canal en vivo: realice la edición, vea qué efecto tiene en los últimos cientos de mensajes reales y luego guarde.
También es la forma de descubrir que un cambio que parece obviamente correcto afecta mensajes que no había pensado — un paso condicionado a un evento de activación, en una fuente que resulta llevar cuatro.
13.5 Qué no puede decirte el seguimiento
Ejecuta la configuración actual. Si el canal se ha editado desde que llegó el mensaje, el seguimiento es hipotético en lugar de una reconstrucción, y Perfuse lo marca como tal en lugar de dejar que la distinción pase silenciosamente.
Tampoco ejercita el transporte. Un seguimiento muestra qué se enviaría, no si el destino lo aceptaría — un mensaje correcto y un destino que rechaza conexiones producen un seguimiento limpio y una entrega fallida. Esos son visibles en el registro de entrega en su lugar.
13.6 Scripts
Un script es un paso opaco en el seguimiento. Perfuse puede mostrar qué se introdujo y qué salió, y no puede mostrar qué sucedió en el medio o qué campos se leyeron.
Este es el costo concreto de utilizar un script en lugar de pasos declarativos, y vale la pena sopesarlo al elegir. Consulte transformation.
14 Pruebas
El consejo estándar para probar una interfaz es construir mensajes de muestra que reflejen su propio entorno — los códigos de su laboratorio, su mezcla de casos, los hábitos de su remitente — y quitar los datos de paciente a mano. Eso es lento, y hacerlo de manera incompleta es el resultado normal.
Perfuse ofrece tres cosas en su lugar: tráfico generado que coincide con la forma de una fuente sin su contenido, un canal propuesto a partir de una muestra que alguien le envió, y una comparación con el motor que está reemplazando.
14.1 Tráfico de prueba generado
Si un canal ha manejado mensajes, Perfuse puede producir más mensajes similares a ellos.
¿Qué es fiel:
- Desencadenar eventos en las proporciones realmente observadas, con al menos uno de cada uno para que un evento raro aún aparezca.
- Campos poblados a las tasas que realmente son — por lo que el caso en el que un campo está *ausente* se prueba, que es el caso que rompe las cosas.
- Repeticiones donde ocurren repeticiones, estructura compuesta donde ocurre.
- Códigos extraídos de las tablas de códigos realmente vistas.
¿Qué no es fiel, y esto se establece en lugar de implicar: coherencia clínica. Un paciente generado tiene un número de registro no relacionado, una cadena con forma de nombre y un diagnóstico sin relación con la observación. Estos mensajes ejercitan una interfaz. No son un conjunto de pruebas clínicas y no se pueden utilizar para validar la lógica clínica.
14.1.1 ¿Por qué no contiene datos reales
El argumento es estructural en lugar de una afirmación sobre el esfuerzo. La única entrada del generador es el perfil de tráfico del canal, y un perfil contiene tasas de llenado, formas, longitudes, recuentos de repeticiones y frecuencias de segmentos. No contiene valores.
La única excepción son los campos de tablas de códigos, y un código no identifica a nadie.
Esta es una garantía más fuerte que la desidentificación de mensajes reales, que es un proceso que se puede realizar de manera incompleta y generalmente se hace. Aquí no hay camino por el cual un nombre podría llegar a la salida, porque el nombre nunca estuvo en la entrada.
Cada mensaje generado se analiza antes de devolverlo — un corpus no analizable enviaría a alguien a buscar un error en su interfaz que en realidad es un error en el generador — y cada uno está marcado T en MSH-11, con una aplicación y facilidad de envío sintéticas a propósito.
T y el remitente sintético no son cosméticos.14.2 Construir un canal a partir de una muestra
Pegue el mensaje que un laboratorio, hospital o proveedor le envió por correo electrónico y Perfuse propone un canal para él. No es necesario que nada esté en ejecución primero.
Maneja lo que la gente realmente pega: varios mensajes en un bloque, cualquier terminación de línea, encuadre MLLP que sobrevivió a la copia y la nota de cobertura sobre el mensaje. Rechazar una muestra porque llegó con terminaciones de línea de Windows sería un obstáculo en lugar de una verificación.
14.2.1 Tres listas
La propuesta viene con lo que la muestra muestra, lo que se ha adivinado y lo que la muestra no puede decirle — tres listas separadas, con igual peso en la interfaz.
Están separadas porque un lector que no puede decir cuál es cuál tiene que verificar todo o confiar en todo, y ambas opciones son peores que saber dónde buscar.
A partir de un solo mensaje, los límites se nombran específicamente en lugar de de manera general: qué campos son opcionales, qué segmentos se repiten, qué códigos existen más allá de los que están aquí, si el remitente alguna vez envía un evento de disparo diferente. Una advertencia general es algo que la gente pasa por alto.
La confianza es una palabra — baja, moderada, razonable — nunca un porcentaje. Un porcentaje implica un cálculo, y esto es un juicio sobre la cantidad de muestras que hay.
Los segmentos Z se llaman por su nombre, porque un segmento local es la cosa que no menciona ninguna especificación y es la razón más común por la que un canal construido a partir de una plantilla estándar no funciona.
14.2.2 La advertencia de separador
Si el separador de campos de la muestra no es el usual |, eso aparece primero, y la confianza disminuye a baja.
La razón es un fallo silencioso genuino. HL7 toma su separador de campos del mensaje en sí, por lo que una muestra cuyas tuberías han sido reemplazadas por similares tipográficos — lo que hace un procesador de texto — se analiza perfectamente en campos que están todos mal. No hay errores en ninguna parte.
Rechazar dicha muestra sería incorrecto, porque un remitente puede legítimamente utilizar otro separador. Por lo tanto, se marca, se nombra el carácter y se sugiere solicitar un archivo de texto plano.
14.3 Comparación con el motor antiguo
Antes de mover una fuente de datos en vivo, la pregunta es si Perfuse produce la misma salida que la cosa que ha estado funcionando durante años. Nada de la calidad del diseño responde a eso. Consulte migración.
14.4 Verificando contra la cosa real, en contenedores
Tres comprobaciones se ejecutan contra implementaciones reales en lugar de contra la propia idea de Perfuse de un protocolo. Estas viven fuera de make check porque necesitan un entorno de ejecución de contenedores, y una comprobación que necesita Docker es una comprobación que la gente deja de ejecutar.
scripts/interop-up.sh inicia todos ellos e imprime lo que cada uno desbloquea.
| Contenedor | Comprobaciones | Lo que encontró |
|---|---|---|
| Keycloak 26 | web/e2e-saml/, y internal/saml fixtures | Un defecto. La forma canónica que una firma cubre se calculó con prefijos de namespace eliminados, por lo que ningún proveedor de identidad real podría haber firmado a nadie |
| Microsoft Entra | internal/saml pruebas en vivo y fixtures | Dos defectos, ambos en lo que rodea el protocolo. Una cuenta se creó bajo el NameID opaco de Entra, porque la lógica de nombre legible quería una reclamación de correo electrónico y Entra no envía ninguna. Y una cuenta de SAML se registró como OIDC, porque el almacén derivó el protocolo de un emisor que parece idéntico para ambos |
| HAPI FHIR | go test ./internal/engine/ -run HAPI | Un defecto. Una autoridad de asignación no mapeada produjo un sistema de identificador que anuncia un OID y lleva una palabra, que HAPI aceptó sin queja |
| ActiveMQ | go test ./internal/engine/ -run RealBroker | Correcto, incluyendo el caso de encuadre de byte nulo. Afirmado a través de la interfaz de administración del propio corredor en lugar de a través de una función que devuelve nil |
| nginx, certificados de cliente | go test ./internal/engine/ -run ClientCertificate | Correcto |
| Orthanc (un PACS real) | go test ./internal/engine/ -run RealPACS | Correcto. Una C-STORE llega y el nombre del paciente, el identificador, la fecha del estudio, la modalidad y la clase SOP sobreviven |
| PostgreSQL | go test ./internal/engine/ -run RealPostgres | Correcto, y provocó un mejor error: una instrucción en los marcadores de posición del dialecto incorrecto ahora se rechaza cuando se guarda en lugar de convertirse en un error de sintaxis en el servidor cuando llega un mensaje |
| OpenSSH sftp-server | go test ./internal/engine/ -run OpenSSHServer | Correcto. Las pruebas SFTP existentes utilizan la misma biblioteca de Go en ambos extremos; OpenSSH es una implementación diferente y la que se encuentra en el extremo lejano de casi todas las fuentes de datos reales |
| Mirth 4.5.2 | go test ./internal/mirth/ -run RealMirth | Correcto ahora. El único fixture utilizado solía estar escrito a mano y Mirth no lo cargaba; scripts/mirth-author-channel.sh tiene Mirth que autoriza uno en su lugar, y Perfuse lo lee |
Cada uno salta en lugar de fallar cuando su contenedor está ausente, por lo que make check pasa en una máquina con no Docker.
Por qué esto está separado del resto de la suite. Cada otra prueba de estas características tenía ambas mitades escritas aquí: un documento que este código base firmó, leído por el código que lo firmó. Eso es acuerdo con uno mismo, y vale menos de lo que parece.
El canonizador SAML es el caso que prueba el punto. Llevaba mil líneas de pruebas, cinco de ellas específicamente sobre la canonización, y todas pasaron mientras ningún proveedor de identidad real podría haber firmado a nadie — la forma canónica que una firma cubre se calculó con prefijos de namespace eliminados, y la única forma de encontrar eso fue entregarle un documento que Perfuse no había escrito.
Dos de las ocho comprobaciones encontraron defectos directamente, y una tercera provocó una mejora real. Tres no encontraron nada mal. Esa proporción es el argumento para mantener todos ellos: una comprobación que pasa ha dicho algo que no se podría haber sabido de otra manera, y no había forma de saber de antemano cuáles serían.
Un patrón que vale la pena nombrar, porque apareció en ambos defectos: la aceptación no es verificación. HAPI aceptó el sistema de identificador malformado y lo almacenó; el defecto solo apareció al leer el recurso de nuevo. Un receptor que tolera algo no es evidencia de que esté bien, solo de que este receptor fue indulgente.
Dos afirmaciones capturadas se comprometieron, en internal/saml/testdata/keycloak-assertion.xml y entra-response.xml, con pruebas sobre ambas — para que esa regresión particular se capture sin un contenedor.
Ambos son necesarios en lugar de que uno sea de repuesto. Keycloak prefija su aserción y firma, Entra no, y esos son los dos casos opuestos en la canonización — que es precisamente donde estaba el defecto. Una prueba afirma cada documento todavía cubre su propio caso, porque una captura de reemplazo que sucedió que estuviera prefijada reduciría silenciosamente la cobertura sin fallar nada. La lección general no se generaliza tan barato: para cualquier otra cosa, el contenedor es la comprobación.
14.5 Diagnosticar una prueba que falla una vez y nunca más
Una prueba que falla en una ejecución completa y pasa sola dieciséis veces no es una prueba inestable. Es una prueba que compartió algo con la ejecución, y la forma de descubrir qué es medir la cosa compartida en lugar de la prueba.
Existen dos instrumentos para esto, y ambos están destinados a ejecutarse junto con una suite completa:
./scripts/e2e-watchdog.sh /tmp/watchdog.log # the HTTP server and the shared session
./scripts/e2e-mllp-probe.py /tmp/mllp.log # the fixture channel's listener
Cada uno descubre los puertos de la ejecución desde el archivo de estado de configuración propio y muestrea cada 500 milisegundos hasta que el servidor sale. Registra cada muestra en lugar de solo los fallos, lo que los hace útiles: un error se muestra en una ejecución donde nada falló, por lo que una falla que aparece una vez en tres ejecuciones se vuelve medible en cada una.
Establezca PERFUSE_E2E_KEEP=1 para mantener el directorio temporal, que contiene la base de datos tal como se dejó y el registro del servidor para toda la ejecución. El registro del servidor suele ser la respuesta. En un caso investigado, registró que un canal se detuvo y su oyente se volvió a enlazar 421 milisegundos después, y la conexión rechazada del sonda cayó dentro de esa ventana.
Lea la traza en lugar de error-context.md, que ha estado vacío en más de una ocasión donde la traza contenía el mensaje real.
Copia los artefactos antes de volver a ejecutar algo. Una ejecución que pasa borra test-results, por lo que la traza del error que está investigando desaparece en el momento en que intenta reproducirlo. Una vez perdido, la pregunta que habría respondido no puede ser respondida.
Detenga los contenedores de interoperabilidad primero. Deténgalos antes de una ejecución completa del navegador y arráncelos con ./scripts/interop-up.sh después si todavía los necesita.
Ocho contenedores ocupan alrededor de 9,8 GB de memoria, y en una máquina sin mucho más que eso, queda muy poca memoria para los procesos de Chromium, node y la cadena de herramientas de Go juntos. Lo que produce no es un error de memoria agotada, sino un renderizador que deja de programarse: una traza registrada muestra un intervalo de 60,2 segundos sin cuadros, durante el cual Playwright esperó a que un botón se volviera "visible, habilitado y estable" y no pudo obtener dos cuadros de animación consecutivos para comparar. El servidor estuvo saludable durante todo el tiempo, la solicitud más lenta en toda la ejecución fue de 154 milisegundos.
Ese error se lee como un defecto en la prueba que resultó que se estaba ejecutando. Vale la pena conocer su forma: todas las solicitudes rápidas, la página visualmente congelada, el bucle de reintento nunca alcanza una segunda iteración, y todo pasa en aislamiento.
Solo una ejecución a la vez. La suite comparte un servidor, una sesión iniciada en web/e2e/.auth/admin.json y un archivo de estado que nombra el proceso del servidor. Nada de eso es por ejecución, por lo que una segunda ejecución que comienza mientras la primera se ejecuta sobrescribe el archivo de estado, y el desmontaje que termina primero mata el servidor de la otra ejecución y elimina su sesión.
Eso es lo que parece, nada como la causa. Produjo una racha de 279 fallos cuyo primer mensaje fue ENOENT: no such file or directory, open './e2e/.auth/admin.json', una racha que se detuvo temprano informando 217 aprobados, y dos pruebas fallidas con bind: address already in use — ninguno de los cuales sugiere dos ejecuciones luchando. La suite ahora toma un bloqueo y se niega a iniciar, nombrando el proceso que lo mantiene. Un bloqueo dejado por una ejecución interrumpida se toma en lugar de bloquear para siempre, porque una medida de seguridad que bloquea la suite es algo que alguien elimina junto con la seguridad.
Una trampa relacionada, ahora arreglada: dos especificaciones nombraron sus puertos de escucha directamente, por lo que un servidor dejado atrás por una ejecución interrumpida los hizo fallar en algo con lo que no tenían nada que ver. Ahora se piden puertos al sistema operativo.
No responda a ninguno de esto con reintentos. Una prueba que pasa en el segundo intento encontró una carrera real y la ocultó, y playwright.config.ts establece retries: 0 por esa razón.
Una advertencia: muestrear cada 100 milisegundos generó veinte procesos por segundo, estiró una ejecución en un cincuenta por ciento, y falló dos pruebas en tiempos de espera que no tenían nada malo. Un instrumento que cambia lo que mide produce hallazgos sobre sí mismo.
14.6 Lo que todavía necesita infraestructura real
Varias partes de Perfuse están implementadas y nunca se han ejercitado contra lo real. Se enumeran aquí en lugar de dejar que se descubran:
- SMB contra una compartición de Windows real — el código del protocolo está completo y probado para seguridad de ruta y manejo de argumentos, pero ninguna prueba abre un socket a un servidor unido a un dominio.
- Un puerto serie real.
- El importador de Mirth contra XML real producido por Mirth. Probado contra Mirth 4.5.2 en un contenedor, y el hallazgo fue sobre este repositorio: el único fixture de migración aquí fue escrito a mano y Mirth no lo carga — almacena el canal como inválido y descarta todos los conectores. Lo que se decidió es que los atributos
versionque Mirth real escribe en cada elemento se analizan en el mismo canal, por lo que no son un peligro; lo que no se decidió es todo lo demás que contiene una exportación real. - SAML contra un proveedor de identidad comercial alojado. Keycloak está verificado; un proveedor prueba que una implementación coincide con un proveedor.
- Una reclamación de NCPDP a través de un conmutador de farmacia real.
- Un mensaje de SCRIPT a través de Surescripts, donde la certificación es un proceso comercial en lugar de técnico.
Las características de este capítulo y el siguiente existen para que pueda recopilar sus propias pruebas en lugar de tomar la palabra de alguien.
14.7 Medido contra corpora de conformidad públicos
Una suite de pruebas cuyos fixtures se escribieron aquí prueba el acuerdo con uno mismo. Estos son los datos de otras personas.
14.7.1 FHIR R4, los ejemplos de la especificación
Todos los 2.912 archivos de ejemplo publicados con la especificación FHIR R4 — cada ejemplo que los autores del estándar escribieron.
| Resultado | Recuento |
|---|---|
| Recursos validados con ningún hallazgo | 13.723 |
| Recursos informados como inválidos | 0 |
| Archivos rechazados como un tipo de recurso no implementado | 753 |
| Entradas de paquete omitidas como un tipo no implementado | 1.348 |
No se validó incorrectamente ningún recurso. Cada fallo es un rechazo explícito que nombra el tipo que no puede leer — 672 ValueSet, 80 ConceptMap y un Parameters. Un validador que pasa silenciosamente lo que no entiende es peor que uno que lo dice.
El corpus encontró un defecto, que es la razón para ejecutarlo. La validación de un paquete imprimió "La validación de paquetes desde un archivo no es compatible todavía" y continuó. Eso fue falso en ambos sentidos: los paquetes cuyas entradas casualmente eran tipos que el analizador podía contener ya estaban siendo validados, y un paquete que era genuinamente inválido produjo la misma línea, no contó nada y salió cero — incluyendo bajo -strict, que existe para que esto pueda bloquear una compilación. Cuarenta y dos de los propios paquetes de la especificación estaban siendo omitidos.
Los paquetes ahora se validan entrada por entrada, con tres resultados mantenidos aparte: una entrada que está mal falla y nombra el campo, una entrada de un tipo que esta compilación no puede leer se cuenta como omitida y se informa, y un paquete que lleva ningún cuerpo de recurso no es ninguno. El mismo corpus pasó de 2.100 recursos comprobados a 13.723.
14.7.2 HL7 v2, el corpus de pruebas HAPI
Los arreglos de mensaje de HAPI, la implementación de referencia de Java HL7 v2 — material deliberadamente incómodo que incluye payloads codificados en uu, delimitadores escapados y grupos repetidos.
59 mensajes, 59 analizados, nada rechazado. Siete tipos de mensaje en un corpus que cualquier sistema individual describiría como una sola cosa, que es el punto perfuse profile existe para hacer.
14.7.3 Lo que no se ha ejecutado
DICOM contra un conjunto de conformance público, y X12 contra un corpus publicado. Ninguno de los dos se ha realizado, y no se debe inferir ninguno de los dos de los dos anteriores.
15 Migración desde Mirth
Se necesitan dos cosas para reemplazar un motor de trabajo: los canales tienen que cruzar, y alguien tiene que poder mostrar que el reemplazo produce la misma salida. Perfuse hace lo primero importando las propias exportaciones de Mirth, y lo segundo comparando los dos motores en su propio tráfico.
Todo en este capítulo se aplica igualmente al Open Integration Engine y BridgeLink. Mirth Connect dejó de ser de código abierto en la versión 4.6 en marzo de 2025; OIE (alojado por la Eclipse Foundation) y BridgeLink (Innovar Healthcare) son las bifurcaciones que continúan la línea de código abierto, y ambos escriben los mismos formatos de exportación. El importador se prueba contra los tres, a continuación.
15.1 Importación de canales
Perfuse lee cada exportación que escriben estos motores:
| Exportación | Desde el Administrador | Lo que se cruza |
|---|---|---|
| Un canal | Exportar canal | El canal |
| Un grupo de canales | Exportar grupo | Cada canal en él, cada uno manteniendo el grupo |
| Plantillas de código | Exportar plantillas de código | Cada biblioteca como un archivo de script |
| Una copia de seguridad del servidor | Copia de seguridad de configuración, o GET /api/server/configuration | Todo lo anterior a la vez |
Apunte perfuse translate a cualquiera de ellos, o suéltelo en Administrar → Migrar:
perfuse translate -o ./channels server-backup.xml
Una biblioteca de plantillas de código se convierte en lib/<library>.js junto a los canales, y cada canal de Mirth que tenga habilitada la biblioteca obtiene scripts.include: [lib/<library>.js] — por lo que un transformador que llama a una función de sitio como formatMRN() todavía la encuentra. Un canal para el que la biblioteca no esté habilitada no la incluye, porque sus scripts no podían verla en Mirth either. Se omiten los fragmentos de arrastrar y soltar: Mirth solo los pega en un paso a mano, por lo que no son código que se ejecuta por sí solo. En el navegador, importar un canal guarda las bibliotecas que incluye primero.
Un Channel Writer nombra el canal al que entrega por id. En un grupo o una copia de seguridad, el destino está en el mismo documento, por lo que se convierte en un destino de channel que nombra ese canal; desde una exportación de un solo canal se informa para que lo complete. Los scripts globales del servidor no tienen un equivalente en Perfuse y se informan en lugar de eliminarse.
El informe dice, por canal, si se tradujo limpiamente, se tradujo con advertencias o no se pudo leer.
El titular es una fracción con su denominador — "37 de 40 listos" — porque "37 listos" no le dice nada sobre lo que todavía tiene que hacer.
Un canal que no se pudo leer es un problema diferente de uno que no se pudo traducir. El primero significa que el XML en sí no se analiza y generalmente es una exportación truncada o parcial; el segundo significa que el contenido del canal tiene algo sin un equivalente en Perfuse. La importación los separa.
15.2 Lo que se traduce y lo que necesita una decisión
Las fuentes, destinos, filtros y los pasos de transformador ordinarios se traducen directamente. Los conectores que se traducen: TCP/MLLP (y TCP raw con su enmarcado), HTTP, Archivo, Base de datos, JavaScript, DICOM, Servicio web (SOAP), SMTP y Lector/Escritor de canal. JMS y el Escritor de documento no se traducen, y se informan como bloqueadores.
Un paso de Asignador que establece una variable la coloca en el mapa que su ámbito nombra — channelMap por defecto — exactamente como Mirth lo hace, así que los pasos posteriores que la leen con $('name') la encuentran.
Los transformadores de JavaScript no lo hacen, en general. Los canales de Mirth comúnmente contienen un código sustancial en un transformador, y no hay una conversión automática de JavaScript arbitrario a pasos declarativos. El importador informa sobre estos en lugar de intentar una traducción que podría estar sutilmente equivocada: un transformador mal traducido es peor que uno marcado para un humano, porque se ejecutará.
Donde un script genuinamente tiene que permanecer como script, Perfuse puede ejecutarlo — véase la clave scripts en la referencia de canal — a costa de la traza de nivel de paso descrita en depuración.
15.3 Lo que el importador ha sido probado
Cinco motores, cada uno creando sus propios documentos: Mirth 4.5.2, OIE 4.5.2, 4.6.0 y 4.7.0, y BridgeLink 26.9.0. scripts/mirth-engine-corpus.sh compila un pequeño programa contra los jars de cada motor y tiene su propio serializador que escribe tres canales ordinarios — una fuente MLLP con un mapeador, un script que llama a una biblioteca de plantillas de código y un filtro; una fuente HTTP a una base de datos; una votación de archivo sobre MLLP — más la biblioteca y un grupo de canales. Carga en el motor en ejecución y descarga la copia de seguridad del servidor del motor. Los resultados se confirman en internal/mirth/testdata/engines/, y la suite de pruebas mantiene al importador en cada uno de ellos en cada compilación: cada copia de seguridad se lee, se traduce, se carga y el canal ADT se ejecuta con dos mensajes para verificar que la biblioteca rellena el número de registro y el filtro descarta el resultado.
Las propias exportaciones de Perfuse van en la otra dirección — los canales de Perfuse importados en cada motor en ejecución y se verificó que el motor no los almacenó como inválidos. Los cinco aceptan cada transporte que Perfuse exporta. Open Integration Engine 4.7.0 agregó dos configuraciones de HTTP sin equivalente aquí: un remitente HTTP vinculado a una dirección local elegida se informa como una advertencia, y un límite de encabezado de solicitud de escucha de HTTP solo cuando supera el 1 MB que Perfuse acepta.
La primera ejecución contra este corpus encontró cuatro defectos, todos corregidos: el traductor rechazó fuentes HTTP y de archivo como "solo se admite MLLP" mucho después de que Perfuse tuviera ambos; leyó la dirección de un remitente HTTP de un campo que ninguno de los motores escribe; tradujo el tamaño del búfer de socket de Mirth como un límite de mensaje de 64 KB; y una variable de un mapeador se convirtió en una variable global de JavaScript que $('mrn') nunca vio, por lo que la biblioteca rellenó una cadena vacía.
The single-channel fixture that started this was also exported by Mirth 4.5.2, authored by Mirth itself rather than written here. That distinction earned its place: the only fixture in this repository used to be hand-written, and a real Mirth will not load it — the API accepts the POST and then stores the channel as This channel is invalid. Verify all required extensions are loaded correctly, with every destination connector discarded.
Escribir uno a mano nunca iba a funcionar, y la razón vale la pena saber si está produciendo exportaciones propias. El serializador de Mirth ignora los elementos que no reconoce, por lo que un nombre de elemento incorrecto no produce ningún error en absoluto — el conector simplemente no está allí después.
scripts/mirth-author-channel.sh construye uno utilizando las clases de modelo de Mirth y su propio serializador, luego lo publica en un servidor en ejecución y falla si el servidor lo rechaza. El importador de Perfuse lee el resultado: nombre, transporte de origen y destinos todos sobreviven.
15.4 Probar que coincide
Esta es la parte que decide si ocurre una migración, y necesita evidencia en lugar de confianza.
Perfuse compara su propia salida con la del antiguo motor, mensaje por mensaje, en su tráfico. Lo que necesita son pares: el mensaje tal como llegó, y el mensaje que el antiguo motor produjo a partir de él. Ambos están en la propia tienda de mensajes de Mirth y se pueden exportar desde su navegador de mensajes.
15.4.1 Cuatro números, no una tasa de aprobación
| Resultado | Significado |
|---|---|
| Idéntico | Byte por byte el mismo. |
| Solo cosmético | Difiere en formas que no llevan información — campos vacíos finales, un terminador final. |
| Difiere | El valor de un campo es diferente. |
| No se ejecutaría | Perfuse no podría procesar el mensaje en absoluto. |
Son cuatro números porque significan cuatro cosas diferentes y colapsarlos oculta los que necesitan una decisión.
Idéntico significa byte por byte, no equivalente. Una prueba más débil permitiría diferencias en el orden de segmentos o separadores finales, y esas son exactamente lo que un sistema descendente quisquilloso rechaza.
Las diferencias cosméticas se cuentan por separado en lugar de incorporarse en la columna buena. Realmente no llevan información, por lo que tratarlos como fallos enterraría los hallazgos reales bajo miles de nada — pero no son idénticos tampoco, y un receptor que realice su propia análisis estricto puede no estar de acuerdo.
"No se ejecutaría" es separado de "difiere" porque generalmente es una brecha de configuración que el importador no pudo llenar, no una decisión de mapeo. Si todo está en esa columna, el veredicto lo dice y le dice que mire la configuración antes de leer cualquier otra cosa.
15.4.2 Las diferencias se agrupan por campo
Una ejecución de más de cincuenta mil mensajes que informa cuatro mil diferencias no le ha dicho nada que pueda actuar. La misma ejecución que informa que PID-8 difiere en cada mensaje porque el antiguo motor escribió Male donde Perfuse escribe M le ha dicho exactamente una cosa que decidir.
Entonces cada hallazgo es un campo, con un recuento, y dice si la diferencia es sistemática — el mismo par de valores cada vez — o si el contenido del campo difiere.
Esa distinción es la cosa más útil en el informe. Un par de valores distintos en cuatro mil mensajes es una sola decisión de mapeo. Cuatro mil pares distintos es el contenido que difiere, que es un problema diferente y mucho peor, y los dos no deben leer igual.
Los hallazgos se ordenan por la cantidad de mensajes que afectan, por lo que la causa única más grande es la primera.
15.4.3 El veredicto no redondea
Proporciona recuentos exactos: 49.993 de 50.000, no "más del 99%". Los siete son el punto de ejecutarlo.
Está escrito para ser citable en una solicitud de cambio, porque es donde termina el número.
15.5 Una secuencia sugerida
- Importe los canales y resuelva cualquier problema informado como ilegible.
- Para cada canal, revise las advertencias. La mayoría serán scripts.
- Exporte varios miles de pares de mensajes de Mirth para el canal más ocupado y ejecute la comparación.
- Trabaje en los resultados. Los sistemáticos son una decisión cada uno.
- Vuelva a ejecutar hasta que las únicas diferencias sean las que ha decidido que son correctas.
- Ejecute Perfuse en sombra junto con Mirth en tráfico en vivo, sin entregar en ningún lugar, y compare de nuevo durante un período que incluya un fin de semana y un fin de mes.
- Cambie a un canal, no a todos.
15.6 Lo que solo se ejecuta en el software de un proveedor
La pantalla de migración responde a una segunda pregunta, y vale la pena preguntar antes de tener alguna opinión sobre Perfuse: ¿cuánta de su lógica de integración solo se puede ejecutar en el software que tiene.
Cada canal obtiene un veredicto de Portabilidad, de la misma exploración que produce las notas de migración. Se ejecuta contra una exportación de canal, por lo que no necesita nada instalado y no cambia nada.
15.6.1 La distinción es el punto principal
La mayoría del Java en los scripts de Mirth no es de bloqueo. SimpleDateFormat, HashMap, Apache Commons — todo funciona alrededor de un motor de JavaScript de 2009, todo es ordinario, todo tiene equivalentes directos. Un canal con trescientas referencias de ese tipo es completamente portable, y una herramienta que informara "300 referencias de Java, está atrapado" estaría mintiendo.
El bloqueo es específicamente las llamadas que necesitan el servidor del proveedor: com.mirth.connect.* y jars del proveedor. Cinco de esos y está atrapado. Quinientos del otro tipo y está libre.
Entonces, el veredicto comienza con el número que importa y dice en la misma oración que el número más grande no es ese. Donde hay llamadas del proveedor, se enumeran por nombre — un recuento es una afirmación, y una lista es algo que su propio ingeniero puede ir y verificar.
Se mantiene una tercera categoría por separado: capacidad que existe pero no desde un script. Iniciar un canal desde JavaScript no tiene equivalente aquí, y hay un punto final de API que hace exactamente lo mismo. Llamar a eso bloqueo exageraría el resultado.
15.6.2 Llamadas a Java que se ejecutan sin cambios
La mayoría de esas clases de Java se ejecutan tal como están escritas. Perfuse no tiene un tiempo de ejecución de Java, por lo que estas son clases de JavaScript con los mismos nombres y métodos, suficientes para la forma en que los scripts de canal los utilizan; importPackage(java.util) y importClass(...) los traen al alcance por sus nombres cortos, como en Rhino (los Date, Map, Set, Math, String y Boolean de JavaScript nunca se reemplazan: los de Java todavía están allí por nombre completo). El informe de migración cuenta cada una de estas llamadas como ya soportadas.
| Paquete | Clases |
|---|---|
java.util | ArrayList, LinkedList, Vector, List, Collection, HashMap, LinkedHashMap, Hashtable, TreeMap, Properties, HashSet, LinkedHashSet, TreeSet, Arrays, Collections, Date, Calendar, GregorianCalendar, UUID, Base64, regex.Pattern (con Matcher), concurrent.ConcurrentHashMap |
java.lang | String, StringBuilder, StringBuffer, Integer, Long, Short, Byte, Double, Float, Boolean, Math, System (solo tiempo y separador de línea) |
java.text, java.math, java.net, java.nio | SimpleDateFormat y DateFormat (letras de patrón de Mirth), BigDecimal (como doble), URLEncoder, URLDecoder, StandardCharsets |
| Apache Commons | lang3.StringUtils y lang.StringUtils (alrededor de sesenta métodos), StringEscapeUtils, codec.binary.Base64 |
Se comportan como Java donde los scripts dependen de él: Integer.parseInt("x") lanza NumberFormatException, String.replaceAll y split toman expresiones regulares mientras que replace es literal, los mapas clave las cadenas por valor, TreeMap y TreeSet iteran en orden, y SimpleDateFormat.parse devuelve una fecha getTime() funciona en ella. Un método que un script llama que el equivalente carece falla en esa línea, nombrándolo; cualquier otra clase de Java falla por nombre, como antes. BigDecimal es un doble aquí, por lo que no mantiene la aritmética decimal exacta de Java: verifique los scripts que lo utilizan para el dinero.
15.6.3 Los scripts externos se informan como desconocidos
Un paso que ejecuta un archivo de script externo hace referencia a una ruta en el servidor antiguo, y el archivo no está en la exportación. Nada se puede decir sobre lo que contiene, por lo que se cuenta como desconocido en lugar de asumirlo de una manera u otra. Recupere esos archivos antes de concluir cualquier cosa — en ambos sentidos.
15.6.4 El propio bloqueo de Perfuse, declarado en el mismo aliento
Esta sección sería deshonesta sin ello.
Perfuse es Apache 2.0, que es irrevocable para el código ya publicado. Puede bifurcar lo que existe hoy y nadie puede retirar eso. Los canales son archivos YAML y los scripts son JavaScript ordinario, por lo que no hay superficie de propiedad para llamar — el sabor de Java de Mirth es estructuralmente inaccesible aquí, que es el único lugar donde una limitación es genuinamente una característica.
Lo que eso no promete: que una versión futura permanezca abierta, que exista un servicio alojado a un precio que le guste, o que migrar desde Perfuse cueste nada. Mover cualquier capa de integración significa volver a probar cada canal contra tráfico real. Lo que está eligiendo es si los artefactos que construye son legibles y portátiles, y si la salida depende de una licencia que alguien más controla.
15.6.5 Configuración del paso 6
La sección Shadow inicia una comparación. Elija el canal que se está ejecutando, elija el candidato para comparar desde la lista y comience.
Tres cosas sobre ello valen la pena saber antes de confiar en los números.
El candidato se elige de una lista, no se escribe. Una sombra nombra un archivo de canal y un nombre que no se resuelve hace que el canal *en vivo* sea inválido: Perfuse rechaza un canal en su totalidad cuando algo a lo que hace referencia está roto. Así que un error tipográfico no produce una advertencia, hace que el canal en ejecución desaparezca.
La participación es un porcentaje. Redúzcala solo en una fuente ocupada y conozca su costo: la muestra reduce el trabajo y la confianza en la misma proporción, y el mensaje que habría mostrado la diferencia es el inusual.
Campos para ignorar suele ser necesario. Un canal que estampa una marca de tiempo o un número de secuencia es diferente en cada mensaje, lo que informa una tasa de diferencia del 100% y no le dice nada. MSH-7 y MSH-10 son el par común.
Una comparación se escribe en el archivo de canal tan pronto como se inicia y comienza a observar cuando el canal se carga nuevamente: la pantalla lo indica en lugar de afirmar que ya está en ejecución. Detener una comparación deja el canal candidato en su lugar, porque el candidato es la cosa que se supone que debe estar en vivo.
El paso 6 es el que no se puede acortar. Las diferencias que importan suelen estar en el tráfico que solo aparece ocasionalmente: un evento de disparo raro, un lote de fin de mes, un remitente que se comporta de manera diferente a medianoche, y una muestra tomada durante una tarde no los contendrá.
15.7 Volver en el otro sentido
Un canal se puede exportar como un archivo de canal de Mirth, desde el navegador: Canales, luego A Mirth en el canal que desee.
Esto existe porque la objeción a reemplazar un motor de trabajo rara vez es si el reemplazo es bueno. Es lo que sucede si la decisión resulta ser incorrecta, y una exportación que vuelve por donde vino es la respuesta más barata a eso.
Lea lo que el diálogo le dice antes de tomar el archivo. El formato de Mirth no tiene dónde poner un filtro de Perfuse, una cadena de transformación, un contrato o una comparación de sombra, así que un canal exportado hace menos que el que vino. Los transportes y sus direcciones se convierten; la lógica no. Cada pérdida se enumera por nombre antes de que se ofrezca el archivo, y la misma lista se incluye en la descripción del canal para que quien lo importe también lo vea.
Un canal que Mirth no puede expresar se rechaza en lugar de aproximarse. Un destino S3 no tiene equivalente en Mirth, y un canal que se importó limpiamente mientras se entregaba en algún lugar que no sea el bucket sería peor que no tener archivo en absoluto. El rechazo nombra el destino en falta.
Las definiciones de conector no se escriben a partir de una lectura del formato de Mirth. scripts/mirth-dump-connector-templates.sh tiene el serializador propio de Mirth que produce los valores predeterminados para cada clase de conector, y Perfuse sustituye valores en esos documentos: rechazando cualquier configuración que el modelo no contenga ya. Esto importa porque de cómo Mirth responde a un canal que no puede analizar: no rechaza la importación. Almacena un canal cuya descripción ha sido reemplazada con "Este canal es inválido. Verifique que todas las extensiones requeridas estén cargadas correctamente" y cuyos destinos han sido descartados silenciosamente. Un exportador verificado contra su propia salida en lugar de contra un servidor pasa todas las pruebas mientras produce documentos que se descartan, lo cual es exactamente lo que sucedió aquí antes de que las exportaciones se probaran contra un Mirth en ejecución.
Lo que cruza hoy: MLLP y TCP en ambos sentidos, HTTP en ambos sentidos, lectura y escritura de archivos, lectura y escritura de bases de datos, DICOM en ambos sentidos, y destinos SMTP, SOAP y JavaScript. Cada par se verifica mediante la importación a un Mirth 4.5.2 real y comprobando que el servidor no reemplazó silenciosamente el canal.
16 CMS-0057 para pagadores
La regla final de interoperabilidad y autorización previa de CMS (CMS-0057-F) requiere que los pagadores afectados: las organizaciones de Medicare Advantage, los programas de Medicaid y CHIP estatales y sus planes de atención administrada, y los emisores de QHP en los intercambios federales, ejecuten cuatro API de FHIR a partir del 1 de enero de 2027, y publiquen las métricas de autorización previa en su sitio web público cada año a partir de 2026.
Perfuse proporciona las cuatro API en su punto de conexión FHIR y las conversiones que las llenan: reclamaciones adjudicadas como CARIN Blue Button, autorizaciones previas como Da Vinci PDex, y las métricas anuales en el diseño de la plantilla propia de CMS. La vista CMS-0057, bajo Intercambio, dice cuál de las cuatro esta instancia puede responder y qué cada una todavía necesita, y realiza las tres conversiones en el navegador.
Los medicamentos están fuera del alcance en todo momento, ya que lo están en las disposiciones de autorización previa de la regla: no hay reclamaciones de farmacia, no hay formularios, y una fila de métricas que nombra un medicamento se rechaza en lugar de contarse.
16.1 Las cuatro API
| API | Para qué sirve | Guías | Activado por |
|---|---|---|---|
| Acceso del paciente | Las reclamaciones, datos clínicos y autorizaciones previas de un miembro, a una aplicación que elija | CARIN Blue Button 2.2.0, PDex 2.2.0, US Core, SMART App Launch | -fhir y un emisor SMART (-smart-issuer) |
| Acceso del proveedor | Lo mismo para los miembros atribuidos de un proveedor, en bulk | PDex 2.2.0, Da Vinci ATR 2.1.0, Bulk Data | -fhir-payer-apis y -fhir-bulk-export |
| Pago a pago | El registro de un miembro a otro pago, después de emparejarlos | HRex 1.2.0 $member-match, PDex 2.2.0 | -fhir-payer-apis |
| Autorización previa | Lo que una orden necesita, la documentación, luego la solicitud y la decisión | Da Vinci CRD, DTR y PAS 2.2.x (2.2.1, 2.2.0, 2.2.1) y 2.1.0, con traducción X12 278 | -fhir, escribible, y -crd-rules |
La API de acceso de paciente no necesita ninguna operación propia: es la lectura y búsqueda ordinarias del punto final de FHIR, protegidas por SMART, con recursos CARIN y PDex en el almacén. La búsqueda de ExplanationOfBenefit admite los parámetros con nombres CARIN — patient, identifier, type, service-date, billable-period-start, provider, insurer, coverage — y Coverage admite subscriber-id, identifier y payor.
16.1.1 Dos versiones de las guías: 2.1.0 y 2.2.x
CMS-0057-F recomienda CRD, DTR y PAS 2.0.1 y 2.1.0. Las versiones 2.0.1 se basan en US Core 3.1.1, que expiró el 1 de enero de 2026, por lo que la versión 2.1.0 es la versión que probablemente hable un sistema de registros electrónicos de salud construido para la regla; las versiones más recientes son CRD 2.2.1, DTR 2.2.0 y PAS 2.2.1. Perfuse responde en ambas. Determina qué versión habla el llamador, primero la más específica:
- desde la solicitud: la extensión CDS Hooks de CRD
davinci-crd.requestedVersion("2.1"), o la versión en un perfil de Da Vinci que una entrada DTR o PAS reclama (profile-pas-request-bundle|2.1.0); - desde el registro del llamador:
davinci_version: "2.1"en un cliente SMART (-smart-clients) o un cliente CDS (-cds-clients); - desde el servidor:
serve -davinci-version 2.1; el valor predeterminado es 2.2.
clients:
- issuer: https://ehr.example.org
jwks_url: https://ehr.example.org/.well-known/jwks.json
davinci_version: "2.1"
Cada respuesta se decide y mantiene en la forma 2.2.x y se reescribe para un llamador 2.1.0 cuando se envía, por lo que las reglas, la cola de revisión, el enlace X12 278 y las métricas son las mismas para ambas. La reescritura cubre lo que las versiones deletrean de manera diferente: CRD 2.1.0 no tiene indeterminate (Perfuse dice condicional, con la razón) y no tiene categoría en un detalle de cobertura; DTR 2.1.0 nombra el paquete PackageBundle y deja la QuestionnaireResponse a la aplicación; PAS 2.1.0 responde con un solo Bundle en $inquire y carece de las extensiones del revisor y la transmisión. La lista completa y la comprobación con cada ejemplo de la guía 2.1.0 se encuentra en conformance/davinci21. Una notificación PAS sigue -davinci-version, ya que una suscripción no especifica qué versión lee su propietario.
16.2 Requisitos y documentación de cobertura: CRD y DTR
La API de Autorización Previa en la práctica son tres guías de Da Vinci juntas. CRD informa al clínico, mientras que la orden aún es un borrador, si está cubierto, si necesita autorización previa y qué documentación se requiere. DTR entrega al EHR el cuestionario que recopila esa documentación. PAS envía la solicitud y devuelve la decisión. Perfuse sirve como pagador para las tres.
-crd-rules rules.yaml sirve CRD a través de CDS Hooks en /cds-services: los seis hooks de CRD, order-sign, order-select, order-dispatch, appointment-book, encounter-start y encounter-discharge, cada uno con una pre-carga para la Cobertura activa del paciente. Los dos hooks de encuentro también pre-cargan el encuentro y responden sobre el encuentro en sí, coincidiendo con su tipo y tipo de servicio, al igual que appointment-book responde sobre una cita. Las reglas son la política del pagador, mantenidas en un archivo porque cambian sin lanzamientos de software:
payer: Springfield Health Plan
rules:
- description: Home oxygen equipment
codes: [E0424, E0431]
covered: covered # covered, not-covered, conditional
pa: auth-needed # no-auth, auth-needed, satisfied, performpa, conditional
documentation: [clinical]
questionnaire: https://www.springfield-health-plan.example/fhir/Questionnaire/home-oxygen
Cada pedido recibe una actualización de systemActions que lleva la extensión de información de cobertura de CRD, que el EHR almacena en el pedido y más tarde envía con la autorización previa, y una tarjeta que el clínico lee. Un pedido cuyo código no tiene regla se responde como conditional, nunca se asume que esté cubierto; una solicitud sin cobertura en absoluto recibe una tarjeta que lo indica. examples/crd/ tiene un archivo de reglas y un cuestionario coincidente.
Una regla también puede llevar el resto de lo que CRD permite que un pagador diga:
billing_codes: los códigos para facturar bajo.details: límites y costos, comoallowed-periodoin-network-copay.contact: quién llamar.expiry_days: cuánto tiempo mantiene la respuesta.depends_on: códigos de otras órdenes de las que depende la respuesta, para que el EHR pregunte de nuevo si cambian.doc_purpose: para qué es la documentación (withclaim,withorder,retain-doc).withpase rechaza, porque la propia invariante crd-ci-q4 de CRD 2.2.1 la rechaza.
Lo que el EHR no prefetched, Perfuse lee del fhirServer del EHR con el token que envió. Si eso falla, la respuesta es indeterminate con la razón technical de CRD, nunca una suposición. Una Cobertura que ha finalizado o no está activa se responde como not-covered, no-active-coverage.
members: fhir en el archivo de reglas resuelve la cobertura contra los registros propios del pagador en el almacén de FHIR de este servidor (inícielo con -fhir), los mismos registros que $member-match lee:
- un paciente que no es miembro recibe
no-member-found; - un miembro cuya cobertura no coincide recibe
coverage-not-found; - una autorización previa ya aprobada allí para el servicio (un ClaimResponse con un
preAuthRef) hace que la respuesta seasatisfied,
con ese número.
Discovery declara davinci-crd.version y una opción de configuración coverage-info. Cuando un cliente desactiva la opción, la respuesta está vacía. Los JWT de EHR se verifican como los define CDS Hooks, con iss, aud, exp, iat y jti y sin sub, y se rechaza un jti repetido.
Las llamadas se autenticarán con el JWT que un EHR firma para cada llamada, desde un emisor nombrado en -cds-clients con su URL de JWKS - que es cómo CDS Hooks lo describe - o con un token de API de Perfuse. El descubrimiento es abierto, como requiere CDS Hooks.
DTR: cargue los cuestionarios en el punto de conexión de FHIR, y POST /fhir/Questionnaire/$questionnaire-package los devuelve como paquetes de DTR, con las bibliotecas de CQL que nombran. La solicitud puede nombrar el cuestionario, o enviar la orden y dejar que su información de cobertura diga cuál.
Intentándolo: Payers → Claims & auth → Coverage requirements (CRD) envía una orden a las reglas de este servidor, o a la URL del servicio de CDS de un pagador, y muestra las tarjetas y la información de cobertura.
16.3 Reclamaciones como CARIN Blue Button
perfuse cms0057 carinbb empareja cada reclamación en un 837 con su pago en un 835, por el número de cuenta de paciente que el 835 está obligado a devolver, y escribe una transacción de FHIR para cada una: la ExplanationOfBenefit con el Patient, Coverage, Organizations y Practitioners que referencia.
perfuse cms0057 carinbb -system https://fhir.your-plan.org/identifier -network innetwork \
claims.837 remittance.835
El perfil sigue la reclamación: las reclamaciones profesionales (837P) se convierten en C4BB-ExplanationOfBenefit-Professional-NonClinician, y las institucionales (837I) en pacientes internos o externos según el tipo de factura. Cada entrada es un PUT con un id tomado de los identificadores de origen, por lo que cargar la misma reclamación dos veces la actualiza en lugar de crear una segunda copia.
Tres cosas no están en ninguno de los archivos X12, y cada una se maneja diciendo que no están en lugar de adivinar:
- El sistema de identificador. CARIN requiere que el identificador de miembro nombre el espacio de nombres del pagador. Se requiere
-system; no hay un valor predeterminado, porque un valor predeterminado daría a todos los pagadores que utilizan Perfuse el mismo espacio de nombres. - El estado de la red. Ninguna de las transacciones indica si el proveedor de facturación está en la red, y decide lo que paga el miembro. Sin
-network, el estado de pago de beneficio esothery las notas de conversión explican por qué. - Una reclamación que el 835 no menciona. Una ExplanationOfBenefit de CARIN es una reclamación adjudicada. Sin el 835, no hay número de reclamación del pagador, no hay pago y no hay responsabilidad del miembro, por lo que la reclamación se informa como omitida y no se convierte.
Los diagnósticos se escriben con su punto decimal (J02.9, no J029), porque así es como el sistema de código los publica y un servidor de terminología preguntado sobre la otra ortografía dice que no existe. El identificador de miembro de un dependiente es el del suscriptor, que es lo que lleva el 837, y las notas lo dicen. Un NPI cuyo dígito de verificación es incorrecto se informa antes de que US Core lo rechace.
16.4 Autorizaciones previas como PDex
perfuse cms0057 priorauth convierte una respuesta de reclamación Da Vinci PAS — o el paquete de respuesta PAS que la transporta — en una explicación de beneficio de autorización previa PDex, que es lo que sirven las tres APIs de datos.
perfuse cms0057 priorauth pas-response.json pas-claim.json
La reclamación PAS es opcional y casi siempre es necesaria. Una explicación de beneficio debe indicar qué cobertura se tomó la decisión y solo la solicitud lleva eso; sin ella, la conversión se detiene y lo indica. La acción de revisión — aprobada, denegada, pendiente, con su número de autorización y motivo — se mueve de la extensión de PAS a la de PDex, y el tipo de servicio X12 mantiene su código bajo el URI que PDex lo enlaza.
16.5 Las APIs de acceso de proveedor y de pago a pago
Con -fhir-payer-apis el punto de conexión FHIR responde a tres operaciones más:
POST /fhir/Patient/$member-match— coincidencia de miembros HRex. Los identificadores de cobertura se reducen a unos pocos candidatos, y el apellido y la fecha de nacimiento deben coincidir; un nombre dado se compara por inicial, y el género solo cuando ambos lados lo indican. Más de un candidato sobreviviente no es una coincidencia, y la respuesta es 422 en lugar de una suposición, porque una suposición revela las reclamaciones de un desconocido. Se requiere un consentimiento activo que permita la divulgación, ya que la API de pago a pago es opt-in para el miembro;-fhir-member-match-without-consentlo desactiva para pruebas.GEToPOST /fhir/Group/{id}/$davinci-data-export— la exportación masiva de Da Vinci de los miembros actuales de un Grupo.exportTypeelige la audiencia:hl7.fhir.us.davinci-pdex#provider-downloado#provider-deltapara el acceso de proveedor,#payertopayerpara el pago a pago.GET /fhir/Group/{id}/$export— la exportación de datos masivos estándar del Grupo, tratada como acceso de proveedor.
Las exportaciones aplican las exclusiones de la regla en el camino. Las reclamaciones pierden el pago y todos los montos de costos compartidos y reembolso, y mantienen los servicios, códigos, fechas y proveedores; el perfil CARIN se elimina de meta.profile porque el recurso ya no cumple con él. El pago a pago también deja fuera las autorizaciones previas denegadas. Los miembros que tienen un consentimiento activo de opt-out de acceso de proveedor PDex se dejan fuera de una exportación de proveedor, y el registro recoge cuántos. El parámetro patient solo puede reducirse a miembros del Grupo; nombrar a cualquier otra persona se rechaza.
El acceso a la exportación de un Grupo se decide por la autenticación del punto de conexión. Los ámbitos de SMART dicen qué tipos de recurso puede leer un token, no qué Grupo, por lo que cada proveedor necesita un token que pueda llegar solo a su propia lista de atribución. Un token de API se puede limitar a Grupos cuando se crea — perfuse token create -label riverside-provider-access -fhir-groups riverside-attributed, o el campo Limitar a Grupos de FHIR en Users → Machine credentials — y en el punto de conexión de FHIR puede leer esos Grupos, ejecutar $davinci-data-export o $export en ellos y recuperar lo que produjo la exportación, y nada más. Un Grupo de otro proveedor responde no encontrado en lugar de prohibido, por lo que un token no aprende qué otras listas de atribución existen; cualquier otra solicitud, incluida la coincidencia de miembros y la búsqueda ordinaria, se rechaza. El límite se aplica a los tokens de API; un token de un emisor de SMART no lleva un límite de Grupo. Cuando SMART está configurado también, los tokens de API se rechazan a menos que Settings → FHIR → Accept Perfuse API tokens beside SMART tokens esté activado (ver Seguridad, *Quién puede llamar al punto de conexión de FHIR*); un token limitado a Grupo de un proveedor necesita esto.
16.6 Un directorio de proveedores: Plan-Net
CMS-0057 también solicita a los pagadores un directorio de proveedores, y el punto de conexión FHIR sirve uno a Da Vinci Plan-Net 1.1.0 sin opción para activarlo: carga a los Practitioners, PractitionerRoles, Organizations, OrganizationAffiliations, Locations, HealthcareServices, InsurancePlans y Endpoints, y las aplicaciones de los miembros pueden buscarlos. Cada parámetro de búsqueda de Plan-Net está indexado - un rol de red y servicio, un área de cobertura de un plan, una organización participante de una afiliación, una organización partof - junto con las cadenas de inclusión y reversas (_has) que las consultas de la guía utilizan, y la CapabilityStatement enumera los perfiles de Plan-Net.
La suite Plan-Net de Inferno pasa 199 pruebas. Las 10 que falla son cadenas hacia adelante donde el kit toma su valor de búsqueda del primer Organization que ha visto en lugar de uno al que los recursos buscados se refieren; eso se informa al kit (su problema 34), y el servidor responde correctamente esas consultas cuando se le da un valor que los datos contienen.
16.7 Métricas de autorización previa
perfuse cms0057 metrics lee un CSV de decisiones exportadas del sistema de gestión de utilización y produce la página pública que CMS requiere, en el diseño de su *Informe y plantilla de métricas de autorización previa*.
perfuse cms0057 metrics -year 2025 -org "Springfield Health Plan" -services services.csv \
decisions.csv > prior-authorization-metrics-2025.html
Las columnas se encuentran por encabezado: priority, received, decided y decision son obligatorias, y request_id, line_of_business, extended, appealed, appeal_decision, service_code y service_description se leen cuando están presentes. Una solicitud pertenece al año en que se decidió; una que aún está pendiente se enumera como excluida, nunca contada. Cada fila que no se puede leer se informa con su número de línea.
La aritmética sigue la plantilla: para solicitudes estándar y expeditas por separado, la participación aprobada y denegada, aprobada y denegada dentro del plazo de decisión, aprobada después de que se extendió el tiempo de revisión, y aprobada después de apelación — de apelaciones, no de solicitudes — con el tiempo medio y mediano desde la recepción hasta la decisión. Una mediana inferior a un día se escribe en horas en lugar de redondearse a "0 días", que la plantilla solicita explícitamente. Las solicitudes aprobadas parcialmente se cuentan como aprobadas, ya que la plantilla no tiene categoría para ellas, y la página indica cuántas hubo. Las métricas se informan por línea de negocio, que es el nivel que CMS requiere.
La página es autocontenida, no tiene script, ni hoja de estilo externa ni fuente, porque se publica en un sitio web público por quien lo mantiene, y cualquier cosa que cargue desde otro lugar es algo más que puede fallar o rastrear a un visitante. -format csv y -format json dan los mismos números para otros usos. Sin -services, la página lleva un aviso de que la lista de artículos y servicios que requieren autorización previa está falta, porque CMS lo requiere en la misma página.
16.8 Medicamentos: el formulario, el Beneficio de Receta en Tiempo Real y la autorización previa electrónica
CMS-0062-P (propuesto el 14 de abril de 2026) introduciría medicamentos en la autorización previa: los pagadores afectados admitirían la autorización previa electrónica de NCPDP SCRIPT, el estándar de formularios y beneficios de NCPDP y el beneficio de prescripción en tiempo real de NCPDP a partir del 1 de octubre de 2027, decidirían las solicitudes de medicamentos dentro de las 24 horas (expedidas) y 72 horas (estándar), darían una razón específica para cada denegación, y informarían métricas de medicamentos a partir de 2028. Medicare Part D ya requiere SCRIPT ePA. Un archivo, el formulario, responde a todo esto:
perfuse serve -fhir -pas -crd-rules payer-rules.yaml -formulary formulary.yaml
examples/formulary/formulary.yaml es un ejemplo cuyos medicamentos son conceptos reales de RxNorm con NDCs de RxNorm para ellos (comprobados contra RxNav). Un archivo nombra al pagador, sus planes (cada uno con la formularios que utiliza, sus identificadores de grupo y sus participaciones en costos por beneficio de farmacia y nivel) y sus formularios. Cada medicamento es un SCD o SBD de RxNorm con su grupo de RxNorm (el SCDG o SBDG que requiere la API de Formulario de Medicamentos), sus NDCs, un nivel, y sus límites: prior_authorization, step_therapy, quantity_limit (una cantidad en un número determinado de días, y un máximo diario), not_covered, alternatives (códigos de RxNorm, con el más preferido primero), notes, y questionnaire, la URL canónica del cuestionario FHIR que solicita una autorización previa. cost_30_day es el costo del plan para un suministro de 30 días, que es un porcentaje de la coparticipación; sin él, la coparticipación se cita como un porcentaje, nunca como una cantidad inventada. Todo el archivo se comprueba cuando se carga, y se lista cada problema de una vez. Un miembro se encuentra por la lista members del archivo, por grupo, o a través de la tienda FHIR: una Coverage cuyo subscriberId es el ID del miembro y cuya class de tipo plan es el ID del plan. Payers › Drugs muestra el formulario, precios de un medicamento y ejecuta una autorización previa de principio a fin.
16.8.1 La API de formulario de medicamentos Da Vinci US
Publicado en /formulary/fhir como Formulario de Medicamentos de Da Vinci US 2.1.0: cada plan es un PayerInsurancePlan, cada formulario es un Formulary (ambos InsurancePlan), cada medicamento es un FormularyDrug (MedicationKnowledge) y cada entrada es un FormularyItem (Basic). Las búsquedas de la guía se sirven (Basic?code=formulary-item&formulary=...&drug-tier=...&pharmacy-benefit-type=...&period=..., subject:MedicationKnowledge.code, InsurancePlan?formulary-coverage=..., coverage-type, MedicationKnowledge?code=...&doseform=...) con _include=Basic:subject, Basic:formulary y InsurancePlan:formulary-coverage. Es público, sin credenciales, como requiere CMS de un formuario de un pagador: se sirve desde una tienda propia, en memoria, que contiene el formulario y nada más, por lo que estar abierto no puede llegar a un registro de paciente. Es de solo lectura. Un medicamento excluido no se publica, ya que la API enumera lo que cubre un formulario; RTPB y ePA aún responden como no cubierto. Verificado con el validador HL7 FHIR contra el paquete 2.1.0 de la guía con terminología comprobada: cada recurso generado a partir del ejemplo no tiene errores.
16.8.2 Beneficio de Receta en Tiempo Real
POST /ncpdp/rtpb toma un RTPBRequest (NCPDP RTPB v13) con un token de API del rol de operador y responde con un RTPBResponse del formulario: el estado de cobertura, el estado del formulario, la participación estimada del miembro y la del plan, y las restricciones de cobertura, con hasta cinco alternativas cubiertas con el mismo precio. Los códigos de restricción y el estado al que cada uno se asigna siguen las Recomendaciones de implementación de RTPB de NCPDP: 75 autorización previa requerida, 608 terapia de paso, 76 límites del plan excedidos, 7X días de suministro superados al límite del plan (un medicamento no disponible por correo, por ejemplo), 70 no cubierto, todos *cubiertos con restricciones* (CR) excepto 70 (NC). Un miembro con una autorización previa aprobada en archivo obtiene CC y AB8, como solicita la guía de Surescripts, para que el prescriptor no sea enviado a solicitar nuevamente. Una solicitud que no se puede preciar es NotProcessed con un código de rechazo de NCPDP: 52 para un miembro no encontrado, E7 y 19 para una cantidad o días de suministro faltantes. El beneficio de farmacia se lee desde la solicitud: un llenado superior a 34 días es un suministro de tres meses, y el tipo de farmacia M es por correo.
16.8.3 Autorización previa electrónica (NCPDP SCRIPT)
POST /ncpdp/script toma PAInitiationRequest, PARequest, PAAppealRequest y PACancelRequest con un token de API del rol de operador, y responde a cada uno con su respuesta:
| Solicitud | Respuesta |
|---|---|
PAInitiationRequest | Closed cuando no se necesita: CC no se requiere autorización previa, FN dentro del límite de cantidad, FM no cubierto o no en el formulario, FQ una aprobación en archivo, CD no hay miembro, CG uno ya en proceso. De lo contrario, Open, con un ID de caso y el conjunto de preguntas. |
PARequest | Decidido como una solicitud de PAS: Approved con el número de autorización y período, Denied con la razón, o Open bajo revisión. |
PAAppealRequest | Un caso denegado decidido nuevamente, en la razón en PANote (un recurso sin ella se solicita). |
PACancelRequest | Approved mientras el caso está abierto o bajo revisión, y la solicitud de PAS se cancela; Denied una vez que se decide. |
El conjunto de preguntas es el cuestionario FHIR del medicamento de la tienda FHIR, el mismo que DTR completa: un booleano se convierte en una elección de sí/no, una elección en una elección, un número en numérico, una fecha en fecha, y cualquier otra cosa en texto libre; los grupos se aplanan en orden, y enableWhen no se transporta, ya que SCRIPT ramifica por elección y no por condición. Un medicamento sin cuestionario se le hacen tres preguntas de texto libre: el diagnóstico, qué se intentó, y por qué este medicamento. Un PARequest se convierte en una solicitud de PAS de Da Vinci: un Claim para el medicamento (RxNorm y NDC), la MedicationRequest que solicita, el paciente, el prescriptor, el pagador y la cobertura, y las respuestas como un QuestionnaireResponse, con cualquier adjunto como DocumentReference. Luego se decide como se decide cada autorización previa en este servidor: el archivo de reglas, un servicio de decisión (-pas-decider-url), o la cola de revisores, donde se marca Medicamento y se mantiene en los plazos del medicamento. Un servicio de decisión ve una solicitud de medicamento exactamente como ve una solicitud de un cliente de PAS de un sistema de registros electrónicos de salud. Recomendamos Vaglio, el motor de decisión de código abierto (PolyForm Shield) de BioDream, que tiene políticas de medicamentos con terapia de paso; el motor de un pagador se conecta a través de la misma -pas-decider-url, y no se necesita ninguno para que el archivo de reglas y los revisores decidan.
Una decisión tomada después de que se respondió la solicitud (una del revisor, una apelación) se envía al prescriptor como una respuesta no solicitada PAResponse o PAAppealResponse, enrutada al canal nombrado por -epa-reply-channel, que la entrega a la red. Sin uno, o cuando la entrega falla, el caso dice que el prescriptor no ha sido informado y la pantalla la envía nuevamente.
Payers › Drugs › Prior authorization envía los mensajes que el sistema de un prescriptor enviaría a este servidor (iniciar, responder las preguntas, ver la decisión), enumera cada caso con lo que se le informó al prescriptor, y muestra las cifras del medicamento que propone CMS-0062-P: solicitudes aprobadas, denegadas y pendientes, apeladas y aprobadas o denegadas en apelación, la media y mediana de horas desde la solicitud hasta la decisión, y cuántas se decidieron tarde. Los medicamentos de beneficio médico solicitados a través de PAS (un código HCPCS J con una MedicationRequest, o un artículo codificado en RxNorm o NDC) se cuentan como medicamentos en las cifras de PAS también.
Lo que esto se basa en, expuesto claramente. Los esquemas y guías de implementación de NCPDP están licenciados. Los nombres de los elementos y el orden que se siguen aquí son los de las guías de acompañamiento públicas de Surescripts ePA y RTPB, y sus ejemplos, y las recomendaciones públicas de RTPB de NCPDP. Dos elementos que el material público nunca muestra están escritos en el patrón SCRIPT utilizado en otros lugares y deben verificarse contra una copia licenciada antes de la certificación: Closed/ReasonCode y Denied/DenialReason. Ningún mensaje ha sido verificado contra el XSD de NCPDP o la certificación de una red. El archivo de Formulario y Beneficio de NCPDP no se produce: su diseño completo está en la guía licenciada de NCPDP, y un archivo parcial sería rechazado por la red que lo carga.
16.9 Qué se verificó y contra qué
Las salidas de CARIN y PDex se validaron con el validador oficial de HL7 FHIR, cargado con los paquetes publicados (hl7.fhir.us.carin-bb#2.2.0, hl7.fhir.us.davinci-pdex#2.2.0): reclamaciones profesionales, de pacientes internados y externados y dos decisiones de PAS, sin errores. Las entradas de PDex fueron los propios ejemplos de la guía de PAS en lugar de las formas escritas aquí para estar de acuerdo con el convertidor. El validador encontró cinco defectos en la primera ejecución — entre ellos que CARIN requiere la versión del perfil en meta.profile y la versión del sistema de codificación de tipo de reclamación en reclamaciones institucionales — y cada uno está corregido y cubierto por una prueba.
La construcción de esto encontró un defecto más antiguo que él: el almacen de FHIR eliminaba cada elemento que su modelo interno no declaraba, por lo que una ExplanationOfBenefit perdía sus elementos, diagnósticos, adjudicación y pago en el camino. El almacen ahora mantiene lo que lleva cada recurso, y una prueba carga una reclamación de CARIN a través del servidor y la lee completa nuevamente.
La salida de CRD - el pedido actualizado y la respuesta de CDS Hooks contra el modelo lógico propio de CRD - y la salida del paquete de DTR se validan en el validador oficial de HL7 contra CRD 2.2.1 y DTR 2.2.0 sin errores.
DTR aquí es el lado del pagador de 2.2.0. $questionnaire-package toma los cuestionarios nombrados, aquellos que CRD nombró en un pedido, o la identificación de la afirmación de cobertura que CRD devolvió (context, mientras el mismo proceso está sirviendo a CRD), y devuelve cada uno como un paquete de bundle con una QuestionnaireResponse para rellenar, las Bibliotecas que su CQL necesita y sus dependencias. Los conjuntos de valores que sus respuestas utilizan viajan en el paquete cuando se cargan (PUT /fhir/ValueSet/{id}), y $expand enumera sus códigos. $log-questionnaire-errors escribe lo que una aplicación DTR informa al registro del servidor.
Cuestionarios adaptativos: escriba todo el cuestionario una vez, con la extensión questionnaireAdaptive de SDC y enableWhen en las preguntas que solo se aplican a veces. El paquete le da a la aplicación un shell vacío, y cada llamada a $next-question agrega la siguiente pregunta que se aplica a las respuestas hasta ahora, hasta que no hay nada más que preguntar y la respuesta se marca como completada. El orden y la ramificación son las reglas propias del pagador; las preguntas que no se aplican nunca se muestran.
Iniciar sesión como cliente: los clientes DTR y PAS llegan al pagador con los Servicios de backend SMART - un JWT firmado intercambiado por un token en el servidor de autorización del pagador. Apunte Perfuse a ese servidor con -smart-issuer (y -smart-jwks si su dirección no se resuelve desde Perfuse), y pase -smart-backend-services para que el documento de descubrimiento diga que sí. La suite DTR de Inferno pasa por este camino con Keycloak como servidor de autorización.
Perfuse puede ser ese servidor de autorización en sí mismo: serve -fhir -smart-clients clients.yaml emite tokens de Servicios de backend en /auth/token a los clientes enumerados, cada uno con las claves públicas que firma sus afirmaciones y los ámbitos del sistema que puede ser concedido, y el punto de conexión de FHIR los acepta. No se necesita un servidor de identidad separado para los clientes de máquina a máquina de un pagador.
Con -smart-users users.yaml también, los miembros y los clínicos inician sesión allí para autorizar aplicaciones, lo que necesita la API de Acceso de Pacientes: la aplicación envía a la persona a /auth/authorize, inician sesión, un clínico elige al paciente y ambos ven lo que la aplicación solicita y pueden desmarcar cualquier parte de ello. Un miembro solo recibe su propio registro, independientemente de lo que la aplicación solicite. Cada persona en el archivo nombra el recurso de FHIR que es, Patient/… o Practitioner/…, y un hash de contraseña de perfuse smart hash; la contraseña en sí nunca se escribe.
Las personas pueden iniciar sesión en el proveedor de identidad de la organización en su lugar: -smart-oidc oidc.yaml lo nombra (emisor, id de cliente y dónde está el secreto), y la página de inicio de sesión ofrece "Iniciar sesión con" él junto a la contraseña. Cada persona está vinculada por el sujeto del proveedor, oidc_subject en el archivo de usuarios, para que el archivo aún decida quién puede autorizar aplicaciones y qué recurso de FHIR es; una cuenta vinculada a nadie es rechazada. Los códigos, los tokens de actualización y las revocaciones se mantienen en la base de datos, para que un reinicio no desconecta a nadie.
Usuarios → Aplicaciones SMART en la consola enumera las aplicaciones registradas y las personas, y las agrega, edita y elimina. Cada guardar es verificado como los archivos en el inicio, escritos en ellos y utilizados de inmediato. Una aplicación confidencial guardada sin secreto se le asigna uno, muestra una vez; una contraseña se establece allí y nunca se muestra nuevamente. Eliminar una aplicación, o una persona, detiene los tokens de acceso ya emitidos para ella o concedidos por ellos de inmediato, no cuando expiren, y sus tokens de actualización con ellos.
Los ámbitos pueden ser granulares, como define SMART App Launch 2 y US Core: patient/Observation.rs?category=laboratory solo lee las Observaciones que encontraría esa búsqueda. Una concesión que se filtra para un tipo aplica su filtro a cada búsqueda y cada lectura de ese tipo; una concesión no filtrada para el tipo, o para *, levanta el filtro. Un filtro que no se puede leer no concede nada en lugar de todo.
16.10 Decidiendo solicitudes de autorización previa: PAS
serve -fhir -pas -crd-rules rules.yaml responde a Da Vinci PAS 2.2.1 en POST /fhir/Claim/$submit y POST /fhir/Claim/$inquire. Las decisiones provienen del mismo archivo de reglas que las respuestas de CRD, por lo que el orden en que se le informa a un clínico que necesita autorización se decide mediante la regla que así lo indicó:
- description: Physical therapy
codes: ["PT"] # matched on the item's productOrService, then the code of the order it requests
system: https://codesystem.x12.org/005010/1365
covered: covered
pa: auth-needed
pa_decision: approve # approve, deny or pend; empty: not-covered is denied, no-auth approved, the rest pended
details:
- {code: allowed-quantity, value: "6"} # asked for more, it is certified for six (A6, with the authorised detail)
- description: MRI
codes: ["62"]
pa_decision: approve
pa_alternative: {system: "https://codesystem.x12.org/005010/1365", code: "4"} # approved as an X-ray instead (an added item)
- description: Surgery
codes: ["2"]
pa: auth-needed # pended for a reviewer, asking for:
questionnaire: https://payer.example.org/Questionnaire/surgery # a DTR questionnaire
pa_attachments: ["18776-5"] # and documents, by LOINC attachment code
Cada elemento recibe una acción de revisión X12: A1 certificado, A3 no certificado, A4 pendiente, A6 modificado, C cancelado (tipo de certificación 3). Una línea que no nombra ningún servicio no se decide pero se responde con un error (X12 901 AG, correcto y vuelva a presentar). La respuesta repite lo que una respuesta 278 repite — números de seguimiento de elementos, las fechas de servicio solicitadas, las fechas del encuentro, los identificadores de transmisión con el remitente y el receptor intercambiados — y da a cada línea el número de referencia de administración del pagador. Un servicio que ninguna regla nombra se pende, nunca se aprueba: una decisión que nadie escribió es una decisión para una persona.
Solicitudes pendientes esperan a un revisor, que responde con POST /fhir/Claim/$decide:
{"resourceType": "Parameters", "parameter": [
{"name": "claimResponse", "valueString": "<the ClaimResponse id>"},
{"name": "decision", "valueCode": "approve"},
{"name": "reason", "valueString": "Reviewed with the plan of care."},
{"name": "reviewer", "valueString": "<the reviewer's NPI>"}]}
decision también puede ser modify: certificado, pero no como se solicitó, con quantity (valueDecimal, las unidades certificadas) y/o alternative (valueCoding, el servicio aprobado en su lugar). La respuesta entonces dice A6 Modificado, con el detalle certificado o el alternativo como un elemento agregado, y un número de autorización. item (valueInteger, repetible) limita la decisión a algunas líneas. La decisión se convierte en una nueva versión de la respuesta, marcada como revisada por una persona, y con -fhir-subscriptions se entrega en el tema PAS (PASSubscriptionTopic, filtrado por org-identifier, el identificador de la organización solicitante) como el paquete de respuesta PAS completo, en la misma transacción que lo registra. $inquire devuelve la respuesta actual a cada solicitud que el proveedor realizó sobre ese miembro; una consulta de otro proveedor no encuentra nada. $decide necesita un alcance de escritura; $inquire es de lectura.
Las solicitudes y cada versión de sus respuestas se mantienen en pas_requests y pas_responses en la base de datos del servidor.
16.10.1 La cola del revisor
En la consola web, Payers → CMS-0057 → Cola de revisor lista las solicitudes pendientes próximas a vencerse primero: una solicitud urgente (Claim.priority stat) vence 72 horas después de su llegada, una estándar en 7 días. Seleccionar una muestra al afiliado, al proveedor, cada servicio con las unidades solicitadas y su estado, lo que se solicitó en la respuesta y cada documento recibido para ello, con documentos de texto plano mostrados en línea. Un editor registra la decisión allí: aprobar, denegar o modificar (unidades certificadas, o otro servicio aprobado en su lugar), con un motivo y el NPI del revisor. Una modificación debe certificar menos unidades de las solicitadas; tantas o más es una aprobación y se rechaza como modificación. Un servicio aprobado en su lugar comienza en el sistema de códigos del solicitado. Es la misma decisión que toma $decide, por lo que la suscripción del proveedor la entrega. Leer una solicitud y decidir sobre ella se escriben ambos en el registro de auditoría.
Monitor → Team dashboards → Prior authorization muestra, para los últimos 30 días, cuántas solicitudes estándar y urgentes se decidieron dentro de los plazos de CMS-0057, se decidieron con retraso, todavía están esperando a tiempo y vencidas ahora, con el tiempo medio para decidir, y cómo se respondieron los servicios solicitados: aprobados, modificados, denegados o todavía pendientes. Estos dos mosaicos se leen de las solicitudes en sí, por lo que no están en la exportación de Grafana del panel, que dice que no en su descripción.
16.10.2 Recibir los documentos que solicita una solicitud pendiente
Una respuesta pendiente solicita documentos con una Tarea y una Solicitud de Comunicación por documento. Un cuestionario se solicita como PAS lo describe: una Solicitud de Comunicación con el único código LOINC 102089-0, bajo el número de seguimiento de la línea que es para (itemTraceNumber, que el ClaimResponse eco; una línea enviada sin uno se le da uno). Ese número de seguimiento es el contexto de DTR: $questionnaire-package con solo context establecido en él devuelve el cuestionario. El proveedor envía lo que se solicitó con Da Vinci CDex $submit-attachment (POST /fhir/$submit-attachment): el TrackingId es el número de seguimiento de la solicitud (el primer identificador del ClaimResponse, que también es el de la Tarea), o un identificador de Solicitud de Comunicación, AttachTo es preauthorization, y MemberId debe ser el miembro de la solicitud. Cada DocumentReference o QuestionnaireResponse de la adjunta se mantiene con la solicitud y se muestra a su revisor; un documento que llega decide nada.
16.10.3 Decidir en un sistema de gestión de utilización X12
La mayoría de los pagadores deciden las autorizaciones previas en un sistema de gestión de utilización que habla X12 278. Con -pas-278-url, Perfuse es la interfaz de FHIR de ese sistema: cada $submit se envía como una solicitud 278 (005010X217), y cada elemento es un evento de paciente. Cada uno lleva lo que PAS nombra para ello: el tipo de revisión (extension-serviceItemRequestType, UM01: servicios de salud, admisión o atención especializada), el tipo de certificación (extension-certificationType, UM02), el tipo de servicio, el lugar de servicio (UM04), el nivel de servicio (extension-levelOfServiceCode, UM06: electivo, urgente o de emergencia; una prioridad stat se utiliza en lugar de urgente), la fecha, las fechas de admisión y alta de una revisión de admisión, los diagnósticos, el proveedor del equipo de atención que realizará el servicio y el procedimiento y la cantidad, como un servicio profesional (SV1) o, en una reclamación institucional, una línea de servicio de hospital (SV2). El código de acción y el número de autorización de la respuesta 278 se convierten en la acción de revisión del elemento: certificado, en parte o modificado, denegado, pendiente, cancelado o no considerado (pendiente para un revisor, con los motivos de rechazo). Las reglas locales no se consultan.
perfuse serve -fhir -pas -pas-278-url https://um.example/x12/278 -pas-278-receiver ACMEUM \
-pas-278-token-env UM_TOKEN
Un sistema de UM que pende primero y decide después publica su respuesta posterior 278 en POST /fhir/Claim/$decide-278; cada evento se asocia con su solicitud mediante el número de seguimiento que Perfuse envió, y se decide como $decide lo decidiría. Si no se puede alcanzar el sistema de UM o responde con algo que no es una respuesta 278, cada elemento se pone en espera para un revisor y al proveedor solo se le informa de eso; la razón se encuentra en el registro del servidor.
Consultas que el sistema UM responde
La $inquire de un proveedor debe encontrar una autorización sin importar cómo se solicitó: por fax, a través del portal del pagador, por teléfono o en un 278 que otro remitente envió. Perfuse solo mantiene las solicitudes que llegaron a través de él; el sistema de gestión de utilización (UM) mantiene todas. Así, con un sistema UM que responde en tiempo real (cada transporte excepto sftp), cada $inquire también se envía allí como una consulta de autorización 278 (005010X215: BHT02 28, el miembro, el proveedor, el número de autorización del insurance.preAuthRef del reclamo cuando existe, y las fechas de eventos desde billablePeriod o las fechas de servicio de los artículos, de lo contrario un año a cada lado de la fecha actual). Cada autorización que la respuesta contiene y que Perfuse no tiene (coincidiendo con los números de seguimiento que envió y los números de autorización que ya dio) regresa como un paquete de respuesta de consulta de reclamo adicional: la decisión del sistema UM para cada línea de servicio, el número y período de autorización, y una nota que indica dónde se encontró. No se crea un reclamo para una autorización que nadie presentó en FHIR: su ClaimResponse.request lleva una razón de ausencia de datos, como permite el perfil de PAS. Una consulta solo lee; nada en los propios registros de Perfuse cambia. Si el sistema UM está caído o responde con un TA1 o 999, el proveedor recibe lo que Perfuse mantiene, y el estado del enlace indica por qué. La forma sigue las guías de acompañamiento de la consulta 278 que publican UnitedHealthcare, Cigna y Blue Cross Blue Shield de Massachusetts; no se ha probado contra un sistema UM real.
Cómo los 278s llegan al sistema UM
La guía de conectividad del sistema UM dice cómo toma los 278, y -pas-278-transport (Configuración, Pagadores, Enlace de gestión de utilización) lo sigue:
| Transporte | Qué se envía | Autenticación |
|---|---|---|
x12 (predeterminado) | El 278 se envía como application/edi-x12; la respuesta es el cuerpo de la respuesta | Un token de portador, OAuth, autenticación básica de HTTP, un certificado de cliente |
core-soap | El sobre de SOAP 1.2 en tiempo real de CAQH CORE (COREEnvelopeRealTimeRequest, tipo de carga X12_278_Request_005010X217E1_2, o X12_278_Request_005010X215 para una consulta): Regla de conectividad vC2.2.0 con la carga en el sobre, que la mayoría de los pagadores y CMS esMD ejecutan, o -pas-278-core-version C4.0.0 con la carga como un adjunto MTOM | vC2.2.0: un nombre de usuario y una contraseña (Token de seguridad de nombre de usuario de WS) o un certificado de cliente. vC4.0.0: un certificado de cliente y un token de OAuth |
core-mime | El sobre de MIME HTTP de CAQH CORE vC2.2.0, la carga como una parte de archivo | El nombre de usuario y la contraseña del sobre, o un certificado de cliente |
sftp | Cada 278 se coloca en una carpeta en el servidor SFTP del sistema UM (-pas-278-sftp, -pas-278-sftp-outbound); sus respuestas se recopilan de otra (-pas-278-sftp-inbound, cada -pas-278-sftp-every) | Una clave SSH o una contraseña; la clave del servidor debe estar en -pas-278-sftp-known-hosts |
Un certificado de cliente es -pas-278-cert y -pas-278-key; -pas-278-ca confía en la autoridad de certificación del pagador. OAuth es -pas-278-oauth-token-url, -pas-278-oauth-client-id y el secreto, obtenido con la concesión de credenciales de cliente y mantenido hasta un minuto antes de que expire. Las contraseñas, tokens y secretos nunca son indicadores: cada uno se nombra mediante una variable de entorno (-pas-278-password-env, -pas-278-token-env, -pas-278-oauth-secret-env, -pas-278-sftp-password-env), o se escribe en la pantalla de configuración, donde se mantiene en el archivo de configuración (legible solo por la cuenta del servidor) y nunca se muestra nuevamente.
UM_PASSWORD=... perfuse serve -fhir -pas -pas-278-transport core-soap \
-pas-278-url https://um.example/CORE/RealTime -pas-278-receiver ACMEUM \
-pas-278-user perfuse -pas-278-password-env UM_PASSWORD
Sobre SFTP, cada solicitud se mantiene pendiente hasta que llega su respuesta: la respuesta 278 decide los artículos, y Perfuse coloca un 999 que lo acepta en la carpeta de salida. Un archivo que no se puede leer permanece en la carpeta de entrada para que lo revise una persona y se ofrece nuevamente en la próxima lectura.
Cuando el sistema UM no puede leer una solicitud
Un sistema UM que no puede leer un 278 responde con un TA1 (el sobre) o un 999 (la transacción) en lugar de una decisión. Perfuse lee cuál y qué dice, mantiene pendiente cada artículo ("la gestión de la utilización no pudo leer la solicitud"), y lo registra. La cola del pagador, CMS-0057, Reviewer muestra el enlace: qué se envió en los últimos siete días, cada intercambio por su número de control, y qué sucedió con él (respondido; reconocido, respuesta a venir; rechazado, con el segmento, elemento y código del 999; no alcanzado). Cada intercambio tiene su propio número de control, uno más que el último, que es lo que un TA1 o 999 nombra cuando responde.
16.10.4 Decidir sobre la evidencia clínica, con un servicio de decisión
Un archivo de reglas decide por código de servicio: un código no necesita autorización, no está cubierto o va a un revisor. Si este paciente cumple con los criterios para él es una pregunta diferente, respondida desde las condiciones, respuestas de cuestionarios y documentos que el proveedor envió con la solicitud. Con -pas-decider-url, cada elemento que las reglas dejan a un revisor se envía primero a un servicio de decisión, como Vaglio, que lee el mismo paquete de solicitud PAS a través del paquete pas publicado de Perfuse. Un Vaglio público, en solicitudes inventadas solo, se ejecuta junto a la instancia de prueba de Perfuse en https://vaglio.perfuse.health:8443 (inicie sesión como demo, contraseña try-vaglio-demo; puede ver todo y no cambiar nada): cada decisión allí tiene su prueba, una carta en lenguaje plano y un recibo que cualquier persona puede verificar.
perfuse serve -fhir -pas -crd-rules payer-rules.yaml -pas-decider-url https://vaglio.example/api/pas/decide \
-pas-decider-token-env VAGLIO_TOKEN
La URL debe ser https, o http plano a esta máquina (localhost, 127.0.0.1), porque lleva el registro del paciente y el token. Vaglio mismo responde solo en https; cuando utiliza un certificado de su propia autoridad, pase esa autoridad con -pas-decider-ca DATA/tls/ca.crt (o cualquier autoridad interna del pagador del que proviene el certificado del servicio). El servicio recibe el paquete de solicitud como lo envió el proveedor, POST <url>?item=<sequence> (application/fhir+json), y responde {"decision": "approve" | "deny" | "pend", "why": "...", "requested": ["..."]}, o 404 cuando no decide nada para ese elemento, lo que deja la respuesta de las reglas en pie. why es la nota de proceso de la respuesta y es obligatoria para una denegación, que de lo contrario se pendiente; requested se dice al proveedor como la información que una solicitud pendiente necesita. Un pendiente también puede llevar "questionnaire": "<canonical>" y "attachments": ["<LOINC>", ...], que se solicitan en la tarea CDex y las solicitudes de comunicación de la respuesta junto con cualquier cuestionario DTR o adjuntos que la regla solicite (el cuestionario de la regla gana, ya que una tarea solicita uno; un código que no tiene la forma de LOINC se descarta y se registra). Una regla que aprueba o deniega es la política de cobertura del pagador y no se envía al servicio. Un fallo, un tiempo de espera o una respuesta que no es una de las tres decisiones pendiente el elemento para un revisor, con la razón en el registro del servidor: nada aquí convierte un fallo en una aprobación. No se puede combinar con -pas-278-url, donde el sistema UM responde cada elemento.
Consejo antes de enviar. Con -crd-advisor-url junto a -crd-rules, CRD también pregunta a un servicio de decisión qué le diría a una solicitud de autorización previa para cada orden que la necesite, según el estado actual del registro, y muestra la respuesta como una tarjeta más: "Antes de enviar: una solicitud para esto necesitaría: ...", o "sería aprobada", con lo que aún se necesita, el cuestionario que lo solicita y qué lo aprobaría. Vaglio responde en /api/pas/advise, con el mismo token y autoridad que antes:
perfuse serve -fhir -pas -crd-rules payer-rules.yaml \
-crd-advisor-url https://vaglio.example/api/pas/advise -crd-advisor-token-env VAGLIO_TOKEN
CRD envía POST <url> con {"service": {"system": "...", "code": "..."}, "resources": [ ... ]}, los recursos siendo el prefetch del gancho (el paciente primero, la cobertura omitida), y lee {"decides": true, "answer": "approve" | "pend" | "deny", "summary": "...", "missing": [...], "questionnaire": "...", "toApprove": [[...]], "policyTitle": "..."}. Es un consejo y no cambia nada: la información de cobertura, las otras tarjetas y la decisión de PAS son lo que dicen las reglas. Espera como máximo tres segundos, y no hay respuesta, un error o "decides": false deja la respuesta exactamente como está sin ella. Perfuse nunca necesita un servicio de decisión.
La solicitud lleva X-PAS-Request-Id, el id por el que se conoce la respuesta. Un servicio con sus propios revisores que pendiente un elemento puede enviar su decisión más tarde como la de cualquier revisor: Claim/$decide con ese id como claimResponse, la secuencia del elemento y la decisión y razón. La suscripción del proveedor lo entrega como si se hubiera hecho en la cola del revisor.
Una suscripción con contenido id-only informa al proveedor qué respuesta cambió sin decir qué es: el enfoque de la notificación es Bundle/<id>. GET /fhir/Bundle/<id> devuelve el paquete de respuesta PAS más reciente, GET /fhir/Bundle/<id>/_history/<n> la versión que envió una notificación, y GET /fhir/ClaimResponse/<id> la ClaimResponse sola. Un token vinculado a un paciente se le dice que no existen, como para cualquier registro fuera de su paciente.
Verificado con la suite oficial de Inferno PAS Server v2.2.1 (ver verificación). No se envía, aunque PAS marca must-support: ClaimResponse.adjudication con una acción de revisión, ClaimResponse.extension:authorizedProvider y ClaimResponse.item.extension:communicatedDiagnosis, porque las extensiones que necesitan no están permitidas allí por su propio contexto en PAS 2.2.1 y el validador rechaza una respuesta que las lleva; y request.extension:DataAbsentReason, porque Perfuse siempre conoce la solicitud. Los 278 enviados a un sistema de gestión de la utilización (arriba) se verifican contra los propios ejemplos de solicitud de PAS 2.2.1, los ejemplos de esMD 278 publicados por CMS y un verificador de 278 escrito a partir de guías de acompañamiento públicas, y cada transporte contra un sistema UM de reemplazo escrito a partir de las reglas de conectividad de CAQH (ver verificación); aún no contra un sistema UM real o una cámara de compensación, y no certificado por CORE. No implementado: adjuntos enviados con CDex.
Un archivo de reglas se verifica contra las invariantes propias de CRD cuando se carga: un cuestionario necesita documentación, un servicio que no está cubierto no puede necesitar autorización, satisfied pertenece a un caso y no a una regla, y cualquier cosa condicional debe dar una razón.
No verificado: las exportaciones y la coincidencia de miembros contra la implementación de otro pagador, las métricas contra las cifras publicadas de un pagador, y CRD contra un cliente CDS Hooks real de un sistema de registros electrónicos de salud.
16.11 Enviar solicitudes de autorización previa a un pagador (para proveedores)
Todo lo anterior responde a solicitudes, como un pagador. El lado de un proveedor es al revés: una aplicación de registro electrónico de salud o una aplicación de registro de tratamiento construye un paquete de solicitud de Da Vinci PAS, y algo tiene que enviarlo al pagador, mantener la respuesta y pasarla. Un destino pas hace eso.
name: prior-auth-out
dataType: raw
source:
type: http
http:
listen: 127.0.0.1:8095
path: /pas
destinations:
- name: acme-health
type: pas
pas:
payer: https://pas.acme-health.example/fhir
bearer_token: ${ACME_PAS_TOKEN} # or auth: { type: client_credentials, token_url, client_id, client_secret }
results_to: pas-answers # optional: a channel that takes each answer back to the EHR
Cada mensaje debe ser una solicitud completa de PAS en un canal sin procesar. Perfuse la publica sin cambios en {payer}/Claim/$submit: el pagador decide sobre la Solicitud de Reclamo junto con el paciente, la cobertura y los recursos de apoyo en el Paquete, y una Solicitud de Reclamo reconstruida a partir de un resumen perdería estos. Fuera de esta máquina, la dirección del pagador debe ser https.
Una decisión es una solicitud entregada, sea lo que sea. Una aprobación, una denegación y una pendiente son todas registradas y ninguna se vuelve a intentar, porque volver a intentar una denegación presenta la misma solicitud dos veces. Lo que falla la entrega, y se vuelve a intentar, es un pagador que no decidió: un estado de error, un OperationOutcome (su texto es el error) o una ClaimResponse cuyo resultado es error. La decisión se lee artículo por artículo desde los códigos de acción de revisión de PAS (X12 306: A1 aprobado, A2 parcialmente, A3 denegado, A4 pendiente, A6 modificado); una respuesta sin código se enumera como "sin código de decisión" en lugar de tomarse como una aprobación. Una denegación sin razón en ningún lugar se rechaza por no cumplir con CMS-0057.
La respuesta del pagador es la respuesta del destino, por lo que Inspeccionar la respuesta puede juzgarla, y results_to la pasa a otro canal. Cada solicitud se enumera en CMS-0057, Solicitudes enviadas a pagadores: cuándo se envió, el paciente, el servicio, la decisión y la referencia del pagador (preAuthRef), que una consulta posterior nombra. La lista se mantiene en prior-auth.json junto a la base de datos (como máximo 10,000 solicitudes; las decididas más antiguas van primero, una pendiente nunca lo hace), y cada inquilino solo ve el suyo.
Verificado contra la implementación de referencia de pagador de HL7 Da Vinci PAS (imagen de docker hlseven/davinci-prior-auth): una solicitud enviada a través de un canal en ejecución se pende por ella con su referencia, y la respuesta se muestra en la pantalla (ver verificación). Todavía no se ha intentado contra un pagador de producción. No construido: seguir una solicitud pendiente con $inquire, o recibir la respuesta posterior del pagador por suscripción; un canal puede hacer cualquiera de estas cosas hoy con un destino http o fhir.
16.12 Redes Aligned de CMS
El Marco de Interoperabilidad de CMS (Ecosistema de Tecnología de Salud de CMS) solicita a las redes de datos de salud que se autoevalúen con respecto a 26 criterios y sean incluidas como Redes Alineadas con CMS; los registros electrónicos de salud (EHR), los proveedores, los pagadores y las aplicaciones se conectan a ellas. Los criterios 13 a 16 han estado en vigor desde el 4 de julio de 2026: APIs de FHIR en US Core con un CapabilityStatement completo y datos en bloque, documentos como adjuntos de FHIR legibles por humanos, notificaciones de citas y encuentros mediante suscripción de FHIR, y un localizador de registros.
Administrar › Red Alineada con CMS enumera cada criterio con lo que Perfuse proporciona para él y dónde se encuentra este servidor, leído desde lo que está activado aquí:
| Estado | Significado |
|---|---|
| Listo aquí | Perfuse lo proporciona y está activado. |
| Listo, con una brecha | Está activado, y parte del criterio está fuera de él (nombrado en la pantalla). |
| Construido, no activado | Perfuse lo proporciona; la pantalla nombra la configuración o la bandera que lo activa. |
| De ustedes hacer | La parte de la organización: un acuerdo, una certificación, publicación en el Directorio Nacional de Proveedores. |
| No construido | El software lo haría, y Perfuse no. |
Lo que los cuatro en vigor necesitan de este servidor:
| Criterio | En Perfuse | Activar |
|---|---|---|
| 13 APIs FHIR, US Core, Bulk Data | El punto de conexión FHIR con su CapabilityStatement, y $export | -fhir, -fhir-bulk-export |
| 14 Documentos como adjuntos FHIR | DocumentReference con Binary, y IHE MHD sobre el registro XDS | -fhir (MHD también -xds) |
| 15 Notificaciones de encuentros y citas | Suscripciones basadas en temas (R5 Backport para R4) desde fuentes ADT y SIU | -fhir-subscriptions |
| 16 Localizador de registros | El registro XDS.b, XCA para otras comunidades, PIXm | -xds, -xds-home-community |
La pantalla es una hoja de trabajo para la autoevaluación, no una certificación: CMS revisa las redes que sospecha no cumplen con los criterios, y el texto del marco es la autoridad. No se ha construido hoy: consultas de brechas de calidad abiertas (criterio 8), una consulta de un pagador para los datos de encuentro de una reclamación (9), informes de métricas de consultas al Directorio Nacional de Proveedores (19), verificación de proveedores contra ese directorio (6), lectura de licencias de conducir móviles o tarjetas de seguro digitales (3, 23), y aplicación de una política de consentimiento general en cada lectura (5). La verificación de identidad IAL2 es del servicio de identidad; Perfuse acepta los tokens del que elija.
17 Seguridad
Perfuse maneja datos clínicos. Este capítulo es lo que hace al respecto y lo que deja a usted.
17.1 Seguridad de transporte
TLS en la interfaz web se configura con -tls-cert y -tls-key. Sin ellos, la interfaz es HTTP plano, lo que es aceptable en una dirección de bucle de retorno para una primera mirada y en ningún otro lugar — lleva credenciales y contenido de mensaje, ambos legibles en el cable.
TLS en los transportes de canal es por fuente y por destino, bajo un bloque tls. Los ajustes disponibles están en las referencias de fuente y destino.
insecure_skip_verify acepta cualquier certificado. Existe porque los sistemas hospitalarios presentan rutinariamente certificados que no se pueden verificar, y rechazar la conexión significaría rechazar la integración. Está nombrado para ser conspicuo, y usarlo significa que la conexión está cifrada pero no autenticada — tiene protección contra la interceptación pasiva y ninguna contra un servidor interposición.
17.2 Autenticación y roles
Los usuarios se almacenan en la base de datos. Cada uno tiene uno de cinco roles, y cada rol incluye todo lo que está en los roles anteriores:
| Rol | Puede |
|---|---|
| Monitor | Ver si todo funciona: estado del canal, recuentos, mapa de flujo, alertas, paneles, métricas y profundidad de la cola. No contenido de mensajes, archivos de canal o mensajes en cola. |
| Viewer | También leer mensajes, archivos de canal, trazas y registro de actividad. |
| Operator | También reenviar: reprocesar un mensaje, reproducir un canal y reintenta entregas en cola. |
| Editor | También crear y cambiar canales, y ejecutar comparaciones. |
| Admin | También iniciar y detener canales, limpiar colas, y administrar personas, tokens y configuración del servidor. |
Un sexto rol, Plataforma, pertenece a quien ejecuta un servidor compartido: crea inquilinos y establece sus límites, y no tiene canales propios.
Monitor es para un escritorio de ayuda o para una pantalla de operaciones en una pared: todo lo necesario para ver que una fuente de alimentación se ha detenido, y nada que nombre a un paciente. El visor puede leer el contenido del mensaje, que es datos clínicos; es un rol de baja privilegio para *configuración*, no para privacidad. El operador separa la reparación de una entrega fallida por la noche del cambio de lo que hace una fuente de alimentación: la persona que reenvía no necesita poder reescribir el canal.
17.2.1 Contraseñas incorrectas repetidas
Las contraseñas incorrectas y los intentos de inicio de sesión de passkey fallidos se cuentan por la dirección desde la que proviene la conexión, y después de unos pocos seguidos, esa dirección espera antes de poder intentarlo nuevamente (la respuesta es 429, con Retry-After). Las cuentas no se bloquean por nombre, porque eso permitiría que cualquier persona bloqueara a un administrador a propósito.
La dirección es la propia de la conexión, nunca una que una solicitud afirma en X-Forwarded-For, que escribe el remitente. Detrás de un proxy inverso en la misma máquina, Perfuse utiliza la última entrada X-Forwarded-For en su lugar, la que agregó el proxy, para que cada cliente siga siendo contado por separado. Un proxy en otra máquina alcanza Perfuse desde una dirección, y todos sus clientes comparten un recuento: limita la tasa de inicio de sesión en ese proxy también.
17.2.2 Grupos de canales
Una persona (o un token de API) se puede limitar a algunos grupos de canales: entonces solo ve y actúa en los canales cuyo group es uno de los suyos. Este es el arreglo de un grupo hospitalario en un servidor, donde los analistas de cada instalación se encargan de sus propios flujos de datos y un equipo central, sin límites, se encarga de todos ellos. Rhapsody lo llama un locker.
El límite se aplica en todas partes al mismo tiempo, porque se aplica a la solicitud en lugar de ser recordado por cada pantalla: la lista de canales, el panel de instrumentos y el mapa de flujo, los mensajes y su búsqueda, los recuentos y gráficos, la cola, las alertas, las métricas, los certificados, los contratos y el uso de conjuntos de códigos. Un canal fuera de los grupos de una persona no existe para ellos; pedirlo por nombre responde *no encontrado*, por lo que su nombre no se confirma tampoco. Un canal sin grupo se ve solo por personas sin límite.
Guardar un canal lo mantiene dentro de los grupos de la persona: no pueden crear un canal en otro grupo, o mover uno fuera del suyo. Lo que cada instalación comparte - tablas de códigos, bibliotecas de scripts, decisiones de autorización previa - pueden leer pero no cambiar. Las cifras del servidor (autorización previa, uso de tokens, túneles) se muestran solo a personas sin límite, y también se muestran las métricas y alertas sobre todo el servidor en lugar de un canal.
Un administrador limitado a grupos es el administrador propio de una instalación. Administran las personas y los tokens de sus grupos, inician y detienen sus canales y borran sus colas. Un colega que agregan obtiene sus grupos a menos que nombre algunos de ellos, no pueden dar un grupo que no tengan, y nunca ven ni cambian a nadie sin límite. Todo lo demás que un administrador puede hacer cambia todo el servidor - configuración, inicio de sesión, reinicio, reglas de alertas, marca, flota, el servidor SMART - y se les niega.
Los grupos se establecen en Usuarios (y para tokens en Tokens), como una lista separada por comas. Cada cambio se registra en el registro de actividad y tiene efecto en la próxima solicitud de la persona.
Un rol diferente en cada grupo
Una persona puede tener un rol diferente en cada uno de sus grupos: lab=editor, adt en Usuarios edita los canales del laboratorio y tiene el rol elegido junto a él - digamos visor - en los de admisiones. Ve los canales de ambos grupos en cada pantalla. Guardar, iniciar o reenviar uno en un grupo donde su rol es demasiado bajo se niega con el rol que tiene allí; no pretende que el canal esté perdido, ya que puede verlo. Su rol en la parte superior de la pantalla es el más fuerte que tiene, lo que abre las pantallas que ese rol utiliza, y cada acción en esas pantallas sigue estando limitada a los grupos donde lo tiene.
Solo las personas limitadas a grupos de canales pueden tener uno. Alguien que ve todos los canales tiene un rol, porque "editor de los canales del laboratorio" para una persona sin límite sería editor de todo lo que pertenece a ningún grupo, y de las páginas del servidor. Un administrador de instalación puede dar un rol por grupo solo en los grupos que administra, y nunca por encima de su propio rol allí.
17.3 Quién puede llamar al punto final de FHIR
El punto final de FHIR (serve -fhir) verifica uno de dos tipos de token:
- Tokens de API de Perfuse, creados en Usuarios → Credenciales de máquina o con
perfuse token create. Cada uno lleva un rol y puede estar
limitado a Grupos de FHIR. Este es el valor predeterminado cuando no se configura un servidor de autorización SMART.
- Tokens de acceso SMART, del propio servidor de autorización de Perfuse (
-smart-clients) o de uno externo (-smart-issuer). Cada
uno lleva ámbitos y el punto de conexión mantiene cada solicitud a ellos.
Con SMART configurado, solo se aceptan tokens SMART, por lo que cada llamador se ajusta a sus ámbitos. Un token de API tiene un rol y no tiene ámbitos: un token de visor puede leer el registro de cada paciente. Un pagador que activó SMART para limitar cada aplicación a lo que se le otorgó no debe tener una forma de entrada que no esté limitada.
Algunos sitios necesitan ambos, lo mismo - una fuente de reclamaciones interna que mantenga un token de API y no tenga un cliente SMART, además de aplicaciones de miembros que se conectan a través de SMART. Para ellos, Configuración → FHIR → Aceptar tokens de API de Perfuse además de tokens SMART (-fhir-api-tokens al iniciar) permite que los tokens de API también se utilicen. Está desactivado por defecto, tiene efecto en la próxima solicitud en cualquier dirección y el cambio se escribe en el registro de auditoría. Un token se distingue por su forma: un token SMART es un JWT firmado y un token de API nunca lo es, por lo que uno nunca se verifica como el otro. Donde sea posible, proporcione a dicho sistema un cliente de servicios de backend SMART con solo los alcances del sistema que necesite.
17.3.1 Un punto final de FHIR abierto a desconocidos
Un servidor de prueba público proporciona credenciales que pueden escribir, porque probar la autorización previa o las suscripciones requiere escrituras. Dos límites evitan que un cliente lo estropee para todos, ambos bajo Settings → FHIR y ambos desactivados de forma predeterminada:
- Escribe un minuto desde un cliente (
-fhir-writes-per-minute): crea, actualiza y elimina desde una dirección de cliente. Por encima de él, una escritura se responde con 429 yRetry-After: 60. Las lecturas y búsquedas, incluidas las búsquedas por POST, nunca se cuentan. - La mayoría de los recursos mantenidos (
-fhir-max-resources): más allá de él, los nuevos recursos se rechazan con 507, mientras que las actualizaciones y eliminaciones aún funcionan.
El punto final de un hospital propio no necesita ninguno de los dos.
17.4 Agentes de inteligencia artificial: el servidor MCP
Con Configuración › Seguridad › Agentes de inteligencia artificial (MCP) activado (o serve -mcp), Perfuse sirve el Protocolo de contexto de modelo en /mcp, para que un agente de inteligencia artificial como Claude, ChatGPT o un asistente de IDE pueda preguntarle qué está sucediendo. Está desactivado por defecto y desactivarlo detiene a cada agente en su próxima llamada.
Un agente se conecta con un token de API desde Administrar › Usuarios › Credenciales de máquina como un token de portador, nunca con una sesión de navegador de una persona (que se rechaza, por lo que un agente nunca actúa con el acceso completo de una persona). Una configuración de cliente se parece a:
{ "mcpServers": { "perfuse": { "type": "http", "url": "https://perfuse.example/mcp",
"headers": { "Authorization": "Bearer <token>" } } } }
| Herramienta | Rol | Qué hace |
|---|---|---|
server_status, list_channels, queue, alerts | monitor | Si las cosas funcionan, sin contenido de mensajes |
get_channel, search_messages, get_message, find_patient, fhir_search | viewer | Canales, mensajes (con contenido), mensajes de un paciente en varios formatos, búsqueda de FHIR |
retry_delivery, reprocess_message | operador, y mcp:write | Reintentar una entrega en cola, enviar un mensaje a través de su canal nuevamente |
start_channel, stop_channel | administrador, y mcp:write | Iniciar o detener un canal |
Cada herramienta es respondida por la misma ruta o búsqueda de FHIR que utiliza la pantalla de una persona, llamada con el token del agente, por lo que el rol del token, los grupos de canales y el inquilino deciden qué ve, exactamente como lo hacen en la consola; y fhir_search obtiene el acceso propio del token en el punto de conexión de FHIR (que puede rechazar tokens de API por completo; véase la sección de FHIR anterior). A un agente solo se le ofrece las herramientas que su token puede utilizar. La lectura es el valor predeterminado: una herramienta que cambia algo también necesita el alcance mcp:write del token ("Dejar que un agente de inteligencia artificial actúe con él" al emitir el token), por lo que un token emitido para un agente de informes no puede ser hablado para actuar por una solicitud. Las respuestas de un token de monitor tienen los contenidos de los mensajes eliminados, independientemente de lo que devuelva una ruta.
Cada llamada está en Actividad como mcp.tool con el nombre del token, la herramienta, sus argumentos y el resultado (ok, refused, invalid, rate-limited o el estado HTTP); mcp.connect registra qué cliente se conectó. Cada token puede realizar 60 llamadas de herramienta por minuto (Llamadas de herramienta por minuto desde un token); por encima de ese límite, una llamada se responde con un error que el agente puede leer y no se realiza nada. Una solicitud desde una página de navegador en otro origen se rechaza, lo que MCP solicita a un servidor para detener el reenlace de DNS. El transporte es Streamable HTTP de MCP, respondido como JSON sin flujo de eventos; se aceptan las versiones del protocolo 2025-06-18, 2025-03-26 y 2024-11-05. Los mensajes y los recursos de FHIR contienen datos de pacientes: las respuestas de un agente van a cualquier modelo en el que se ejecuta, así que proporcione un token a un agente solo donde ese modelo esté autorizado a verlos y proporcione el rol más bajo que realice el trabajo.
17.5 Una cuenta de demostración
serve -demo-account demo:PASSWORD crea una cuenta compartida para un servidor de demostración pública, y la página de inicio de sesión muestra su nombre de usuario y contraseña con un botón que inicia sesión con ellos. La cuenta es un espectador, por lo que puede ver todo y no cambiar nada. A diferencia de cualquier otro espectador, también puede abrir las pantallas de los administradores (usuarios, tokens de máquina, configuración, protección de datos, los pares de la flota), de solo lectura: una demostración se juzga por sus pantallas. Las lee como un espectador lee canales, con cada credencial mostrada como establecida o no, nunca su valor, y cada cambio rechazado. También puede presionar las comprobaciones que no cambian nada y no alcanzan nada fuera del servidor: una comprobación de script y la validación y análisis del editor de canales. No puede cambiar su contraseña ni agregar una passkey, porque el primer visitante que haga cualquiera de las dos cosas bloquearía a todos los demás. Cada inicio lo restaura como un espectador con la contraseña publicada. La contraseña debe tener al menos 12 caracteres, como cualquier otra. Solo úsela en un servidor que contenga datos de prueba: cualquier persona que lea la página de inicio de sesión puede leer cada mensaje en el servidor.
17.6 Auditoría
Los cambios de configuración, los inicios de sesión y el acceso al contenido de los mensajes se registran con quién, qué y cuándo.
El registro de auditoría está en la misma base de datos que los mensajes. Esto es conveniente y es una limitación: alguien que pueda modificar la base de datos puede modificar el registro de auditoría. Si sus requisitos incluyen una auditoría con evidencia de manipulación, el registro necesita enviarse a algún lugar de solo agregar, y la salida de registro de Perfuse es la forma de hacerlo.
17.7 Secretos en la configuración
Las contraseñas, los códigos y las claves aparecen en la configuración del canal, lo que significa que están en los archivos YAML.
Dos consecuencias. Los archivos necesitan permisos de sistema de archivos que reflejen su contenido, y si están en control de versiones — lo que de otro modo se recomienda — el repositorio contiene credenciales y debe ser tratado en consecuencia.
Donde un valor es un secreto, la interfaz lo representa como un secreto y la API no lo devuelve una vez guardado. Eso lo protege de ser leído sobre el hombro de alguien o filtrado en una captura de pantalla. No protege el archivo.
17.8 Lo que está en la base de datos
El almacén de mensajes mantiene los mensajes tal como se recibieron, lo que para una fuente clínica significa datos identificables de pacientes. Debe estar en almacenamiento cifrado, respaldado como datos clínicos con la retención que implica, y controlado de acceso a nivel de sistema de archivos, así como a través de los propios roles de Perfuse.
cleanup.periodDays limita cuánto tiempo se mantiene. Consulte el almacén de mensajes para la tensión entre eso y tener suficiente historial para alertas con conciencia de ritmo.
17.9 Protección de datos (GDPR y GDPR del Reino Unido)
La pantalla Protección de datos (Administrar, solo administradores) responde a las solicitudes de una persona bajo el GDPR y el GDPR del Reino Unido. No decide nada en nombre del controlador: si una solicitud debe ser concedida y si se aplica una exención es la decisión del controlador, registrada con la solicitud.
Encontrar lo que se posee. Una persona se nombra mediante identificadores: system|value (un número del NHS, un MRN con su autoridad asignadora) o un número por sí solo. Con un índice de pacientes, esos se amplían a cada número de cada registro vinculado a la misma persona, por lo que una solicitud que cita un número de hospital encuentra los registros archivados bajo los demás. Ver lo que se posee busca entonces en cada tienda que mantiene datos de pacientes:
| Almacén | Encontrado por |
|---|---|
| Mensajes | el índice de identidad (Configuración → Datos → Índice de identificadores de pacientes); sin él, los mensajes no se pueden encontrar por identificador |
| La cola de entrega | leyendo los identificadores de cada mensaje en cola |
| El almacén de FHIR | los recursos de Paciente que llevan un identificador, y cada recurso cuyas referencias indexadas apuntan a uno |
| Solicitudes de autorización previa | los identificadores del paciente de la solicitud |
| El índice de pacientes | el identificador, o el EUID |
Lo que no puede alcanzar lo dice: trazas enviadas a un recolector (-trace-endpoint), el registro de auditoría (conservado: es el registro de quién hizo qué, incluida esta solicitud), y copias de seguridad.
Solicitudes. Una solicitud se registra con su tipo (acceso, Artículo 15; portabilidad, Artículo 20; eliminación, Artículo 17; restricción, Artículo 18), quién es de acuerdo con los registros del controlador, los identificadores y una nota (cómo se confirmó la identidad, una extensión y su razón). Debe responderse dentro de un mes después de la recepción (Artículo 12(3)); una solicitud vencida se muestra en rojo.
- Acceso y portabilidad: Descargar la copia proporciona un zip: cada mensaje tal como se recibió con su registro, cada recurso FHIR con cada versión almacenada, solicitudes de autorización previa y sus respuestas, y los registros del índice de pacientes, en formato JSON en todo, con un archivo README que explica qué es cada parte. Marcar como respondido cierra la solicitud.
- Borrado: un segundo administrador aprueba el borrado (quien lo registró no puede hacerlo). Borrar ahora elimina todo lo encontrado: mensajes completos (registro, cuerpo, entregas, identificadores, adjuntos que no utiliza ningún otro mensaje), entregas en cola (no se envían), los recursos FHIR con su historial, solicitudes de autorización previa con sus respuestas, adjuntos y números de seguimiento, y la persona en el índice de pacientes con cada registro vinculado y su historial (un evento registra que se produjo un borrado y quién lo realizó).
- Restricción: se registra como una retención legal, por lo que nada se borra o caduca mientras dure.
- Rechazar cierra una solicitud con su motivo, que debe ser comunicado a la persona.
Una solicitud cerrada solo mantiene los últimos caracteres de los números de la persona: permanece como registro de una respuesta sin ser otra copia de ellos. Cada paso se registra en el registro de auditoría con los números enmascarados.
Retenciones legales. Una retención nombra a una persona por identificadores, con su motivo y una fecha de finalización opcional. Mientras esté en vigor, un borrado se rechaza directamente, y la ventana de retención del almacén de mensajes preserva los mensajes de la persona (registros y cuerpos) hasta que se levante la retención.
Copias de seguridad y registro de borrados. Una copia de seguridad realizada antes de un borrado aún contiene lo que se borró hasta que caduca. Cada borrado se escribe en un registro: cuándo, por quién, aprobado por quién, cuánto se eliminó de cada almacén y los identificadores de la persona como hashes SHA-256 salados en lugar de en claro. Después de restaurar una copia de seguridad, Aplicar borrados nuevamente hash los identificadores que los almacenes restaurados contienen con la sal de cada entrada y borra a cada persona que el registro nombra. Los números cortos se pueden encontrar desde sus hashes intentando cada valor, por lo que el registro es en sí mismo datos personales y se mantiene con la protección propia de la base de datos.
Retención. La pantalla enumera cuánto tiempo se mantiene cada tipo de registro aquí y qué lo cambia: registros y cuerpos de mensajes por sus ventanas de retención, la cola hasta que se entrega o se elimina, el almacén FHIR, solicitudes de autorización previa y el índice de pacientes hasta que se borran (los registros clínicos no se eliminan según un calendario), el registro de auditoría se mantiene.
Registro de procesamiento (Artículo 30). Registro de procesamiento descarga, como Markdown o CSV, lo que este servidor conoce de él: el controlador, el oficial de protección de datos y dónde se almacenan los datos (Configuración → Datos → Protección de datos), la retención de cada tipo de registro y una actividad de procesamiento por canal con lo que recibe, de dónde, a qué destinos y los hosts a los que envía. Los propósitos, bases legales y categorías de sujeto de datos se dejan para que el controlador los complete, y se indica que así es. Una celda de CSV que comienza con =, +, - o @ se escribe con una comilla simple al principio, para que una hoja de cálculo no la ejecute.
Dónde se almacenan los datos. Dónde se almacenan los datos (Configuración → Datos → Protección de datos) establece, en palabras que un regulador puede verificar, dónde se ejecutan el servidor, su base de datos y sus copias de seguridad; la pantalla de protección de datos y el registro lo muestran. Un borrador de acuerdo de procesamiento de datos para organizaciones que BioDream hospeda se encuentra en docs/legal/dpa-template.md; es un borrador para un abogado, no asesoramiento legal.
La API es /api/privacy (resumen), /api/privacy/find, /api/privacy/requests (y /{id}/approve, /{id}/export, /{id}/erase, /{id}/close), /api/privacy/holds, /api/privacy/reapply y /api/privacy/register.
17.10 Inquilinos
Un inquilino es un límite de aislamiento: los canales, mensajes, usuarios y configuraciones pertenecen a uno, y nada cruza.
Esto existe para un proveedor de servicios que ejecuta fuentes de alimentación para varias prácticas. No lo utilice para separar departamentos dentro de una organización que necesitan ver el tráfico entre sí: el aislamiento es real y no hay vista entre inquilinos. Para eso, utilice grupos de canales (anteriormente), que dividen a las personas de una organización mientras permiten que un equipo central vea todo.
17.10.1 Límites
Un operador de plataforma establece los límites de cada inquilino en Tenants, para que un cliente no pueda saturar el resto de un servidor compartido. Deje un límite vacío para que no haya ninguno.
- Canales: cuántos canales puede tener el inquilino. Crear uno más es rechazado, con el recuento y el límite.
- Mensajes por minuto: cuántos mensajes pueden recibir juntos sus canales en un minuto.
- Almacenamiento: cuánto contenido de mensaje almacenado puede mantener, en megabytes.
Ninguno de ellos pierde un mensaje. Por encima de la tasa o el límite de almacenamiento, un mensaje recibido se rechaza de la misma manera que un receptor ocupado rechaza uno, antes de que se almacene o entregue nada: MLLP no recibe acuse de recibo (no es un rechazo, que algunos remitentes toman como "no reenvíe"), HTTP recibe 429 con Retry-After, SOAP recibe 503, DICOM recibe un estado que se puede reintentar, y una cola, cubeta o archivo se deja donde estaba. El remitente vuelve a enviar, como hace cada motor de interfaz y EHR. Nada que ya esté almacenado se elimina para hacer espacio. Un límite aumentado entra en vigor dentro de un minuto, y cada cambio de límites se registra en el registro de actividad.
17.11 Datos de prueba
Los mensajes generados descritos en testing existen en parte como un control de privacidad. Reproducir un problema en otro lugar, enviar un ejemplo a un proveedor o poblar un entorno de prueba son todas cosas que de otra manera se realizan con mensajes reales.
La salida del generador no contiene datos de paciente por construcción en lugar de por redacción. Prefiéralo a los mensajes reales despojados a mano, que es un proceso que se puede realizar de manera incompleta y generalmente se hace.
17.12 Inicio de sesión único
Tres mecanismos, y cuentas locales junto con todos ellos.
OpenID Connect (-oidc), un directorio sobre LDAP (-ldap) y SAML 2.0 (-saml). Cada uno se configura desde Administrar → Inicio de sesión y se escribe en el archivo que el indicador nombra, para que la pantalla y el archivo sean dos vistas de una cosa en lugar de dos lugares para mantener en sincronía.
Las cuentas locales siempre están disponibles y no se pueden desactivar. Eso es deliberado: un motor de integración local cuya única forma de entrada es un proveedor de identidad se vuelve inaccesible exactamente cuando el proveedor de identidad es inaccesible, y una interfaz clínica no es un buen lugar para aprender eso. La pantalla de inicio de sesión le indica cuántos administradores locales existen antes de animarlo a depender de cualquier otra cosa.
17.12.1 Qué decide un mapeo de grupo
Los tres mapean grupos desde el directorio a roles de Perfuse, y alguien en grupos que coinciden con más de uno obtiene el más privilegiado. Esa es la única dirección segura para resolverlo — la alternativa es que una persona pierda el acceso al que tiene derecho porque también está en un grupo menor.
Cualquiera de los seis roles se puede otorgar: monitor, espectador, operador, editor, administrador y plataforma.
Un grupo también puede limitarse a algunos grupos de canales, en la segunda casilla de su fila (channel_groups en el archivo). Una vez que cualquier grupo nombra uno, cada inicio de sesión establece los grupos de canales de la persona desde los grupos que otorgaron su función: alguien en un grupo que edita los canales del laboratorio y en otro que ve cada canal es un editor de los canales del laboratorio y nada más. El rol más fuerte nunca se une al alcance más amplio, porque ningún grupo otorga eso. Un grupo con una casilla vacía otorga cada canal. Mientras ningún grupo nombra ninguno, el inicio de sesión deja los grupos de canales como los estableció un administrador en Usuarios.
Cuando cada grupo al que pertenece una persona coincide con los nombres de los grupos de canales, cada grupo de canales obtiene el rol más fuerte entre los grupos que lo nombran: con Lab-Interfaces (editor, laboratorio) y Clinical-IT (visor, laboratorio y admisiones), la persona edita los canales del laboratorio y ve los de admisiones, como si se hubiera configurado manualmente. Cuando uno de sus grupos otorga todos los canales, no hay lista para mantener un rol por grupo, y se aplica la regla anterior.
roles:
editor: [Lab-Interfaces]
operator: [Night-Desk]
viewer: [Clinical-IT]
channel_groups:
Lab-Interfaces: [lab, pathology]
Night-Desk: [adt]
Este es el archivo OpenID Connect y SAML. El archivo del directorio asigna cada grupo a un rol (roles: {Lab-Interfaces: editor}), y channel_groups se encuentra junto a roles allí.
Un mapeo que no otorga nada a nadie se rechaza en lugar de guardarse. Una configuración que autentica a las personas y las rechaza a todas parece idéntica a un producto roto desde afuera: el viaje de ida y vuelta se completa y todos son rechazados sin explicación.
Crear una cuenta en el primer inicio de sesión está desactivado de forma predeterminada. Con esto desactivado, alguien debe existir en Perfuse antes de que puedan iniciar sesión, por lo que el directorio decide quiénes son y Perfuse decide quién está permitido.
17.12.2 SAML, específicamente
Las partes que vale la pena conocer antes de configurarlo:
- Solo el certificado que configura es de confianza. Un certificado transportado dentro de una respuesta nunca se utiliza. Un atacante que pueda enviar un documento también puede poner su propio certificado en él, y un verificador que lo lee es uno al que cualquiera puede autenticar como cualquiera.
- Una respuesta debe responder a un inicio de sesión que inició este servidor. Perfuse recuerda la solicitud que envió y rechaza una respuesta que nombra cualquier otra cosa, o que nombra una solicitud que ya ha respondido. Sin esta comprobación, una respuesta válida es un token de portador para quien la posee: alguien inicia sesión como ellos mismos, mantiene la respuesta y la publica en su navegador, y entonces estás dentro de su cuenta con el registro de auditoría que registra su nombre.
- Los inicios de sesión iniciados en el proveedor de identidad están desactivados de forma predeterminada. Active *Aceptar inicios de sesión iniciados en el proveedor de identidad* solo si necesita un mosaico de portal, y sabiendo que es lo que hace posible lo anterior.
- El atributo de grupo no tiene un valor predeterminado y es obligatorio. Entra envía
groups, Okta envía lo que se configuró para la aplicación, ADFS envía una URI de reclamación. Una suposición produciría un inicio de sesión que funciona y no otorga nada a nadie. Si un inicio de sesión se rechaza por no tener rol, el registro del servidor nombra los atributos que llegaron — que es generalmente toda la respuesta. - La URL de respuesta debe coincidir exactamente con lo registrado en el proveedor. Una respuesta dice dónde estaba destinada y una dirigida a otro lugar se rechaza, porque aceptarla significaría aceptar una respuesta destinada a un servicio diferente.
17.12.3 Qué envía cada proveedor
El atributo de grupo es donde los inicios de sesión van mal, y los tres proveedores comunes lo nombran de manera diferente.
| Proveedor | Normalmente envía | Notas |
|---|---|---|
| Keycloak | groups | Necesita un mapeador de membresía de grupo agregado al cliente. |
| Entra ID | una URI de reclamación que termina /claims/groups | Envía identificadores de objeto de grupo a menos que la aplicación esté configurada para enviar nombres. Mapee los identificadores o cambie el proveedor. |
| Okta | lo que se configuró para la aplicación | Comúnmente groups, con un filtro que decide qué se envía. |
| ADFS | una URI de reclamación | Firma la Respuesta en lugar de la Asertión, que Perfuse acepta. |
Una URI de reclamación puede ingresarse por su segmento final: escribir groups encuentra http://schemas.microsoft.com/ws/2008/06/identity/claims/groups. Un nombre que simplemente termina con la palabra no coincide — excluded_groups no es groups.
La membresía de grupo que llega como un solo valor separado por comas se trata como un solo nombre de grupo y no coincide con nada. Esa es la lectura correcta del estándar, y dividir por comas significaría que un grupo que legítimamente contiene uno se convirtió silenciosamente en dos. Si eso es lo que su proveedor envía, cambie allí.
Probar en la pantalla de inicio de sesión comprueba todo lo que se puede comprobar sin una persona: la configuración, el certificado y si se puede construir una solicitud de inicio de sesión. Dice claramente que no puede probar que alguien pueda iniciar sesión, porque eso necesita una aserción real sobre una persona real y la única forma de obtener una es que lo intenten.
17.13 Cuentas aprovisionadas por el proveedor de identidad: SCIM
Con -scim (Configuración → Inicio de sesión → Cuentas automáticas), un proveedor de identidad como Okta, Microsoft Entra ID o Google Workspace crea, actualiza y elimina cuentas de Perfuse por sí mismo en /scim/v2/Users, por lo que una persona que deja la organización pierde acceso cuando su cuenta de directorio se deshabilita, no cuando alguien lo recuerda. El proveedor se autentica con un token de API que tiene el rol de administrador, creado bajo Usuarios → Credenciales de máquina, enviado como un token de portador.
- Deshabilitar una cuenta termina sus sesiones de inmediato, y el servidor verifica que ninguna sobrevivió antes de responder. Un proveedor nunca se le informa que una cuenta fue deshabilitada cuando no lo fue, porque un proveedor no vuelve a intentar lo que se le dijo que tuvo éxito.
- Un eliminar deshabilita primero, luego elimina, por lo que un fallo a mitad de camino deja una cuenta con la que nadie puede iniciar sesión. Se mantiene el historial de auditoría de una cuenta eliminada.
- El rol proviene de los grupos o roles de la persona, coincidiendo por nombre: un grupo cuyo nombre contiene *admin*, *editor*, *operador*, *espectador* (o *solo lectura*) o *monitor* otorga ese rol, por lo que
APP-Perfuse-EditoryPerfuse Operatorsfuncionan como están nombrados. Cualquier otra cosa obtiene-scim-default-role, espectador a menos que se establezca de otra manera. La plataforma nunca se puede otorgar de esta manera. - No hay contraseñas. Una cuenta creada por SCIM inicia sesión a través del proveedor de identidad; una segunda contraseña que nadie conoce en la parte superior sobreviviría a una deshabilitación allí.
/scim/v2/ServiceProviderConfigdice qué está soportado: crear, leer, reemplazar, parchear y eliminar de Usuarios, y filtros de hasta 200 resultados. Las operaciones por lotes, la ordenación y las etiquetas ETags no están.
17.14 Qué no hace Perfuse
No cifra la base de datos en reposo. Utilice cifrado de sistema de archivos o volumen.
No administra sus propios certificados ni los renueva. Apúntelo a archivos y administre esos archivos con lo que ya utiliza.
SAML ha sido verificado contra dos proveedores independientes, cada uno con un inicio de sesión de navegador real, una aserción real y una sesión real: Keycloak 26 y Microsoft Entra. Dos asuntos son más de dos veces más que uno, porque los dos discrepan de una manera que resultó ser fundamental — Keycloak escribe su aserción con prefijos de namespace y Entra escribe los mismos elementos sin ellos, y el único defecto que tiene esta área ha sido en el manejo de prefijos.
Comience leyendo los metadatos de su proveedor. Configuración, luego Inicio de sesión, luego la pestaña SAML: proporcione la URL de metadatos o pegue el documento, y la dirección de inicio de sesión y el certificado de firma se rellenarán automáticamente. Cada proveedor de SAML publica uno de estos — Okta, Entra, AWS IAM Identity Center, Keycloak, ADFS — y es la diferencia entre configurar un proveedor y transcribir uno. Hacerlo manualmente significa encontrar el certificado dentro del XML, eliminar los saltos de línea del base64 y envolverlo en encabezados PEM, donde un solo espacio extra produce un error de firma que no dice nada sobre el formato.
Lo que se devuelve se describe en lugar de volcar: el sujeto de cada certificado, vencimiento y huella digital SHA-256, para que pueda verificarlo contra lo que muestra la consola del propio proveedor. Un certificado caducado se llama caducado en lugar de dejarlo como una fecha para comparar visualmente. Nada se guarda al leer — aplicarlo al formulario es un clic separado, porque un documento que alguien pegó no es aún una decisión sobre qué confiar.
Una URL de metadatos se recupera solo sobre https, y la dirección se verifica contra la misma política que rige los destinos del canal, por lo que esto no se puede utilizar para hacer que el servidor lea credenciales de instancia de nube. Las redirecciones no se siguen, porque una dirección permitida que redirige a una bloqueada pasaría directamente por esa verificación.
Si está configurando Entra, cinco cosas no coincidirán con lo que muestran los ejemplos. No envía ninguna reclamación de correo electrónico en absoluto. Su NameID es un identificador opaco en lugar de una dirección. No envía ninguna reclamación de grupos a menos que la aplicación esté configurada explícitamente para emitirla, por lo que un mapeo de roles escrito contra grupos rechaza cada inicio de sesión. Cuando se emiten grupos, son GUID de objeto en lugar de nombres, a menos que su inquilino se sincronice desde Active Directory local. Y sus nombres de reclamación viven bajo schemas.microsoft.com/identity donde la mayoría de la documentación muestra schemas.xmlsoap.org. Perfuse maneja los cinco; se enumeran porque los cuatro primeros de lo contrario parecerían errores en Perfuse cuando son Entra siendo Entra.
Esa distinción no es teórica aquí. La primera aserción real de Keycloak falló directamente, porque la forma canónica que cubre la firma se estaba computando con prefijos de namespace eliminados. Ningún proveedor de identidad real podría haber iniciado sesión a alguien, y las cinco pruebas de canonización del paquete pasaron en todo momento — cada una de ellas firmada y verificada con el mismo código, por lo que solo demostraron que estaba de acuerdo consigo mismo. Si es el primero en apuntar un proveedor diferente a esto, espere encontrar algo, y el registro del servidor nombrará la etapa que falló.
18 Operaciones
18.1 Implementación
Un binario y dos rutas: un directorio de archivos de canal y un archivo de base de datos. No se requiere un servidor de aplicaciones, una capa web separada ni un agente de mensajes.
perfuse serve -channels ./channels -db ./perfuse.db -addr 0.0.0.0:8443 \
-tls-cert ./cert.pem -tls-key ./key.pem
Ejécutelo bajo lo que supervisa los servicios en el host — systemd, launchd, un tiempo de ejecución de contenedor. Espera ser reiniciado si sale.
18.2 Dónde puede ir los datos (residencia de datos)
serve -residency (Configuración → Datos → Dónde pueden ir los datos) nombra las regiones de nube a las que los datos de este servidor pueden ser enviados o leídos: áreas (eea, uk, ch) y códigos de región (eu-central-1, uksouth, europe-west2), separados por comas. eea es las regiones de AWS, Azure y Google Cloud en el Área Económica Europea (Frankfurt, Irlanda, París, Estocolmo, Milán, España; Países Bajos, Alemania, Francia, Suecia, Noruega, Italia, Polonia, España; Bélgica, Países Bajos, Alemania, Italia, Francia, Finlandia, España, Polonia), uk las de Reino Unido, ch las de Suiza.
Con esto configurado, un canal se rechaza cuando se guarda y cuando se carga, si un conector nombra una región fuera de la lista: una fuente o destino S3, SQS o SNS region (o su endpoint, cuando es un hostname de AWS), o cualquier destino cuya dirección es un hostname de AWS que lleva una región (sqs.us-east-1.amazonaws.com, una URL de API Gateway). El servidor se niega a iniciarse con un colector de trazas o un webhook de alerta en otra región. Un conector apuntando a un host que no nombra región (el propio sistema de un hospital, MinIO en la red del sitio) no se juzga: el registro de procesamiento en la pantalla de protección de datos enumera todos los hosts a los que cada canal envía, para que el controlador lo verifique. El panel y la pantalla de protección de datos indican dónde puede ir los datos.
Alojamiento en la UE y el Reino Unido. deploy/hosted contiene OpenTofu (o Terraform) para un servidor alojado por organización en Frankfurt, Irlanda o Londres: el servidor, su base de datos PostgreSQL y copias de seguridad, un bucket de copias de seguridad, registros y registros de sesiones de soporte, todo cifrado con una clave en la región, nada replicado a otra región, soporte a través del administrador de sesiones solo, y Perfuse ejecutado con -residency. Su archivo README dice qué construye, qué no cubre, qué se ha verificado (validado y planeado contra una cuenta real; no aplicado todavía) y qué cuesta a precio de lista en cada región.
18.3 Configuración como archivos
Los canales son archivos, lo que significa que pueden estar en control de versiones, revisados antes de la implementación y diferenciados después de un incidente.
Los archivos se vuelven a leer sin reiniciar. Un archivo que no se analiza se informa y la versión previamente cargada sigue funcionando, así que un error de sintaxis no puede sacar un flujo de trabajo del aire.
18.4 Copia de seguridad
Dos cosas que respaldar, y tienen características diferentes.
El directorio del canal es pequeño, cambia raramente y es lo que necesita para reconstruir el servicio. Respalde como configuración; el control de versiones suele ser suficiente.
La base de datos contiene mensajes, usuarios e historial de auditoría. Es grande, cambia constantemente y contiene datos clínicos, por lo que necesita respaldarse como datos clínicos, con la retención y el cifrado que implica. Perderla no detiene el servicio, pero pierde la capacidad de investigar cualquier cosa que ya haya sucedido y la historia en la que se basa el aprendizaje de ritmo.
18.5 Comportamiento de reinicio
Al reiniciar, se cargan los canales y las fuentes comienzan a escuchar. Las entregas en cola se reanudan.
La cosa que hay que entender es qué sucede con el trabajo en vuelo. Bajo el reconocimiento de entrega predeterminado on_delivery, un mensaje que no había sido reconocido aún no es reconocido, por lo que el remitente lo reenviará, lo cual es el resultado correcto. Bajo on_receipt, un mensaje que fue reconocido pero no entregado aún está en la cola y se reanuda; si la cola está deshabilitada, se pierde. Vea flujo de mensaje.
18.6 Actualización
Reemplace el binario y reinicie. El esquema de la base de datos se migra automáticamente hacia adelante al iniciar.
Las migraciones solo son hacia adelante. No hay degradación, por lo que una reversión a un binario anterior después de que el esquema haya cambiado requiere la base de datos anterior. Tome una copia antes de actualizar, esta es la única precaución operativa que vale la pena ser disciplinada.
18.7 Supervisión
La alerta desde dentro de Perfuse se cubre en alerta y es lo que le dirá que una fuente ha dejado de funcionar.
Desde fuera, supervise el proceso, el disco donde se encuentra la base de datos y si la interfaz web responde. La base de datos crece y un disco lleno detiene la grabación de mensajes, lo que es un modo de fallo sin otra advertencia.
18.7.1 SNMP
Para herramientas de monitoreo que hablan SNMP (Nagios, Zabbix, PRTG, SolarWinds, LibreNMS), Perfuse ejecuta un agente SNMPv2c de solo lectura y envía una trampa cuando se dispara o resuelve una alerta. Configúrelo en Settings → Monitoring → SNMP, o con indicadores:
perfuse serve -snmp-listen :161 -snmp-community 'long-random-community' -snmp-allow 10.0.5.0/24 \
-snmp-traps nms.example.org,10.0.5.9:1162
- El agente responde a GET, GETNEXT y GETBULK, y solo desde las redes en
-snmp-allow: la comunidad v2c es una contraseña enviada en texto claro, por lo que nunca está abierta a todos. Una comunidad de menos de 6 caracteres, opublic/private, detiene el servidor al inicio. - Sirve el grupo del sistema (sysDescr, sysUpTime, sysName desde la etiqueta de la flota, sysContact, sysLocation), totales bajo
BASE.1(canales, en ejecución, rotos, recibidos, entregados, fallidos, en cola) y una tabla de canales bajoBASE.2.1(nombre, en ejecución o detenido, recibidos, entregados, fallidos, en cola, edad del mensaje en cola más antiguo). Los números son los que muestra el panel y/metrics. - Las trampas van a cada administrador en
-snmp-traps:BASE.0.1una alerta se disparó,BASE.0.2se resolvió, llevando el tipo de alerta, gravedad, canal, destino y resumen. Nunca contenido de mensaje. Una alerta confirmada no envía trampa. BASEes1.3.6.1.4.1.8072.9999.9999.16(espacio experimental de Net-SNMP) a menos que-snmp-base-oidlo coloque bajo su propio número de empresa. Descargar el MIB en la página de configuración (oGET /api/snmp/mib) da un MIB para la base en uso; el mismo panel dice si el agente está respondiendo y cuántas trampas se fueron.
18.7.2 Un inventario de cada interfaz
-inventory interfaces.md (Configuración → Monitoreo → Inventario) escribe un documento Markdown que enumera cada canal - su formato, si se ejecuta, qué recibe de y a qué envía - seguido de la descripción de cada canal, y lo reescribe cada -inventory-every (un día por defecto). Está destinado a ser confirmado junto con los archivos de canal, o entregado a un auditor, y leído sin que la aplicación esté en ejecución.
18.8 Capacidad
El límite de rendimiento de Perfuse no se ha medido en hardware real bajo tráfico real, y este manual no imprimirá un número que no pueda respaldar.
Lo que se puede decir: la arquitectura es una goroutine por conexión con colas por destino, por lo que el rendimiento se escala con los destinos en lugar de serializarse a través de un trabajador; la grabación de mensajes es una escritura SQLite por mensaje, lo que hace que el disco sea la restricción más probable; y el almacén de mensajes crece linealmente con el tráfico, por lo que la retención es la configuración que gobierna el uso de disco a largo plazo.
Si está dimensionando esto para una gran fuente de datos, mézclelo con su propio tráfico utilizando el corpus de prueba generado descrito en testing. Esa es la respuesta honesta, y también es una mejor respuesta que un número de alguien más en su hardware.
18.9 Donde esta instalación se bloqueó
La actividad lleva un panel, visible para los administradores, que muestra qué Perfuse se ha negado a hacer — agrupado por mensaje, el más frecuente primero — y cuánto ha avanzado la instalación hacia un canal de trabajo.
Existe porque todas las demás afirmaciones sobre si este software es fácil de usar provienen de las personas que lo escribieron. Una negativa no: alguien quería algo, el servidor dijo que no, y ninguna de las partes estaba adivinando. Es la única evidencia en el producto que no proviene de BioDream LLC.
El embudo nombra el paso que aún no ha sucedido en lugar de dejar que se comparen los timestamps. El estado que vale la pena vigilar es los mensajes llegan y ninguno ha sido entregado a un destino — un canal puede tomar tráfico durante semanas y no entregar ninguno, y desde cualquier otra pantalla parece que está funcionando.
Nada de lo que alguien escribió se registra. La tabla mantiene la ruta de la solicitud con identificadores reemplazados por {id}, el estado y la oración del propio servidor. Un mensaje de validación nombra un campo y una regla, que es la parte útil; el valor que rompió la regla es muy a menudo datos de paciente, y una tabla de esos sería una responsabilidad peor que la fricción que midió.
Dos exclusiones deliberadas. Un 5xx es un error en este software y ya se registra como tal — mezclar los dos enterraría los casos en los que alguien no podía averiguar qué escribir bajo los casos en los que nada de lo que escribieron habría ayudado. Y una sesión expirada produce una serie de 401 que no dicen nada sobre la usabilidad y ahogarían todos los hallazgos que sí.
Leer el informe también recorta la tabla a sus cinco mil filas más recientes. Las negativas son respuestas 4xx, por lo que crece lentamente, y la lectura es un momento natural para ordenar: no hay temporizador y nada que se ejecute cuando nadie esté mirando.
GET /api/friction devuelve lo mismo para cualquiera que prefiera leerlo como JSON.
18.10 Registros
Perfuse registra en la salida estándar, que es donde un supervisor o un tiempo de ejecución de contenedor lo recopilará.
El detalle a nivel de mensaje va al almacén de mensajes en lugar del registro, deliberadamente. Una línea de registro por mensaje en una fuente de datos ocupada es ruido que oculta las líneas que importan, y el almacén es searchable de maneras que un archivo de registro no lo es.
El registro lleva lo que el almacén no puede: inicio y cierre, resultados de carga de configuración, cambios de estado de canal y errores que no se pueden atribuir a un mensaje específico.
18.11 Ejecutar dos instancias
Dos o más servidores pueden ejecutarse como un clúster: cuando el que ejecuta un flujo se detiene, otro lo toma dentro de unos pocos segundos, y el siguiente intento del remitente lo alcanza. Comparten una base de datos PostgreSQL en lugar de que cada uno mantenga un archivo SQLite , y acuerdan entre ellos, a través de ella, quién ejecuta qué.
perfuse serve -db 'postgres://perfuse:SECRET@db.hospital.internal/perfuse?sslmode=verify-full' \
-cluster active-passive -node-name perfuse-01 -node-address https://perfuse-01.hospital.internal:8443 \
-data /var/lib/perfuse
Inicie cada servidor de la misma manera con su propio nombre y dirección. La cuenta de la base de datos necesita crear tablas y, una vez, la extensión citext (o un administrador ejecuta CREATE EXTENSION citext primero). Cada servidor mantiene sus propios archivos en -data: su registro, su id de nodo y sus copias de la configuración.
Dos formas de compartir el trabajo.
active-passive: el líder ejecuta cada canal y cada trabajo que debe ejecutarse una vez (notificaciones de suscripción de FHIR). Los
demás esperan. El más simple de razonar, y lo que un sitio con un servidor ocupado y uno de respaldo quiere.
active-active: los canales se distribuyen entre los servidores en vivo, más o menos uniformemente. Un servidor que se detiene pasa sus canales
al resto; uno que se une toma su parte a medida que los canales están libres, uno a la vez. Los canales que se enrutan entre sí (un destino de canal) se ejecutan en un servidor y se mueven juntos, porque el enrutamiento ocurre dentro de un servidor. Un administrador puede mover un canal a mano desde Monitorear → Flota; permanece donde se colocó mientras ese servidor viva.
Quién ejecuta qué, y ningún momento con dos. Cada pieza de trabajo es un contrato de arrendamiento en la base de datos, renovado cada quinta parte de -cluster-lease (15 s por defecto). Un servidor que no puede renovarlo (se estrelló, su red falló, la base de datos es inaccesible) detiene ese trabajo por sí solo una vez que su propio reloj dice que el contrato de arrendamiento está a punto de terminar, con un tercio del contrato de arrendamiento de margen. Otro servidor toma el contrato de arrendamiento solo después de que su propio reloj haya visto que el contador del contrato de arrendamiento se ha mantenido quieto durante todo el contrato de arrendamiento. No se compara ningún reloj con otro, por lo que los relojes de los servidores y la base de datos no necesitan estar de acuerdo; solo necesitan funcionar a una velocidad similar, lo que hace cada reloj. Entre el antiguo titular que se detiene y el nuevo que comienza, hay una breve brecha con nadie escuchando, que un remitente ve como una conexión rechazada y vuelve a intentarlo; nunca hay un momento con dos escuchando en un flujo o dos trabajadores drenando una cola.
Un servidor que se apaga limpiamente, o un administrador que elige Retroceder en el líder, entrega de inmediato en lugar de después del contrato de arrendamiento.
Qué comparte cada servidor. Los mensajes, la cola de entrega, las cuentas y las sesiones, el registro de auditoría, el almacenamiento de FHIR, las tablas de terminología y el índice de pacientes están en la base de datos, por lo que cualquier servidor que ejecute un canal encuentra la cola que el último dejó. Los archivos de canal, el archivo de configuración, las reglas de alerta, los archivos de inicio de sesión único, el logotipo, los certificados Direct y las vistas guardadas se mantienen iguales en cada servidor a través de la base de datos: cada uno mantiene su propia copia en su propio disco, un cambio en cualquier servidor (en la consola, a mano, por un git pull) alcanza a los demás dentro de dos segundos, y un canal cambiado en otro servidor se reinicia con su nuevo archivo. El mismo archivo cambiado en dos servidores al mismo tiempo mantiene la versión que alcanzó la base de datos primero, y el otro al lado como NAME.conflict-SERVER. La configuración que solo se aplica en un reinicio (la base de datos de mensajes, los puertos que la consola escucha) se aplica en cada servidor en su próximo reinicio, y el registro lo dice. Lo que nombra algo en una máquina permanece en la línea de comandos del servidor y fuera del archivo compartido: el certificado y la clave (-tls-cert, -tls-key), la dirección de escucha de SNMP y el nombre del servidor en las trazas. La configuración los muestra como establecidos en cada servidor.
Iniciar y detener. Iniciar o detener un canal en cualquier servidor se aplica al clúster: el servidor que lo ejecuta lo inicia o detiene dentro de un segundo, y la respuesta dice qué servidor es ese.
El equilibrador de carga delante. La dirección de un flujo debe llegar al servidor que lo ejecuta. Apunte al remitente a una dirección virtual (un equilibrador de carga, una IP flotante, un nombre de DNS con un TTL corto) y haga que el equilibrador de carga verifique /readyz?channel=NAME, que responde 200 solo en el servidor que ejecuta ese canal, y 503 en los demás. Para la consola, /readyz es 200 en cada servidor vivo; /readyz?role=leader solo en el líder.
En un AWS Network Load Balancer, utilice un grupo de destino por alimentación con una comprobación de estado HTTPS en /readyz?channel=NAME, un intervalo de 5 segundos y un umbral de 2, y active la distribución de carga entre zonas. Con ella desactivada, el nodo del equilibrador de carga en una zona donde no se ejecuta ningún servidor de la alimentación solo ve objetivos no saludables, los envía de todos modos y un remitente que se resolvió a ese nodo espera hasta que la alimentación regrese a su zona (95 segundos en una prueba). Establezca el retraso de desregistración en 0, ya que un servidor que deja de ejecutar una alimentación no tiene nada que drenar.
Lo que se probó. Dos servidores en PostgreSQL 16, un remitente que transmite 300 mensajes, reenviando hasta que se respondan, y el servidor que ejecuta la alimentación se mata con SIGKILL dos veces (la primera, y luego la segunda después de que la primera regresara). Cada mensaje reconocido llegó al receptor, en ambas formas intentadas: entregado antes del reconocimiento y encolado en el almacén compartido mientras el receptor lo rechazaba (185 mensajes reconocidos esperaban en la cola en el momento del cese; el superviviente los entregó todos). El tiempo de conmutación por error tomó de 3 a 4 segundos en un contrato de arrendamiento de 3 segundos. Es posible un duplicado y se vio una vez en 600: un mensaje que el servidor moribundo entregó y no pudo reconocer, que el remitente reenvió. Las propias pruebas del coordinador cubren un bloqueo, un servidor desconectado de la base de datos mientras se ejecuta, degradación, propagación, movimientos y configuración compartida con conflictos. Se ejecutan en CI contra PostgreSQL con el detector de carreras, con la suite completa.
Lo mismo se hizo luego en AWS: dos servidores Linux (ARM) y un Windows Server 2022 en tres zonas de disponibilidad, Amazon RDS PostgreSQL 16 sobre TLS con el certificado verificado (sslmode=verify-full), un equilibrador de carga de red interno al frente y remitentes que reenvían hasta que se respondan:
| Prueba | Mensajes | Reconocidos pero perdidos | Duplicados | Tiempo máximo que un remitente esperó |
|---|
|---|---|---|---|---| | Activo-pasivo, el servidor que ejecuta el flujo de datos asesinado dos veces (Linux, luego Windows), receptor que se niega en medio | 2,000 | 0 | 0 | 15 s | | Un líder Linux cortado de la base de datos (cortafuegos), su proceso dejado en ejecución | 3,000 | 0 | 1 | 16 s | | Un líder Windows cortado de la base de datos | 3,000 | 0 | 0 | 20 s | | El líder asesinado mientras el reloj de un servidor se ejecutaba 2 minutos rápido y otro 90 segundos lento | 2,000 | 0 | 0 | 18 s | | Activo-activo, cuatro flujos de datos sobre tres servidores, uno asesinado y vuelto a iniciar | 6,000 | 0 | 2 | 24 s |
Con el contrato de arrendamiento predeterminado de 15 segundos, un servidor desconectado de la base de datos detuvo su alimentación 10 segundos después de su última renovación, y el siguiente servidor la inició 6 a 8 segundos después de eso: nunca ambos al mismo tiempo. Los relojes sesgados no cambiaron nada, ya que no se compara ningún reloj con otro.
Todavía no. Un clúster sirve a una organización: -cluster con -multi-tenant se rechaza. Los oyentes se mueven con su canal, así que el equilibrador de carga es del sitio para proporcionarlo.
Un servidor solo sigue funcionando como siempre, en SQLite, sin clúster en absoluto.
18.12 Más de un servidor
Un sitio con dos instancias tiene una pregunta que ninguna consola individual puede responder: ¿está todo en ejecución? Mirth cobra por la respuesta. Esta es la vista de la flota.
Una instancia se nombra como el lugar donde se busca. Se sondrea a los demás y muestra cada canal en cada servidor en una tabla, con un resumen a través de todos ellos.
18.12.1 Agregar un par
Configuración → Flota, o PUT /api/fleet/peers. Un par necesita un nombre, una URL y un token emitido por el par mismo.
El nombre es obligatorio y no es la URL, porque una URL no es algo que alguien reconozca a las tres de la mañana.
El token debe crearse en el par, bajo Usuarios, y debe tener un alcance de visualizador. Agregar un par sin uno se rechaza, con esas instrucciones, en lugar de ser aceptado y luego fallar silenciosamente en cada sondeo. Leer el estado de salud de otra instancia es el privilegio más pequeño que existe, y es el único que necesita la vista de flota.
18.12.2 Controlar un par, que está apagado
allow_control permite iniciar y detener los canales de ese par desde aquí. Está desactivado por default y cada uso es auditado por nombre.
Los dos privilegios están separados deliberadamente. Leer el estado de salud de otro servidor es menor; detener sus canales durante una transfusión no lo es, y el segundo nunca debe llegar silenciosamente adjunto al primero.
18.12.3 Lo que dice el informe
Cada servidor informa el número total de canales, cuántos están en ejecución, detenidos o con errores, la profundidad de la cola y la edad del mensaje en espera más antiguo, cuántas alertas están activadas, y si se está drenando para apagar.
Las cuentas de rollup cuentan los servidores de tres maneras: accesibles, inaccesibles y indeterminados. La tercera cubre a los pares cuya salud nadie conoce: no se ha sondeado aún, se niega al token o está ejecutando una versión que esta no puede leer. Se mantiene separada de los inaccesibles porque "no podemos decir" no es lo mismo que "está caído", y combinar los dos es cómo una página de flota comienza a mentir.
Por la misma razón, el resumen lleva knownFrom: cuántas instancias se leyeron realmente los números del canal y la cola. Doce canales en ejecución en una flota significan algo diferente cuando dos de cinco servidores no respondieron, y el total solo no puede decirlo.
Un par inaccesible dice por qué en términos que un operador puede actuar. Una conexión rechazada se informa como un host que está arriba con nada escuchando en ese puerto, lo que es un problema diferente a un host que no responde en absoluto.
18.12.4 Desfase de reloj
El resumen informa la mayor diferencia entre los relojes de los servidores en él. Vale la pena un número propio: correlacionar un incidente a través de dos instancias cuyos relojes no están de acuerdo por cuatro minutos produce una secuencia de eventos que no sucedió.
18.12.5 Lo que un par aprende sobre usted
Recuentos y tasas. El informe que una instancia da a otra no lleva identificadores de mensajes, no detalles a nivel de canal y no datos de pacientes de ningún tipo.
Eso es lo que evita que una vista de flota sea una centralización de datos clínicos: la instancia que agrega aprende cuántos canales están en ejecución y nada en absoluto sobre lo que fluyó a través de ellos. Si desea los mensajes, abre la consola propia del servidor, donde el registro de auditoría registra que lo hizo.
18.12.6 Un par apuntando a sí mismo
Rechazado. Una instancia que sondea su propia dirección a través de su propia pila HTTP aparece dos veces en su propia vista de flota y cuenta dos veces cada canal que tiene.
18.12.7 TLS en redes internas
insecure_skip_verify acepta el certificado de un par sin verificarlo. Existe porque la infraestructura hospitalaria se ejecuta en autoridades de certificación privadas, y rechazar trabajar en absoluto empujaría a las personas hacia HTTP plano, que es peor. Está nombrado para que no pueda confundirse con una buena idea.
19 La línea de comandos
Todo lo que Perfuse hace a un sistema en ejecución es accesible desde la interfaz web, y eso es deliberado: una característica que necesita una terminal se trata como un defecto en lugar de una elección de diseño. Los comandos aquí son para el trabajo que sucede *alrededor* de un sistema en ejecución — antes de que exista, mientras decide si confiar en él, y cuando necesita entregar algo a alguien más.
Varios de ellos nunca tocan un servidor en absoluto. perfuse explain, perfuse profile, perfuse deident y perfuse translate leen archivos y escriben archivos, por lo que son útiles en una laptop contra un patrimonio que todavía funciona enteramente con algo más.
Ejecute cualquier comando con -h para sus indicadores. Este capítulo dice para qué es cada uno.
-h se genera a partir del código y no puede quedar desactualizado; una lista copiada en la prosa puede.19.1 Canalizando canales
19.1.1 perfuse serve
Ejecuta la interfaz web y la API, y por defecto ejecuta los canales también. Este es el comando que utiliza una instalación.
Toda opción que acepta se enumera en Opciones del servidor, generado desde el programa. Se niega a servir HTTP plano a cualquier cosa excepto loopback a menos que se pase -insecure. Eso no es una configuración de cortesía: el tráfico clínico y una cookie de sesión sobre una conexión de LAN sin cifrar es un evento informable, y la bandera existe para que decir sí sea una decisión que alguien tomó en lugar de un valor predeterminado que heredó. Consulte seguridad.
19.1.2 perfuse run
Ejecuta canales desde YAML sin interfaz web y sin base de datos. Útil en un contenedor que debe hacer exactamente una cosa, y útil cuando desea ver el comportamiento de un canal sin que nada más esté ejecutándose.
19.1.3 perfuse check
Valida las definiciones de canales e imprime lo que hacen, sin ejecutarlos. La segunda mitad es el punto: un canal que se carga no es necesariamente el canal que se pretendía, y leer un resumen en inglés plano de lo que un archivo hará detecta un error que la validación no puede ver.
Sale con un código de error distinto de cero en caso de error, por lo que pertenece a cualquier comprobación de su configuración antes de que llegue a un servidor. -quiet no imprime nada en caso de éxito, para ese uso.
-allow-metadata-egress permite destinos apuntando a direcciones de metadatos de instancias en la nube. Esas direcciones contienen las credenciales de la propia máquina, por lo que un canal que pueda ser convencido para enviar allí es una fuga de credenciales; la comprobación las rechaza a menos que usted diga lo contrario.
19.1.4 perfuse test
Ejecuta pruebas escritas contra definiciones de canales. Una prueba envía un mensaje a través del filtro real, las transformaciones reales y los scripts reales del canal, en el orden real, con solo la red reemplazada — así que lo que pasa aquí es lo que el canal hace.
Las pruebas viven en *_test.yaml o *.test.yaml junto a los canales. Consulte pruebas.
19.2 Migración desde otro motor
Estos tres son los que se deben ejecutar primero, antes de decidir nada. Consulte migración.
19.2.1 perfuse explain
Lee exportaciones de Mirth, Open Integration Engine y BridgeLink — un canal, un grupo de canales o una copia de seguridad completa del servidor — y describe qué hace cada canal y qué bloquearía una migración. No cambia nada y no necesita una instalación de Perfuse, lo que lo convierte en el primer paso más barato posible: apúntelo a una exportación de su propiedad y lea lo que regresa.
-strict sale con un código de error distinto de cero si algo está bloqueado, por lo que puede controlar una tubería. -json emite los mismos hallazgos para una máquina.
19.2.2 perfuse translate
Convierte exportaciones de Mirth, Open Integration Engine y BridgeLink en archivos de canales de Perfuse: un canal, un grupo de canales, una exportación de biblioteca de plantillas de código o una copia de seguridad completa del servidor.
perfuse translate -o ./channels server-backup.xml
Las bibliotecas de plantillas de código se escriben en lib/ junto a los canales, y cada canal de Mirth incluye una biblioteca habilitada para ella. Consulte migración.
Nada se descarta silenciosamente. Cada parte de un canal es traducida, transportada como un script que se ejecuta sin cambios o informada como necesitada de un humano. La tercera categoría es la honesta — un traductor que produjera un archivo limpio para cada entrada estaría ocultando las decisiones que más necesita tomar.
19.2.3 perfuse compare
Ejecuta el mismo tráfico a través de dos motores y informa dónde no están de acuerdo, agrupados por campo.
Este es el comando que responde a "¿por qué debería confiar en esto con datos de pacientes?", y responde con evidencia que ha recopilado en lugar de una afirmación hecha aquí. Es el último paso recomendado antes de cambiar y el primer paso recomendado en cualquier argumento sobre si Perfuse está listo.
19.3 Comprender una fuente de datos
19.3.1 perfil de perfuse
Lee los mensajes e informa qué hay en ellos en realidad: qué campos siempre están poblados, cuáles nunca lo están, los conjuntos de valores reales de los campos codificados, los recuentos de repetición y qué segmentos Z aparecen.
Los valores solo se informan para los campos cuyos valores forman un conjunto de código pequeño, por lo que un perfil de tráfico real no se convierte en un archivo lleno de datos de pacientes.
Guardar un perfil y compararlo más tarde es cómo se descubre que un remitente cambió algo:
perfuse perfil -guardar last-month.json corpus/ perfuse perfil -contra last-month.json new/
19.3.2 contrato de perfuse
Convierte la salida de esa reflexión en algo ejecutable. Un contrato dice cómo debe lucir una fuente de datos, para que el día que cambie se le informe, en lugar de enterarse semanas después de un receptor que se detuvo — los mensajes siguen siendo válidos HL7 cuando un campo desaparece, por lo que nada más lo nota.
perfuse contrato promocionar -o adt.contrato.yaml corpus/ perfuse contrato verificar adt.contrato.yaml today/
promote genera un punto de partida, no una respuesta. Léalo y elimine la mayoría de él: cada línea registra si se midió o decidió, lo que le dice qué líneas mantener.
19.4 Producción de mensajes
19.4.1 perfuse generar
Produce mensajes sintéticos HL7 v2. Todo es inventado — nombres obviamente ficticios, identificadores secuenciales, direcciones inventadas — por lo que la salida es segura para comprometer, enviar por correo y adjuntar a un ticket.
Puede enviar directamente a un canal en lugar de escribir un archivo, lo que es la forma más rápida de probar que un oyente funciona:
perfuse generar -n 100 -enviar 127.0.0.1:6661
19.4.2 perfuse desidentificar
Lee los mensajes reales y escribe un corpus que mantiene su estructura, sus códigos y sus intervalos, y nada de lo que identifica a un paciente. Esta es la diferencia entre poder enviar a alguien una reproducción y no.
No es la misma herramienta que perfuse generate y la distinción importa. Los mensajes generados se inventan y, por lo tanto, son seguros pero poco realistas; un corpus desidentificado se *deriva del tráfico real*, por lo que reproduce las rarezas que realmente rompen las interfaces.
Dos propiedades que vale la pena comprender antes de usarlo:
- La misma sal siempre produce el mismo corpus, por lo que un corpus compartido se puede regenerar en lugar de archivarse.
- Mantenga la sal en secreto. Los seudónimos se derivan de valores reales, por lo que cualquier persona que tenga la sal puede confirmar una suposición sobre a quién se refiere un registro.
-keep-local-segments pasa los segmentos Z sin cambios y es inseguro. Los segmentos Z son exactamente donde los sitios ponen nombres, notas y texto libre, así que mantenerlos derrota el propósito de la herramienta. Existe porque un problema estructural a veces está *en* un segmento Z, y entonces necesitas un corpus que no debes compartir.19.5 Mover mensajes a mano
19.5.1 perfuse escuchar
Acepta HL7 v2 sobre MLLP y lo confirma. Un receptor que existe durante el tiempo que lo necesite, para probar que un remitente puede llegar a esta máquina en absoluto.
19.5.2 enviar perfuse
Envía mensajes HL7 v2 sobre MLLP desde archivos.
19.6 FHIR
19.6.1 perfuse fhir
Convierte HL7 v2 a FHIR, valida FHIR o lo sirve, dependiendo del subcomando.
El subcomando de validación es útil por sí solo: le dirá si un recurso que alguien le envió satisface US Core, que es el perfil contra el que se escribe la regulación estadounidense. Ser capaz de responder a eso sin adoptar un motor es el punto.
19.6.2 perfuse x12
Converts X12 (or other delimited EDI) to the XML Mirth Connect and Open Integration Engine write for it, and back: perfuse x12 xml FILE and perfuse x12 from-xml FILE, standard input when no file is given. The output is what OIE 4.6 writes, byte for byte, so it can be compared with what a Mirth server produced or fed to a system built against it. A channel does the same with x12.arrives_as: xml and a destination's x12_as: xml; see X12.
19.6.3 perfuse sof
Ejecuta un SQL en FHIR ViewDefinition sobre archivos FHIR (el NDJSON de una exportación masiva, Bundles o matrices JSON; - para stdin) y escribe la tabla: -view FILE (requerido), -format csv|ndjson|json|parquet (csv por defecto), -header=false, y -o FILE (requerido para Parquet). Vea SQL on FHIR views.
19.6.4 perfuse measure
Evalúa una medida de calidad con criterios FHIRPath sobre archivos FHIR (el NDJSON de una exportación masiva, Bundles o matrices JSON; - para stdin) y escribe el MeasureReport. Indicadores:
-measure FILE, o-measure example:IDpara un ejemplo enviado: obligatorio.-examplesenumera los ejemplos.-period-starty-period-end: obligatorio.-type summary|subject|subject-list.-subject Patient/ID.-reporter NAME.-o FILE.
Consulte medidas de calidad digitales.
19.6.5 Indicadores FHIR en perfuse serve
| Bandera | Efecto |
|---|---|
-fhir | Servir el punto de conexión FHIR en /fhir. Activado por defecto. |
-fhir-version | La versión servida. R4 por defecto. |
-fhir-read-only | Rechazar escrituras. |
-fhir-api-tokens | Con SMART configurado, aceptar tokens de API de Perfuse, así como tokens SMART (desactivado por defecto; también en Configuración → FHIR). |
-fhir-bulk-export | Habilitar $export. Desactivado por defecto. |
-fhir-subscriptions | Habilitar suscripciones de temas: notificaciones de encuentro y cita a puntos de conexión que los clientes FHIR eligen. Desactivado por defecto; necesita R4 y un punto de conexión editable. |
-fhir-subscriptions-allow-http | Permitir que las suscripciones entreguen a http simple. Para un receptor de prueba, nunca para datos de paciente en una red. |
-fhir-patient-directory-search | Permitir que un token limitado a un paciente busque el directorio (profesionales de la salud, organizaciones, ubicaciones), dentro de sus ámbitos. Desactivado por defecto. |
-fhir-declares | Guías que este servidor declara en su declaración de capacidad, separadas por comas: la dirección de la Declaración de capacidad de una guía, o Type=profile dirección. Una declaración para clientes y kits de prueba; no cambia nada que el almacén acepte. |
La tienda mantiene documentos así como recursos: un documento, una colección o un paquete de mensaje Bundle enviado a /fhir/Bundle/{id} se mantiene entero, se lee con cada entrada en la versión servida y se encuentra por type, identifier, timestamp y composition. Una transacción enviada allí se mantiene como un registro y no se aplica; un POST a /fhir aplica uno.
Las respuestas son JSON a menos que el cliente solicite XML (Accept: application/fhir+xml, o _format=xml). Perfuse lee solo JSON: un cuerpo enviado como XML obtiene 415. Un paquete de documentos POSTeado a /fhir se mantiene cuando este servidor mantiene, por identificador, el paciente y el encuentro que su Composición nombra, y se rechaza con 422 de lo contrario. Una lectura binaria con Accept establecida en su propio tipo de contenido responde con el contenido en sí.
19.7 Configuración y operación
19.7.1 perfuse init
Crea el diseño de directorios, un canal de ejemplo y una definición de servicio para el sistema operativo en el que se ejecuta. Nada existente se reemplaza a menos que se pase -force.
perfuse init -dir /opt/perfuse -service systemd
19.7.2 perfuse service
Registra Perfuse como un servicio de Windows, lo elimina o informa si está instalado y en ejecución. Solo para Windows; en Linux, el equivalente es el archivo de unidad que perfuse init puede escribir para usted. Consulte operaciones.
19.7.3 perfuse token
Emite credenciales para máquinas en lugar de personas. Un token no caduca y no se ve afectado por que alguien cierre la sesión, lo que es lo que un servidor que sondea a su vecino necesita y lo que una sesión del navegador deliberadamente no es.
perfuse token create -db perfuse.db -label fleet-from-site-a -role viewer perfuse token list -db perfuse.db perfuse token revoke -db perfuse.db -label fleet-from-site-a
Solo se almacena el hash, por lo que un token se muestra una vez cuando se crea y no se puede recuperar después. Un token robado de la base de datos, por lo tanto, no produce un token utilizable. Asigne a un token el rol más bajo que haga el trabajo: una flota de vistas necesita viewer y nada que solo lea debe contener nada más.
-fhir-groups riverside-attributed,riverside-north limita un token a esos grupos FHIR, para una exportación de acceso de proveedor CMS-0057: en el punto de conexión FHIR puede leer esos grupos y exportarlos, y nada más. Consulte CMS-0057 para pagadores.
19.7.4 Perfuse athena
Imprime la tabla CREATE EXTERNAL TABLE de Amazon Athena para un archivo S3 escrito con format: ndjson, utilizando la proyección de partición en la parte dt=YYYY-MM-DD de la clave, para que no tenga que ejecutarse ningún rastreador a medida que se agregan días.
Perfuse athena -bucket hospital-archive -destination archive
Se imprime en lugar de ejecutarse, porque crear una tabla requiere permisos de Athena y Glue que este programa no debe tener.
19.7.5 Perfuse sbom
Enumera todo lo que se vincula al binario, leído desde la propia compilación en lugar de desde go.mod — por lo que describe el binario que se encuentra frente a usted y no lo que alguien solicitó una vez.
-json emite CycloneDX y -spdx emite etiqueta-valor SPDX, que es lo que normalmente solicita una revisión de adquisiciones o seguridad. El código del navegador incrustado en el binario se incluye, porque un revisor que coincide con un binario contra avisos necesita saber que contiene una aplicación React.
19.7.6 Perfuse versión
Imprime la versión y el commit desde el que se construyó. Vale la pena incluir en un ticket: "la versión actual" no es una versión, y dos máquinas que se instalaron con una semana de diferencia a menudo no ejecutan la misma compilación.
20 Opciones del servidor
Toda opción que perfuse serve acepta, generada a partir del propio programa, por lo que esta lista no puede nombrar una opción que no exista o dejar una fuera. La mayoría también son una configuración en la interfaz web, nombrada en la última columna: un valor guardado allí gana sobre la marca en el siguiente inicio, y la marca sigue siendo la forma de establecerlo desde una definición de servicio. La dirección de escucha, la base de datos y el directorio del canal permanecen como marcas solo, porque dicen dónde encuentra el servidor lo que estaría editando.
Los secretos nunca son marcas. Una opción que necesita una contraseña o un token nombra una variable de entorno que la contiene.
20.1 Todas las opciones
| Option | Takes | Default | What it does | In the console |
|---|---|---|---|---|
-addr | string | 127.0.0.1:8080, or 127.0.0.1:8443 with -tls-cert | address to serve the interface on | — |
-alert-severity | string | warning | minimum severity to send to the webhook: warning or critical | Settings › Alerts › Delivery: Only send alerts at least this serious |
-alert-webhook | string | — | POST alerts to this URL as JSON. Works with Slack, Teams, Mattermost, Alertmanager or anything that accepts a webhook. The payload carries channel names, counts and thresholds; never message content | Settings › Alerts › Delivery: Send alerts to this address |
-alerts | string | — | YAML file of alert rules. Default: alerts.yaml beside the database, written when the rules are first saved in the console; until then a conservative default set is used | — |
-allow-metadata-egress | on/off | — | permit destinations pointed at cloud instance metadata addresses, which hold this machine's credentials | Settings › Security › What may leave: Allow destinations at cloud instance metadata addresses |
-cds-clients | string | — | the EHRs trusted to call the CDS services with signed JWTs (issuer and jwks_url) | Settings › Payers › Prior authorization: EHRs trusted to call the CDS services |
-channels | string | ./channels | directory holding channel files | — |
-cluster | string | — | share the work with other servers on the same PostgreSQL -db: active-passive (one runs everything, another takes over when it stops) or active-active (channels spread over all) | — |
-cluster-lease | duration | 15s | how long a crashed server holds its work before another takes it over; renewed five times as often | — |
-crd-advisor-ca | string | — | a PEM file of certificate authorities to trust for -crd-advisor-url beside the system's (Vaglio's is its DATA/tls/ca.crt) | Settings › Payers › Decision service: Certificate authorities to trust for the advice service |
-crd-advisor-token-env | string | — | name of an environment variable holding a bearer token for -crd-advisor-url (the token itself is never a flag) | Settings › Payers › Decision service: Environment variable holding the advice service's token |
-crd-advisor-url | string | — | with -crd-rules, ask the decision service at this URL (such as Vaglio's /api/pas/advise) what a prior authorization request for each order needing one would be told as the record stands, shown as one more CRD card; the prefetched record is POSTed to it. No answer, or any failure, leaves the cards as they are without it | Settings › Payers › Decision service: Advice before submitting (CRD) |
-crd-rules | string | — | a Da Vinci CRD rules file: serve coverage requirements over CDS Hooks at /cds-services | Settings › Payers › Prior authorization: Coverage rules file (Da Vinci CRD) |
-dashboards | string | — | a file mapping directory groups and roles to the dashboard each opens on | Settings › Monitoring › Dashboards: Which dashboard each group opens on (file) |
-data | string | — | directory for this server's own files (settings, alerts, sign-in configuration, the log): default beside -db when it is a file, the current directory when it is a PostgreSQL URL | — |
-davinci-version | string | 2.2 | the Da Vinci CRD, DTR and PAS release answered when neither a request nor its client's registration says: 2.2 (CRD 2.2.1, DTR 2.2.0, PAS 2.2.1) or 2.1 (2.1.0) | — |
-db | string | ./perfuse.db | database for users, messages and stored resources: a file, or a postgres:// URL for servers that share one store (a cluster) | — |
-demo-account | string | — | for a public demonstration server: username:password of a shared viewer account shown on the sign-in page | — |
-dicomweb-archive | string | — | keep imaging objects in this directory and serve them over DICOMweb (QIDO-RS, WADO-RS, WADO-URI, STOW-RS) at /dicomweb | Settings › Data › Imaging archive: Keep images in this folder and serve them over DICOMweb |
-dicomweb-cors | string | — | comma-separated browser origins, such as an OHIF viewer's, allowed to call /dicomweb | Settings › Data › Imaging archive: Browser origins allowed to call it |
-drain-for | duration | — | after SIGTERM, report not-ready for this long before shutting down (for rolling deploys) | Settings › Engine › Shutdown: Wait for in-flight messages on shutdown |
-ecr-agency | string | — | play a public health agency for testing eCR: answer case reports at $process-message with a Reportability Response, using the agency YAML file given (name, phone, address, endpoint; optional rctc, reply) | Settings › Public health › Case reporting: Act as a test public health agency |
-ecr-responses | on/off | — | accept eCR Reportability Responses at the FHIR endpoint's $process-message, storing each as DocumentReference/<id> derived from the eICR it answers | Settings › Public health › Case reporting: Receive Reportability Responses |
-engine | on/off | true | run the channel engine in this process, so the interface can start, stop and monitor channels | — |
-epa-payer-id | string | — | this payer's identifier on the prescribing network, the From of every RTPB and ePA response (default: the formulary's payer id) | Settings › Payers › Drug formulary: Payer ID on the prescribing network |
-epa-reply-channel | string | — | route ePA decisions made after the request was answered (a reviewer's, an appeal's) to this channel, which delivers them to the prescriber's network | Settings › Payers › Drug formulary: Channel that sends later ePA decisions |
-fhir | on/off | true | serve a FHIR endpoint at /fhir | Settings › FHIR › The FHIR server: Serve a FHIR endpoint at /fhir |
-fhir-api-tokens | on/off | — | with SMART configured, accept Perfuse API tokens on the FHIR endpoint as well (unscoped; the console's FHIR settings can switch it) | Settings › FHIR › Access: Accept Perfuse API tokens beside SMART tokens |
-fhir-bulk-export | on/off | — | enable $export, which produces a file holding every record the FHIR endpoint serves | Settings › FHIR › Bulk data: Bulk Data export ($export) |
-fhir-declares | string | — | guides this FHIR server declares in its capability statement, comma-separated: a CapabilityStatement address, or Type=profile address | Settings › FHIR › Capability statement: Guides this server declares |
-fhir-max-resources | int | — | for a public server: refuse new FHIR resources once this many are held (0: no limit) | Settings › FHIR › Access: Most resources held |
-fhir-member-match-without-consent | on/off | — | let $member-match answer without an active Consent; the Payer-to-Payer API is opt-in, so only for testing | Settings › Payers › CMS-0057-F: Answer $member-match without the member's consent |
-fhir-open | on/off | — | serve the FHIR endpoint with no authentication at all, exposing every stored record | Settings › FHIR › Access: Let anyone read the FHIR endpoint without signing in |
-fhir-patient-directory-search | on/off | — | let a token limited to one patient search the directory (practitioners, organisations, locations) within its scopes | Settings › FHIR › Access: Let a patient's app search the directory |
-fhir-payer-apis | on/off | — | enable the CMS-0057 payer operations: $member-match (Payer-to-Payer) and Group $davinci-data-export (Provider Access); the Group exports also need -fhir-bulk-export | Settings › Payers › CMS-0057-F: The CMS-0057-F payer APIs |
-fhir-read-only | on/off | — | refuse writes to the FHIR endpoint | Settings › FHIR › Access: Refuse writes to the FHIR endpoint |
-fhir-subscriptions | on/off | — | enable topic-based subscriptions, which send encounter and appointment notifications to URLs FHIR clients choose | Settings › FHIR › Notifications: FHIR subscriptions (encounter and appointment notifications) |
-fhir-subscriptions-allow-http | on/off | — | let subscriptions deliver to plain-http endpoints; for a test receiver, never for patient data on a network | Settings › FHIR › Notifications: Allow subscriptions to notify plain-HTTP addresses |
-fhir-version | string | 4.0.1 | FHIR release to serve: R4, R4B or R5 | Settings › FHIR › The FHIR server: FHIR version clients see |
-fhir-writes-per-minute | int | — | for a public server: writes one client address may make to the FHIR endpoint a minute (0: no limit) | Settings › FHIR › Access: Writes a minute from one client |
-fleet-label | string | — | how this instance names itself in a fleet view (default: hostname) | Settings › Fleet › This server: Name for this server |
-formulary | string | — | publish this drug formulary over the Da Vinci US Drug Formulary API at /formulary/fhir and answer NCPDP Real-Time Prescription Benefit and SCRIPT electronic prior authorization from it | Settings › Payers › Drug formulary: Drug formulary file |
-icd11-client-id | string | — | a WHO ICD API client: ICD-11 codes not imported are looked up there | Settings › Data › Code systems: WHO ICD API client ID |
-icd11-client-secret | string | — | the WHO ICD API client's secret | Settings › Data › Code systems: WHO ICD API client secret |
-icd11-release | string | 2025-01 | the ICD-11 MMS release the WHO ICD API is asked about | Settings › Data › Code systems: ICD-11 release to ask for |
-icd11-url | string | — | the ICD API's address when it is not the WHO's: the WHO's ICD-API container on your network needs no client | Settings › Data › Code systems: ICD API address |
-index-identity | on/off | true | index patient identifiers from message contents so a message can be found by MRN, name or accession in any format; has no effect when -store-payloads is off | Settings › Data › What is kept: Index patient identifiers |
-insecure | on/off | — | serve over plain HTTP on a non-loopback address (not recommended) | — |
-inventory | string | — | write a Markdown inventory of every interface to this file, and keep it current | Settings › Monitoring › Inventory: Write an interface inventory to |
-inventory-every | duration | 24h0m0s | how often to rewrite the interface inventory | Settings › Monitoring › Inventory: Rewrite the inventory every |
-json-logs | on/off | — | emit structured JSON logs | Settings › Monitoring › Logging: Write logs as JSON |
-ldap | string | — | YAML file configuring directory (LDAP) sign-in (default: ldap.yaml beside the database, once saved in the console) | — |
-log-file | string | — | append the log, and the first administrator's one-time password, to this file (mode 0600) instead of the console. A Windows service has no console, so there it defaults to perfuse.log beside the database | — |
-mcp | on/off | — | serve the Model Context Protocol at /mcp, so AI agents with an API token can read status, messages, the queue, alerts and the FHIR store with that token's own access | Settings › Security › AI agents (MCP): Let AI agents connect over MCP |
-measure-reporter | string | Perfuse | the reporting organisation's name on MeasureReports from $evaluate-measure when the request names no reporter | — |
-metrics-open | on/off | — | serve /metrics to anybody who can reach the port. Only safe where that port is genuinely private: metric labels name your channels, and on a multi-tenant server they name every tenant's | Settings › Security › Metrics: Serve /metrics without a token |
-metrics-token | string | — | credential the Prometheus scrape endpoint requires. Give the scraper the same value in bearer_token_file. Without this, only a signed-in operator can read /metrics | Settings › Security › Metrics: Token a metrics scraper presents |
-mpi | on/off | — | keep a patient index (master patient index) in mpi.db beside the database: patient records from every system linked into one enterprise identifier per person, fed by mpi destinations and the PIX v3 identity feed, answering Patient/$match, PIXm, PIX v3 (/pixv3) and PDQ v3 (/pdqv3) | Settings › Data › Patient index: Keep a patient index |
-multi-tenant | on/off | — | keep each tenant's channels in their own subdirectory of -channels, so one server can run integration for several organisations without them being able to see each other | — |
-node-address | string | — | this server's console URL as the other servers and a load balancer reach it | — |
-node-name | string | — | this server's name in the cluster (default: an id kept in -data) | — |
-oidc | string | — | YAML file configuring OpenID Connect sign-in (default: oidc.yaml beside the database, once saved in the console) | — |
-pas | on/off | — | serve Da Vinci PAS 2.2.1 and 2.1.0 (Claim/$submit, $inquire, $decide) on the FHIR endpoint; decisions come from -crd-rules, and what no rule decides is pended for a reviewer. With -fhir-subscriptions, the PAS topic delivers each pended request's result | Settings › Payers › Prior authorization: Da Vinci PAS prior authorization server |
-pas-278-ca | string | — | a PEM file of certificate authorities to trust for the UM system beside the system's | Settings › Payers › Utilization management link: Certificate authorities to trust for the UM system |
-pas-278-cert | string | — | a PEM client certificate for X.509 mutual TLS with the UM system (CORE's authentication) | Settings › Payers › Utilization management link: Client certificate file |
-pas-278-core-receiver | string | — | the CORE envelope's ReceiverID, as the UM system's connectivity guide gives it (default: -pas-278-receiver) | Settings › Payers › Utilization management link: CORE envelope ReceiverID |
-pas-278-core-rule-version | string | — | what to write in the CORE envelope's CORERuleVersion when the UM system's guide asks for something other than the version itself (CMS esMD's sample says v2.2.0) | Settings › Payers › Utilization management link: CORERuleVersion written in the envelope |
-pas-278-core-sender | string | — | the CORE envelope's SenderID, as the UM system's connectivity guide gives it (default: -pas-278-sender) | Settings › Payers › Utilization management link: CORE envelope SenderID |
-pas-278-core-version | string | 2.2.0 | the CAQH CORE Connectivity Rule the UM system runs, for core-soap and core-mime: 2.2.0 (payload in the envelope) or C4.0.0 (payload as an MTOM attachment; SOAP only) | Settings › Payers › Utilization management link: CAQH CORE Connectivity Rule |
-pas-278-key | string | — | the private key (PEM) for -pas-278-cert | Settings › Payers › Utilization management link: Client certificate key file |
-pas-278-oauth-client-id | string | — | the OAuth client ID for -pas-278-oauth-token-url | Settings › Payers › Utilization management link: OAuth client ID |
-pas-278-oauth-scope | string | — | the scope asked for with the OAuth token, when the UM system wants one | Settings › Payers › Utilization management link: OAuth scope |
-pas-278-oauth-secret-env | string | — | name of an environment variable holding the OAuth client secret (the secret itself is never a flag) | Settings › Payers › Utilization management link: Environment variable holding the OAuth client secret |
-pas-278-oauth-token-url | string | — | an OAuth 2.0 token endpoint: a token got with the client-credentials grant is sent as a bearer token (CORE vC4.0.0's authorization) | Settings › Payers › Utilization management link: OAuth token endpoint |
-pas-278-password-env | string | — | name of an environment variable holding the password for -pas-278-user (the password itself is never a flag) | Settings › Payers › Utilization management link: Environment variable holding the password |
-pas-278-payer-id | string | — | the UMO's payer ID (NM1*X3) when the request's insurer carries no identifier | Settings › Payers › Utilization management link: Payer ID for the 278s |
-pas-278-production | on/off | — | mark the 278s sent as production (ISA15 P) rather than test | Settings › Payers › Utilization management link: Mark the 278s as production |
-pas-278-receiver | string | — | ISA08/GS03 receiver ID on the 278s sent to the UM system | Settings › Payers › Utilization management link: Receiver ID on the 278s |
-pas-278-sender | string | PERFUSE | ISA06/GS02 sender ID on the 278s sent to the UM system | Settings › Payers › Utilization management link: Sender ID on the 278s |
-pas-278-sftp | string | — | host:port of the UM system's SFTP server, for -pas-278-transport sftp | Settings › Payers › Utilization management link: SFTP server |
-pas-278-sftp-every | duration | 1m0s | how often the SFTP inbound folder is read | Settings › Payers › Utilization management link: Read the SFTP inbound folder every |
-pas-278-sftp-inbound | string | — | the SFTP folder the UM system's 278 responses, 999s and TA1s are collected from | Settings › Payers › Utilization management link: SFTP inbound folder |
-pas-278-sftp-key | string | — | an OpenSSH private key file for the SFTP user, instead of a password | Settings › Payers › Utilization management link: SFTP key file |
-pas-278-sftp-known-hosts | string | — | an OpenSSH known_hosts file holding the SFTP server's key; required, so requests never go to whoever answers | Settings › Payers › Utilization management link: SFTP known_hosts file |
-pas-278-sftp-outbound | string | — | the SFTP folder the 278 requests (and the 999s for the answers) are put in | Settings › Payers › Utilization management link: SFTP outbound folder |
-pas-278-sftp-password-env | string | — | name of an environment variable holding the SFTP password (the password itself is never a flag) | Settings › Payers › Utilization management link: Environment variable holding the SFTP password |
-pas-278-sftp-user | string | — | the SFTP user name | Settings › Payers › Utilization management link: SFTP user |
-pas-278-token-env | string | — | name of an environment variable holding a bearer token for the UM system (the token itself is never a flag) | Settings › Payers › Utilization management link: Environment variable holding the token |
-pas-278-transport | string | x12 | how the 278s reach the UM system: x12 (POSTed as application/edi-x12), core-soap (a CAQH CORE SOAP envelope), core-mime (CAQH CORE HTTP MIME multipart), or sftp (batch files over -pas-278-sftp, answers collected from its inbound folder) | Settings › Payers › Utilization management link: How the 278s travel |
-pas-278-url | string | — | forward every PAS request to the payer's utilization management system as an X12 278 request sent to this URL (by -pas-278-transport), and answer with its 278 response; later 278 responses go to Claim/$decide-278 | Settings › Payers › Utilization management link: Send PAS requests on to the UM system at |
-pas-278-user | string | — | user name for the UM system: CORE's UserName (WS-Security UsernameToken in SOAP), or HTTP Basic for x12 | Settings › Payers › Utilization management link: User name |
-pas-decider-ca | string | — | a PEM file of certificate authorities to trust for -pas-decider-url beside the system's, such as the payer's own authority or the decision service's (Vaglio's is its DATA/tls/ca.crt) | Settings › Payers › Decision service: Certificate authorities to trust for the decision service |
-pas-decider-token-env | string | — | name of an environment variable holding a bearer token for -pas-decider-url (the token itself is never a flag) | Settings › Payers › Decision service: Environment variable holding the decision service's token |
-pas-decider-url | string | — | ask the decision service at this URL (such as Vaglio) to decide each PAS item on the request's clinical evidence, when -crd-rules leave it to a reviewer; the PAS request Bundle is POSTed to it. Cannot be combined with -pas-278-url | Settings › Payers › Decision service: Decision service URL |
-passkey-allowed-models | string | — | comma-separated authenticator AAGUIDs that may enrol; requires -passkey-attestation-roots | — |
-passkey-attestation-roots | string | — | a PEM file of authenticator vendor certificates; passkey attestation is verified against these | — |
-passkey-domain | string | — | the bare domain passkeys are registered for, such as perfuse.example.org. No scheme, no port, no path. Setting it turns passkeys on; leaving it empty leaves them off | Settings › Sign-in › Passkeys: Domain passkeys are bound to |
-passkey-name | string | Perfuse | the name an authenticator shows a person when they register a passkey | Settings › Sign-in › Passkeys: Name shown on the device |
-passkey-origins | string | — | comma-separated origins the console is reached at, with scheme and port: https://perfuse.example.org. Defaults to https:// plus the domain | Settings › Sign-in › Passkeys: Other origins passkeys are accepted from |
-peers | string | — | YAML file listing other Perfuse instances to show in a fleet view | Settings › Fleet › Fleet: Other Perfuse servers to show (file) |
-public-url | string | — | the https address patients' phones and other organisations reach this server at, for the SMART Health Links it hosts (default: the listen address) | Settings › FHIR › The FHIR server: Address others reach this server at |
-rctc | string | — | eCR trigger codes for the case report preview: a FHIR ValueSet or Bundle of them, such as the eRSD | Settings › Public health › Case reporting: Trigger codes file (RCTC or eRSD) |
-residency | string | — | cloud regions this server's data may go to or come from: areas (eea, uk, ch) and region codes (eu-central-1), comma-separated; channels with connectors elsewhere are refused | Settings › Data › Data protection: Where data may go (cloud regions) |
-retention-days | int | 30 | how long to keep recorded messages; 0 keeps them for ever, which will eventually fill the disk | Settings › Data › How long it is kept: Delete records after |
-saml | string | — | YAML file configuring SAML 2.0 sign-in (default: saml.yaml beside the database, once saved in the console) | — |
-scim | on/off | — | accept SCIM 2.0 provisioning from an identity provider at /scim/v2. Okta, Entra ID and Google Workspace use it to create accounts and, more importantly, to disable them when somebody leaves | Settings › Sign-in › Automatic accounts: Let the identity provider manage accounts |
-scim-default-role | string | viewer | role for an account the identity provider does not describe. Defaults downward on purpose: an account whose role could not be determined should be able to read and nothing else | — |
-settings | string | — | settings file the web interface may edit (default: perfuse-settings.yaml beside the database) | — |
-shl-allow-http | on/off | — | let a received SMART Health Link point at plain HTTP; for testing only | Settings › Public health › Health links: Allow SMART Health Links over plain HTTP |
-signing-cert | string | — | certificate for signing documents (defaults to -tls-cert) | Settings › Security › Document signing: Certificate for signing documents |
-signing-key | string | — | key for signing documents (defaults to -tls-key) | Settings › Security › Document signing: Key for signing documents |
-smart-audience | string | — | the audience a SMART token must be issued for, normally the FHIR base URL | Settings › SMART › Authorization: Audience tokens must name |
-smart-authorize | string | — | the authorization endpoint to advertise to SMART apps | Settings › SMART › Authorization: Authorization endpoint |
-smart-backend-services | on/off | — | advertise that the SMART authorization server issues backend-services (client_credentials, signed JWT) tokens | Settings › SMART › Built-in server: Backend Services (system-to-system tokens) |
-smart-clients | string | — | run Perfuse's own SMART authorization server at /auth for the apps registered in this YAML file | Settings › SMART › Built-in server: Registered apps file (turns on the built-in server) |
-smart-issuer | string | — | accept SMART on FHIR access tokens from this authorization server | Settings › SMART › Authorization: Accept SMART access tokens from this authorization server |
-smart-jwks | string | — | where to fetch the SMART issuer's signing keys, when not from its published metadata (split DNS, a private address) | Settings › SMART › Authorization: Key set file |
-smart-jwks-uri | string | — | the authorization server's public key set URL, advertised to SMART apps (needed to claim OpenID Connect sign-in) | Settings › SMART › Authorization: Key set address |
-smart-key | string | smart-signing.key | the built-in authorization server's RSA signing key (PEM, mode 0600), created if missing | Settings › SMART › Built-in server: Signing key file |
-smart-oidc | string | — | with -smart-users, let people sign in at this OpenID Connect provider (YAML: issuer, client_id, client_secret_file or client_secret_env, label); each is linked by oidc_subject | Settings › SMART › Built-in server: Sign people in through the OpenID Connect provider |
-smart-token | string | — | the token endpoint to advertise to SMART apps | Settings › SMART › Authorization: Token endpoint |
-smart-users | string | — | with -smart-clients, the people who may sign in to authorize apps (YAML: username, password_hash, fhir_user) | Settings › SMART › Built-in server: People who sign in to authorize apps (file) |
-snmp-allow | string | — | comma-separated networks or addresses the SNMP agent answers, such as 10.0.5.0/24; required with -snmp-listen | Settings › Monitoring › SNMP: Networks that may ask |
-snmp-base-oid | string | 1.3.6.1.4.1.8072.9999.9999.16, Net-SNMP's experimental space | where Perfuse's values sit, under your enterprise number | Settings › Monitoring › SNMP: Base OID |
-snmp-community | string | — | the SNMPv2c community (a password sent in clear: at least 6 characters, not public or private) | Settings › Monitoring › SNMP: SNMP community |
-snmp-contact | string | — | sysContact the SNMP agent reports | Settings › Monitoring › SNMP: Contact (sysContact) |
-snmp-listen | string | — | answer SNMPv2c GET/GETNEXT/GETBULK on this UDP address, such as :161 (off when empty). Read-only: channel states, counts and queue depths | Settings › Monitoring › SNMP: Answer SNMP on this address |
-snmp-location | string | — | sysLocation the SNMP agent reports | Settings › Monitoring › SNMP: Location (sysLocation) |
-snmp-traps | string | — | comma-separated managers (host or host:port, default port 162) that receive an SNMPv2 trap when an alert fires or resolves | Settings › Monitoring › SNMP: Send traps to |
-store-messages | on/off | true | record every message handled, which is what makes the message browser work | Settings › Data › What is kept: Keep a record of each message |
-store-payloads | on/off | true | store message contents as well as their outcomes; turn this off where PHI must not be held at rest | Settings › Data › What is kept: Keep message contents |
-tls-cert | string | — | TLS certificate file | Settings › Security › Encryption: TLS certificate file |
-tls-key | string | — | TLS private key file | Settings › Security › Encryption: TLS private key file |
-trace-endpoint | string | — | OTLP/HTTP base URL to export message traces to, for example http://localhost:4318 | Settings › Monitoring › Tracing: Send traces to |
-trace-instance | string | — | name for this instance in traces; defaults to the hostname, which is what distinguishes replicas | Settings › Monitoring › Tracing: This instance's name in traces |
-trace-sample | float | 1 | fraction of traces to record, 0 to 1 | — |
-tsa-url | string | — | RFC 3161 time-stamping authority for DSDR signatures on claims attachments | Settings › Security › Document signing: Time-stamping authority |
-vpn | string | — | partner VPN tunnels to report on the Connections dashboard (YAML: AWS Site-to-Site VPN, Azure VPN Gateway or strongSwan, and both sides' settings); see examples/vpn.yaml | Settings › Monitoring › Connections: VPN tunnels to watch (file) |
-watch-access | on/off | true | watch traffic and reads of patient data for what a breach looks like (bursts, night traffic, new destinations, bulk reads) and raise alerts | — |
-xds | string | — | keep clinical documents here as an IHE XDS.b registry and repository with this repository uniqueId (an OID), answering ITI-41, ITI-42, ITI-18 and ITI-43 at /xds/repository and /xds/registry | Settings › Data › Document sharing (IHE XDS): Keep documents here as an XDS registry and repository |
-xds-home-community | string | — | this community's home community id (urn:oid:...), so /xca/gateway answers XCA ITI-38 and ITI-39 for it | Settings › Data › Document sharing (IHE XDS): Home community id, for XCA |
21 Referencia de canal
Cada clave que puede aparecer en el nivel superior de un archivo de canal. Estas se extraen de la fuente, por lo que este capítulo no puede describir una opción que no exista o omitir una que sí exista.
21.1 Canal
Un canal es un flujo de mensajes configurado: somewhere los mensajes llegan, un filtro opcional y uno o más lugares a donde van.
Definido como Channel en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
name | texto | sí | — |
description | texto | no | — |
group | texto | no | — |
attachments | Adjuntos (un bloque, omitir para dejar fuera) | no | — |
delimited | Delimitado (un bloque, omitir para dejar fuera) | no | — |
tables | lista de texto | no | — |
contract | ContractRef (un bloque, omitir para dejar fuera) | no | — |
enabled | true o false (un bloque, omitir para dejar fuera) | no | — |
dataType | DataType | no | hl7 |
hl7v3 | HL7v3Options (un bloque, omitir para dejar fuera) | no | — |
x12 | X12Options (un bloque, omitir para dejar fuera) | no | — |
ncpdp | NCPDPOptions (un bloque, omitir para dejar fuera) | no | — |
edifact | EDIFACTOptions (un bloque, omitir para dejar fuera) | no | — |
script | ScriptOptions (un bloque, omitir para dejar fuera) | no | — |
dicom | DICOMOptions (un bloque, omitir para dejar fuera) | no | — |
source | Fuente | sí | — |
filter | texto | no | — |
transformations | lista de transform.Step | no | — |
scripts | Scripts (un bloque, omitir para dejar fuera) | no | — |
shadow | Shadow (un bloque, omitir para dejar fuera) | no | — |
destinations | lista de Destino | sí | — |
21.1.1 nombre
El nombre identifica el canal en los registros y métricas.
Requerido y único.
21.1.2 grupo
El grupo es a lo que pertenece este canal, para organizar una lista que ha crecido demasiado para leer.
Cosmético a cinco canales y estructural a doscientos: los canales de un sitio se dividen por el sistema con el que hablan, o por el equipo que los posee, y una lista plana de doscientos nombres es inutilizable sin importar lo bueno que sea el resto de la interfaz.
Una cadena simple en lugar de un objeto de grupo con su propio archivo, porque un grupo no tiene comportamiento. En el momento en que tiene un archivo, adquiere configuraciones, y las configuraciones en un grupo significan que el comportamiento de un canal depende de algo que no está escrito en el canal - lo que es la propiedad que hace que la configuración de Mirth sea difícil de razonar.
21.1.3 adjuntos
Adjuntos mueve las grandes cargas útiles fuera del mensaje y las vuelve a poner antes de la entrega.
Opt-in por canal: una fuente de mensajes ADT simples no tiene nada que extraer y no debe pagar por la maquinaria.
21.1.4 delimitado
Delimitado configura un canal delimitado.
Solo significativo cuando el tipo de datos es delimitado.
21.1.5 tablas
Tablas nombres archivos de tablas de mapeo compartidas que una transformación puede referenciar por nombre.
Compartidas porque el dominio de impuestos es en gran parte re-derivar un mapeo que alguien ya derivó: los códigos de sexo y las clases de pacientes de un sitio se traducen de manera idéntica en cada canal que los toca, y una tabla dentro de un canal es una tabla que solo ese canal puede usar.
21.1.6 habilitado
Habilitado predeterminado es true.
Un canal deshabilitado se carga y se valida pero no se inicia, por lo que un archivo que está temporalmente fuera es aún verificado por CI.
21.1.7 tipo de dato
El tipo de dato es el formato de los mensajes que este canal recibe.
Por defecto es hl7, por lo que todos los canales escritos antes de que esto existiera siguen funcionando.
21.1.8 x12
X12 contiene la configuración que solo tiene sentido en un canal X12.
Establecerlo en cualquier otro tipo se rechaza en lugar de ignorarse.
21.1.9 script
Script configura un canal NCPDP SCRIPT.
Sus pasos viven aquí en lugar de en las transformaciones de nivel superior para la misma razón por la que los formatos no v2 lo hacen: los tipos de pasos difieren por formato, por lo que una clave significaría cosas diferentes dependiendo del tipo de dato.
21.1.10 dicom
Imágenes configura los pasos de transformación con nombre de un canal DICOM.
Se llama Imágenes en lugar de DICOM porque Origen y Destino ya tienen un campo DICOM y tres campos con el mismo nombre en diferentes niveles es cómo alguien establece el incorrecto. La clave yaml es dicom, que es lo que una persona que escribe el archivo espera, y el nombre de Go es el que evita un error en el sitio de llamada.
21.1.11 filtro
Filtro es una expresión que debe cumplirse para que un mensaje se reenvíe.
Un mensaje que no coincide sigue siendo reconocido: el remitente no hizo nada mal, simplemente no estamos interesados.
21.1.12 transformaciones
Las transformaciones son cambios declarativos aplicados a los mensajes aceptados, en orden, antes de que se ejecute cualquier script.
Esta es la capa destinada a realizar el trabajo ordinario: cada paso se valida cuando se carga el archivo, aparece en una diferencia y puede ser revisado por alguien que no lee JavaScript.
21.1.13 scripts
Scripts contiene JavaScript para los casos en que los pasos declarativos no pueden expresarse, y es donde se colocan sin cambios los scripts propios de un canal Mirth.
Un canal con un script se marca como tal en la interfaz, porque su comportamiento no se puede establecer leyendo la configuración.
21.1.14 sombra
Sombra ejecuta una versión candidata de este canal al lado y informa sobre las diferencias.
Nada de lo que hace puede llegar a un receptor.
22 Referencia de origen
Cada forma en que un canal puede recibir mensajes y cada clave que cada uno acepta. Un canal tiene exactamente una fuente.
22.1 Fuente AMQP
AMQPSource recibe de una cola o suscripción.
Un mensaje se acepta solo después de que el canal lo ha procesado. Uno que no se pudo procesar se libera como una entrega fallida, por lo que el broker lo vuelve a entregar y, en Service Bus, más allá del recuento máximo de entregas de la cola, lo envía a la carpeta de elementos muertos.
Definido como AMQPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
prefetch | número entero | no | 10 |
reconnect | duración, como 30s o 5m | no | 5s |
22.1.1 prefetch
Prefetch es la cantidad de mensajes que el broker puede enviar por adelantado.
Por defecto es 10. Aún se procesan uno a la vez, en orden.
22.1.2 reconnect
Reconnect es la espera después de una conexión perdida.
Defaults to 5s.
22.2 Fuente AS2
AS2Source recibe AS2 (RFC 4130): EDI publicado sobre HTTP, firmado y cifrado con S/MIME, respondido con un recibo (un MDN) que lleva un resumen de lo que llegó.
La carga útil es lo que el canal procesa; X12 y EDIFACT son los habituales.
Definido como AS2Source en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
path | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejarlo fuera) | no | — |
id | texto | sí | — |
cert_file | texto | sí | — |
key_file | texto | sí | — |
partners | lista de AS2Partner | sí | — |
max_message_size | número entero | no | — |
duplicate_window | duración, como 30s o 5m | no | — |
22.2.1 path
Path es la ruta de URL a la que los socios publican.
Default /as2.
22.2.2 tls
TLS sirve HTTPS.
La mayoría de los socios lo requieren en su acuerdo, además de S/MIME.
22.2.3 id
ID es nuestro identificador AS2, que los socios envían en AS2-To.
Un mensaje a cualquier otro se rechaza.
22.2.4 partners
Los socios son quienes pueden enviar mensajes aquí.
Un mensaje de cualquier otra persona es rechazado.
22.2.5 ventana_duplicado
DuplicateWindow es el tiempo durante el que se recuerda el Message-ID de un socio, para que un mensaje enviado nuevamente (el socio nunca recibió nuestro recibo y lo volvió a intentar) sea respondido con un recibo y no se pase dos veces (RFC 4130 sección 7.5).
Default 30 days.
22.3 Origen de blobs de Azure
AzureBlobSource recoge blobs de un prefijo de contenedor.
Definido como AzureBlobSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
prefix | texto | sí | — |
suffix | texto | no | — |
after_read | texto | no | — |
move_to | texto | no | procesado/ |
error_prefix | texto | no | error/ |
poll_interval | duración, como 30s o 5m | no | 30s |
max_object_size | número entero | no | 16 MiB |
framed | verdadero o falso | no | — |
22.3.1 mover_a
MoveTo es donde se mueve un blob tratado.
Predeterminado es procesado/.
22.3.2 prefijo_de_error
ErrorPrefix es donde se mueve un blob que no se puede tratar, para que no bloquee el resto.
Predeterminado es error/.
22.3.3 intervalo_de_sondeo
PollInterval entre listados.
Defaults to 30s.
22.3.4 tamaño_máximo_de_objeto
MaxObjectSize rechaza un blob más grande, que se queda donde está.
Predeterminado es 16 MiB.
22.4 Origen de corredor
BrokerSource lee mensajes de un corredor de mensajes.
Lector JMS de Mirth, alcanzado a través de STOMP porque JMS es una API de Java en lugar de un protocolo. Consulte internal/stomp para saber por qué STOMP y no OpenWire.
Definido como BrokerSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
addr | texto | sí | — |
destination | texto | sí | — |
login | texto | no | — |
passcode | texto | no | — |
host | texto | no | el host de la dirección |
selector | texto | no | — |
subscription_id | texto | no | el nombre del canal |
heartbeat | duración, como 30s o 5m | no | treinta segundos |
reconnect | duración, como 30s o 5m | no | cinco segundos |
timeout | duración, como 30s o 5m | no | treinta segundos |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
22.4.1 addr
Addr es el host y el puerto del broker.
Requerido. 61613 es el puerto STOMP habitual.
22.4.2 destino
Destino es la cola o tema de la que se lee.
Required.
Los brokers escriben estos de manera diferente - /queue/name en ActiveMQ, un nombre sin adornos en RabbitMQ - por lo que se pasa sin cambios en lugar de ensamblarse aquí. Adivinar significaría trabajar en contra de un broker y silenciosamente leer nada en otro.
22.4.3 inicio de sesión
Inicio de sesión y código de autorización autenticados.
La mayoría de los brokers rechazan una conexión anónima.
22.4.4 código de autorización
Inicio de sesión y código de autorización autenticados.
La mayoría de los brokers rechazan una conexión anónima.
22.4.5 host
Host es el host virtual.
Predeterminado en el host de la dirección.
22.4.6 selector
Selector filtra mensajes en el broker.
Un selector JMS, pasado sin interpretar. Filtrar en el broker en lugar de aquí significa que los mensajes que este canal no quiere nunca cruzan la red - lo que en una cola compartida es la diferencia entre leer cien mensajes al día y cien mil.
22.4.7 id_de_suscripción
ID de suscripción nombra la suscripción.
Predeterminado en el nombre del canal.
Nombrado en lugar de generado, porque una suscripción duradera se identifica por él: uno generado crearía una nueva suscripción en cada reinicio y dejaría las antiguas acumulando mensajes que nadie lee.
22.4.8 latido
Latido mantiene la conexión viva.
Predeterminado en treinta segundos.
Activado por defecto aquí, a diferencia del valor predeterminado del propio cliente, porque una fuente mantiene una conexión abierta durante semanas. Sin latidos, una conexión a través de un firewall que ha eliminado silenciosamente la ruta parece perfectamente saludable desde este extremo y los mensajes simplemente dejan de llegar, sin nada registrado.
22.4.9 reconnect
Reconexión es el tiempo que se espera antes de volver a conectarse después de un fallo.
Predeterminado en cinco segundos.
22.4.10 tiempo de espera
Límites de tiempo de espera al conectar.
Predeterminado en treinta segundos.
22.5 Fuente de consulta DICOM
DICOMQuerySource sondea un archivo con C-FIND y emite un mensaje por cada nuevo estudio.
Mirth no tiene un conector equivalente. Su soporte DICOM es un listener y un remitente: puede recibir imágenes que se le envían y reenviarlas, y eso es todo. Pedir a un archivo qué contiene ha sido solicitado en sus foros desde 2015.
Lo que lo hace valioso no es la consulta, sino la automatización: una prefetch que se ejecuta a las tres de la mañana, una reconciliación que nota que el archivo falta un estudio que el RIS dice que existe, una lista de trabajo construida a partir de lo que se escaneó realmente en lugar de lo que se ordenó. Esos son trabajos que los sitios actualmente realizan con un script programado fuera del motor, lo que significa que son invisibles para él: no hay reintentos, no hay alertas, no hay historial de mensajes.
Definido como DICOMQuerySource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
address | texto | sí | — |
called_ae | texto | sí | — |
calling_ae | texto | no | — |
level | texto | no | ESTUDIO |
patient_root | verdadero o falso | no | — |
match | asignación de texto a texto | no | — |
return | lista de texto | no | — |
interval | duración, como 30s o 5m | sí | — |
window | duración, como 30s o 5m | no | — |
overlap | duración, como 30s o 5m | no | una hora |
limit | número entero | no | 500 |
timeout | duración, como 30s o 5m | no | dos minutos |
tls | tlsconf.Settings (un bloque, omitir para dejarlo sin) | no | — |
emit_on_first_poll | verdadero o falso | no | falso |
22.5.1 dirección
La dirección es el host y el puerto del archivo.
Required.
Nombrado para coincidir con el destino DICOM en lugar de abreviado. Los dos fueron "dirección" y "addr" durante una tarde, lo que es el tipo de inconsistencia que alguien que escribe un archivo de canal a mano se equivoca una vez y luego desconfía de todo el formato.
22.5.2 called_ae
CalledAE es el título AE del archivo.
Requerido en la práctica: la mayoría de los archivos rechazan una asociación dirigida a cualquier otra cosa, y ese rechazo se lee como una interrupción en lugar de como un error de configuración.
22.5.3 calling_ae
CallingAE es el título AE que presenta Perfuse.
Los archivos comúnmente lo utilizan para el control de acceso.
22.5.4 nivel
Nivel es el nivel de consulta: PACIENTE, ESTUDIO, SERIE o IMAGEN.
Predeterminado en ESTUDIO.
ESTUDIO es el predeterminado porque es lo que casi todas las consultas útiles desean y porque las alternativas sorprenden a las personas: el nivel IMAGEN contra una CT grande devuelve una respuesta por rebanada, lo que es cientos por estudio.
22.5.5 coincidencia
Coincidencia son las claves de consulta.
Un valor filtra; un valor vacío pide que se devuelva el campo.
Las claves están nombradas en lugar de numeradas - "PatientID" en lugar de "0010,0020" - porque un archivo de canal se lee por personas, y un número de etiqueta en un archivo de configuración es una búsqueda que cada lector debe realizar.
22.5.6 devolver
Los campos de devolver son los campos que se deben regresar sin filtrarlos.
Separado de la coincidencia con un valor vacío, aunque codifiquen de manera idéntica, porque la intención es diferente y un lector puede ver qué claves están restringiendo la búsqueda y cuáles se están recopilando.
22.5.7 intervalo
El intervalo es con qué frecuencia se realiza la consulta.
Required.
22.5.8 ventana
La ventana es cuánto tiempo atrás busca cada consulta, como un rango de fechas de estudio.
Cero deshabilita completamente el filtrado por fechas.
Una ventana es lo que mantiene esto limitado. Sin una, cada consulta le pide al archivo todo lo que ha tenido alguna vez y luego descarta lo que ha visto antes, lo que funciona en un archivo de prueba y es antisocial contra uno real que contiene millones de estudios.
22.5.9 superposición
La superposición vuelve a consultar este período ya consultado.
El valor predeterminado es de una hora.
Necesario en lugar de cauteloso. Un estudio puede estar registrado con la fecha de ayer, nuestro reloj y el del archivo no necesitan coincidir, y un límite de consulta que coincide exactamente con una llegada lo pierde. La superposición vuelve a preguntar, y la comprobación de lo ya visto evita el duplicado, así que el costo de la superposición es una consulta ligeramente más grande y el costo de no superponer es un estudio perdido en silencio.
22.5.10 límite
El límite limita las coincidencias aceptadas de una sola consulta.
Defaults to 500.
Un límite en lugar de no tener límite porque una clave de coincidencia mal escrita convierte esto en "dame todo", y el primer síntoma de eso contra un archivo real es que el administrador del archivo pregunta quién lo está bombardeando.
22.5.11 tiempo de espera
El tiempo de espera limita una sola consulta.
El valor predeterminado es de dos minutos.
22.5.12 emitir_en_la_primera_consulta
EmitOnFirstPoll envía mensajes para todo lo que encuentra la primera consulta.
El valor predeterminado es falso.
Falso es importante. Apuntar esto a un archivo que ya contiene un millón de estudios y hacer que emita un mensaje para cada uno no es una primera ejecución útil, y es el tipo de error que se nota en el flujo descendente en lugar de aquí. La primera consulta registra lo que existe y no emite nada; la segunda consulta en adelante informa lo que es nuevo.
22.6 Fuente DICOM
DICOMSource recibe objetos de imagen como proveedor de la clase de servicio C-STORE.
Lo que un hospital apunta a una modalidad o un PACS. Mirth llama a esto el oyente DICOM y solo hace C-STORE, así que esto es paridad en lugar de un subconjunto.
Definido como DICOMSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
ae_title | texto | no | — |
allowed_calling_ae | lista de texto | no | — |
sop_classes | lista de texto | no | — |
transfer_syntaxes | lista de texto | no | — |
max_object_bytes | número entero | no | — |
idle_timeout | duración, como 30s o 5m | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
22.6.1 listen
Listen es la dirección en la que se sirve.
Requerido. 104 es el puerto DICOM registrado; 11112 es el alternativo común donde no es posible vincular un puerto con privilegios.
22.6.2 ae_title
AETitle es el nombre que se le da a este punto de conexión.
Fuertemente recomendado en lugar de requerido, y advertido cuando está ausente. El control de acceso de un sitio en un punto de conexión DICOM es frecuentemente el título AE, por lo que un punto de conexión que acepta cualquier título acepta cualquier cosa que pueda llegar a él.
22.6.3 allowed_calling_ae
AllowedCallingAE enumera los títulos AE permitidos para conectarse.
Vacío acepta cualquier llamador.
Este es el control de acceso que realmente tiene DICOM. Un PACS se configura rutinariamente para aceptar un título de llamada nombrado y rechazar todo lo demás, y este extremo no tenía equivalente: se verificaba el título llamado - que es el nombre que el llamador marca, así que cualquier persona puede enviarlo correctamente - mientras que quién estaba llamando se registraba en el registro y se ignoraba de otra manera.
Vacío todavía acepta cualquier cosa, porque una modalidad que nadie ha enumerado es el estado normal de una instalación inicial y rechazar por defecto rompería cada implementación en el día uno. En su lugar, advierte, y la advertencia nombra los títulos que se han conectado para que alguien pueda rellenar esto desde lo que realmente llegó.
22.6.4 sop_classes
SOPClasses restringe los tipos de objetos que se aceptan.
Vacío acepta cualquier cosa.
Vacío es un valor predeterminado razonable aquí porque este motor relaya objetos en lugar de interpretarlos, por lo que rechazar una clase desconocida rechazaría imágenes válidas sin beneficio.
22.6.5 transfer_syntaxes
TransferSyntaxes restringe los codificados que se aceptan, en orden de preferencia.
Vacío acepta los tres sin comprimir.
22.6.6 max_object_bytes
MaxObjectBytes limita un objeto recibido.
Cero aplica un valor predeterminado.
22.6.7 idle_timeout
IdleTimeout cierra una asociación que ha guardado silencio.
Cero significa nunca, que generalmente es correcto: una modalidad mantiene una asociación abierta entre estudios.
22.6.8 tls
TLS cifra las asociaciones entrantes y puede requerir un certificado de cliente.
Vale la pena decirlo claramente: Mirth necesita su extensión de administrador de SSL de pago para esto, y las bifurcaciones de la comunidad no pueden enviarlo en absoluto. DICOM lleva nombres de pacientes en sus metadatos, por lo que este es uno de los lugares donde ser una reescritura en lugar de una bifurcación es una ventaja real en lugar de una estilística.
22.7 Fuente Mweb DICO
DICOMwebSource acepta solicitudes STOW-RS.
Definido como DICOMwebSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
path | texto | no | /dicom-web |
token | texto | no | — |
username | texto | no | — |
password | texto | no | — |
max_object_bytes | número entero | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
22.7.1 listen
Listen es la dirección en la que se sirve.
Required.
22.7.2 path
La ruta es la raíz del servicio.
Los objetos se aceptan en Path/estudios y Path/estudios/{uid}. De forma predeterminada es /dicom-web, que es lo que utilizan Orthanc y dcm4chee, por lo que un remitente configurado para cualquiera de ellos solo necesita cambiar el host.
22.7.3 token
El token, cuando se establece, es requerido como un token de portador.
El nombre de usuario y la contraseña requieren autenticación básica de HTTP.
22.7.4 nombre de usuario
El token, cuando se establece, es requerido como un token de portador.
El nombre de usuario y la contraseña requieren autenticación básica de HTTP.
22.7.5 contraseña
El token, cuando se establece, es requerido como un token de portador.
El nombre de usuario y la contraseña requieren autenticación básica de HTTP.
22.8 Fuente de base de datos
DatabaseSource lee filas de una base de datos y convierte cada una en un mensaje.
Esta es la forma en que comienzan muchas interfaces de hospital. Un sistema de departamento no tiene capacidad HL7, por lo que alguien escribe filas en una tabla de espera y el motor de interfaz la sondea. No es glamoroso y está en todas partes.
También es la forma más común en que un canal de Mirth se bloquea, y la razón es casi siempre la misma: una fila que no se puede procesar se lee, falla y se vuelve a leer, para siempre, porque nada la marcó. El canal parece estar vivo. La cola parece vacía. Nada se mueve, y cada mensaje detrás de la fila mala espera a una fila que nunca tendrá éxito.
La mayor parte del diseño aquí se trata de eso.
Definido como DatabaseSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
driver | texto | sí | — |
dsn | texto | sí | — |
query | texto | sí | — |
after_query | texto | no | — |
key_column | texto | no | — |
template | texto | no | — |
column | texto | no | — |
poll_interval | duración, como 30s o 5m | no | — |
batch_size | número entero | no | 100 |
max_attempts | número entero | no | 3 |
query_timeout | duración, como 30s o 5m | no | 30s |
22.8.1 controlador
El controlador nombra la base de datos.
See SupportedDrivers.
22.8.2 dsn
DSN es la cadena de conexión.
Prefiera una referencia de variable de entorno sobre un literal, porque esto normalmente contiene una contraseña y los canales están diseñados para vivir en git.
22.8.3 consulta
La consulta selecciona las filas que están esperando ser enviadas.
Required.
Debe ser un SELECT. Una consulta que realice escrituras silenciosas se ejecutaría en un calendario sin registro de qué cambió.
22.8.4 después_de_la_consulta
AfterQuery marca una fila como procesada y se ejecuta con la clave de la fila vinculada a ella.
Strongly recommended.
Sin ella, cada consulta de actualización vuelve a leer cada fila que la consulta aún coincide, por lo que la consulta en sí debe excluir las filas procesadas de alguna otra manera. Si ninguna de las anteriores es cierta, el canal vuelve a enviar las mismas filas indefinidamente, lo que es una tormenta de duplicados en lugar de un bloqueo y es peor.
22.8.5 columna_clave
KeyColumn identifica una fila para AfterQuery, cuarentena y eliminación de duplicados.
Requerido cuando AfterQuery está establecido.
22.8.6 plantilla
La plantilla construye un mensaje a partir de una fila.
Se requiere uno de estos o Column.
Las referencias de columna utilizan ${nombre_de_columna}. Las columnas que faltan son un error al cargar en lugar de una cadena vacía en un mensaje clínico.
22.8.7 tamaño_de_lote
BatchSize limita las filas tomadas por consulta de actualización.
Defaults to 100.
Esto está limitado porque una primera consulta de actualización no limitada contra una tabla que alguien ha estado llenando durante un año leería todo en memoria y luego lo enviaría todo, lo que es cómo una migración inunda un receptor en vivo.
22.8.8 intentos_máximos
MaxAttempts es la cantidad de veces que una sola fila puede fallar antes de ser puesta en cuarentena y la consulta de actualización continúa.
Defaults to 3.
Este es el ajuste que evita el bloqueo. Volver a intentarlo indefinidamente no es resistencia; es una mala fila que detiene una alimentación de hospital.
22.8.9 tiempo_de_espera_de_la_consulta
QueryTimeout limita una sola consulta de actualización.
Defaults to 30s.
22.9 Fuente directa
DirectSource recibe correo Direct que el HISP entrega: un listener SMTP para el correo de esta dirección.
Cada mensaje se descifra, se verifica su firma contra los anclajes y su firmante contra From, y se pasa su contenido: los documentos de un paquete XDM, de lo contrario cada archivo adjunto, de lo contrario el texto.
Definido como DirectSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
listen | texto | no | — |
tls_cert | texto | no | — |
tls_key | texto | no | — |
max_bytes | número entero | no | — |
smtp_host | texto | no | — |
username | texto | no | — |
password | texto | no | — |
imap | DirectIMAP (un bloque, omitir para dejar sin configurar) | no | — |
xdr | DirectXDR (un bloque, omitir para dejar sin configurar) | no | — |
22.9.1 smtp_host
SMTPHost, Username y Password son el servidor de envío de HISP, utilizado para enviar una notificación de entrega "procesada" (MDN) al remitente una vez que se ha tomado un mensaje.
Sin él, no se envía ninguna notificación.
22.9.2 nombre de usuario
SMTPHost, Username y Password son el servidor de envío de HISP, utilizado para enviar una notificación de entrega "procesada" (MDN) al remitente una vez que se ha tomado un mensaje.
Sin él, no se envía ninguna notificación.
22.9.3 contraseña
SMTPHost, Username y Password son el servidor de envío de HISP, utilizado para enviar una notificación de entrega "procesada" (MDN) al remitente una vez que se ha tomado un mensaje.
Sin él, no se envía ninguna notificación.
22.10 Origen de correo electrónico
EmailSource lee correo de una bandeja de entrada y convierte archivos adjuntos, cuerpos o mensajes completos en mensajes.
Definido como EmailSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
protocol | texto | no | — |
host | texto | sí | — |
port | número entero | no | — |
security | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
username | texto | sí | — |
password | texto | no | — |
oauth | EmailOAuth (un bloque, omitir para dejar sin) | no | — |
mailbox | texto | no | — |
take | texto | no | — |
attachment_names | lista de texto | no | — |
allowed_senders | lista de texto | no | — |
after_read | texto | no | — |
move_to | texto | no | — |
error_folder | texto | no | — |
poll_interval | duración, como 30s o 5m | no | — |
max_message_size | número entero | no | — |
22.10.1 contraseña
La contraseña puede ser ${ENV}; para Gmail, una contraseña de aplicación.
Ignorado con oauth.
22.10.2 attachment_names
AttachmentNames limita qué archivos adjuntos se toman, por patrón de nombre de archivo (*.hl7).
Vacío toma todos los archivos adjuntos.
22.10.3 allowed_senders
AllowedSenders limita cuyo correo se lee: direcciones y dominios.
El encabezado From se puede falsificar, por lo que esto mantiene alejados accidentes y correo casual, no a un atacante.
22.10.4 mover_a
MoveTo es la carpeta a la que se mueve el correo procesado; Procesado de forma predeterminada.
Creado cuando falta.
22.11 Origen FTP
FTPSource recopila archivos de un servidor FTP o FTPS.
Por qué FTP sigue aquí
Han pasado treinta años desde su reemplazo recomendado y muchos sistemas hospitalarios todavía exportan a él: dispositivos cuyo firmware nunca se actualizará, sistemas de facturación cuyo proveedor cobra por un cambio. Negarse a hablarlo no hace que esos sistemas desaparezcan, significa que el motor de integración no se puede utilizar.
FTPS es el predeterminado. Es el mismo protocolo con TLS, la mayoría de los servidores supervivientes lo admiten y es una conversación mucho más fácil con un equipo de seguridad que el FTP plano.
Definido como FTPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | — |
user | texto | no | — |
password | texto | no | — |
security | ftpconn.Security | no | explícito |
insecure_skip_verify | true o false | no | — |
root | texto | no | — |
timeout | duración, como 30s o 5m | no | treinta segundos |
22.11.1 host
El host es el servidor, con un puerto opcional.
Se asume el puerto 21.
22.11.2 usuario
El usuario y la contraseña autenticán.
Ambos pueden omitirse para un servidor anónimo.
El FTP anónimo sigue siendo la forma en que se publican algunas fuentes de referencia públicas, por lo que un usuario vacío es una configuración legítima en lugar de una omisión y no se rechaza. En un servidor que sí espera credenciales, omitirlas produce un error de inicio de sesión en el momento de la conexión, que se informa contra el canal en lugar de volver a intentarlo silenciosamente.
22.11.3 contraseña
El usuario y la contraseña autenticán.
Ambos pueden omitirse para un servidor anónimo.
El FTP anónimo sigue siendo la forma en que se publican algunas fuentes de referencia públicas, por lo que un usuario vacío es una configuración legítima en lugar de una omisión y no se rechaza. En un servidor que sí espera credenciales, omitirlas produce un error de inicio de sesión en el momento de la conexión, que se informa contra el canal en lugar de volver a intentarlo silenciosamente.
22.11.4 seguridad
La seguridad es explícita, implícita o ninguna.
Predeterminada explícita, que es FTPS negociado con AUTH TLS en el puerto ordinario. "Ninguna" es FTP plano y debe solicitarse por nombre.
22.11.5 raíz
La raíz es el directorio relativo al que se aplican todos los caminos y contra el que se aplica la contención.
El servidor no tiene noción de ella: un servidor FTP aceptará felizmente ../.. como camino. Entonces, esto es lo que evita que un move_to escriba dondequiera que la cuenta pueda llegar.
22.11.6 tiempo de espera
El tiempo de espera limita una operación, no toda la encuesta.
Predeterminado en treinta segundos.
Por operación porque una encuesta que lista un directorio y recupera veinte archivos realiza veintiuna operaciones, y un solo presupuesto para todas ellas abortaría una transferencia perfectamente saludable de un archivo grande.
22.12 Fuente de archivo
FileSource lee archivos de un directorio que este servidor puede ver.
El conector que un sitio intenta primero
Un analizador escribe resultados en una carpeta. Un sistema de facturación suelta un lote por la noche. La radiología exporta informes a un directorio que alguien montó hace años. Nada de esto implica un protocolo, y un motor de integración que no puede leer una carpeta falla en su evaluación en la primera tarde.
Definido como FileSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
root | texto | sí | — |
follow_symlinks | true o false | no | — |
22.12.1 raíz
La raíz limita todos los caminos que esta fuente toca.
Required.
Separado de Dir para que move_to y error_dir puedan ser relativos y aún estar contenidos. Dado solo Dir, un error_dir de "../failed" estaría fuera de cualquier cosa que la fuente hubiera recibido, y rechazarlo sería arbitrario porque no habría nada contra lo que rechazarlo.
22.13 Fuente GCS
GCSSource recoge objetos de un prefijo de bucket, como las fuentes s3 y azure_blob.
Definido como GCSSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
prefix | texto | sí | — |
suffix | texto | no | — |
after_read | texto | no | — |
move_to | texto | no | procesado/ |
error_prefix | texto | no | error/ |
poll_interval | duración, como 30s o 5m | no | 30s |
max_object_size | número entero | no | 16 MiB |
framed | verdadero o falso | no | — |
22.13.1 mover_a
MoveTo es donde va un objeto tratado cuando after_read es move.
Predeterminado es procesado/.
22.13.2 prefijo_de_error
ErrorPrefix es donde va un objeto que no se pudo tratar.
Predeterminado es error/.
22.13.3 intervalo_de_sondeo
PollInterval es con qué frecuencia se lista el prefijo.
Defaults to 30s.
22.13.4 tamaño_máximo_de_objeto
MaxObjectSize deja los objetos más grandes donde están.
Predeterminado es 16 MiB.
22.14 Fuente HTTP
HTTPSource acepta mensajes a través de HTTP.
Un oyente en lugar de un sondeador: algo publica un mensaje y obtiene un acuse de recibo, que es cómo se integra la mitad moderna del hospital cuando MLLP no está disponible para él.
Definido como HTTPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
path | texto | no | "/" |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
token | texto | no | — |
max_message_size | número entero | no | — |
read_timeout | duración, como 30s o 5m | no | — |
ack | Ack | no | — |
22.14.1 listen
Listen es la dirección en la que se sirve.
Required.
22.14.2 path
Path es la ruta de URL para aceptar mensajes.
Defaults to "/".
22.14.3 token
Token, cuando se establece, es requerido en un encabezado Authorization: Bearer.
No hay un valor predeterminado y no hay un conmutador de modo anónimo: un punto final HTTP en una red de hospital con ninguna autenticación en absoluto es una decisión que tiene que ser visible en el archivo, por lo que dejar esto vacío se informa como una advertencia al cargar.
22.14.4 max_message_size
MaxMessageSize limita el cuerpo de entrada.
Cero aplica un valor predeterminado.
22.15 Fuente IBMMQ
IBMMQSource lee una cola.
Definido como IBMMQSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
poll_interval | duración, como 30s o 5m | no | 2s |
22.15.1 intervalo_de_sondeo
PollInterval es la espera cuando la cola está vacía, o después de un mensaje que no se pudo tratar.
Defaults to 2s.
22.16 Fuente Java Script
JavaScriptSource configura una fuente que ejecuta un script en un temporizador y alimenta los mensajes devueltos al canal.
Este es el equivalente del lector de JavaScript de Mirth.
El script se ejecuta en una máquina virtual goja con la misma caja de arena que los scripts de transformador: msg no está disponible (no hay mensaje de entrada todavía), pero globalMap, configurationMap, logger y DateUtil están presentes. El script debe devolver una cadena (un mensaje) o una matriz de cadenas (muchos). Devolver nada o una matriz vacía significa que la encuesta no produjo mensajes, lo que es normal.
Definido como JavaScriptSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
script | texto | sí | — |
poll_interval | duración, como 30s o 5m | no | — |
timeout | duración, como 30s o 5m | no | — |
22.16.1 intervalo_de_sondeo
PollInterval es la frecuencia con la que se ejecuta el script.
Cero significa 5 segundos.
22.16.2 tiempo de espera
Timeout limita la ejecución de un script individual.
Cero significa 30 segundos.
22.17 Origen de Kafka
KafkaSource lee mensajes de un tema de Kafka.
Definido como KafkaSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
brokers | lista de texto | sí | — |
topics | lista de texto | sí | — |
group | texto | sí | — |
from_beginning | verdadero o falso | no | — |
commit_after_delivery | verdadero o falso (un bloque, omitir para dejar fuera) | no | — |
max_message_size | número entero | no | el límite del canal |
session_timeout | duración, como 30s o 5m | no | cuarenta y cinco segundos |
timeout | duración, como 30s o 5m | no | treinta segundos |
sasl | KafkaSASL (un bloque, omitir para dejar fuera) | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar fuera) | no | — |
22.17.1 corredores
Brokers es la lista de servidores de arranque.
Required.
Una lista en lugar de una dirección, porque un solo corredor de arranque es un solo punto de fallo para el inicio: el clúster sobrevive a la pérdida de este y este canal no.
22.17.2 temas
Los temas son los temas que se van a leer.
Al menos uno es requerido.
22.17.3 grupo
El grupo es el grupo de consumidores.
Required.
Nombrado en lugar de generado, y requerido en lugar de predeterminado, porque es lo que recuerda cuánto ha leído este canal. Un grupo generado comenzaría desde el principio - o desde el final, perdiendo todo - en cada reinicio, y dejaría atrás el antiguo grupo que mantiene los desplazamientos comprometidos que nadie lee. Requerirlo hace que la persona nombre la cosa que tiene que permanecer igual en los reinicios.
22.17.4 from_beginning
FromBeginning lee el tema desde su inicio cuando el grupo no tiene un desplazamiento comprometido.
Desactivado por defecto, para que un nuevo canal apuntando a un tema con dos años de historia no reproduzca dos años de eventos de pacientes en un sistema en vivo el día que se activa. La primera persona que lo haga lo haría en producción, porque ese es el lugar donde está el tema con la historia.
22.17.5 commit_after_delivery
CommitAfterDelivery compromete el desplazamiento solo una vez que el mensaje ha sido manejado.
Activado por defecto, y la razón es la diferencia entre perder un mensaje y verlo dos veces. Comprometer en la lectura significa que un fallo entre el compromiso y la entrega pierde el mensaje silenciosamente, y nada en ningún lugar registra que existió. Comprometer después significa que un fallo en la misma ventana lo entrega nuevamente, lo que es visible y lo que los receptores de HL7 están diseñados para tolerar - un duplicado de A08 es una molestia y un resultado de laboratorio perdido es un evento de seguridad del paciente.
22.17.6 max_message_size
MaxMessageSize limita un registro.
Predeterminado en el límite del canal.
22.17.7 tiempo_de_expiracion_de_sesion
SessionTimeout es el tiempo que espera el coordinador de grupo antes de asumir que este consumidor se ha ido y reasignar sus particiones.
Por defecto es de cuarenta y cinco segundos.
22.17.8 tiempo de espera
Límites de tiempo de espera para conexiones y solicitudes de metadatos.
Predeterminado en treinta segundos.
22.18 Origen MQTT
MQTTSource se suscribe a un filtro de tema.
Definido como MQTTSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
topic | texto | sí | — |
reconnect | duración, como 30s o 5m | no | 5s |
22.18.1 reconnect
Reconectar es la espera después de una conexión perdida o después de un mensaje que no se pudo manejar.
Defaults to 5s.
22.19 Origen Pub Sub
PubSubSource extrae de una suscripción de Pub/Sub.
Definido como PubSubSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
subscription | texto | sí | — |
max_messages | número entero | no | 10 |
reconnect | duración, como 30s o 5m | no | 5s |
22.19.1 max_mensajes
MaxMensajes es la cantidad que se extrae al mismo tiempo.
Aún se manejan uno a la vez, en orden. Por defecto es 10.
22.19.2 reconnect
Reconectar es la espera después de un error.
Defaults to 5s.
22.20 Origen S3
S3Source recoge objetos de un bucket.
Definido como S3Source en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
bucket | texto | sí | — |
path_style | verdadero o falso | no | — |
prefix | texto | no | — |
suffix | texto | no | — |
after_read | texto | no | — |
move_to | texto | no | procesado/ |
error_prefix | texto | no | error/ |
poll_interval | duración, como 30s o 5m | no | 30s |
max_object_size | número entero | no | 16 MiB |
framed | verdadero o falso | no | — |
22.20.1 prefijo
Prefijo limita qué objetos se leen, por ejemplo, entrante/.
Lecturas vacías leen todo el bucket, lo que se rechaza con después_de_leer: mover a menos que mover_a esté fuera de él - de lo contrario, los objetos movidos se leerían nuevamente.
22.20.2 despues_de_leer
AfterRead se mueve (el predeterminado) o elimina.
No hay dejar: un bucket no tiene un archivo que un recopilador pueda recordar haber visto a través de un reinicio, por lo que dejar objetos significa leerlos todos nuevamente cada vez que Perfuse se inicia.
22.20.3 mover_a
MoveTo es el prefijo al que se mueve un objeto tratado.
Predeterminado es procesado/.
22.20.4 prefijo_de_error
ErrorPrefix es donde se mueve un objeto que no se pudo tratar, para que un archivo malo no bloquee el resto.
Predeterminado es error/.
22.20.5 intervalo_de_sondeo
PollInterval entre listados.
Defaults to 30s.
22.20.6 tamaño_máximo_de_objeto
MaxObjectSize rechaza cualquier cosa más grande.
Predeterminado es 16 MiB.
22.20.7 enmarcado
Framed lee varios mensajes enmarcados en MLLP de un objeto.
De lo contrario, un objeto es un mensaje.
22.21 Origen SFTP
SFTPSource recopila mensajes de un directorio en un servidor SFTP.
Extremadamente común y rara vez hablado. Un laboratorio o un sistema de radiología escribe un archivo cada pocos minutos, un servidor SFTP lo mantiene y algo tiene que venir y recogerlo. La mitad de las fuentes descritas como "le enviamos HL7" son esto.
La parte que merece cuidado no es la transferencia. Es que un archivo que se está escribiendo y un archivo que ha terminado de escribirse parecen idénticos a través de SFTP, por lo que una encuesta que es incluso ligeramente demasiado ansiosa recopila medio mensaje. Eso produce un mensaje HL7 truncado pero a menudo analizable, que es el peor resultado disponible: se acepta, se confirma, se almacena y se entrega, y la mitad que falta nunca se menciona de nuevo.
Definido como SFTPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | 22 |
user | texto | sí | — |
password | texto | no | — |
key_file | texto | no | — |
key_passphrase | texto | no | — |
known_hosts_file | texto | no | — |
insecure_skip_host_key_check | verdadero o falso | no | — |
dir | texto | sí | — |
pattern | texto | no | todo |
poll_interval | duración, como 30s o 5m | no | — |
after_read | texto | no | — |
move_to | texto | no | — |
error_dir | texto | no | — |
stable_for | duración, como 30s o 5m | no | 5s |
framed | verdadero o falso | no | — |
max_file_size | número entero | no | 64MB |
timeout | duración, como 30s o 5m | no | 60s |
22.21.1 host
El host es el servidor, con un puerto opcional.
Defaults to 22.
22.21.2 contraseña
Contraseña autentica con una contraseña.
Prefer KeyFile.
22.21.3 key_file
KeyFile es una clave privada.
La mejor opción, y la que los equipos de seguridad del hospital solicitan.
22.21.4 known_hosts_file
KnownHostsFile verifica la identidad del servidor.
Requerido a menos que InsecureSkipHostKeyCheck esté establecido. Sin él, no hay nada que distinga al servidor real de cualquier cosa que responda en esa dirección, y las credenciales se entregan antes de que alguien se dé cuenta.
22.21.5 insecure_skip_host_key_check
InsecureSkipHostKeyCheck acepta cualquier clave de host.
Existe porque rechazar directamente envía a las personas a un script de shell con StrictHostKeyChecking=no, lo que es peor en todos los sentidos, incluida la auditoría. Advierte en voz alta y nunca es el predeterminado.
22.21.6 patrón
El patrón selecciona archivos por glob.
Por defecto, selecciona todo.
22.21.7 mover_a
MoveTo es el directorio remoto donde se mueven los archivos después de leerlos.
Requerido cuando AfterRead es "mover".
22.21.8 estable_during
EstableDuring es el tiempo que debe transcurrir sin cambios en el tamaño y la hora de modificación de un archivo antes de que se lea.
Defaults to 5s.
Esta es la configuración que evita que se recolecte medio mensaje. Cero significa leer lo que esté allí, lo que funciona correctamente hasta que un archivo es lo suficientemente grande o una red es lo suficientemente lenta como para que la escritura tome más tiempo que una consulta.
22.21.9 enmarcado
Framed indica que el archivo contiene mensajes con marco MLLP.
Cuando es falso, el archivo se divide en límites MSH.
22.21.10 tamaño_máximo_de_archivo
TamañoMáximoDeArchivo rechaza un archivo más grande que este número de bytes.
Por defecto, 64MB.
22.21.11 tiempo de espera
Timeout limita una conexión individual.
Defaults to 60s.
22.22 Origen SMB
SMBSource recopila archivos de un recurso compartido de archivos de Windows.
Definido como SMBSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
host | texto | sí | — |
share | texto | sí | — |
user | texto | no | — |
password | texto | no | — |
domain | texto | no | — |
root | texto | no | — |
timeout | duración, como 30s o 5m | no | treinta segundos |
22.22.1 host
El host es el servidor, con un puerto opcional.
Se asume el puerto 445.
22.22.2 compartir
Compartir es el nombre de la compartición solo: el "datos" en \\\server\datos.
Una ruta aquí se rechaza en lugar de recortarse.
22.22.3 usuario
Usuario y Contraseña son la cuenta de Windows.
Requerido en la práctica.
Una compartición accesible sin credenciales en absoluto es lo suficientemente rara en un dominio como para que un usuario vacío aquí sea mucho más probable que sea un error que una compartición de invitado - pero se acepta, porque rechazarla bloquearía el único arreglo en el que es correcto. Cuando la cuenta pertenece a un dominio, establezca Dominio también: autenticar una cuenta de dominio sin él falla de una manera que se lee como una contraseña incorrecta.
22.22.4 contraseña
Usuario y Contraseña son la cuenta de Windows.
Requerido en la práctica.
Una compartición accesible sin credenciales en absoluto es lo suficientemente rara en un dominio como para que un usuario vacío aquí sea mucho más probable que sea un error que una compartición de invitado - pero se acepta, porque rechazarla bloquearía el único arreglo en el que es correcto. Cuando la cuenta pertenece a un dominio, establezca Dominio también: autenticar una cuenta de dominio sin él falla de una manera que se lee como una contraseña incorrecta.
22.22.5 dominio
El dominio es el dominio o grupo de trabajo de Windows.
Vacío suele ser correcto para una cuenta local.
22.22.6 tiempo de espera
Timeout limita una operación.
Predeterminado en treinta segundos.
Vale la pena aumentar en un recurso compartido alcanzado a través de un enlace de sitio. Una operación SMB contra un servidor remoto es más lenta que la misma operación de forma local por suficiente tiempo como para que un ajuste predeterminado para una red local produzca timeouts que parecen que el recurso compartido no está disponible.
22.23 Origen SOAP
SOAPSource acepta mensajes envueltos en un sobre SOAP.
El contraparte de la destino SOAP, y la dirección menos común: la mayoría de la integración hospitalaria tiene Perfuse llamando a un servicio en lugar de ser uno. Existe porque el Web Service Listener de Mirth lo hace, y un sitio cuyo sistema de envío solo habla SOAP no puede migrar sin él.
Deliberadamente no es un marco de servicios web general. Acepta un sobre, saca el mensaje de él y responde con un sobre. No hay WS-Security, no MTOM, no WS-Addressing y no enlaces generados - las mismas omisiones que el destino, por la misma razón: una implementación parcial de WS-Security es peor que ninguna.
Definido como SOAPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
profile | texto | no | — |
path | texto | no | "/" |
version | texto | no | 1 |
element | texto | no | — |
base64 | verdadero o falso | no | — |
response_element | texto | no | "AckResponse" |
response_namespace | texto | no | — |
wsdl | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
token | texto | no | — |
username | texto | no | — |
password | texto | no | — |
max_message_size | número entero | no | — |
read_timeout | duración, como 30s o 5m | no | — |
ack | Ack | no | — |
fault_on_nak | verdadero o falso | no | — |
22.23.1 listen
Listen es la dirección en la que se sirve.
Required.
22.23.2 perfil
El nombre del perfil es un servicio estándar que este punto de conexión implementa.
Vacío acepta cualquier sobre según la configuración a continuación.
"cdc-iis" responde como un registro de inmunización lo hace, en la interfaz CDC (urn:cdc:iisb:2011): connectivityTest se repite, submitSingleMessage's hl7Message es el mensaje y el reconocimiento del canal se devuelve en la respuesta, el WSDL se sirve en ?wsdl, y el nombre de usuario y la contraseña, cuando están configurados, se verifican contra los de el cuerpo (donde el WSDL los coloca) en lugar de contra la autenticación HTTP. Para un doble de prueba de un registro, o para un sitio que recibe envíos de la manera que los registros lo hacen.
22.23.3 path
La ruta es la ruta de URL para aceptar sobres.
Defaults to "/".
22.23.4 versión
La versión es la versión SOAP que se responde, "1.1" o "1.2".
Defaults to 1.1.
Las solicitudes se aceptan en cualquier versión, porque un remitente que se equivoca en esto es común y rechazarlo no enseña nada a nadie. La configuración controla la respuesta, donde equivocarse significa que el remitente no puede leer la respuesta.
22.23.5 elemento
El elemento nombra el elemento dentro del cuerpo SOAP que contiene el mensaje.
Vacío significa todo el texto del cuerpo.
Nombrado en lugar de adivinado porque un cuerpo puede transportar varios elementos - un mensaje junto con un identificador de remitente y una marca de tiempo - y tomar todo el cuerpo daría al motor un fragmento XML donde se esperaba HL7.
22.23.6 base64
Base64 decodifica el contenido del elemento antes de tratarlo como un mensaje.
Común en servicios reales, porque HL7 transporta retornos de carro y los llamadores que no pensaron en eso terminan con XML que los desfigura. Si el contenido se decodifica como base64 pero no estaba destinado a serlo, el resultado no es un mensaje y el motor lo dice - lo que explica por qué esto es explícito en lugar de detectado.
22.23.7 elemento_de_respuesta
ResponseElement nombra el elemento que envuelve el acuse de recibo en la respuesta.
Por defecto es "AckResponse".
22.23.8 wsdl
WSDL es un archivo servido en la ruta de escucha con ?wsdl.
Vacío significa que no se sirve ninguno.
Un archivo en lugar de algo generado. Un WSDL generado describiría lo que este punto de conexión acepta, lo cual es casi nunca lo que el sistema de envío fue diseñado para - y un WSDL que está casi bien cuesta más tiempo que no tener WSDL en absoluto, porque parece autorizado.
22.23.9 nombre de usuario
Nombre de usuario y Contraseña, cuando están configurados, requieren autenticación básica HTTP.
Se ofrece junto con un token porque los llamadores SOAP esperan abrumadoramente la autenticación básica, y un sitio cuyo sistema de envío no puede ser cambiado necesita la opción que ya tiene.
22.23.10 contraseña
Nombre de usuario y Contraseña, cuando están configurados, requieren autenticación básica HTTP.
Se ofrece junto con un token porque los llamadores SOAP esperan abrumadoramente la autenticación básica, y un sitio cuyo sistema de envío no puede ser cambiado necesita la opción que ya tiene.
22.23.11 max_message_size
MaxMessageSize limita el cuerpo de entrada.
Cero aplica un valor predeterminado.
22.23.12 fault_on_nak
FaultOnNak responde a un acuse de recibo negativo con una falla SOAP en lugar de una respuesta normal que lleva el NAK.
Desactivado por defecto, que es el valor predeterminado correcto y vale la pena explicar. HL7 ya tiene una forma de decir "Recibí esto y lo rechacé", y esa es una NAK - un intercambio exitoso que informa de un mensaje rechazado. Convertir lo mismo en una falla de transporte le dice al remitente que su solicitud estaba malformada, lo cual no es así, y muchos clientes responden intentándolo nuevamente para siempre.
Existe porque algunos llamadores solo miran el estado HTTP y tratarían de otra manera un rechazo como éxito.
22.24 Fuente SQS
SQSSource lee de una cola SQS.
Un mensaje se elimina solo después de que el canal lo ha procesado - entregado, o filtrado, o fallado de una manera que el canal registra. Hasta entonces, permanece en la cola, invisible durante visibility_timeout, y vuelve si Perfuse se detiene. Así que un mensaje nunca se pierde; el precio es que uno puede llegar dos veces, lo cual SQS en sí ya permite.
Definido como SQSSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
queue_url | texto | sí | — |
wait_seconds | número entero | no | 20: una cola vacía cuesta una solicitud cada veinte segundos |
max_messages | número entero | no | 10 |
visibility_timeout | duración, como 30s o 5m | no | la configuración propia de la cola |
22.24.1 visibility_timeout
VisibilityTimeout es el tiempo que un mensaje recibido permanece oculto mientras se procesa.
Por defecto es la configuración propia de la cola.
22.25 Fuente Serie
SerialSource lee mensajes de un puerto serie.
¿Por qué un conector serie en 2026
Porque el equipo todavía está allí. Un analizador de gases sanguíneos comprado en 2009 con un sensor que funciona y un zócalo de 25 pines, un monitor de cabecera, una báscula en una unidad de diálisis, una máquina de anestesia más antigua. Ninguno de ellos tendrá nunca un puerto Ethernet, varios de ellos cuestan seis cifras, y la alternativa a leerlos es que alguien escriba los resultados en un formulario.
Mirth tiene un conector serie. Perfuse no tenía nada y un sitio con uno de estos no tenía ninguna ruta de entrada en absoluto.
El encuadre se comparte con el conector TCP
Los encuadres son los mismos independientemente de dónde provengan los bytes: un analizador que habla STX/ETX a través de un socket habla STX/ETX a través de un cable. Por lo tanto, se reutiliza el bloque de encuadre TCP en lugar de definir el propio, lo que también significa que un dispositivo que se mueve de un cable serie a un adaptador serie-a-Ethernet mantiene su configuración de encuadre.
¿Qué es diferente en una línea serie
No hay conexión. Un socket te informa cuando el peer se ha ido; un cable no. Un cable desconectado, un dispositivo apagado y un dispositivo con nada que decir son indistinguibles: los tres son silencio. Es por eso que existe quiet_after: sin algo que diga "esta línea ha estado en silencio durante más tiempo del que debería", una fuente muerta se parece exactamente a una noche tranquila.
Definido como SerialSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
port | texto | sí | — |
baud | número entero | sí | — |
data_bits | número entero | no | 8 |
parity | texto | no | none |
stop_bits | texto | no | 1 |
flow_control | texto | no | none |
max_message_size | número entero | no | — |
quiet_after | duración, como 30s o 5m | no | — |
reply | texto | no | — |
reply_text | texto | no | — |
reopen_after | duración, como 30s o 5m | no | — |
22.25.1 baud
Baud es la velocidad.
Requerido, porque adivinar produce bytes en lugar de un error.
Una velocidad de baud incorrecta no falla. Entrega basura plausible: los bytes de encuadre aparecen al azar, las corridas ocasionales se decodifican como caracteres imprimibles y un analizador indulgente acepta algunos de ellos. No hay valor predeterminado deliberadamente.
22.25.2 parity
La paridad es none, odd, even, mark o space.
Predeterminado es none.
22.25.3 stop_bits
StopBits es 1, 1.5 o 2, escrito como "1", "1.5" o "2".
Defaults to 1.
22.25.4 flow_control
FlowControl es none, hardware o software.
Predeterminado es none.
Vale la pena configurarlo correctamente en lugar de dejarlo. Con control de flujo de hardware esperado y no configurado, un dispositivo deja de enviar a mitad de un mensaje largo y el resultado es un mensaje truncado en lugar de un error.
22.25.5 quiet_after
QuietAfter registra una advertencia cuando no se ha recibido nada durante este tiempo.
La única forma de notar una fuente serie muerta. Un cable desconectado y una noche tranquila son el mismo silencio, así que alguien tiene que decir cuánto tiempo es demasiado para este dispositivo en particular.
22.25.6 reopen_after
ReopenAfter es cuánto tiempo esperar antes de volver a abrir un puerto que falló.
Un adaptador serie USB desconectado y vuelto a conectar vuelve como la misma ruta de dispositivo pero con un controlador del kernel diferente, así que el antiguo devuelve errores para siempre. Volver a abrir es el único medio de recuperación y hacerlo en un bucle ajustado llena el registro.
22.26 Origen
El origen es donde un canal recibe mensajes.
Definido como Source en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
type | SourceType | sí | — |
listen | texto | sí | — |
max_message_size | número entero | no | — |
idle_timeout | duración, como 30s o 5m | no | — |
max_connections | número entero | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
http | HTTPSource (un bloque, omitir para dejar sin) | no | — |
soap | SOAPSource (un bloque, omitir para dejar sin) | no | — |
dicomweb | DICOMwebSource (un bloque, omitir para dejar sin) | no | — |
direct | DirectSource (un bloque, omitir para dejar sin) | no | — |
dicom | DICOMSource (un bloque, omitir para dejar sin) | no | — |
file | FileSource (un bloque, omitir para dejar sin) | no | — |
tcp | TCPSource (un bloque, omitir para dejar sin) | no | — |
ftp | FTPSource (un bloque, omitir para dejar sin) | no | — |
smb | SMBSource (un bloque, omitir para dejar sin) | no | — |
webdav | WebDAVSource (un bloque, omitir para dejar sin) | no | — |
serial | SerialSource (un bloque, omitir para dejar sin) | no | — |
dicom_query | DICOMQuerySource (un bloque, omitir para dejar sin) | no | — |
broker | BrokerSource (un bloque, omitir para dejar sin) | no | — |
kafka | KafkaSource (un bloque, omitir para dejar sin) | no | — |
sqs | SQSSource (un bloque, omitir para dejar sin) | no | — |
s3 | S3Source (un bloque, omitir para dejar sin) | no | — |
amqp | AMQPSource (un bloque, omitir para dejar sin) | no | — |
azure_blob | AzureBlobSource (un bloque, omitir para dejar sin) | no | — |
email | EmailSource (un bloque, omitir para dejar sin) | no | — |
as2 | AS2Source (un bloque, omitir para dejar sin) | no | — |
pgp | PGPReceive (un bloque, omitir para dejar sin) | no | — |
mqtt | MQTTSource (un bloque, omitir para dejar sin) | no | — |
ibm_mq | IBMMQSource (un bloque, omitir para dejar sin) | no | — |
pubsub | PubSubSource (un bloque, omitir para dejar sin) | no | — |
gcs | GCSSource (un bloque, omitir para dejar sin) | no | — |
database | DatabaseSource (un bloque, omitir para dejar sin) | no | — |
sftp | SFTPSource (un bloque, omitir para dejar sin) | no | — |
javascript | JavaScriptSource (un bloque, omitir para dejar sin) | no | — |
ack | Ack | no | — |
22.26.1 tipo
El tipo es el transporte.
Solo se ha implementado mllp.
22.26.2 max_message_size
MaxMessageSize limita un mensaje de entrada.
Cero utiliza el valor predeterminado del transporte.
22.26.3 idle_timeout
IdleTimeout cierra una conexión que ha estado silenciosa durante este tiempo.
Cero significa nunca, lo que suele ser correcto: una fuente de hospital mantiene una conexión abierta durante meses y se queda silenciosa durante la noche.
22.26.4 max_conexiones
MaxConnections limita las conexiones concurrentes.
Cero significa ilimitado.
22.27 Origen TCP
TCPSource escucha en un socket para mensajes que no necesariamente son HL7.
Por qué esto es separado de un origen mllp
MLLP es un marco y Perfuse siempre lo ha hablado. Gran parte del equipo no lo hace: un analizador de laboratorio, una báscula, un monitor de cabecera, un sistema de facturación más antiguo. Cada uno tiende a tener su propia convención, y el conector TCP de Mirth cubre todos ellos, por lo que un sitio con una fuente de socket que no era HL7 no podía usar Perfuse en absoluto.
Definido como TCPSource en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
listen | texto | sí | — |
max_message_size | número entero | no | — |
idle_timeout | duración, como 30s o 5m | no | — |
max_connections | número entero | no | — |
reply | texto | no | — |
reply_text | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
22.27.1 idle_timeout
IdleTimeout cierra una conexión que ha estado silenciosa durante este tiempo.
Cero significa nunca, lo que suele ser correcto: una fuente de dispositivo mantiene una conexión abierta durante meses y se queda silenciosa durante la noche.
22.27.2 max_conexiones
MaxConnections limita las conexiones concurrentes.
Cero significa ilimitado.
22.27.3 respuesta
La respuesta es lo que se envía después de cada mensaje: nada, ack o una cadena fija.
La mayoría de las fuentes brutas esperan nada. Algunas esperan un solo byte ACK. Un dispositivo que espera una respuesta y no la recibe generalmente vuelve a intentar el mismo mensaje para siempre, por lo que vale la pena ser explícito al respecto.
22.28 Origen Web DAV
WebDAVSource recopila archivos de una colección WebDAV.
Donde se presenta: sistemas de gestión de documentos, SharePoint, Nextcloud y portales de proveedores que exponen una carpeta de destino a través de HTTPS porque es el único puerto de salida que permiten los firewalls de sus clientes.
Definido como WebDAVSource en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
url | texto | sí | — |
user | texto | no | — |
password | texto | no | — |
insecure_skip_verify | verdadero o falso | no | — |
timeout | duración, como 30s o 5m | no | sesenta segundos |
22.28.1 usuario
Usuario y Contraseña autenticán con autenticación básica de HTTP.
La autenticación básica envía la contraseña en un encabezado que solo está protegido por TLS, por lo que estos pertenecen a una URL https. Sobre http son legibles por cualquier cosa en la ruta y no hay advertencia del servidor que te lo indique.
22.28.2 contraseña
Usuario y Contraseña autenticán con autenticación básica de HTTP.
La autenticación básica envía la contraseña en un encabezado que solo está protegido por TLS, por lo que estos pertenecen a una URL https. Sobre http son legibles por cualquier cosa en la ruta y no hay advertencia del servidor que te lo indique.
22.28.3 tiempo de espera
El tiempo de espera limita una solicitud.
Predeterminado a sesenta segundos.
Más largo que los valores predeterminados de FTP y SMB deliberadamente. Una lista de colección WebDAV es un solo PROPFIND que el servidor puede tardar mucho en ensamblar para un directorio grande, y no hay resultado parcial al que recurrir.
23 Referencia de destino
Cada forma en que un canal puede enviar mensajes, y cada clave que cada uno acepta. Un canal puede tener cualquier número de destinos, y son independientes: uno que falla no detiene a los demás.
23.1 Destino AMQP
AMQPDestination envía a una cola o tema, e informa el éxito solo una vez que el broker ha aceptado el mensaje.
Definido como AMQPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
content_type | texto | no | el tipo de datos del canal |
23.1.1 content_type
ContentType se envía con cada mensaje.
Predeterminado al tipo de datos del canal.
23.2 Destino AS2
AS2Destination envía cada mensaje como AS2: firmado con nuestra clave, cifrado con el certificado del socio, publicado en la URL del socio y, por defecto, entregado solo una vez que el recibo firmado del socio nombra el mismo resumen.
Definido como AS2Destination en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
url | texto | sí | — |
from | texto | sí | — |
to | texto | sí | — |
cert_file | texto | no | — |
key_file | texto | no | — |
partner_cert_file | texto | no | — |
unsigned | verdadero o falso | no | — |
unencrypted | verdadero o falso | no | — |
compress | verdadero o falso | no | — |
receipt | texto | no | — |
unsigned_receipt | verdadero o falso | no | — |
receipt_url | texto | no | — |
content_type | texto | no | — |
file_name | texto | no | — |
subject | texto | no | — |
username | texto | no | — |
password | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
23.2.1 compress
Compress comprime la carga útil (zlib, RFC 5402) antes de firmar y cifrar.
Cada receptor AS2 que anuncia la versión 1.1 o posterior lo lee; pregúntele al socio antes de activarlo.
23.2.2 recibo_sin_firmar
UnsignedReceipt solicita un recibo que no esté firmado.
Desactivado: se solicita uno firmado y se rechaza uno sin firmar.
23.2.3 content_type
ContentType es el tipo MIME de la carga útil.
Predeterminado por el tipo de datos del canal: application/edi-x12, application/edifact, application/hl7-v2, de lo contrario application/octet-stream.
23.3 Destino de Blob de Azure
AzureBlobDestination escribe cada mensaje como un blob en bloque.
Definido como AzureBlobDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
blob | texto | no | ${date}/${channel}/${control_id} |
tier | texto | no | — |
content_type | texto | no | — |
23.3.1 blob
Blob nombra cada blob, con los marcadores de posición de destino de S3.
Predeterminado es ${date}/${channel}/${control_id}.hl7.
23.3.2 tier
Tier es Caliente, Fresco, Frío o Archivo.
Archivo está fuera de línea: leer un blob nuevamente significa rehidratarlo, lo que lleva horas.
23.4 Destino de Broker
BrokerDestination publica mensajes en un broker de mensajes.
Escritor JMS de Mirth.
Definido como BrokerDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
addr | texto | sí | — |
destination | texto | sí | — |
login | texto | no | — |
passcode | texto | no | — |
host | texto | no | el host de la dirección |
content_type | texto | no | texto/plano para HL7 |
headers | asignación de texto a texto | no | — |
persistent | verdadero o falso (un bloque, omitir para dejar fuera) | no | verdadero |
timeout | duración, como 30s o 5m | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar fuera) | no | — |
23.4.1 addr
Addr es el host y el puerto del broker.
Required.
23.4.2 destino
Destination es la cola o tema a publicar.
Required.
23.4.3 inicio de sesión
Inicio de sesión y código de autorización autenticados.
La mayoría de los brokers rechazan una conexión anónima.
23.4.4 código de autorización
Inicio de sesión y código de autorización autenticados.
La mayoría de los brokers rechazan una conexión anónima.
23.4.5 host
Host es el host virtual.
Predeterminado en el host de la dirección.
Vale la pena establecer explícitamente en un broker que hospeda varios hosts virtuales, donde el predeterminado aterriza la publicación en el que responde al nombre del servidor - una cola que existe, acepta el mensaje y es leída por nadie.
23.4.6 content_type
ContentType se envía con cada mensaje.
Predeterminado es texto/plano para HL7.
23.4.7 headers
Los encabezados se envían con cada mensaje.
Sólo configuración, nunca contenido de mensaje. Un encabezado tomado de un campo pondría un identificador de paciente en los metadatos del broker que se registran, indexan y son visibles para cada otra aplicación en la cola.
23.4.8 persistente
Persistente solicita al broker que sobreviva a un reinicio.
Predeterminado en true.
True porque un mensaje no persistente se pierde cuando el broker se reinicia, y eso no es un valor predeterminado razonable para los datos clínicos. False existe para una alimentación transitoria genuina - un latido de monitoreo - y debe ser elegido.
23.5 Destino CDA
CDADestination configura un destino cda: toma el documento clínico transportado dentro de un mensaje HL7 v2, lo convierte y escribe o publica el resultado.
Esto existe porque un documento casi nunca llega por sí solo. Llega codificado en base64 dentro de un MDM^T02, y cada motor que trata ese mensaje como un blob opaco deja el documento sin leer. Poseer ambos extremos de eso es el punto.
Definido como CDADestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | no | — |
dir | texto | no | — |
write | texto | no | — |
version | texto | no | R5 |
identifier_systems | asignación de texto a texto | no | — |
require_agreement | true o false | no | — |
on_no_document | texto | no | — |
headers | asignación de texto a texto | no | — |
bearer_token | texto | no | — |
23.5.1 url
URL es un servidor FHIR para publicar el paquete convertido.
Déjelo vacío para escribir en Dir en su lugar. Uno de los dos es obligatorio.
23.5.2 dir
Dir escribe la salida en archivos en lugar de publicarla.
Útil para una zona de aterrizaje que otro proceso recoge, y para ver qué produce la conversión antes de apuntar a un servidor en vivo.
23.5.3 write
Write selecciona qué se escribe o publica:
fhir el paquete de transacción convertido (predeterminado) documente el documento original en bytes, sin cambios ambos, con el documento junto con el paquete de transferencia Resumen de transición de atención de PACIO: un documento de paquete de FHIR R4 para el lugar al que va el paciente a continuación (un hogar de ancianos, una unidad de rehabilitación, atención en el hogar), publicado en url/Bundle o escrito en dir
"documento" vale la pena tenerlo por sí solo: un sitio que quiere sus CDAs en disco para un equipo de registros no necesita la conversión FHIR en absoluto.
23.5.4 identifier_systems
IdentifierSystems asigna una raíz OID del documento a un URI, para que un MRN se convierte en espaciado.
Un CDA identifica a las personas por OID y FHIR por URI, y nada puede derivar uno del otro.
23.5.5 require_agreement
RequireAgreement rechaza un documento cuyas entradas narrativas y codificadas se contradicen entre sí.
Desactivado por defecto, y deliberadamente así. La verificación es valiosa, pero es un juicio sobre un documento que alguien más ha autorizado, y un canal que silencia los documentos clínicos reales porque el generador de C-CDA del remitente es descuidado es peor que uno que los pasa con una advertencia. Un sitio que ha examinado sus propios hallazgos y ha decidido que quiere la puerta puede activarlo.
23.5.6 sin_documento
OnNoDocument decide qué sucede cuando el mensaje no lleva ningún documento clínico:
omitar no entregar nada y reportar éxito (predeterminado) fallar tratarlo como un fallo de entrega
El valor predeterminado es omitir, porque un canal que lleva una alimentación mixta de ADT y MDM fallaría en cada admisión. Un canal dedicado a documentos debe establecer fallar, ya que un mensaje de documento sin documento en él es un problema real en el remitente.
23.5.7 token_portador
BearerToken se envía como un encabezado de Autorización.
Preferir una referencia de variable de entorno sobre un literal en un archivo que se ingresa en git.
23.6 Destino del Canal
ChannelDestination enruta a otro canal en el mismo servidor.
Definido como ChannelDestination en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
name | texto | sí | — |
23.7 Destino DICOM
DICOMDestination envía objetos de imagen como un usuario de la clase de servicio C-STORE.
Mirth llama a esto el remitente DICOM.
Definido como DICOMDestination en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
address | texto | sí | — |
called_ae | texto | no | — |
calling_ae | texto | no | — |
transfer_syntaxes | lista de texto | no | — |
timeout | duración, como 30s o 5m | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
23.7.1 dirección
La dirección es el host y puerto remotos.
Required.
23.7.2 called_ae
CalledAE es lo que el remoto se llama a sí mismo, y CallingAE lo que este extremo se llama a sí mismo.
Ambos importan. Los sitios configuran un PACS para aceptar un título de llamada específico y rechazar todo lo demás, por lo que una discrepancia es la razón más común por la que una primera conexión falla, y el fallo parece un problema de red.
23.7.3 calling_ae
CalledAE es lo que el remoto se llama a sí mismo, y CallingAE lo que este extremo se llama a sí mismo.
Ambos importan. Los sitios configuran un PACS para aceptar un título de llamada específico y rechazar todo lo demás, por lo que una discrepancia es la razón más común por la que una primera conexión falla, y el fallo parece un problema de red.
23.7.4 transfer_syntaxes
TransferSyntaxes restringe lo que se ofrece, en orden de preferencia.
Vacío ofrece la codificación del objeto y luego implicit VR little endian como una opción de respaldo.
23.7.5 tiempo de espera
Timeout limita una operación de almacenamiento.
Cero aplica un valor predeterminado.
23.8 Destino Mweb DICOM
DICOMwebDestination almacena cada objeto con STOW-RS.
Definido como DICOMwebDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | sí | — |
token | texto | no | — |
username | texto | no | — |
password | texto | no | — |
headers | asignación de texto a texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
timeout | duración, como 30s o 5m | no | — |
23.9 Destino de base de datos
DatabaseDestination escribe cada mensaje en una base de datos.
Definido como DatabaseDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
driver | texto | sí | — |
dsn | texto | sí | — |
statement | texto | sí | — |
params | lista de texto | no | — |
timeout | duración, como 30s o 5m | no | 30s |
max_open_conns | número entero | no | 4 |
23.9.1 controlador
El controlador nombra la base de datos.
See SupportedDrivers.
23.9.2 declaración
La declaración se ejecuta una vez por mensaje, con Params enlazados a ella en orden.
Los marcadores de posición son los propios del controlador: $1 para postgres, ? para mysql y sqlite, @p1 para sqlserver. Perfuse no los reescribe, porque una capa de traducción que obtuviera un marcador de posición incorrecto enlazaría el nombre de un paciente con la columna incorrecta y el resultado parecería datos válidos.
23.9.3 params
Params son rutas HL7 o expresiones ${...}, enlazadas a la declaración en orden.
Los valores siempre se enlazan como parámetros y nunca se interpolan en el SQL. Eso no solo se trata de inyección: O'Brien es un nombre común, y una consulta concatenada se rompe en la comilla.
23.9.4 tiempo de espera
Timeout limita una sola declaración.
Defaults to 30s.
23.9.5 max_open_conns
MaxOpenConns limita el grupo.
Defaults to 4.
23.10 Destino
El destino es un lugar al que un canal envía mensajes.
Definido como Destination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
name | texto | sí | — |
type | DestinationType | sí | — |
enabled | true o false (un bloque, omitir para dejar sin efecto) | no | — |
filter | texto | no | — |
address | texto | no | — |
dir | texto | no | — |
file_name | texto | no | — |
temp_suffix | texto | no | " |
append | true o false | no | — |
retain_hours | número entero | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin efecto) | no | — |
fhir | FHIRDestination (un bloque, omitir para dejar sin efecto) | no | — |
cda | CDADestination (un bloque, omitir para dejar sin efecto) | no | — |
http | HTTPDestination (un bloque, omitir para dejar sin efecto) | no | — |
database | DatabaseDestination (un bloque, omitir para dejar sin efecto) | no | — |
sftp | SFTPDestination (un bloque, omitir para dejar sin efecto) | no | — |
tcp | TCPDest (un bloque, omitir para dejar sin efecto) | no | — |
smtp | SMTPDestination (un bloque, omitir para dejar sin efecto) | no | — |
channel | ChannelDestination (un bloque, omitir para dejar sin efecto) | no | — |
s3 | S3Destination (un bloque, omitir para dejar sin efecto) | no | — |
ftp | FTPDestination (un bloque, omitir para dejar sin efecto) | no | — |
document | DocumentDestination (un bloque, omitir para dejar sin efecto) | no | — |
soap | SOAPDestination (un bloque, omitir para dejar sin efecto) | no | — |
dicomweb | DICOMwebDestination (un bloque, omitir para dejar sin efecto) | no | — |
xds | XDSDestination (un bloque, omitir para dejar sin efecto) | no | — |
direct | DirectDestination (un bloque, omitir para dejar sin efecto) | no | — |
as2 | AS2Destination (un bloque, omitir para dejar sin efecto) | no | — |
mpi | MPIDestination (un bloque, omitir para dejar sin efecto) | no | — |
tefca | TEFCADestination (un bloque, omitir para dejar sin efecto) | no | — |
pas | PASDestination (un bloque, omitir para dejar sin efecto) | no | — |
x12_as | texto | no | — |
dicom | DICOMDestination (un bloque, omitir para dejar sin efecto) | no | — |
javascript | JavaScriptDestination (un bloque, omitir para dejar sin efecto) | no | — |
broker | BrokerDestination (un bloque, omitir para dejar sin efecto) | no | — |
kafka | KafkaDestination (un bloque, omitir para dejar sin efecto) | no | — |
sqs | SQSDestination (un bloque, omitir para dejar sin efecto) | no | — |
sns | SNSDestination (un bloque, omitir para dejar sin efecto) | no | — |
amqp | AMQPDestination (un bloque, omitir para dejar sin efecto) | no | — |
azure_blob | AzureBlobDestination (un bloque, omitir para dejar sin efecto) | no | — |
mqtt | MQTTDestination (un bloque, omitir para dejar sin efecto) | no | — |
ibm_mq | IBMMQDestination (un bloque, omitir para dejar sin efecto) | no | — |
pubsub | PubSubDestination (un bloque, omitir para dejar sin efecto) | no | — |
gcs | GCSDestination (un bloque, omitir para dejar sin efecto) | no | — |
elr | ELRDestination (un bloque, omitir para dejar sin efecto) | no | — |
pgp | PGPSend (un bloque, omitir para dejar sin efecto) | no | — |
response_transformer | texto | no | — |
timeout | duración, como 30s o 5m | no | — |
retry | Retry | no | — |
queue | QueueConfig (un bloque, omitir para dejar sin efecto) | no | — |
23.10.1 nombre
El nombre identifica el destino en los registros y las métricas.
Requerido dentro de un canal.
23.10.2 file_name
FileName, para un destino de archivo, crea un archivo por mensaje (${timestamp}, ${date}, ${control_id}, ${message_type}, ${destination}).
Vacío mantiene el valor predeterminado: un archivo por día y tipo de mensaje, agregado a.
23.10.3 temp_suffix
TempSuffix es el nombre que lleva un archivo por mensaje mientras se escribe, eliminado por un cambio de nombre una vez que esté completo, para que lo que recopila los archivos nunca lea la mitad de uno.
Predeterminado es ".part".
23.10.4 retain_hours
RetainHours elimina los archivos en Dir que tienen más de esta edad.
Cero los mantiene para siempre.
23.10.5 x12_as
X12As es xml para un destino en un canal X12 que recibe el intercambio como el XML que escriben Mirth Connect y Open Integration Engine para X12, para un receptor construido contra lo que Mirth envía.
Vacío o x12 envía X12.
23.10.6 elr
ELR convierte este destino en un destino de informes de laboratorio de salud pública: cada resultado de laboratorio (ORU^R01) se reorganiza en un mensaje ELR 2.5.1 que solo contiene las órdenes con un resultado informable, y eso es lo que se entrega; un mensaje con nada informable no se envía en absoluto.
Se aplica a los destinos que entregan un mensaje HL7 v2 tal como es (mllp, http, archivo, sftp y el resto), en un canal hl7.
23.10.7 response_transformer
ResponseTransformer inspecciona lo que respondió el receptor y puede marcar la entrega como fallida.
Existe porque "¿llegó esto?" a menudo no es una pregunta que el transporte pueda responder: un receptor MLLP devuelve un reconocimiento de aplicación cuyo significado está en su texto, y un receptor HTTP devuelve 200 con un documento de error. En ambos casos, el transporte tuvo éxito y el mensaje no llegó.
Solo es significativo en un destino que recibe una respuesta. En uno que no puede, se rechaza en la carga en lugar de dejarlo nunca en ejecución.
23.10.8 cola
La cola hace que la entrega sea duradera: un mensaje que no se puede entregar ahora se mantiene en disco y se vuelve a intentar hasta que se pueda.
Desactivado por defecto.
23.11 Destino directo
DirectDestination envía cada mensaje como Direct: firmado con esta dirección, cifrado con el certificado de cada destinatario y enviado al HISP por SMTP.
Definido como DirectDestination en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
to | lista de texto | sí | — |
subject | texto | no | — |
text | texto | no | — |
attach_name | texto | no | — |
xdm | verdadero o falso | no | — |
xdm_source_id | texto | no | — |
recipient_certs_dir | texto | no | — |
dns | texto | no | — |
ldap | texto | no | — |
smtp_host | texto | sí | — |
username | texto | no | — |
password | texto | no | — |
request_mdn | verdadero o falso | no | — |
timeout | duración, como 30s o 5m | no | — |
23.11.1 para
Para son las direcciones Direct de los destinatarios.
Las referencias de campo se sustituyen, por lo que un destinatario puede provenir del mensaje.
23.11.2 directorio_certificados_destinatarios
DirectorioCertificadosDestinatarios contiene los certificados de los destinatarios (PEM, un archivo por dirección o dominio, address.pem o domain.pem) encontrados con anticipación; vacío lee el libro de direcciones Direct del servidor (la pantalla Direct).
Los destinatarios que no están allí se buscan en DNS (registros CERT). Direct no envía a un destinatario cuyo certificado no se pueda encontrar.
23.11.3 ldap
LDAP es el directorio donde se busca el certificado de un destinatario cuando DNS no tiene ninguno (host:puerto, puerto 389): la segunda forma en que permite la guía de descubrimiento de certificados Direct.
Vacío encuentra el directorio del dominio del destinatario mediante su registro SRV de DNS (_ldap._tcp), como dice la guía; "off" nunca pregunta a un directorio.
23.12 Destino de documento
DocumentDestination renderiza cada mensaje en un documento.
Definido como DocumentDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
dir | texto | sí | — |
format | DocumentFormat | no | |
template | texto | sí | — |
title | texto | no | — |
file_name | texto | no | — |
font_size | número | no | 10 |
landscape | verdadero o falso | no | — |
temp_suffix | texto | no | " |
timeout | duración, como 30s o 5m | no | 30s |
23.12.1 formato
El formato es pdf o texto.
Predeterminado es pdf.
23.12.2 plantilla
La plantilla es el cuerpo del documento, con referencias de estilo ${PID-5.1} a rutas de mensaje.
Requerido. No hay un valor predeterminado sensato: un documento sin plantilla sería el mensaje crudo, que nadie quiere imprimir, o estaría vacío.
23.12.3 tamaño_fuente
Tamaño de fuente en puntos.
Predeterminado es 10, que cabe 80 caracteres en A4.
23.12.4 temp_suffix
SufijoTemporal se agrega mientras se escribe y se elimina mediante un cambio de nombre.
Predeterminado es ".part".
La misma cortesía que cualquier otro destino de archivo, y aquí importa más: un observador de impresión que recoge un PDF escrito a medias produce una página en blanco, y nadie investiga una página en blanco.
23.12.5 tiempo de espera
Los límites de tiempo limitan una escritura.
Defaults to 30s.
23.13 Destino ECR
ECRDestination configura el informe de casos electrónicos.
Definido como ECRDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
rctc | texto | no | — |
source | texto | sí | — |
facility | ECRFacility | sí | — |
23.13.1 rctc
RCTC es un conjunto de valores FHIR, o una colección de ellos como el eRSD, que contiene los códigos de activación.
Sin él, se utiliza el ejemplo breve integrado de Perfuse, que es para pruebas: las agencias esperan el RCTC.
23.14 Destino ELR
ELRDestination configura el informe de laboratorio electrónico.
Definido como ELRDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
config | texto | sí | — |
rctc | texto | no | — |
23.14.1 config
La configuración es el archivo ELR que perfuse elr -config lee: las aplicaciones y instalaciones de envío y recepción por OID, el modo de procesamiento y lo que llena los espacios que deja una alimentación de laboratorio (instalación de pedidos, laboratorio que realiza, autoridades de asignación).
Se lee una ruta relativa desde el directorio del archivo del canal.
23.14.2 rctc
RCTC es un conjunto de valores FHIR, o una colección de ellos como el eRSD, que contiene los códigos de activación.
Sin él, se utiliza el ejemplo breve integrado de Perfuse, que es para pruebas: las agencias esperan el RCTC. Relativo al archivo del canal, como Config.
23.15 Destino FHIR
FHIRDestination configura la conversión a FHIR y la entrega a un servidor FHIR.
Definido como FHIRDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | sí | — |
version | texto | no | R5 |
timezone | texto | no | — |
default_identifier_system | texto | no | — |
identifier_systems | asignación de texto a texto | no | — |
claim_us_core | true o false | no | — |
validate_before_send | true o false (un bloque, omitir para dejar sin efecto) | no | true: enviar un recurso que un servidor rechazará desperdicia un presupuesto de reintento y oculta el problema real detrás de un error de transporte |
reject_on_warning | true o false | no | — |
headers | asignación de texto a texto | no | — |
bearer_token | texto | no | — |
auth | FHIRAuth (un bloque, omitir para dejar sin efecto) | no | — |
ecr | ECRDestination (un bloque, omitir para dejar sin efecto) | no | — |
bundle_script | texto | no | — |
23.15.1 url
La URL es la URL base del servidor FHIR, por ejemplo "https://fhir.example.org/fhir".
La colección se publica directamente en ella.
23.15.2 versión
La versión es la versión de FHIR que se produce: R4, R4B o R5. De manera predeterminada, es R5, la versión publicada más reciente.
Una implementación que se comunica con un EHR casi siempre quiere R4 y debe decirlo, porque degradar silenciosamente ocultaría una decisión real de interoperabilidad.
23.15.3 zona horaria
La zona horaria se aplica a las marcas de tiempo de HL7 v2 que no llevan offset. v2 permite un tiempo local sin offset y FHIR no, por lo que algo tiene que suministrar uno.
Cuando no está establecido, se utiliza el offset que el remitente puso en MSH-7, ya que v2 presume que un tiempo sin offset es la hora local del remitente; un mensaje cuyo MSH-7 no tiene offset cae en UTC.
23.15.4 sistema_de_identificador_predeterminado
DefaultIdentifierSystem espacia los identificadores cuya autoridad de asignación no tiene una URI configurada.
Sin un sistema, un MRN es ambiguo entre instalaciones.
23.15.5 claim_us_core
ClaimUSCore agrega el perfil US Core 9.0.0 a cada recurso producido que ha sido verificado contra él y es conforme.
Un recurso que no cumple es enviado sin la reclamación, con una nota que explica por qué.
23.15.6 validar_antes_de_enviar
ValidateBeforeSend se niega a publicar un paquete que no supera la validación.
Predeterminado en true: enviar un recurso que un servidor rechazará desperdicia un presupuesto de reintento y oculta el problema real detrás de un error de transporte.
23.15.7 rechazar_en_advertencia
RejectOnWarning también se niega en caso de advertencias, como un identificador US Core que falta.
Desactivado por defecto, porque una advertencia es un juicio sobre la conformidad del perfil en lugar de la validez.
23.15.8 token_portador
BearerToken se envía como un encabezado de Autorización.
Preferir una referencia de variable de entorno, ${NAME}, sobre un literal en un archivo que se incorpora a git.
23.15.9 ecr
ECR convierte este destino en un caso de notificación de salud pública.
Cada mensaje se verifica en busca de códigos de activación; uno que tiene alguno se envía como un eCR 2.1 eICR, envuelto en un mensaje eCR, a la URL $process-message, y uno sin ninguno no se envía en absoluto.
23.15.10 guion de paquete
BundleScript es JavaScript que se ejecuta en cada paquete convertido antes de que se valide y envíe: bundle es el paquete como un objeto, editado en su lugar.
Es la forma en que un sitio agrega lo que sus propios sistemas saben y el mensaje v2 no dice - el código IKNR o ODS de su organización, un nivel de contacto de un perfil nacional - sin que el convertidor lo adivine. Un guion que lanza una excepción falla el mensaje, al igual que cualquier otro transformador. No se ejecuta para informes de casos (ecr).
23.16 Destino FTP
FTPDestination carga un archivo por mensaje.
Definido como FTPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | 21 |
user | texto | no | — |
password | texto | no | — |
security | FTPSecurity | no | explícito en lugar de none |
insecure_skip_verify | true o false | no | — |
allow_clear_password | true o false | no | — |
dir | texto | sí | — |
file_name | texto | no | — |
temp_suffix | texto | no | " |
framed | true o false | no | — |
timeout | duración, como 30s o 5m | no | 60s |
23.16.1 host
El host es el servidor, con un puerto opcional.
Predeterminado en 21, o 990 para TLS implícito.
23.16.2 usuario
El usuario y la contraseña autenticán.
Un usuario vacío significa anónimo.
23.16.3 contraseña
El usuario y la contraseña autenticán.
Un usuario vacío significa anónimo.
23.16.4 seguridad
Seguridad selecciona TLS: explícito, implícito o ninguno.
Predeterminado a explícito en lugar de ninguno, porque el valor predeterminado de FTP plano haría que la opción insegura sea la opción silenciosa, y un sitio que genuinamente no tiene TLS debería tener que escribirlo.
23.16.5 insecure_skip_verify
InsecureSkipVerify acepta cualquier certificado.
Necesario con más frecuencia de lo que a nadie le gustaría: estos servidores suelen tener un certificado auto-firmado generado hace años que nadie puede volver a emitir. Nombrado por separado para que la elección sea deliberada y visible en el archivo en lugar de oculta dentro de un modo de "seguridad: relajada".
23.16.6 allow_clear_password
AllowClearPassword permite una contraseña sobre FTP plano.
Rechazado por defecto, porque eso envía una credencial a través de la red en texto plano en cada entrega. Algunos analistas genuinamente no tienen TLS, así que tiene que ser posible, pero tiene que estar escrito, para que sea una decisión que alguien tomó en lugar de un valor predeterminado que heredó. El nombre está deliberadamente diseñado para ser incómodo de escribir.
23.16.7 temp_suffix
TempSuffix se agrega mientras se escribe el archivo y se elimina mediante un cambio de nombre una vez que esté completo.
Predeterminado es ".part".
Quienquiera que recopile estos archivos suele ser un trabajo programado que toma lo que encuentra, y sin el cambio de nombre, eventualmente toma la mitad de un mensaje.
23.16.8 tiempo de espera
Timeout limita una transferencia.
Defaults to 60s.
23.17 Destino GCS
GCSDestination escribe cada mensaje como un objeto.
Definido como GCSDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
object | texto | no | ${date}/${channel}/${control_id} |
storage_class | texto | no | — |
content_type | texto | no | — |
23.17.1 object
Object es el nombre del objeto, con los mismos marcadores de posición que una clave s3.
Predeterminado es ${date}/${channel}/${control_id}.hl7.
23.18 Destino HTTP
HTTPDestination publica mensajes en un punto de conexión HTTP.
La cosa más común que un canal de Mirth hace que Perfuse no podría, y el bloqueador más común que el traductor informa. La mayoría de los usos son poco glamorosos: publicar el mensaje en una API interna, publicarlo en un servicio web que envuelve un sistema heredado, publicarlo en algo que alguien escribió en una tarde.
Definido como HTTPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | sí | — |
method | texto | no | — |
content_type | texto | no | — |
headers | asignación de texto a texto | no | — |
bearer_token | texto | no | — |
username | texto | no | — |
password | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar fuera) | no | — |
success_status | lista de número entero | no | cualquier 2xx |
fail_on_body | lista de texto | no | — |
follow_redirects | verdadero o falso | no | — |
23.18.1 url
URL es donde publicar.
Required.
23.18.2 method
Method predeterminado es POST.
PUT está permitido para un punto de conexión que lo desee.
GET y DELETE son rechazados: esto envía un cuerpo de mensaje y un transporte que silenciosamente descartó el mensaje debido a una elección de método sería una forma muy silenciosa de perder datos.
23.18.3 content_type
ContentType se establece de forma predeterminada en application/hl7-v2+er7, que es el tipo registrado para un mensaje HL7 v2 delimitado por tuberías.
Muchos puntos de conexión desean text/plain en su lugar.
23.18.4 token_portador
BearerToken se envía como un encabezado de Autorización.
Preferir una referencia de variable de entorno sobre un literal en un archivo que se ingresa en git.
23.18.5 estado_de_éxito
SuccessStatus enumera los códigos de estado que se consideran entregados.
De forma predeterminada, cualquier código 2xx.
Configurable porque los puntos de conexión no están de acuerdo sobre cómo se ve el éxito: algunos responden con 200 con un error en el cuerpo y algunos responden con 202 para una cola que han aceptado el mensaje.
23.18.6 fallar_en_cuerpo
FailOnBody trata una respuesta que contiene alguna de estas cadenas como un fallo, independientemente del código de estado.
Esto existe porque un punto de conexión que responde con 200 y "ERROR: paciente no encontrado" en el cuerpo es común, y tratarlo como entregado significa que el mensaje se ha perdido y nadie lo sabe.
23.18.7 seguir_redirecciones
FollowRedirects permite que la solicitud sea redirigida.
Desactivado de forma predeterminada: una redirección en una escritura volvería a publicar datos clínicos en algún lugar que la configuración nunca ha nombrado.
23.19 Destino IBMMQ
IBMMQDestination coloca cada mensaje en una cola.
Definido como IBMMQDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
non_persistent | true o false | no | — |
23.19.1 no_persistente
NonPersistent envía mensajes que no sobreviven a un reinicio del administrador de colas.
Desactivado por defecto.
23.20 Destino de secuencia de comandos de Java
JavaScriptDestination ejecuta un script en lugar de enviar a algún lugar.
El escritor de JavaScript de Mirth, y se utiliza mucho más de lo que sugiere su posición en una lista de características. Es la salida de emergencia: cuando un sitio necesita hablar con algo que no cubre ningún conector, o aplicar lógica que no expresa ninguna transformación, escriben un escritor de JavaScript. Una migración que no puede ejecutar esos no puede mover los canales que los tienen, lo que en una instalación madura de Mirth es una gran fracción.
Destinado a no ser un host de scripting general. El script recibe el mensaje y los mismos ayudantes que un transformador recibe; lo que no recibe es una forma de llegar al sistema de archivos o a la red, porque eso es lo que los demás destinos están para y un script que abre su propio socket es invisible para cada reintento, cola y métrica en el motor.
Definido como JavaScriptDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
script | texto | sí | — |
timeout | duración, como 30s o 5m | no | — |
success_on_undefined | true o false (un bloque, omitir para dejar fuera) | no | true |
23.20.1 script
Script es el código a ejecutar.
Required.
23.20.2 tiempo de espera
Timeout limita una ejecución.
Cero aplica un valor predeterminado.
Limitado porque un script de destino está en la ruta de entrega: un bucle infinito en uno no solo falla un mensaje, sino que también bloquea la cola detrás de él.
23.20.3 success_on_undefined
SuccessOnUndefined trata un script que no devuelve nada como un éxito.
Predeterminado en true.
True es el valor predeterminado correcto y vale la pena explicarlo. Los escritores de JavaScript de Mirth hacen su trabajo y no devuelven nada, por lo que requerir un valor de retorno explícito rompería todos ellos al importar. Establecerlo en false es para un sitio que quiere que un script sea explícito sobre si funcionó.
23.21 Destino de Kafka
KafkaDestination publica mensajes en un tema de Kafka.
Definido como KafkaDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
brokers | lista de texto | sí | — |
topic | texto | sí | — |
key | texto | no | — |
acks | texto | no | all |
compression | texto | no | snappy |
headers | asignación de texto a texto | no | — |
timeout | duración, como 30s o 5m | no | treinta segundos |
sasl | KafkaSASL (un bloque, omitir para dejar fuera) | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar fuera) | no | — |
23.21.1 corredores
Brokers es la lista de servidores de arranque.
Required.
23.21.2 tema
Tema es donde publicar.
Required.
23.21.3 clave
Clave es una plantilla para la clave del registro, que decide la partición.
Este es el campo que hace que Kafka sea seguro para una fuente clínica, y vale la pena ser preciso sobre por qué. Kafka garantiza el orden dentro de una partición y en ningún otro lugar. Los registros con la misma clave siempre aterrizan en la misma partición, por lo que clavear en el identificador del paciente mantiene los eventos de ese paciente en orden mientras permite que los pacientes diferentes vayan en paralelo.
Sin una clave no hay tal garantía. Medido contra un broker real, treinta registros no claveados fueron a una partición en lugar de dispersarse: el cliente mantiene un lote unido y elige una nueva partición entre lotes. Entonces, el orden se mantiene por accidente y se rompe en un límite que nadie puede ver, lo que es peor que romper consistentemente - una alta A03 leída antes de su admisión A01, intermitentemente, bajo carga.
Un camino HL7, evaluado contra el mensaje saliente: PID-3.1 clavea por paciente.
23.21.4 acks
Acks es cuántos brokers deben confirmar una escritura: "all", "leader" o "none".
Predeterminado en all, que es la única configuración que sobrevive a un broker que falla entre la escritura y la replicación. "none" es disparar y olvidar y perderá mensajes; existe porque alguien que mueve telemetría no clínica puede legítimamente quererlo, y rechazarlo directamente significaría que escribieron su propio productor en su lugar.
23.21.5 compresión
Compresión es "none", "gzip", "snappy", "lz4" o "zstd".
Predeterminado en snappy.
Activado por defecto porque HL7 es texto altamente comprimible y el cable es generalmente la restricción. Snappy en lugar de zstd como predeterminado: es el más barato en CPU, y un conector en la ruta de entrega no debe ser la cosa que satura un núcleo.
23.21.6 headers
Los encabezados son encabezados de registro para establecer.
Los valores son plantillas evaluadas por mensaje.
23.21.7 tiempo de espera
El tiempo de espera limita una publicación.
Predeterminado en treinta segundos.
23.22 Destino MPI
MPIDestination registra a cada paciente de un mensaje en el índice de pacientes de este servidor: un PID de HL7 v2 (una fusión A40 fusiona), o un paciente de FHIR en un canal sin procesar.
El índice decide qué registros son la misma persona; consulte la pantalla del índice de pacientes.
Definido como MPIDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
authorities | asignación de texto a texto | no | — |
id_type | texto | no | — |
ssn_system | texto | no | — |
no_merge | verdadero o falso | no | — |
23.22.1 autoridades
Las autoridades asignan un espacio de nombres de autoridad de asignación (PID-3.4.1) a su sistema de identificador, para remitentes que no proporcionan un identificador universal.
Los no asignados se convierten en urn:perfuse:aa:<namespace>.
23.22.2 id_type
IDType es el tipo de identificador (PID-3.5) del número del registro.
Default MR.
23.22.3 ssn_system
SSNSystem nombra a PID-19 como identificador.
Default http://hl7.org/fhir/sid/us-ssn.
23.23 Destino MQTT
MQTTDestination publica en un tema.
Definido como MQTTDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
topic | texto | sí | — |
retain | verdadero o falso | no | — |
23.23.1 tema
Tema publicado.
No puede contener comodines.
23.24 Destino PAS
PASDestination envía cada mensaje, un paquete de solicitud Da Vinci PAS, a Claim/$submit de un pagador, para un proveedor cuyos sistemas construyen la solicitud (un registro electrónico de salud, DTR) y necesitan que se envíe, se registre la decisión y se pase la respuesta.
Definido como PASDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
payer | texto | sí | — |
bearer_token | texto | no | — |
auth | FHIRAuth (un bloque, omitir para dejar sin) | no | — |
results_to | texto | no | — |
23.24.1 token_portador
BearerToken es un token estático, ${ENV} permitido.
Autenticación es OAuth credenciales de cliente en su lugar, o Azure, como un destino de FHIR.
23.24.2 autenticación
BearerToken es un token estático, ${ENV} permitido.
Autenticación es OAuth credenciales de cliente en su lugar, o Azure, como un destino de FHIR.
23.25 Destino Pub Sub
PubSubDestination publica en un tema de Pub/Sub.
Definido como PubSubDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
topic | texto | sí | — |
order_by | texto | no | — |
23.26 Destino S3
S3Destination escribe cada mensaje como un objeto.
Definido como S3Destination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
bucket | texto | sí | — |
region | texto | sí | — |
key | texto | no | una ruta con partición por fecha más el id de control |
access_key_id | texto | no | — |
secret_access_key | texto | no | — |
session_token | texto | no | — |
endpoint | texto | no | — |
path_style | verdadero o falso | no | — |
server_side_encryption | texto | no | — |
content_type | texto | no | application/hl7-v2 para HL7 y texto/plain de lo contrario |
framed | verdadero o falso | no | — |
timeout | duración, como 30s o 5m | no | 60s |
storage_class | texto | no | — |
format | texto | no | — |
23.26.1 clave
La clave crea un modelo de clave de objeto, utilizando los mismos marcadores de posición ${date}, ${timestamp}, ${control_id}, ${message_type} y ${channel} que los destinos de archivo y SFTP.
Predeterminado es una ruta con partición por fecha más el id de control.
La partición por fecha es el valor predeterminado porque la alternativa - todos los objetos en un prefijo plano - hace que un bucket sea lento para listar y imposible de gestionar por edad, y nadie lo descubre hasta que hay cuatro millones de objetos en él.
23.26.2 access_key_id
AccessKeyID y SecretAccessKey autenticar.
Cualquiera de los dos puede nombrar una variable de entorno en lugar de contener el valor, utilizando la forma ${NAME}.
23.26.3 secret_access_key
AccessKeyID y SecretAccessKey autenticar.
Cualquiera de los dos puede nombrar una variable de entorno en lugar de contener el valor, utilizando la forma ${NAME}.
23.26.4 path_style
PathStyle dirige el bucket como una ruta en lugar de un subdominio.
La mayoría de las tiendas compatibles con S3 necesitan esto.
23.26.5 content_type
ContentType etiqueta el objeto.
Predeterminado es application/hl7-v2 para HL7 y texto/plain de lo contrario.
23.26.6 tiempo de espera
Timeout limita una carga.
Defaults to 60s.
23.26.7 storage_class
StorageClass es la clase de almacenamiento de S3 que se escribe con cada objeto: STANDARD_IA, INTELLIGENT_TIERING, GLACIER_IR, GLACIER, DEEP_ARCHIVE y así sucesivamente.
Esta es la forma en que un archivo va directamente a Glacier - S3 no tiene una API de Glacier por llamar.
23.26.8 formato
Formato es hl7 (el predeterminado: cada objeto es el mensaje) o ndjson: cada objeto es una línea JSON con el mensaje y los campos que vale la pena consultar, bajo claves con partición por fecha, para que Amazon Athena pueda consultar el archivo.
Ver perfuse athena.
23.27 Destino SFTP
SFTPDestination escribe cada mensaje en un archivo en un servidor SFTP.
Definido como SFTPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | 22 |
user | texto | sí | — |
password | texto | no | — |
key_file | texto | no | — |
key_passphrase | texto | no | — |
known_hosts_file | texto | no | — |
insecure_skip_host_key_check | verdadero o falso | no | — |
dir | texto | sí | — |
file_name | texto | no | una marca de tiempo más el ID de control |
temp_suffix | texto | no | " |
framed | verdadero o falso | no | — |
append | verdadero o falso | no | — |
timeout | duración, como 30s o 5m | no | 60s |
23.27.1 host
El host es el servidor, con un puerto opcional.
Defaults to 22.
23.27.2 contraseña
Contraseña autentica con una contraseña.
Prefer KeyFile.
23.27.3 file_name
FileName crea un nombre para el archivo.
Predeterminado es una marca de tiempo más el ID de control.
23.27.4 temp_suffix
TempSuffix se agrega mientras se escribe el archivo y se elimina mediante un cambio de nombre una vez que esté completo.
Predeterminado es ".part".
La misma cortesía que este conector necesita en el camino de entrada. Quien recopila estos archivos tiene el mismo problema, y un cambio de nombre dentro de un directorio es atómico en cada servidor que vale la pena usar.
23.27.5 tiempo de espera
Timeout limita una conexión individual.
Defaults to 60s.
23.28 Destino SMTP
SMTPDestination configura la entrega por correo electrónico.
Definido como SMTPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | — |
from | texto | sí | — |
to | lista de texto | sí | — |
cc | lista de texto | no | — |
bcc | lista de texto | no | — |
subject | texto | no | — |
body | texto | no | — |
attach | verdadero o falso | no | — |
attach_name | texto | no | el ID de control con un |
username | texto | no | — |
password | texto | no | — |
starttls | verdadero o falso (un bloque, omitir para dejar fuera) | no | — |
insecure_skip_verify | verdadero o falso | no | — |
timeout | duración, como 30s o 5m | no | — |
23.28.1 host
Host es el servidor de correo, con un puerto opcional.
Se asume el puerto 587, que es la presentación con STARTTLS - el puerto que un servidor moderno espera de un cliente. El puerto 25 es de servidor a servidor y generalmente está bloqueado o no autenticado.
23.28.2 de
De es el remitente del sobre.
Requerido, porque un mensaje sin remitente se descarta silenciosamente por gran parte de la infraestructura de correo, lo que lo hace el fallo más difícil de diagnosticar.
23.28.3 para
Para, CC y BCC son destinatarios.
Al menos un Para es requerido.
23.28.4 cc
Para, CC y BCC son destinatarios.
Al menos un Para es requerido.
23.28.5 bcc
Para, CC y BCC son destinatarios.
Al menos un Para es requerido.
23.28.6 cuerpo
Cuerpo es el texto del mensaje.
Las referencias de campo se sustituyen, como en Asunto. Cuando está vacío, el mensaje en sí es el cuerpo.
23.28.7 attach_name
AttachName nombra el archivo adjunto.
Predeterminado es el ID de control con un sufijo .hl7.
23.28.8 nombre de usuario
Nombre de usuario y Contraseña autenticar en el servidor.
Ambos o ninguno.
23.28.9 contraseña
Nombre de usuario y Contraseña autenticar en el servidor.
Ambos o ninguno.
23.28.10 starttls
StartTLS mejora la conexión antes de autenticar.
Está configurado por defecto y desactivarlo con un nombre de usuario establecido se rechaza en lugar de permitirlo: enviar una contraseña a través de una conexión no cifrada no es un intercambio digno de ofrecer.
23.28.11 insecure_skip_verify
InsecureSkipVerify acepta cualquier certificado.
Nombrado de manera inequívoca porque es lo que hace.
23.29 Destino SNS
SNSDestination publica cada mensaje en un tema SNS.
Definido como SNSDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
topic_arn | texto | sí | — |
subject | texto | no | — |
group_by | texto | no | — |
23.29.1 asunto
El asunto se envía con las suscripciones de correo electrónico.
Opcional; ${message_type} y ${control_id} se rellenan.
23.30 Destino SOAP
SOAPDestination publica una solicitud SOAP por mensaje.
Definido como SOAPDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | sí | — |
profile | texto | no | — |
facility_id | texto | no | — |
warnings_are_errors | verdadero o falso | no | — |
action | texto | no | — |
version | texto | no | 1 |
body | texto | sí | — |
header | texto | no | — |
headers | asignación de texto a texto | no | — |
username | texto | no | — |
password | texto | no | — |
tls | tlsconf.Configuración (un bloque, omitir para dejar sin) | no | — |
fault_is_success | lista de texto | no | — |
timeout | duración, como 30s o 5m | no | 30s |
23.30.1 perfil
El nombre del perfil designa un servicio estándar cuyo sobre Perfuse se construye automáticamente.
Vacío es una llamada SOAP simple con el cuerpo escrito en este archivo.
"cdc-iis" es la interfaz del registro de inmunización del CDC (urn:cdc:iisb:2011), que utiliza cada registro estatal de EE. UU.: el mensaje se envía en submitSingleMessage, el nombre de usuario y la contraseña se envían en el cuerpo según lo que dice el WSDL (no como autenticación HTTP), SOAP 1.2 por defecto y el reconocimiento de HL7 del registro decide si el mensaje se entregó. Un registro responde a un disparo rechazado con HTTP 200 y un AR dentro, así que sin esto un rechazo se lee como un éxito.
23.30.2 facility_id
FacilityID es el facilityID de cdc-iis, el identificador del registro para la organización que envía.
Opcional en el WSDL; algunos registros lo requieren.
23.30.3 warnings_are_errors
WarningsAreErrors, para cdc-iis, trata un reconocimiento AE que solo lleva advertencias como un error.
Desactivado por defecto: los registros envían AE para "aceptado con advertencias" y la gravedad ERR dice si algo se perdió.
23.30.4 acción
La acción es la SOAPAction.
Requerido por la mayoría de los servicios 1.1.
23.30.5 cuerpo
El cuerpo es el XML que va dentro del sobre, con referencias de estilo ${PID-5.1} a rutas de mensaje.
Requerido, a menos que un perfil lo construya. No hay un valor predeterminado útil: los nombres de los elementos provienen del esquema propio del servicio y ningún motor puede adivinarlos.
23.30.6 encabezado
El encabezado es el XML para el elemento Encabezado del sobre.
Omitido por completo cuando está vacío, porque algunos servicios rechazan uno vacío.
23.30.7 fault_is_success
FaultIsSuccess enumera los códigos de error que se consideran entregados.
Necesario con más frecuencia de lo que debería ser. Un servicio que responde a una reenvío con "ya enviado" le está diciendo que el mensaje llegó, y sin esto, un sitio tiene que escribir un transformador de respuesta para decirlo - o peor, ve una cola que se reintenta infinitamente contra un receptor que ya tiene el mensaje.
23.30.8 tiempo de espera
El tiempo de espera limita una llamada.
Defaults to 30s.
23.31 Destino SQS
SQSDestination envía cada mensaje a una cola SQS.
Definido como SQSDestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
queue_url | texto | sí | — |
group_by | texto | no | — |
23.31.1 queue_url
QueueURL nombra la cola.
Una que termina en .fifo es una cola FIFO y se agrupa y deduplica.
23.32 Destino TCP
TCPDest envía mensajes a un socket con encuadre configurable.
Definido como TCPDest en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
address | texto | sí | — |
max_message_size | número entero | no | — |
timeout | duración, como 30s o 5m | no | — |
expect_reply | verdadero o falso | no | — |
keep_alive | verdadero o falso | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejarlo fuera) | no | — |
23.32.1 expect_reply
ExpectReply espera bytes de respuesta antes de informar el éxito.
Desactivado por defecto, y vale la pena considerarlo. Con esto desactivado, "entregado" significa que los bytes llegaron al búfer de envío del sistema operativo, que un peer que se bloqueó un momento después nunca leyó. Con esto activado, un peer que nunca responde hace que cada mensaje caduque.
23.32.2 keep_alive
KeepAlive mantiene una conexión abierta a través de los mensajes en lugar de marcar por mensaje.
La mayoría de los puntos de conexión de dispositivos esperan esto; algunos rechazan un segundo mensaje en la misma conexión. Desactivado por defecto porque una conexión por mensaje es el comportamiento que funciona en todas partes, a costa de un apretón de manos cada vez.
23.33 Destino TEFCA
TEFCADestination solicita al socio de FHIR facilitado por TEFCA los registros de cada paciente del mensaje.
Quién es este servidor en la red, su certificado y el socio son configuraciones del servidor (Configuraciones, TEFCA), porque pertenecen a la organización y su acuerdo con un QHIN. Lo que decide un canal es cuándo solicitar y por qué: el propósito de uso es parte del canal, no de quién está conectado, por lo que cada consulta que realiza un feed establece la misma razón y la traza de auditoría se puede leer en contra de ella.
Definido como TEFCADestination en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
purpose | texto | sí | — |
results_to | texto | no | — |
23.33.1 propósito
El propósito es el propósito de uso: tratamiento, pago, operaciones, salud_pública o acceso_individual.
Debe ser uno que declare el participante.
23.33.2 resultados_a
ResultadosA nombra un canal en este servidor que recibe cada paquete que devuelve el socio, como un mensaje propio.
Vacío mantiene el paquete como respuesta de este destino solo, para que un transformador de respuestas lo lea.
23.34 Destino XDS
XDSDestination publica cada documento clínico en un repositorio de documentos XDS.b de IHE con ITI-41 Proporcionar y registrar.
Los metadatos se leen del documento CDA (paciente, título, tipo, tiempo, autor, confidencialidad, idioma) y se completan con estas configuraciones, que indican qué no puede hacer el documento: quién lo envía, qué tipo de instalación y práctica proviene, y cuál de los identificadores del paciente es el dominio de afinidad.
Definido como XDSDestination en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
url | texto | sí | — |
source_id | texto | sí | — |
patient_domain | texto | no | — |
facility_type | XDSCode | sí | — |
practice_setting | XDSCode | sí | — |
class | XDSCode (un bloque, omitir para dejar sin configurar) | no | — |
content_type | XDSCode (un bloque, omitir para dejar sin configurar) | no | — |
token | texto | no | — |
username | texto | no | — |
password | texto | no | — |
headers | asignación de texto a texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin configurar) | no | — |
timeout | duración, como 30s o 5m | no | — |
direct_from | texto | no | — |
direct_to | lista de texto | no | — |
23.34.1 patient_domain
PatientDomain es la autoridad OID de asignación del dominio de afinidad: el identificador de paciente con esta raíz es el que se registra.
Sin él, un documento cuyo paciente tiene un identificador envía ese identificador, y uno con varios es rechazado.
23.34.2 token
Token o Username/Password autenticar; Headers se agregan a cada solicitud.
La mayoría de los dominios de afinidad utilizan TLS mutuo (tls) en su lugar.
23.34.3 nombre de usuario
Token o Username/Password autenticar; Headers se agregan a cada solicitud.
La mayoría de los dominios de afinidad utilizan TLS mutuo (tls) en su lugar.
23.34.4 contraseña
Token o Username/Password autenticar; Headers se agregan a cada solicitud.
La mayoría de los dominios de afinidad utilizan TLS mutuo (tls) en su lugar.
23.34.5 headers
Token o Username/Password autenticar; Headers se agregan a cada solicitud.
La mayoría de los dominios de afinidad utilizan TLS mutuo (tls) en su lugar.
23.34.6 tls
Token o Username/Password autenticar; Headers se agregan a cada solicitud.
La mayoría de los dominios de afinidad utilizan TLS mutuo (tls) en su lugar.
23.34.7 direct_from
DirectFrom y DirectTo hacen que la presentación XDR sea el transporte Direct: un bloque de direcciones Direct que nombra la dirección Direct de este remitente y la de los destinatarios, y los destinatarios en el destinatario previsto de la presentación.
Un HISP que toma XDR la entrega como correo Direct, o al punto final XDR del destinatario.
23.34.8 direct_to
DirectFrom y DirectTo hacen que la presentación XDR sea el transporte Direct: un bloque de direcciones Direct que nombra la dirección Direct de este remitente y la de los destinatarios, y los destinatarios en el destinatario previsto de la presentación.
Un HISP que toma XDR la entrega como correo Direct, o al punto final XDR del destinatario.
24 Referencia de formato
Opciones que rigen cómo se analizan y escriben los mensajes, para los formatos que necesitan más que un nombre.
24.1 Adjuntos
Adjuntos configura la extracción de grandes cargas útiles de un mensaje.
Un mensaje que transporta un informe escaneado o un PDF es común en flujos de trabajo de documentos, y la carga útil es rutinariamente cientos de veces más grande que el mensaje que la rodea. Dejada en línea significa que el almacén crece a la velocidad de los documentos en lugar del tráfico, un mensaje en cola para cinco destinos se copia cinco veces, y la interfaz transmite megabytes para mostrar a alguien un nombre de paciente.
Mirth llama a estos controladores de adjuntos. Esto es equivalente, y es opt-in por canal porque una fuente de mensajes ADT simples no tiene nada que extraer y no debe pagar por la maquinaria.
Definido como Attachments en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
extract | lista de attach.Rule | sí | — |
reassemble | verdadero o falso (un bloque, omitir para dejar sin) | no | verdadero |
24.1.1 reassemble
Reassemble vuelve a ensamblar las cargas útiles antes de la entrega.
Predeterminado en true.
El valor predeterminado es activado porque un receptor que espera un documento debe recibir un documento; un mensaje que contiene un token literal se archivaría como un informe y nadie se enteraría hasta que alguien abriera el registro.
Desactivarlo es para el caso en que el propósito del canal es eliminar documentos, reenviando metadatos a un sistema que no tiene uso para la imagen, donde enviarla sería una cuestión de privacidad más que de ancho de banda.
24.2 Delimitado
Delimitado configura un canal delimitado.
El tipo de datos Delimited de Mirth: CSV, separado por tabuladores y la larga cola de formatos que emite un analizador de laboratorio o un servicio de oficina.
Definido como Delimited en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
delimiter | texto | no | — |
quote | texto | no | comillas dobles |
comment | texto | no | — |
has_header | verdadero o falso | no | — |
columns | lista de texto | no | — |
trim_space | verdadero o falso | no | — |
relaxed | verdadero o falso | no | — |
split | verdadero o falso (un bloque, omitir para dejar fuera) | no | verdadero |
keep_blank_lines | verdadero o falso | no | — |
filter | texto | no | — |
transformations | lista de pasos.Paso | no | — |
24.2.1 delimitador
El delimitador separa las columnas, como un carácter o un nombre.
Un nombre es aceptado porque una pestaña no se puede escribir en un archivo YAML de manera inequívoca, y un archivo de configuración que contiene un carácter invisible es ilegible para quien lo abra después.
24.2.2 comillas
La comilla envuelve los valores que contienen el delimitador.
Predeterminado es una comilla doble; 'ninguna' lo deshabilita.
24.2.3 dividir
Dividir convierte cada fila en su propio mensaje.
Predeterminado en true.
Esta es la decisión que da forma a un canal delimitado y merece decirse en voz alta. Un archivo de cinco mil resultados es o un mensaje o cinco mil. Un mensaje significa un solo reconocimiento, una sola entrada en el navegador y un solo error que afecta a todo el archivo. Cinco mil significa que cada fila se filtra, se transforma, se entrega y se vuelve a intentar por su cuenta, y una sola fila mala no detiene al resto.
La división es el valor predeterminado porque el manejo por fila es lo que casi todos los sitios desean y porque la alternativa se descubre tarde: un canal que parecía correcto en la prueba con un archivo de tres filas falla toda una noche de transferencia por una sola línea malformada.
24.2.4 conservar_lineas_en_blanco
KeepBlankLines evita que se omitan las líneas en blanco.
Omitir es el valor predeterminado, porque cada archivo termina en un salto de línea y un registro de campos vacíos no es un registro. Esto existe para el formato raro en el que una línea en blanco es significativa.
24.2.5 filtro
El filtro decide qué mensajes continúan, escritos contra los nombres de las columnas.
Aquí en lugar de en el canal porque el filtro de nivel de canal compila rutas HL7. Dos ajustes llamados filtro serían confusos, pero un ajuste que significa cosas diferentes según el tipo de datos es peor: un canal delimitado que compilara silenciosamente su filtro como HL7 no coincidiría con nada y eliminaría todo, lo que es el error que la negativa de nivel superior existe para prevenir.
24.2.6 transformaciones
Las transformaciones son los pasos declarativos, que se dirigen a las columnas en lugar de a los campos HL7.
Separado de las transformaciones del canal por la misma razón que el filtro, y es el arreglo que X12 y NCPDP ya utilizan.
24.3 Opciones de HL7v3
Las opciones de HL7v3 son los ajustes que solo se aplican a un canal HL7 v3.
Definido como HL7v3Options en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
filter | texto | no | — |
acknowledge | verdadero o falso (un bloque, omitir para dejar fuera) | no | — |
sender_device | texto | no | — |
sender_oid | texto | no | — |
transformations | lista de hl7v3.Paso | no | — |
24.3.1 filtro
El filtro excluye mensajes utilizando rutas v3.
Es un campo propio en lugar del filtro ordinario del canal, porque los dos son lenguajes diferentes contra modelos de mensaje diferentes. Compartir el campo significaría que un filtro v2 nunca coincidiría silenciosamente en un canal v3, o un filtro v3 fallaría al analizar como v2, y lo segundo solo es mejor porque falla ruidosamente.
Un canal que establece ambos es rechazado al cargar, ya que uno de ellos tendría que ser ignorado y no hay una forma honesta de elegir.
24.3.2 reconocer
Reconocer envía un reconocimiento MCCI_IN000002UV01.
Está activado por defecto, a diferencia de X12. Una interacción v3 sobre un transporte síncrono espera un reconocimiento, la aplicación que envía generalmente espera uno, y un remitente que no recibe nada generalmente lo vuelve a intentar, lo que es cómo un paciente se registra tres veces.
Un puntero para que 'no establecido' y 'establecido en falso' sean diferentes: el valor predeterminado es activado, y alguien que lo desactive debería haberlo dicho.
24.3.3 dispositivo_remitente
SenderDevice es el identificador de dispositivo que colocamos en un reconocimiento.
Requerido al reconocer. Un reconocimiento v3 nombra el dispositivo del que proviene, y un receptor que no reconoce el nuestro puede descartarlo, lo que parece exactamente como no enviar uno.
24.3.4 transformaciones
Las transformaciones cambian el contenido de un documento v3.
Es un campo propio en lugar de las transformaciones ordinarias del canal, por la misma razón que el filtro: son vocabularios diferentes contra modelos de mensaje diferentes. Los pasos v2 se dirigen a segmentos y campos, que un documento v3 no tiene, por lo que compartir el campo significaría un paso que silenciosamente no hace nada.
Los nombres de los pasos coinciden con los de v2 siempre que el significado coincida, por lo que alguien que ha escrito un canal puede escribir el otro. Lo que es diferente es lo que v3 necesita y v2 no puede expresar, principalmente que eliminar un valor tiene tres significados distintos, por lo que claro, nullflavor y eliminar son tres pasos en lugar de uno.
24.4 Encuadernación TCP
La encuadernación TCP es cómo se encuentran los límites de los mensajes en un socket sin procesar.
Compartido por la fuente TCP y el destino TCP, y por la fuente serie, porque las encuadernaciones son las mismas dondequiera que provengan los bytes. Un analizador de laboratorio que habla STX/ETX sobre un socket habla STX/ETX sobre un cable serie también.
Definido como TCPFraming en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
framing | texto | sí | — |
delimiter | texto | no | — |
start_block | texto | no | — |
keep_delimiter | verdadero o falso | no | — |
record_length | número entero | no | — |
trim_padding | verdadero o falso (un bloque, omitir para dejar sin) | no | — |
length_bytes | número entero | no | — |
big_endian | verdadero o falso | no | — |
length_includes_header | verdadero o falso | no | — |
24.4.1 encuadernación
La encuadernación es mllp, delimitada, fija, longitud o completa.
Requerido, sin valor predeterminado. Leer una secuencia con la encuadernación incorrecta produce mensajes que parecen plausibles en lugar de un error, por lo que adivinar es peor que preguntar.
24.4.2 bloque_inicial
StartBlock opcionalmente comienza un mensaje.
Los bytes antes de él se descartan como ruido de una conexión parcial.
24.4.3 mantener_delimitador
KeepDelimiter incluye el delimitador en el mensaje.
Desactivado por defecto: es encuadernación, no contenido.
24.4.4 trim_padding
TrimPadding elimina los espacios y NULs finales de un registro fijo.
Activado por defecto.
24.5 Opciones X12
X12Options son la configuración que solo se aplica a un canal X12.
Definido como X12Options en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
envelope | EnvelopePolicy | no | requerir |
split | verdadero o falso | no | — |
acknowledge | texto | no | — |
ack_sender_id | texto | no | — |
ack_sender_qualifier | texto | no | — |
arrives_as | texto | no | — |
transformations | lista de x12.Step | no | — |
24.5.1 envelope
Envelope indica qué hacer cuando los recuentos autodeclarados no coinciden con lo presente.
Predeterminado es requerir.
24.5.2 dividir
Split envía un mensaje por conjunto de transacciones en lugar de uno por archivo.
Desactivado por defecto. Un solo 837 puede contener cientos de reclamaciones, por lo que activar esto multiplica todo lo que está aguas abajo - recuentos de mensajes, acuse de recibo, filas, umbrales de alerta - y eso debería ser una decisión que alguien tomó en lugar de un valor predeterminado que heredó.
24.5.3 reconocer
Acknowledge indica qué acuse de recibo enviar de regreso: 999, 997, ta1 o ninguno.
Cuál es una propiedad de la relación de socio comercial en lugar de del mensaje, por lo que debe configurarse. Un 999 reemplaza a un 997 para transacciones HIPAA, pero muchos socios - especialmente conexiones de pagadores más antiguas - están configurados para esperar un 997 y tratarán un 999 como un archivo no reconocido. Enviar el incorrecto es peor que no enviar ninguno, porque responde a una pregunta que nadie hizo y deja la real abierta.
Vacío significa ninguno, que es el valor predeterminado seguro: un socio que no espera un acuse de recibo y lo recibe puede tratarlo como un intercambio no solicitado.
24.5.4 ack_sender_id
AckSenderID y AckSenderQualifier son nuestro propio identificador de intercambio, que se convierte en ISA06 y ISA05 de cualquier acuse de recibo.
Requerido siempre que Acknowledge esté configurado, y rechazado en la carga de lo contrario. Estos deben ser los valores que el socio comercial ha configurado para nosotros y no hay forma de adivinarlos: un intercambio cuyo ISA06 el socio no reconoce se descarta antes de que alguien lo lea, por lo que un valor incorrecto produce silencio que parece exactamente como no enviar nada - y alguien pasa una semana buscando en el lugar equivocado.
24.5.5 ack_sender_qualifier
AckSenderID y AckSenderQualifier son nuestro propio identificador de intercambio, que se convierte en ISA06 y ISA05 de cualquier acuse de recibo.
Requerido siempre que Acknowledge esté configurado, y rechazado en la carga de lo contrario. Estos deben ser los valores que el socio comercial ha configurado para nosotros y no hay forma de adivinarlos: un intercambio cuyo ISA06 el socio no reconoce se descarta antes de que alguien lo lea, por lo que un valor incorrecto produce silencio que parece exactamente como no enviar nada - y alguien pasa una semana buscando en el lugar equivocado.
24.5.6 llega_como
LlegaComo es xml cuando la fuente entrega el intercambio como el XML que Mirth Connect y Open Integration Engine escriben para X12 (una raíz X12Transaction, elementos con nombre CLM.05.1), como un motor que se migra de, o un sistema construido contra uno, lo envía.
Se convierte de vuelta en X12 antes de que suceda cualquier otra cosa. Vacío o x12 es X12.
24.5.7 transformaciones
Las transformaciones son cambios declarativos aplicados a intercambios aceptados.
Separado de las transformaciones a nivel de canal para la misma razón que las v3: esas direcciones a los campos HL7 a través de un analizador que lee segmento-campo-componente-subcomponente, y X12 no tiene subcomponente, trata segmentos repetidos como ordinarios en lugar de excepcionales, y se escribe CLM01 por las personas que lo configuran. Un analizador compartido tendría que aceptar ambas notaciones y sería ambiguo en ambas.
Los nombres de los pasos son los mismos que los pasos HL7 y v3 utilizan, por lo que lo que un autor ha aprendido ya se aplica.
25 Referencia de comportamiento
Reconocimiento, encolado, reintento, sondeo, scripting y configuración de sombra. Estas se aplican independientemente del transporte que utilice un canal.
25.1 Conexión AMQP
AMQPConnection es dónde y como conectarse.
Definido como AMQPConnection en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
addr | texto | sí | — |
address | texto | sí | — |
username | texto | no | — |
password | texto | no | — |
hostname | texto | no | el host de addr |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
timeout | duración, como 30s o 5m | no | 30s |
25.1.1 nombre de usuario
Nombre de usuario y contraseña autenticados con SASL PLAIN; ambos vacíos significan ANÓNIMO.
Para Service Bus, el nombre de la directiva y la clave.
25.1.2 contraseña
Nombre de usuario y contraseña autenticados con SASL PLAIN; ambos vacíos significan ANÓNIMO.
Para Service Bus, el nombre de la directiva y la clave.
25.1.3 nombre de host
El nombre de host se envía al broker, que Service Bus enruta.
Predeterminado para el host de addr.
25.1.4 tls
TLS cifra la conexión.
Requerido para Service Bus y Event Hubs.
25.1.5 tiempo de espera
Timeout limita la conexión y cada espera del broker.
Defaults to 30s.
25.2 Socio AS2
AS2Partner es un socio comercial que una fuente AS2 acepta mensajes de.
Definido como AS2Partner en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
id | texto | sí | — |
cert_file | texto | sí | — |
allow_unsigned | verdadero o falso | no | — |
allow_unencrypted | verdadero o falso | no | — |
25.2.1 cert_file
CertFile es el certificado del socio (PEM), para sus firmas.
A menudo auto-firmado: los socios AS2 intercambian certificados directamente en lugar de a través de una autoridad.
25.2.2 allow_unsigned
AllowUnsigned y AllowUnencrypted aceptan un mensaje de este socio que no está firmado o no está cifrado.
Desactivado: tal mensaje se rechaza con un recibo que indica por qué.
25.2.3 allow_unencrypted
AllowUnsigned y AllowUnencrypted aceptan un mensaje de este socio que no está firmado o no está cifrado.
Desactivado: tal mensaje se rechaza con un recibo que indica por qué.
25.3 Acceso a AWS
El acceso de AWS es la forma en que un conector alcanza AWS: la región para la que firma, las claves y un endpoint de anulación opcional.
Las credenciales pueden ser literales o referencias ${ENV_VAR}, que es cómo deben ser suministradas. No hay una cadena de credenciales de instancia de metadatos, deliberadamente: véase internal/awsv4.
Definido como AWSAccess en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
region | texto | sí | — |
access_key_id | texto | no | — |
secret_access_key | texto | no | — |
session_token | texto | no | — |
endpoint | texto | no | — |
25.3.1 secret_access_key
SecretAccessKey es el secreto, literal o ${AWS_SECRET_ACCESS_KEY}.
Preferir la referencia: este archivo se incluye en el control de versiones.
25.3.2 session_token
SessionToken es solo para credenciales temporales.
Expiran y el conector deja de funcionar cuando lo hacen.
25.4 Ack
Ack configura la generación de acuse de recibo.
Definido como Ack en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
when | AckWhen | no | — |
application | texto | no | — |
facility | texto | no | — |
include_trigger_event | verdadero o falso | no | — |
25.4.1 include_trigger_event
IncludeTriggerEvent envía ACK^A01 en lugar de ACK.
Algunos receptores lo requieren y otros lo rechazan, por lo que es explícito.
25.5 Acceso a Azure Blob
AzureBlobAccess es la cuenta, el contenedor y la credencial.
Definido como AzureBlobAccess en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
account | texto | sí | — |
container | texto | sí | — |
key | texto | no | — |
sas | texto | no | — |
endpoint | texto | no | — |
25.5.1 clave
La clave es la clave de la cuenta, o ${AZURE_STORAGE_KEY}.
Establezca la clave o sas.
25.5.2 sas
SAS es una cadena de consulta de firma de acceso compartido, o una referencia ${...}.
Preferido: puede limitarse a este contenedor.
25.6 Referencia de contrato
ContractRef apunta un canal a su contrato.
Definido como ContractRef en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
file | texto | sí | — |
check_every | texto | no | 15 minutos |
over | número entero | no | 500 |
25.6.1 check_every
CheckEvery es la frecuencia con la que se vuelve a comprobar, como una duración.
Predeterminado en 15 minutos.
Efectivamente limitado al intervalo de evaluación de alertas, que es de 30 segundos por defecto, porque la comprobación se ejecuta desde ese bucle en lugar de un temporizador propio, así que un contrato que solicita 3s obtiene 30s. Esto es deliberado: un temporizador es más fácil de entender que dos, y mantiene el resultado de un contrato en sincronía con la lectura a la que está asociado. Vale la pena saber antes de que alguien configure un intervalo corto y concluya que está roto.
No por mensaje. Crear un perfil significa leer el tráfico reciente, y hacerlo en cada mensaje haría que el rendimiento de un canal dependa de la cantidad de historial que tenga, lo que es el tipo de característica de rendimiento que solo se muestra en producción, seis meses después.
25.6.2 over
Over es la cantidad de mensajes recientes que se van a perfilar.
Defaults to 500.
Un recuento en lugar de una ventana de tiempo, porque una fuente tranquila y una ocupada necesitan la misma cantidad de evidencia para justificar una tasa, y una ventana de tiempo les da cantidades muy diferentes.
25.7 Opciones de DICOM
DICOMOptions configura un canal de imágenes.
Por qué los pasos están nombrados en lugar de rutas
Cada otro formato obtiene un escritor de ruta. DICOM deliberadamente no lo hace, y la razón es los datos de píxeles: un objeto es binario, un escritor de etiquetas general puede establecer cualquier etiqueta en cualquier bytes, y un error allí no produce un mensaje rechazado, produce una imagen que se abre y es incorrecta. Un radiólogo que lee un estudio no tiene forma de saber.
Así que las cuatro cosas que los sitios realmente necesitan son acciones nombradas con efectos limitados: desidentificar, reescribir un título de AE, eliminar etiquetas privadas, establecer la institución. Cada una sabe qué etiquetas toca, y ninguna puede llegar a los datos de píxeles.
Por qué este bloque existe ahora
internal/dicom ha llevado steps.go y transform.go durante un tiempo, las cuatro acciones, dicom.Apply y pruebas. Nada las referenciaba. Quinientas líneas sin llamador, lo que la sección 7 de la cola misma nombra: una implementación sin llamador es indistinguible de una característica que no existe. Este bloque y la llamada al motor son el cableado, no la característica.
Definido como DICOMOptions en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
transformations | lista de dicom.Step | no | — |
25.8 Dirección directa
DirectAddress es una dirección Direct que este servidor posee: su certificado y clave, y los anclajes que confía.
El mismo bloque se utiliza para enviar (firmar como esta dirección) y para recibir (decifrar para ella).
Definido como DirectAddress en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
address | texto | sí | — |
cert_file | texto | sí | — |
key_file | texto | sí | — |
anchors_file | texto | sí | — |
25.8.1 cert_file
CertFile y KeyFile son el certificado de la dirección (PEM, con su cadena después) y la clave privada (PEM).
El HISP o su autoridad de registro los emite; son la identidad de la dirección.
25.8.2 key_file
CertFile y KeyFile son el certificado de la dirección (PEM, con su cadena después) y la clave privada (PEM).
El HISP o su autoridad de registro los emite; son la identidad de la dirección.
25.8.3 anchors_file
AnchorsFile es los anclajes de confianza (PEM): las autoridades de certificación cuyos remitentes y destinatarios confía esta dirección, como el paquete DirectTrust.
Un mensaje firmado por cualquier otra cosa es rechazado.
25.9 Direct IMAP
DirectIMAP es el buzón de HISP que lee una fuente Direct.
Definido como DirectIMAP en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
host | texto | sí | — |
port | número entero | no | — |
security | texto | no | — |
username | texto | sí | — |
password | texto | no | — |
mailbox | texto | no | — |
after_read | texto | no | — |
move_to | texto | no | — |
error_folder | texto | no | — |
poll_interval | duración, como 30s o 5m | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
25.9.1 mover_a
MoveTo es la carpeta a la que se mueven los mensajes; Procesados por defecto.
Creado cuando falta.
25.9.2 tls
TLS confía en un archivo CA (o el certificado propio del HISP) y nombra el servidor esperado en él, para un HISP cuyo certificado IMAP no proviene de una autoridad pública.
El certificado siempre se verifica.
25.10 Direct XDR
DirectXDR es el punto final que toma XDR una fuente Direct.
Definido como DirectXDR en internal/config.
| Clave | Tipo | Requerido | Valor predeterminado |
|---|---|---|---|
listen | texto | sí | — |
tls_cert | texto | no | — |
tls_key | texto | no | — |
client_ca_file | texto | no | — |
25.11 Instalación ECR
ECRFacility es la organización que envía informes de casos.
Definido como ECRFacility en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
name | texto | sí | — |
npi | texto | no | — |
phone | texto | sí | — |
line | texto | no | — |
city | texto | sí | — |
state | texto | sí | — |
postal_code | texto | no | — |
25.12 Opciones EDIFACT
EDIFACTOptions son la configuración que solo se aplica a un canal EDIFACT.
Definido como EDIFACTOptions en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
envelope | texto | no | — |
split | verdadero o falso | no | — |
acknowledge | texto | no | — |
25.12.1 reconocer
Acknowledge responde a un remitente que publica sobre HTTP con un CONTRL en la respuesta: aceptado (acción 7) cuando el intercambio se leyó y se entregó, rechazado (4) de lo contrario.
Default none.
25.13 Autenticación de correo electrónico
EmailOAuth inicia sesión con OAuth 2.0 (SASL XOAUTH2), que Microsoft 365 y Gmail requieren en lugar de una contraseña.
Definido como EmailOAuth en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
provider | texto | sí | — |
tenant | texto | no | — |
token_url | texto | no | — |
client_id | texto | sí | — |
client_secret | texto | no | — |
refresh_token | texto | no | — |
scope | texto | no | — |
25.13.1 client_secret
ClientSecret puede ser ${ENV}.
Sin refresh_token, selecciona la concesión de credenciales de cliente solo de aplicación de Microsoft.
25.13.2 refresh_token
RefreshToken puede ser ${ENV}: un buzón de correo que una persona consintió.
Requerido para Gmail.
25.14 Autenticación FHIR
FHIRAuth es la forma en que un destino FHIR se autentica en un servicio FHIR alojado.
Tres configuraciones preestablecidas, porque los servicios alojados son donde un token de portador pegado en un archivo deja de funcionar dentro de una hora:
- aws: AWS HealthLake, que acepta solicitudes firmadas con Signature Version 4 para el servicio "healthlake" - sin token en absoluto. - azure: Azure Health Data Services, que acepta un token de ID de Microsoft Entra para la audiencia del servicio FHIR, obtenido con la concesión de credenciales de cliente de un registro de aplicación que tiene el rol de contribuyente de datos FHIR. - client_credentials: cualquier punto final de token OAuth 2.0 - Keycloak, Okta, un servicio de backend SMART con un secreto de cliente.
Los tokens se recuperan cuando se necesitan y se reutilizan hasta un minuto antes de que expiren.
Definido como FHIRAuth en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
type | texto | sí | — |
region | texto | no | — |
access_key_id | texto | no | — |
secret_access_key | texto | no | — |
session_token | texto | no | — |
tenant_id | texto | no | — |
token_url | texto | no | — |
client_id | texto | no | — |
client_secret | texto | no | — |
scope | texto | no | — |
25.14.1 región
La región es la región de AWS.
Se lee desde una URL de HealthLake cuando no se proporciona.
25.14.2 token_url
TokenURL es el punto final de token OAuth, para client_credentials.
Para azure, se deriva del inquilino.
25.14.3 client_id
ClientID y ClientSecret identifican la aplicación.
El secreto puede ser una referencia ${ENV}, que es cómo se debe proporcionar.
25.14.4 client_secret
ClientID y ClientSecret identifican la aplicación.
El secreto puede ser una referencia ${ENV}, que es cómo se debe proporcionar.
25.14.5 scope
El alcance se solicita con el token.
Para azure, se establece de forma predeterminada en la URL del servicio FHIR seguida de /.default, que es su audiencia.
25.15 Encuesta de archivos
FilePoll es la parte de una fuente que recopila archivos que no tiene nada que ver con el transporte.
Compartido por las fuentes locales, FTP, SMB y WebDAV, y por SFTP. Cada una de estas configuraciones existe porque hay una forma en que los archivos pueden fallar en lugar de una forma en que los transportes difieren, por lo que pertenecen juntos: un sitio que ha trabajado la configuración correcta de stable_for para su analizador no debería tener que volver a trabajarla cuando el analizador se mueve a una acción compartida.
Definido como FilePoll en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
dir | texto | sí | — |
pattern | texto | no | — |
poll_interval | duración, como 30s o 5m | no | — |
after_read | texto | no | — |
move_to | texto | no | — |
error_dir | texto | no | — |
stable_for | duración, como 30s o 5m | no | — |
max_file_size | número entero | no | — |
framed | verdadero o falso | no | — |
raw | verdadero o falso | no | — |
batch_size | número entero | no | — |
sort_by | texto | no | nombre |
25.15.1 patrón
El patrón selecciona archivos por glob.
Vacío significa todos los archivos.
25.15.2 directorio_de_errores
ErrorDir es donde se coloca un archivo que no se pudo procesar.
Separado de MoveTo deliberadamente. Un directorio que solo contiene errores es algo que alguien puede supervisar, y mezclar errores en el archivo significa que la única forma de encontrarlos es leyendo cada archivo.
25.15.3 crudo
Raw trata el archivo completo como un solo mensaje, independientemente del contenido.
La configuración que hace que este conector sea útil para algo más que HL7. Sin ella, un lote CSV, un archivo de reclamación X12 o un PDF se divide en líneas que comienzan con MSH y produce nada, y el error dice que el archivo no es HL7 - cierto, pero no es el punto, ya que el archivo nunca estuvo destinado a ser.
25.15.4 tamaño_de_lote
BatchSize limita la cantidad de archivos que se leerán en una sola llamada.
Cero significa sin límite.
Existe para la primera llamada después de un tiempo de inactividad. Un directorio que contiene cuarenta mil archivos que se acumularon durante la noche, de lo contrario, se leerá en una sola pasada, lo que mantiene el bucle de llamada durante tanto tiempo como sea necesario y hace que el canal parezca colgado. Leer un número limitado de archivos por llamada mantiene el canal respondiendo y la cola drenando visiblemente.
25.15.5 ordenar_por
SortBy ordena los archivos dentro de una llamada: nombre, modificado o ninguno.
Por defecto, es nombre. El orden es más importante de lo que parece: una secuencia ADT donde las actualizaciones A08 llegan antes de la admisión A01 produce pacientes que no existen aún. El orden de la lista de directorios es arbitrario en la mayoría de los sistemas de archivos, por lo que dejarlo sin ordenar significa que el orden cambia entre llamadas sin razón aparente.
25.16 Acceso a GCP
GCPAccess es la forma de autenticar en Google Cloud.
Definido como GCPAccess en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
project | texto | no | — |
credentials_file | texto | no | — |
endpoint | texto | no | — |
25.16.1 archivo_de_credenciales
CredentialsFile es una clave JSON de una cuenta de servicio.
Vacío utiliza la cuenta de servicio adjunta a la VM, el pod de GKE o el servicio de Cloud Run en el que se ejecuta Perfuse (el servidor de metadatos), a menos que Endpoint nombre un emulador.
25.17 Acceso a GCS
GCSAccess es un bucket de Cloud Storage.
Definido como GCSAccess en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
bucket | texto | sí | — |
25.18 Conexión IBMMQ
IBMMQConnection es una cola alcanzada a través de la API de mensajería REST del administrador de colas.
Definido como IBMMQConnection en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
url | texto | sí | — |
queue_manager | texto | sí | — |
queue | texto | sí | — |
username | texto | no | — |
password | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin) | no | — |
api_version | texto | no | — |
timeout | duración, como 30s o 5m | no | 30s |
25.18.1 nombre de usuario
Nombre de usuario y contraseña son un usuario de mqweb con el rol de MQWebUser; la contraseña puede ser ${ENV}.
Dejar ambos vacíos cuando un certificado de cliente (tls.cert_file) autentica.
25.18.2 contraseña
Nombre de usuario y contraseña son un usuario de mqweb con el rol de MQWebUser; la contraseña puede ser ${ENV}.
Dejar ambos vacíos cuando un certificado de cliente (tls.cert_file) autentica.
25.18.3 tiempo de espera
El tiempo de espera limita cada solicitud.
Defaults to 30s.
25.19 Kafka SASL
KafkaSASL autentica a un clúster de Kafka.
Definido como KafkaSASL en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
mechanism | texto | sí | — |
username | texto | sí | — |
password | texto | sí | — |
25.19.1 mecanismo
El mecanismo es "plain", "scram-sha-256" o "scram-sha-512".
PLAIN envía la contraseña legible en el cable, por lo que pertenece con TLS. SCRAM no, lo que hace que valga la pena nombrar la diferencia aquí en lugar de tratar los tres como intercambiables.
25.19.2 contraseña
La contraseña es la contraseña de SASL.
Admite referencias ${ENV} como cualquier otro secreto aquí, por lo que una credencial de clúster no necesita estar escrita en el archivo de canal.
25.20 Conexión MQTT
MQTTConnection es cómo llegar a un broker MQTT 3.1.1: Mosquitto, EMQX, HiveMQ, AWS IoT Core, Azure Event Grid.
Definido como MQTTConnection en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
addr | texto | sí | — |
client_id | texto | no | — |
username | texto | no | — |
password | texto | no | — |
tls | tlsconf.Settings (un bloque, omitir para dejar sin configurar) | no | — |
qos | número entero (un bloque, omitir para dejar sin configurar) | no | — |
timeout | duración, como 30s o 5m | no | 30s |
25.20.1 client_id
ClientID nombra la sesión.
Una fuente mantiene sus mensajes a través de reconexiones solo bajo un id de cliente fijo; predetermina perfuse-<channel>.
25.20.2 nombre de usuario
Nombre de usuario y contraseña autenticán; cualquiera de los dos puede estar vacío.
La contraseña puede ser ${ENV}.
25.20.3 contraseña
Nombre de usuario y contraseña autenticán; cualquiera de los dos puede estar vacío.
La contraseña puede ser ${ENV}.
25.20.4 tiempo de espera
Timeout limita la conexión y cada espera del broker.
Defaults to 30s.
25.21 Opciones de NCPDP
NCPDPOptions son los ajustes que solo se aplican a un canal de reclamo de farmacia.
Definido como NCPDPOptions en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
transformations | lista de ncpdp.Step | no | — |
25.21.1 transformaciones
Las transformaciones son cambios declarativos aplicados a cada transmisión antes de la entrega.
El mismo vocabulario que todos los demás formatos - establecer, copiar, borrar, mapear, reemplazar, recortar, caso, cada uno con una condición opcional - porque el motor de pasos en internal/steps es genérico. Lo que difiere es la notación: una ruta se escribe D1 o 07-D7, utilizando los identificadores de campo de dos caracteres del propio estándar.
25.22 Recibir PGP
PGPReceive abre lo que una fuente recibe con OpenPGP: descifrado con nuestra clave secreta y su firma verificada contra las claves públicas de los remitentes.
Definido como PGPReceive en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
decrypt_with | texto | no | — |
passphrase | texto | no | — |
verify_with | lista de texto | no | — |
require_signature | verdadero o falso | no | — |
allow_unencrypted | verdadero o falso | no | — |
pass_plain | verdadero o falso | no | — |
25.22.1 decrypt_with
DecryptWith es nuestro archivo de clave secreta.
Requerido a menos que allow_unencrypted esté activado y los mensajes solo estén firmados.
25.22.2 passphrase
Passphrase desbloquea DecryptWith cuando está protegido.
Preferir una referencia de variable de entorno, ${NAME}.
25.22.3 verify_with
VerifyWith son los archivos de claves públicas de los remitentes.
Una firma de cualquiera de ellos se verifica y registra.
25.22.4 pass_plain
PassPlain permite que un mensaje que no es PGP en absoluto pase sin cambios, para una carpeta que recibe ambos.
Desactivado: tal mensaje es rechazado, por lo que un remitente que olvidó cifrar es informado.
25.23 Enviar PGP
PGPSend protege lo que un destino entrega con OpenPGP: cifrado con la clave pública del receptor, firmado con la nuestra, o ambos.
Los pagadores, las casas de compensación y las agencias estatales a menudo toman archivos sobre SFTP solo de esta manera.
Definido como PGPSend en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
encrypt_to | lista de texto | no | — |
sign_with | texto | no | — |
passphrase | texto | no | — |
armor | verdadero o falso | no | — |
no_compress | verdadero o falso | no | — |
file_name | texto | no | — |
25.23.1 encrypt_to
EncryptTo son los archivos de claves públicas de los receptores (blindados o binarios).
El mensaje es legible por cualquiera de ellos. Vacío: el mensaje solo está firmado.
25.23.2 firmar_con
SignWith es nuestro archivo de clave secreta, para firmar cada mensaje.
Vacío: solo cifrado.
25.23.3 passphrase
La frase de contraseña desbloquea SignWith cuando está protegido.
Preferir una referencia de variable de entorno, ${NAME}.
25.24 Configuración de cola
QueueConfig activa la cola de mensajes duradera para un destino.
Sin ella, un destino que esté inactivo durante dos minutos pierde todo lo enviado durante esos dos minutos: las reintentos ocurren en memoria y cuando se agotan, el mensaje se registra como fallido y se elimina. Con ella, el mensaje se almacena en disco y mantiene su lugar hasta que el receptor regresa.
Está desactivada por defecto, lo que es una elección deliberada en lugar de precaución. Una cola cambia lo que significa un acuse de recibo y cambia el comportamiento de ordenación durante una interrupción, y ambas son cosas que un operador debe optar por conocer en lugar de descubrir.
Definido como QueueConfig en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
enabled | verdadero o falso | no | — |
max_attempts | número entero | no | — |
backoff | duración, como 30s o 5m | no | — |
max_backoff | duración, como 30s o 5m | no | — |
max_depth | número entero | no | — |
retain_hours | número entero | no | — |
25.24.1 intentos_máximos
MaxAttempts es la cantidad de veces que la cola intenta antes de marcar un mensaje como fallido y pasar al siguiente.
Cero significa seguir intentando indefinidamente, lo que es legítimo para una fuente que no debe perder nada, pero debe ser elegido en lugar de heredado.
25.24.2 max_backoff
MaxBackoff limita el retraso creciente.
El valor predeterminado es un minuto en lugar de algo más grande porque un receptor que regresa después de una interrupción breve debe ser encontrado rápidamente: un límite de una hora convierte un problema de dos minutos en una hora de atraso.
25.24.3 max_depth
MaxDepth rechaza nuevos mensajes una vez que este número esté esperando, por lo que un receptor que ha estado inactivo durante una semana no puede llenar el disco.
Cero significa sin límite.
Cuando se alcanza el límite, el mensaje se marca como fallido en lugar de encolado, y el remitente es informado - lo que es la respuesta honesta, porque ya no prometemos entregarlo.
25.24.4 retain_hours
RetainHours es la cantidad de tiempo que se mantienen los artículos entregados y abandonados antes de ser purgados.
Se mantienen para que alguien que observe una cola de retrasos pueda ver que está sucediendo, y para que un mensaje abandonado pueda ser encontrado después.
25.25 Reintento
Reintento controla la entrega nuevamente después de un fallo.
Definido como Retry en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
attempts | número entero | no | — |
backoff | duración, como 30s o 5m | no | — |
max_backoff | duración, como 30s o 5m | no | — |
25.25.1 intentos
Intentos es el número total de intentos, incluyendo el primero.
Cero significa el valor predeterminado.
25.25.2 retroceso
Retroceso es la espera antes del segundo intento.
Se duplica cada vez, hasta MaxBackoff.
25.26 Opciones de script
ScriptOptions configura un canal NCPDP SCRIPT.
Por qué SCRIPT obtiene un bloque cuando su filtro es de nivel superior
El filtro de canal para cada tipo de datos es la clave de filtro de nivel superior, porque internal/expr es genérico sobre el tipo de mensaje: una clave, compilada contra el árbol o la reclamación que lleva el canal. Los pasos no se pueden organizar de esa manera. Un paso v2 se dirige a un segmento y un campo, un paso X12 se dirige a un elemento de ancho fijo, y los tipos son diferentes, por lo que una clave yaml tendría que contener lo que implicaba el tipo de datos, lo que es un campo que significa cosas diferentes dependiendo de otro campo, y el modo de falla es un paso que silenciosamente no hace nada.
Así que esto sigue hl7v3, x12, ncpdp y delimitado: los pasos viven en el bloque de su propio formato, y la clave de transformaciones de nivel superior se rechaza en un canal SCRIPT en lugar de ser aceptada e ignorada.
Definido como ScriptOptions en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
transformations | lista de hl7v3.Step | no | — |
25.26.1 transformaciones
Transformaciones son los pasos declarativos, que se dirigen a la receta como un árbol XML.
hl7v3.Step en lugar de un tipo propio, y eso no es pereza. Una receta y un documento v3 son ambos XML, la gramática de la ruta es la misma, y internal/eprescribe ya presta hl7v3.Path para su filtro con un comentario que lo dice - v3 y CDA ya lo utilizan, y una tercera copia sería un tercer conjunto de respuestas a lo que //a/b[2]@c significa.
Los pasos que llevan un significado exclusivo de v3 se rechazan en la carga en lugar de estar disponibles silenciosamente. nullflavor es el uno: escribe el atributo v3 que establece por qué un valor está ausente, lo que no tiene equivalente en una receta, y un paso que lo escribiera produciría un documento que el receptor no entiende.
25.27 Scripts
Scripts contiene el JavaScript adjunto a un canal.
Definido como Scripts en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
filter | texto | no | — |
transformer | texto | no | — |
preprocessor | texto | no | — |
deploy | texto | no | — |
undeploy | texto | no | — |
postprocessor | texto | no | — |
language | texto | no | — |
timeout | duración, como 30s o 5m | no | — |
include | lista de texto | no | — |
file_roots | lista de texto | no | — |
allow | lista de texto | no | — |
25.27.1 filtro
Filtro se ejecuta después del filtro de expresión, si lo hay, y debe devolver un booleano.
Un script de filtro de Mirth se coloca aquí sin cambios.
25.27.2 transformador
Transformador se ejecuta después de los pasos declarativos y puede modificar el mensaje.
Un script de transformador de Mirth se coloca aquí sin cambios.
25.27.3 preprocesador
El preprocesador se ejecuta antes de que se analice el mensaje, lee el texto como "mensaje" y devuelve el texto para analizar.
Un preprocesador de Mirth se incorpora aquí sin cambios.
Este es el único lugar donde se puede reparar un mensaje que no se puede analizar. Los sitios lo utilizan para un carácter extraño de una puerta de enlace serie, un terminador de segmento que llegó como un salto de línea o un proveedor que rellena un campo con algo ilegal para su posición. Sin él, Perfuse rechaza el tráfico que Mirth acepta hoy.
25.27.4 implementar
Implementar se ejecuta una vez cuando el canal comienza, antes de que acepte algo.
Este es el lugar donde los sitios calientan una caché de búsqueda o verifican que algo en lo que dependen esté accesible. Se ejecuta antes de que la fuente se una, por lo que un script de implementación que falla detiene el canal de inicio en lugar de dejar que acepte mensajes que no puede manejar.
25.27.5 desimplementar
Desimplementar se ejecuta una vez cuando el canal se detiene.
Después de que la fuente ha dejado de aceptar y después de que los mensajes en vuelo han terminado, por lo que ve un canal que genuinamente está inactivo. Su fallo no puede detener una parada: un canal que no se pudiera cerrar porque un script de limpieza lanzó una excepción sería peor que uno que registró y cerró.
25.27.6 posprocesador
El posprocesador se ejecuta después de que el mensaje ha sido manejado y no puede cambiarlo.
El mensaje y lo que le sucedió son visibles, por lo que puede notificar, contar o registrar. No puede alterar lo que se envió: cuando se ejecuta, el envío ya ha sucedido, y un script que pareciera cambiar un mensaje entregado estaría mintiendo sobre ello.
25.27.7 idioma
El idioma es el lenguaje de secuencias de comandos en el que se escriben estos scripts: javascript o lua.
¿Por qué una configuración para todo el bloque en lugar de una por script
Los scripts de un canal comparten el mapa del canal y el mapa del conector, y se leen juntos por quien los mantiene. Un filtro en Lua junto a un transformador en JavaScript significaría dos idiomas en una revisión, compartiendo estado a través de un mapa cuyos valores tendrían que significar lo mismo en ambos. Los sitios que desean eso pueden dividir el canal.
¿Por qué el valor predeterminado es javascript
Cada script escrito antes de que esta configuración existiera es JavaScript y no dice nada sobre su idioma. Hacer que lua sea el valor predeterminado silenciaría la reinterpretación de todos ellos; requerir el campo los rechazaría. Ninguna de las dos opciones es aceptable para una configuración agregada más tarde.
25.27.8 tiempo de espera
El tiempo de espera limita cada script.
Cero significa cinco segundos.
Hay deliberadamente ninguna forma de deshabilitarlo. Mirth permite que un transformador se bloquee para siempre, lo que detiene el canal, y una interfaz que deja de aceptar admisiones debido a un error tipográfico no es algo que se deba ofrecer como opción.
25.27.9 incluir
Incluir nombres de archivos de código de script compartido que se hace disponible para cada script en este canal, en cualquier idioma que los scripts.idioma nombren.
Esto es los plantillas de código de Mirth. Un sitio con cuarenta canales generalmente tiene una biblioteca que cada canal llama, y sin ella, una migración significa copiar esa biblioteca en cuarenta archivos: cuarenta lugares para corregir la próxima vez que cambie.
Las rutas son relativas al archivo del canal, por lo que una biblioteca junto a los canales viaja con ellos. Es una lista porque la alternativa es un archivo enorme, y las bibliotecas tienden a dividirse por tema.
Los archivos deben estar en el lenguaje del canal. Esto fue solo JavaScript durante un tiempo, y no por negativa: un canal Lua leía los archivos, rechazaba uno que faltaba y luego el tiempo de ejecución no instalaba nada de ello, por lo que cada llamada a la biblioteca fallaba en el primer mensaje con un global nulo: el error exacto que la negativa anterior existe para evitar. Ambos lenguajes lo instalan ahora, y una biblioteca que no se analiza es rechazada en la carga con el nombre del archivo.
25.27.10 file_roots
FileRoots son los directorios que FileUtil puede leer y escribir.
Requerido siempre que se permita "file". Otorgar acceso a archivos sin nombrar los directorios utilizados para significar todo el sistema de archivos: un script podría leer la base de datos propia de Perfuse, que contiene hashes de contraseñas, la contraseña de la cuenta de servicio LDAP y el secreto del cliente OIDC. Crear un canal solo requiere el rol de editor, por lo que la escalada se completó en el momento en que un administrador lo inició.
25.27.11 allow
Allow otorga capacidades que de otro modo se negarían a un script: "file", "database", "route".
Mirth otorga todos estos a cada script; requerir que se nombren significa que un transformador no puede leer silenciosamente el sistema de archivos después de una copia y pegado.
25.28 Sombra
Shadow ejecuta una versión de candidato de un canal junto al canal en vivo y informa dónde difieren.
Esta es la respuesta a la pregunta que hace que el trabajo de interfaz sea aterrador: ¿cómo sabes que un cambio es seguro? No leyendo. Una transformación es un programa que opera en mensajes cuya variedad nadie ha catalogado, y el caso que rompe es siempre el que nadie pensó: el paciente con dos identificadores, el resultado con un campo de unidades vacío, el A08 que llega antes de su A01.
Una prueba de canal prueba los casos que alguien pensó. El modo sombra prueba los casos que realmente llegan, contra tráfico real, antes de que el cambio lleve alguno de ellos.
Dos propiedades lo hacen seguro, y ambas son estructurales en lugar de configurables:
- La sombra nunca entrega. Sus destinos no se construyen, por lo que no hay camino de código desde un canal sombra a un receptor, y ninguna configuración puede crear uno por error. - La sombra nunca afecta el canal en vivo. Se ejecuta después de que el mensaje en vivo se ha entregado y confirmado, y una sombra que se bloquea, lanza o cuelga se registra como una falla de sombra y nada más.
Definido como Shadow en internal/config.
| Key | Type | Required | Default |
|---|---|---|---|
channel | texto | sí | — |
sample | número | no | 1 |
compare | lista de texto | no | — |
ignore | lista de texto | no | — |
max_differences | número entero | no | 100 |
timeout | duración, como 30s o 5m | no | 5s |
25.28.1 channel
Channel es la ruta al archivo de canal de candidato.
Un archivo separado en lugar de un bloque en línea, a propósito: el candidato está destinado a convertirse en el canal en vivo, por lo que debe ser el artefacto que se revisa, confirma y promueve, no un fragmento que tenga que extraerse.
25.28.2 sample
Sample es la fracción de mensajes que se sombrean, desde 0 a 1. De manera predeterminada es 1.
Vale la pena reducir solo en una fuente ocupada, y vale la pena entender antes de hacerlo: la muestra reduce el costo y reduce la confianza en la misma proporción, y el mensaje que habría mostrado la diferencia es el inusual.
25.28.3 compare
Compare limita la comparación a estas rutas de campo.
Una comparación vacía compara todo.
25.28.4 ignore
Ignorar excluye rutas de campo de la comparación.
Necesario en la práctica, porque un canal que estampa una marca de tiempo o un número de secuencia difiere en cada mensaje individual y reportaría una tasa de diferencia del 100% mientras no le dice nada.
25.28.5 max_differences
MaxDifferences limita la cantidad de mensajes diferentes que se mantienen para inspección.
Defaults to 100.
25.28.6 tiempo de espera
Timeout limita una ejecución de sombra.
Defaults to 5s.
Separado del tiempo de espera del propio canal, y más corto. Un candidato con un script desbordado no debe acumular goroutines detrás del tráfico en vivo.
25.29 Código XDS
XDSCode es un valor codificado en la configuración de destino: código, esquema de codificación (un OID) y visualización.
Definido como XDSCode en internal/config.
| Clave | Tipo | Requerido | Predeterminado |
|---|---|---|---|
code | texto | sí | — |
scheme | texto | sí | — |
display | texto | no | — |
26 Cómo funciona cada componente
Perfuse está construido a partir de pequeños paquetes, cada uno con un trabajo. Este capítulo es la cuenta de cada uno sobre lo que hace y por qué está construido de la manera en que lo está, tomado de la fuente. Donde una decisión tiene una razón que no es obvia, la razón está aquí en lugar de en un mensaje de confirmación que nadie encontrará.
26.1 fhir
El paquete fhir implementa recursos FHIR, serialización y validación.
El control de versiones es la primera decisión de diseño, porque "FHIR" por sí solo no identifica un formato de cable. R5 es la versión publicada más reciente y es el valor predeterminado aquí. R4 es lo que la base instalada y la regulación de EE. UU. consumen en realidad, por lo que está completamente compatible en lugar de ser tratado como legado. R6 está en votación en el momento de la escritura y no se implementa deliberadamente: enviar una suposición sobre una especificación no publicada sería peor que no admitirla.
El modelo de recurso tiene forma R5. Donde R4 difiere, la diferencia se aplica al serializar en lugar de mantener dos conjuntos paralelos de structs, que es cómo una base de código termina con dos definiciones ligeramente divergentes de Paciente.
Source: github.com/biodream-llc/perfuse/fhir
26.2 principal
El comando xmlordergen escribe el orden de elementos FHIR XML que necesita, a partir de los StructureDefinitions de un paquete principal.
go run ./fhir/xmlordergen hl7.fhir.r4.core-4.0.1.tgz | gzip -9 -n > fhir/xmlorder_r4.txt.gz
Los objetos JSON pueden contener sus miembros en cualquier orden; XML no puede: cada elemento debe venir en el orden que su definición enumera. Cada línea de salida es una ruta y sus hijos en ese orden, cada hijo con cómo encontrar a sus propios hijos: "@Ruta" (un elemento definido en línea, una columna vertebral), "#Ruta" (el mismo que otro elemento), o sus tipos.
Source: github.com/biodream-llc/perfuse/fhir/xmlordergen
26.3 hl7
El paquete hl7 lee mensajes HL7 de versión 2.
No tiene dependencias fuera de la biblioteca estándar, y una prueba garantiza que. No realiza operaciones de red, no escribe archivos y no inicia goroutines, por lo que es seguro usar desde cualquier lugar.
Reading a message
msg, err := hl7.Parse(raw) if err != nil { return err } mrn := msg.MustGet("PID-3.1") name := msg.MustGet("PID-5.1") msgType, event, _ := msg.Type() // "ADT", "A01"
Las rutas son la notación que la gente ya usa cuando habla sobre HL7: segmento, campo, luego componente y subcomponente opcional, como PID-5.1 o OBX-5.2.3. Las repeticiones se dirigen con corchetes, como PID-3[2].1.
Get devuelve un error para una ruta que no puede analizar; MustGet devuelve la cadena vacía en su lugar. Ambos son seguros en segmentos y campos ausentes, porque una fuente real está llena de ambos y un analizador que se bloquea en datos ausentes es inútil para el trabajo. La ausencia no es un error — un mensaje que simplemente no transporta PID-8 produce "" en lugar de un fallo.
Ausente y vacío
HL7 distingue un campo que no se envió de uno enviado intencionalmente vacío, y clínicamente pueden significar cosas diferentes - "no se registran alergias" no es "no se conocen alergias". Value.Exists informa la diferencia donde los llamadores la necesitan, mientras que String la aplana en "" para el caso común.
Copiando, y la única forma de equivocarse
Parse copia su entrada, por lo que el Message devuelto permanece válido sin importar cómo el llamador reutilice su búfer. Eso cuesta alrededor del 3% y una asignación por mensaje.
WithZeroCopy omite la copia y mantiene una referencia al slice del llamador en su lugar. Es más rápido, y hace que el Message sea inseguro para usar después de que ese slice se escriba nuevamente. Leer en un búfer reutilizado es la forma normal y eficiente de leer desde una red en Go, así que la trampa atrapa el código cuidadoso, y el síntoma es valores de campo incorrectos de forma silenciosa en lugar de un error - los datos de un paciente bajo el nombre de otro paciente. Úselo para entrada que es inmutable o sobrevive al Message, y no de otra manera.
Acknowledgements
AckFor construye un ACK o NAK para un mensaje, incluyendo para uno que falló al analizar, que es el caso que más importa: un remitente necesita una respuesta incluso cuando lo que envió no fue un mensaje. Vea AckOptions.
Lo que este paquete no hace
No escribe ni modifica mensajes. No tiene noción de estructura de mensaje más allá de segmentos, así que no le dirá que un PV1 pertenece a un grupo de visita en particular. No valida contra las tablas de una versión. Esas son omisiones deliberadas en lugar de descuidos: este es un indexador para tráfico real, que con frecuencia no es conforme a los estándares y aún así tiene que ser enrutado.
Source: github.com/biodream-llc/perfuse/hl7
26.4 admitir
El paquete admitir limita la cantidad de trabajo que está en vuelo al mismo tiempo.
El motor crea un goroutine por destino por mensaje. Los goroutines son baratos; lo que contienen no lo es. Cada entrega saliente mantiene un socket en el extremo lejano y un descriptor de archivo aquí, y los mantiene durante todo el presupuesto de reintento - con los valores predeterminados, cinco intentos con un tiempo de espera de treinta segundos más quince segundos de retroceso, así que casi tres minutos. Unos pocos cientos de remitentes contra un receptor que se ha quedado callado es, por lo tanto, unos pocos cientos de descriptores mantenidos durante minutos, y el proceso se agota.
Agotarse es el fallo que vale la pena prevenir, porque no sale a la superficie donde sucedió. Sale a la superficie como "demasiados archivos abiertos" en algún lugar no relacionado: la base de datos no puede abrir su diario, la interfaz deja de aceptar, un registro no puede ser escrito. El motor parece estar roto en tres lugares a la vez y la causa es un sistema de socio que dejó de responder.
Dos límites, y el segundo importa más que el primero:
Un total, para que el proceso se mantenga dentro de su presupuesto de descriptores.
Un límite por destino, para que un receptor no pueda consumir el total. Sin él, un socio colgado derriba todos los demás canales, lo que es peor que no tener límite en absoluto: el radio de explosión crece de una interfaz a todas ellas.
Source: github.com/biodream-llc/perfuse/internal/admit
26.5 alertas
El paquete alertas vigila las métricas y dice cuándo algo está mal.
El punto es que nadie debería tener que estar mirando el panel de instrumentos. "Transacciones fallidas permanecen en cola sin investigación oportuna" es un fallo operativo documentado en esta clase de software, y la razón siempre es la misma: la información estaba en una pantalla que nadie estaba frente a ella a las cuatro de la mañana.
Tres decisiones dan forma a esto.
Una alerta debe borrarse a sí misma. Una notificación que se dispara y nunca se resuelve entrena a las personas para ignorar las notificaciones. Cada regla aquí se evalúa contra el estado actual, así que un receptor que vuelve cierra la alerta que abrió.
Las alertas se amortiguan por duración, no por conteo. "Tasa de error superior al 5% en este momento" se dispara con un solo mensaje desafortunado durante una hora tranquila. "Superior al 5% durante dos minutos" no. Cada regla lleva una duración Para y nada se dispara hasta que la condición ha durado tanto tiempo.
Lo que se envía no contiene datos de paciente. Una alerta lleva un nombre de canal, un nombre de destino, un número y un umbral. Esos provienen de la configuración y del conteo. Un webhook va a una sala de chat, una sala de chat tiene un desplazamiento hacia arriba, y un desplazamiento hacia arriba no es un lugar donde un identificador de paciente debería terminar.
Source: github.com/biodream-llc/perfuse/internal/alerts
26.6 amqp
El paquete amqp es un cliente AMQP 1.0: una conexión, una sesión, enlaces de envío y recepción, establecidos después de que el receptor lo dice.
AMQP 1.0 es lo que Azure Service Bus y Event Hubs hablan, lo que RabbitMQ 4 habla de forma nativa, y lo que ActiveMQ y Artemis hablan además de STOMP. Escrito aquí por la misma razón que el cliente STOMP y el firmante de AWS: una biblioteca de cliente completa sería la mayoría de la lista de dependencias del binario para cuatro performativos que importan.
Lo que hace: SASL PLAIN o ANONYMOUS (Service Bus toma un nombre de clave de acceso compartido y una clave como PLAIN), TLS, abierto/iniciar/adjuntar, flujo basado en crédito, una transferencia dividida en tramas cuando excede el tamaño de trama máximo del par, y disposiciones - aceptadas, liberadas, rechazadas. El remitente espera la disposición del corredor antes de informar el éxito, así que "enviado" significa que el corredor tomó la custodia. Las tramas vacías mantienen la conexión viva dentro del tiempo de espera de inactividad del par, que Service Bus aplica.
Lo que no hace: transacciones, múltiples sesiones, recuperación de enlace y anotaciones de mensaje más allá de leerlas.
Source: github.com/biodream-llc/perfuse/internal/amqp
26.7 analizar
El paquete analyze convierte un canal de Mirth analizado en algo que un ser humano puede utilizar: una descripción simple de lo que hace el canal y una lista honesta de lo que no sobrevivirá a una migración.
La regla de diseño aquí es que un hallazgo debe ser lo suficientemente específico como para actuar sobre él. "Este canal utiliza JavaScript" no es un hallazgo. "El destino 1 llama a java.util.Date, que no tiene equivalente fuera de la JVM" es.
Source: github.com/biodream-llc/perfuse/internal/analyze
26.8 api
El paquete api sirve la interfaz HTTP utilizada por el frontend web.
La restricción de diseño importante: los canales se leen y se escriben en archivos en disco. La API es un editor para esos archivos, no un sistema de almacenamiento diferente que exporte YAML. Un canal creado en el navegador y uno escrito a mano son el mismo artefacto, lo que hace que "exportar para compartir" sea trivial y mantiene a git como el historial de registro.
Source: github.com/biodream-llc/perfuse/internal/api
26.9 as2
El paquete as2 es la Declaración de Aplicabilidad 2 (RFC 4130): EDI sobre HTTP, firmado y cifrado con S/MIME, con un recibo firmado (un MDN) que nombra un resumen de lo que llegó.
Los pagadores y socios comerciales lo utilizan para X12 y EDIFACT.
El S/MIME es internal/cms, el mismo código que utiliza el mensajería directa. Lo que está aquí es propio de AS2: sus encabezados, la comprobación de integridad del mensaje (MIC) y el recibo.
Source: github.com/biodream-llc/perfuse/internal/as2
26.10 attach
El paquete attach mueve las cargas útiles grandes fuera de un mensaje y las vuelve a colocar.
Un mensaje HL7 que lleva un informe escaneado o un PDF es ordinario en flujos de trabajo de documentos, y la carga útil es rutinariamente cientos de veces más grande que el mensaje que la rodea. Almacenar eso en línea significa que el almacén de mensajes crece a la tasa de los documentos en lugar del tráfico, un mensaje en cola para cinco destinos se copia cinco veces, y la interfaz tiene que transmitir megabytes para mostrar el nombre de un paciente.
Así que la carga útil se extrae, se almacena una vez por contenido y se reemplaza en el mensaje con un token. Mirth llama a estos controladores de adjuntos y es una característica de migración documentada, lo que explica que el formato del token esté diseñado para ser reconocible en lugar de inteligente.
Lo que este paquete no decide
No realiza almacenamiento ni E/S. La extracción produce el mensaje reescrito y las cargas útiles; un Almacén en algún otro lugar decide dónde van esas cargas. Esa separación es lo que hace que la extracción sea probada sin una base de datos, y también es lo que permite que la misma lógica se ejecute en el entorno de juego WASM donde no hay base de datos en absoluto.
Source: github.com/biodream-llc/perfuse/internal/attach
26.11 authlimit
El paquete authlimit limita los intentos de autenticación fallidos por dirección de origen.
Existe porque dos oyentes necesitaban lo mismo y uno de ellos no tenía nada. El servidor FHIR obtuvo un limitador cuando obtuvo autenticación; el inicio de sesión de la consola no tenía ninguno en absoluto, y una medición mostró veinte intentos de adivinanza de contraseña por segundo sostenidos contra la cuenta de administrador sin retraso ni bloqueo. A esa tasa, una contraseña débil desaparece en una noche y la única huella es un registro lleno de advertencias idénticas.
No es un limitador de velocidad de propósito general. Limita los fallos, no las solicitudes: un cliente legítimo ocupado nunca se ve afectado, y lo único que se acumula es el error.
Source: github.com/biodream-llc/perfuse/internal/authlimit
26.12 awsmsg
El paquete awsmsg se comunica con Amazon SQS y SNS con la biblioteca estándar y el firmante de Signature Version 4 compartido.
El mismo argumento que s3put: el SDK de AWS duplicaría el tamaño de la lista de materiales para cuatro llamadas a la API. SQS utiliza su protocolo JSON (AWS JSON 1.0, X-Amz-Target AmazonSQS.<Action>), que AWS documenta como el actual; SNS utiliza su protocolo de consulta, que es el único que tiene. Ambos funcionan contra LocalStack y puntos de conexión de VPC a través de un reemplazo de punto de conexión.
Source: github.com/biodream-llc/perfuse/internal/awsmsg
26.13 awsv4
El paquete awsv4 firma solicitudes a AWS con Signature Version 4, utilizando solo la biblioteca estándar.
Comenzó dentro de s3put. SQS, SNS y la lectura desde S3 necesitan la misma firma con un nombre de servicio diferente y con una cadena de consulta, y una segunda copia del código de firma sería un segundo lugar para el único error que importa - una coincidencia de firma lee exactamente como credenciales incorrectas. Así que vive aquí, comprobado contra los vectores de prueba publicados por AWS, y todos los conectores de AWS lo utilizan.
Las credenciales son claves estáticas o variables de entorno estándar. No hay una cadena de credenciales de metadatos de instancia de forma deliberada: significa llamadas a IMDS, caché y actualización, que es donde una implementación pequeña deja de ser pequeña, y Perfuse rechaza las direcciones de metadatos como destinos de forma predeterminada.
Source: github.com/biodream-llc/perfuse/internal/awsv4
26.14 azblob
El paquete azblob lee y escribe Azure Blob Storage con la biblioteca estándar: autorización Shared Key o SAS sobre llamadas HTTP simples.
La misma lógica que s3put: el SDK de Azure es grande y las operaciones que necesita un motor de integración son cinco: poner, obtener, listar, eliminar y establecer el nivel de acceso. La firma de Shared Key se especifica exactamente ("Authorize with Shared Key", Azure Storage REST), y Azurite, el emulador de Microsoft, lo verifica de la misma manera que el servicio.
Source: github.com/biodream-llc/perfuse/internal/azblob
26.15 marcado
El paquete de marcado almacena el logotipo y el nombre del producto de un cliente para que una instalación pueda presentarse como su propio producto en lugar de como Perfuse.
Por qué el logotipo no es una configuración
Todo lo demás que un operador puede cambiar es un escalar en el archivo de configuración. Un logotipo es unos pocos cientos de kilobytes de binario, y colocarlo allí significaría base64 en un archivo YAML que una persona debe poder leer y editar manualmente. En su lugar, vive al lado de ese archivo, y solo su presencia es visible a través de la superficie de configuración.
La parte que importa: una imagen cargada es una entrada no confiable
Un logotipo es cargado por un administrador y luego se sirve a cada navegador que carga la consola, incluida la página de inicio de sesión antes de que alguien se haya autenticado. Eso lo convierte en el contenido de entrada de atacante más ampliamente distribuido en el producto, por lo que se trata en consecuencia:
- Se ignora el tipo de contenido declarado. Los bytes se analizan, y el resultado debe estar en una lista de permitidos. Un archivo llamado logo.png que contiene HTML es HTML. - Se acepta SVG, porque eso es lo que tienen las empresas, pero es un formato de documento ejecutable: puede contener elementos de script, atributos de controladores de eventos, referencias externas y objetos extranjeros incrustados. Se reescribe para eliminar todos ellos antes de almacenarlo, y la reescritura se verifica plantando cada ataque en una prueba. - Se sirve con una política de seguridad de contenido que no permite nada, con análisis de tipo deshabilitado y como una imagen en línea sin adjuntos. Incluso un error de sanitización no debe convertirse en ejecución de script en el origen de la consola. - El nombre del archivo cargado nunca se utiliza para construir una ruta. El nombre almacenado se deriva del tipo analizado, por lo que un nombre como ../../etc/perfuse no puede escapar.
Source: github.com/biodream-llc/perfuse/internal/branding
26.16 cda
El paquete cda lee documentos clínicos.
El diseño comienza con cómo viajan estos documentos en realidad, que no es como archivos. Un resumen de alta sale de un EHR dentro de un mensaje HL7 v2 MDM^T02, con todo el documento codificado en base64 en OBX-5 y descrito por un segmento TXA. Eso son miles de mensajes al día en un hospital de tamaño mediano, y es por eso que este paquete se encuentra junto a un analizador HL7 v2 en lugar de en una herramienta de documento: la mitad v2 ya está aquí, por lo que la tubería desde "un mensaje llegó" hasta "existe una referencia de documento FHIR" es corta.
Tres cosas están deliberadamente en el alcance y una no.
En el alcance: leer un documento y decir qué contiene en términos simples; comprobar que la narrativa legible por humanos y las entradas codificadas coinciden; y convertir a FHIR. La comprobación de la narrativa es la inusual. Cada sección C-CDA lleva la misma información dos veces, una como texto para un clínico y una como códigos para una máquina, y se supone que coinciden. En la práctica, a menudo no lo hacen, y nada lo comprueba - ni las herramientas de certificación, ni los motores. Cuando el texto dice que un paciente es alérgico a la penicilina y la entrada codificada dice algo más, uno de los dos lectores actúa con la información incorrecta. Es un defecto de seguridad del paciente que es mecánicamente detectable, por lo que se detecta aquí.
No en el alcance: generar C-CDA conforme. Escribir uno significa satisfacer varias decenas de reglas de plantilla y un Schematron, y producir documentos que son 95% correctos fallarían la certificación de maneras que son miserables de depurar. Leer, comprobar y convertir es la mayor parte del valor por una fracción del riesgo.
Source: github.com/biodream-llc/perfuse/internal/cda
26.17 channeltest
El paquete channeltest ejecuta pruebas contra una definición de canal.
Un canal es un programa. Filtra, reescribe y enruta mensajes clínicos, y se edita bajo presión por personas que no pueden probarlo contra producción. La ausencia de una forma de probar uno es la razón principal por la que el trabajo de interfaz es aterrador, y "cambie el transformador y nada parecía diferente en el navegador de mensajes" no es una prueba.
La decisión central es que una prueba ejecuta el mensaje a través del camino de transformación real - el mismo filtro, los mismos pasos declarativos, el mismo motor de script, en el mismo orden - con solo el transporte reemplazado. Una prueba que ejerciera una reimplementación pasaría mientras el canal fallaba, lo que es peor que no tener prueba en absoluto porque activamente engaña.
Source: github.com/biodream-llc/perfuse/internal/channeltest
26.18 chanscope
El paquete chanscope reduce las lecturas y escrituras en un contexto a algunos canales.
Depende del contexto en lugar de ser un parámetro de cada consulta porque hay docenas de consultas en todo el almacén de mensajes y la cola de entrega, y el límite es una propiedad de quién lo solicita, no de la pregunta. La API lo establece una vez por solicitud para una persona limitada a algunos grupos de canales; cada recuento, lista, gráfico, búsqueda y acción de cola coincide con los demás, por lo que a nadie se le dice un total de cuarenta errores y se le muestra cuatro.
Source: github.com/biodream-llc/perfuse/internal/chanscope
26.19 cluster
El paquete cluster permite que varios servidores Perfuse compartan un almacén PostgreSQL y decidan, entre ellos, qué ejecuta cada uno.
El problema que resuelve
Un servidor es un solo punto de fallo: cuando se detiene, se detiene cada interfaz. Dos servidores en una base de datos resuelven esto solo si nunca ejecutan el mismo canal, porque dos oyentes MLLP en una fuente se turnan con el remitente, dos trabajadores de cola entregan el mismo mensaje dos veces, y dos sondeos DICOM informan cada uno del mismo estudio.
How
Un contrato de arrendamiento es una fila que nombra quién posee algo, con un contador que su poseedor mueve en cada renovación, cada pocos segundos. No se compara ningún reloj con otro: un poseedor que no puede renovar (se ha estrellado, se ha cortado de la base de datos) detiene lo que cubre el contrato de arrendamiento una vez que su propio reloj dice que el contrato de arrendamiento está a punto de caducar, y otro servidor lo toma solo después de que su propio reloj haya visto que el contador se ha mantenido quieto durante todo el contrato de arrendamiento. Cada uno mide un intervalo en su propio reloj, por lo que los relojes de los servidores y el de la base de datos no necesitan coincidir; solo necesitan funcionar a una velocidad similar. Un reloj que salta (una máquina virtual que se resincroniza, un administrador que establece la hora) no cambia nada: la primera versión utilizó el reloj de la base de datos para la caducidad, y una prueba encontró una base de datos cuyo reloj saltó trece segundos y entregó cada contrato de arrendamiento a un nuevo servidor mientras los antiguos todavía los ejecutaban.
Entre la detención del poseedor y el inicio del siguiente hay una breve brecha con nadie ejecutándolo, lo que cuesta a un remitente unos pocos segundos de conexiones rechazadas, y nunca un momento con dos.
Two modes:
- activo-pasivo: un contrato de arrendamiento, "líder". Su poseedor ejecuta cada canal y cada trabajo que debe ejecutarse una vez (notificaciones de suscripción, alertas). Los demás esperan, y el primero que ve que caduca lo toma. - activo-activo: el líder todavía ejecuta los trabajos, y cada canal tiene un contrato de arrendamiento propio, distribuido uniformemente entre los servidores en vivo. Un servidor que se va da sus canales al resto; uno que se une toma su parte a medida que quedan libres.
Cada contrato de arrendamiento lleva una época, contada cada vez que su poseedor cambia, que nombra una tenencia en la historia.
Source: github.com/biodream-llc/perfuse/internal/cluster
26.20 cms
El paquete cms es la sintaxis de mensaje criptográfico (RFC 5652) como la utilizan S/MIME y el mensajería segura Direct: SignedData (firmar un mensaje, verificar uno y decir quién lo firmó) y EnvelopedData (cifrar un mensaje para los certificados de los destinatarios, descifrar uno con nuestra clave).
Solo lo que requiere la Declaración de aplicabilidad directa y su Guía de implementación: RSA con SHA-256 (SHA-1 aceptado solo para lectura, ya que los antiguos HISPs todavía lo envían), transporte de clave RSA con PKCS#1 v1.5 u OAEP, y AES-128 o AES-256 en modo CBC. La criptografía es la biblioteca estándar; este paquete es el ASN.1 que la rodea.
Source: github.com/biodream-llc/perfuse/internal/cms
26.21 cms0057
El paquete cms0057 contiene las partes del lado del pagador de la regla final de interoperabilidad y autorización previa de CMS (CMS-0057-F) que se refieren a datos en lugar de transporte: reclamaciones adjudicadas como recursos de explicación de beneficios de CARIN Blue Button, autorizaciones previas como recursos de explicación de beneficios de Da Vinci PDex, coincidencia de miembros HRex para la API de pagador a pagador, la lista de miembros detrás de la API de acceso de proveedor, y las métricas de autorización previa que un pagador debe publicar cada año.
Los puntos finales FHIR que sirven estos viven en fhirserver; este paquete decide qué va en ellos.
Los medicamentos están fuera del alcance en todo momento, ya que lo están en las disposiciones de autorización previa de la regla: ninguna explicación de beneficios de farmacia, ninguna formularios, ninguna NCPDP.
Source: github.com/biodream-llc/perfuse/internal/cms0057
26.22 codeset
El paquete codeset contiene tablas de asignación que sobreviven al canal que las necesitó por primera vez.
El impuesto de dominio es en gran parte volver a derivar una asignación que alguien ya derivó. Los códigos de sexo de un sitio, las clases de paciente, los estados de pedido y los identificadores de instalaciones se traducen de la misma manera en cada canal que los toca, y hoy en día cada una de esas traducciones es una tabla separada dentro de un canal separado. Cuarenta canales significan cuarenta copias y cuarenta lugares para corregir la próxima vez que el sistema receptor cambie.
Así que una tabla se convierte en un objeto con nombre al que se hace referencia por nombre. Eso es obvio y es la mitad menor del valor.
La mitad que nadie construye
La mitad mayor es la procedencia. La mayoría de lo que hace que una interfaz sea difícil de mantener no es la asignación, es que nadie sabe *por qué*. "Eliminamos los ceros iniciales porque la columna MRN del laboratorio es numérica" es conocimiento que existe en la cabeza de una persona y abandona el edificio cuando se jubila, y la asignación que queda se convierte en algo que nadie se atreve a cambiar y nadie se atreve a eliminar.
Así que cada tabla registra quién decidió y cuándo, cada entrada puede registrar su propia razón, y ambos sobreviven en el archivo que se introduce en el control de versiones. Una asignación con una razón se puede discutir. Una asignación sin una se convierte en superstición.
Por qué el archivo es separado del canal
Porque es compartido. Una tabla dentro de un canal es una tabla que solo ese canal puede utilizar, lo que es el problema. Y una tabla compartida necesita su propia historia: "quién cambió el mapeo de sexo y cuándo" es una pregunta sobre la tabla, no sobre el canal que se editó ese día.
Source: github.com/biodream-llc/perfuse/internal/codeset
26.23 cumplimiento
El paquete de cumplimiento implementa el flujo de notificación de eventos ADT de CMS requerido por el 42 CFR 482.24(d) desde mayo de 2021.
Los hospitales deben notificar electrónicamente a los proveedores establecidos de un paciente — atención primaria, especialistas, instalaciones de atención post-aguda — cuando el paciente es admitido, dado de alta o transferido. La regulación existe porque los pacientes caen en las grietas durante las transiciones: un PCP que no sabe sobre un alta no puede programar el seguimiento que evita una readmisión.
Este paquete proporciona la maquinaria para: - Seguir qué proveedores están establecidos para cada paciente. - Enviar eventos ADT a los proveedores correctos con la prioridad correcta. - Registrar cada notificación enviada, con estado de entrega, para auditorías de encuestadores de CMS. - Generar informes de cumplimiento que muestran tasas de notificación y fallos.
La seguridad de subprocesos es necesaria en todo momento porque los mensajes ADT llegan concurrentemente desde múltiples canales y la tubería de notificación se ejecuta en paralelo.
Source: github.com/biodream-llc/perfuse/internal/compliance
26.24 config
El paquete de configuración carga definiciones de canales desde archivos.
La configuración vive en archivos en lugar de una base de datos, lo que es la principal diferencia entre Perfuse y Mirth. Mirth mantiene los canales en su propia base de datos, por lo que el control de versiones es un producto separado, por lo que promover un canal de prueba a producción es difícil, y nadie puede responder "qué cambió el martes pasado" sin una llamada de soporte. Los archivos dan git, diffs, revisión, CI y promoción de entorno sin maquinaria adicional.
La validación es deliberadamente estricta y ocurre en la carga: los campos desconocidos son un error, las expresiones de filtro se compilan, las direcciones se verifican. Un canal que no se puede entender debe rechazar iniciar en lugar de ejecutarse de una manera que nadie pretendía.
Source: github.com/biodream-llc/perfuse/internal/config
26.25 contrato
El paquete de contrato convierte lo que parece un feed en lo que un feed debe parecer.
Un perfil responde "qué estoy recibiendo". Eso es útil una vez. El problema costoso es el día que cambia: una actualización del proveedor elimina un campo, aparece un nuevo código, un segmento comienza a repetirse. Ninguno de esos es un error — los mensajes siguen siendo válidos HL7 y cada motor del mundo los acepta — así que nada nota hasta que un receptor se cae, generalmente semanas después, y la investigación comienza desde el extremo equivocado.
Un contrato es el perfil escrito como una expectativa y luego verificado. Es una prueba de contrato, que es una idea resuelta en software en general y casi completamente ausente en la integración de atención médica.
Por qué la unidad es una tasa y no un mensaje
El diseño obvio es verificar cada mensaje y quejarse cuando uno falla. Eso produce un sistema que nadie puede usar. Las fuentes reales contienen una proporción de mensajes genuinamente extraños — un mensaje de prueba del proveedor, un paciente sin sexo registrado, una entrada manual — y alertar sobre cada uno entrena a las personas para ignorar las alertas, en cuyo punto la función es peor que nada porque ha consumido la atención que necesitaba.
Así que una expectativa es sobre una proporción sobre una ventana: "PID-3 está poblado en al menos el 99% de los mensajes". Un mensaje extraño nunca dispara. El cinco por ciento de los mensajes que pierden su número de registro médico sí.
Un contrato describe lo que llega, no lo que sale
Esta es la distinción que la gente se equivoca primero, incluyéndome a mí mientras probaba la integración. Perfuse almacena el mensaje tal como llegó — eso es deliberado, porque el mensaje almacenado es evidencia de lo que un remitente envió — así que un contrato se verifica contra la fuente entrante y no contra la salida de las transformaciones del canal.
Eso es el valor predeterminado correcto por un amplio margen: la cosa que cambia sin previo aviso es el sistema de envío, y el punto de un contrato es notar. Pero significa que una expectativa escrita sobre un valor mapeado ("después del mapeo, solo deben quedar los códigos del laboratorio") fallará de inmediato y de manera confusa, porque los códigos que nombra nunca aparecen en el tráfico que se está perfilando. Cualquier cosa sobre lo que sale pertenece a una prueba de canal, que ejecuta las transformaciones reales.
Por qué una violación nombra la tolerancia que rompió
"PID-3 falló" no le dice a alguien nada. "PID-3 estaba poblado en el 94.2% de los últimos 500 mensajes; el contrato requiere el 99%" le dice cuán malo es, cuán confiado debe estar y si vale la pena despertar a alguien. Los números son el mensaje.
Source: github.com/biodream-llc/perfuse/internal/contract
26.26 crd
El paquete crd es un servicio de descubrimiento de requisitos de cobertura de Da Vinci (CRD 2.2.1): el lado del pagador de la conversación de CDS Hooks que le dice a un clínico, mientras la orden todavía es un borrador, si está cubierta, si necesita autorización previa y qué documentación el pagador querrá.
La API de autorización previa CMS-0057 es en la práctica tres guías de Da Vinci juntas: CRD para decir qué se requiere, DTR para recopilarlo, PAS para presentarlo. Perfuse tenía PAS. Este es el primero de los otros dos; el paquete $questionnaire de DTR está en el punto final de FHIR.
Las decisiones de cobertura provienen de un archivo de reglas, porque son la política del pagador y cambian sin lanzamientos de software: un código (o una lista de códigos) y lo que se aplica a él - cubierto o no, autorización previa necesaria o no, documentación y el cuestionario DTR que lo recopila. Una orden que no coincide con ninguna regla se responde como "condicional" e "indeterminada", lo que es lo que CRD dice que se debe devolver cuando el servicio no puede decidir - nunca una suposición de que está cubierto.
Las respuestas siguen la guía: una acción de sistema que actualiza cada orden con la extensión de información de cobertura, que el EHR almacena en la orden y más tarde envía con la autorización previa; y una tarjeta por orden, para que un clínico la vea.
Source: github.com/biodream-llc/perfuse/internal/crd
26.27 paneles de control
El paquete de paneles de control define los paneles de control que Perfuse envía para cada tipo de persona que lo ve, y cómo se le asigna a una persona: por su rol, por un grupo de directorio que el archivo de paneles de control asigna, o por la vista que guardó.
Los paneles de control están compuestos por mosaicos, y los mosaicos solo muestran agregados - recuentos, tasas, tiempos, estados. Ninguno muestra un mensaje, un paciente o un identificador, por lo que un panel de control se puede asignar a un gerente de departamento o colocar en una pantalla de pared sin ampliar quién ve los datos del paciente. Un mosaico que necesita datos que Perfuse aún no registra no se falsifica: el panel de control lo enumera en Planificado.
La flexibilidad total es el trabajo de Grafana, no de este paquete. Cada mosaico que tiene una métrica detrás de él se exporta como un panel de Grafana a través de /metrics de Perfuse, por lo que un sitio que desee más que una configuración ligera toma el panel de control allí.
Source: github.com/biodream-llc/perfuse/internal/dashboards
26.28 davinci
El paquete davinci indica qué versión de las guías de autorización previa de Da Vinci (CRD, DTR, PAS) habla un cliente.
Perfuse responde en dos: 2.2 (CRD 2.2.1, DTR 2.2.0, PAS 2.2.1), la más nueva, y 2.1 (CRD, DTR y PAS 2.1.0), la versión que la lista recomendada de CMS-0057-F señala una vez que la base de US Core 3.1.1 de las versiones 2.0.1 expira. Las dos difieren en formas que un cliente lee por nombre - los parámetros de $questionnaire-package de DTR (PackageBundle en 2.1, packagebundle en 2.2), la respuesta de $inquire de PAS (un Bundle en 2.1, un Parámetros de ellos en 2.2), extensiones que existen en solo uno de ellos - por lo que una respuesta no puede servir a ambos, y el servidor tiene que saber con cuál está hablando.
Se indica, lo más específico primero: por la propia solicitud (davinci-crd.requestedVersion de CRD; la versión en un perfil que la entrada reclama, perfil|2.1.0), luego por el registro del cliente (davinci_version en un cliente SMART o CDS), luego por la configuración propia del servidor (serve -davinci-version), que predetermina la más nueva.
Source: github.com/biodream-llc/perfuse/internal/davinci
26.29 dbtime
El paquete dbtime formatea las marcas de tiempo para que ordenarlas como texto las ordene como tiempo.
Cada tienda en Perfuse mantiene las marcas de tiempo como TEXT en SQLite y confía en la comparación de cadenas para el orden y las consultas de rango: ORDER BY received_at DESC, WHERE expires_at < ?, WHERE at >= ?. Ese es un diseño razonable - mantiene una base de datos que alguien puede leer con el shell sqlite3 - pero solo funciona si la forma de texto es de ancho fijo.
time.RFC3339Nano no lo es. Su documentación dice que elimina los ceros finales del campo de segundos, por lo que el mismo reloj produce cadenas de diferentes longitudes:
20:56:33.12345Z <- 123450 microsegundos, cero final eliminado 20:56:33.123456Z <- 123456 microsegundos
Comparados como texto, el primero es *mayor*, porque después del prefijo compartido "12345" tiene 'Z' donde el segundo tiene '6', y 'Z' se ordena por encima de cada dígito. Entonces, el instante anterior se ordena más tarde. Aproximadamente una pareja adyacente de cada diez se ve afectada, lo que explica por qué se presentó como una falla de prueba intermitente en lugar de como una función obviamente rota.
Lo que realmente rompió: el registro de auditoría y el navegador de mensajes ambos ordenan por una marca de tiempo y pueden enumerar entradas del mismo segundo en el orden incorrecto. Un registro de auditoría es un artefacto de cumplimiento y un navegador de mensajes es cómo alguien reconstruye lo que sucedió, por lo que en ambos casos el orden es el punto.
El diseño a continuación utiliza .000000000 en lugar de .999999999. Ambos imprimen nanosegundos; la diferencia es que los ceros son significativos en el primero y se eliminan en el segundo.
Source: github.com/biodream-llc/perfuse/internal/dbtime
26.30 delimitado
El paquete delimited analiza registros separados y divididos por caracteres configurados.
Este es el tipo de datos Delimited de Mirth: CSV, archivos separados por tabuladores, archivos separados por tuberías, archivos de columnas fijas y la cola larga de formatos que un analizador de laboratorio o un servicio de oficina emite porque alguien lo escribió en 1994 y funciona.
No es intencionalmente una biblioteca de CSV general. Lo que importa aquí es lo que una alimentación de atención médica hace que un CSV bien formado no hace: una fila de encabezado que puede o no estar presente, una línea vacía final que no debe convertirse en un registro vacío, columnas referenciadas por nombre para que una transformación no se rompa cuando un proveedor inserta uno, y la capacidad de tratar todo el archivo como un mensaje o cada fila como su propio.
La última de ellas es la decisión que da forma a todo lo demás. Un archivo de cinco mil resultados es un mensaje o cinco mil, y los dos no son intercambiables: un mensaje significa un solo reconocimiento y un solo fracaso para todo el archivo, y cinco mil significa que cada fila se puede filtrar, transformar y volver a intentar por su cuenta.
Source: github.com/biodream-llc/perfuse/internal/delimited
26.31 dicom
El paquete dicom lee conjuntos de datos DICOM.
DICOM es donde vive la radiología, y un motor de integración de atención médica que no puede hablarlo está completamente excluido de la imagen. Mirth tiene un oyente y remitente DICOM, por lo que este es un bloqueador de migración para cualquier sitio que realice trabajo de modalidad.
Alcance, declarado claramente
Este paquete analiza conjuntos de datos: la información de metadatos del archivo, los elementos, la sintaxis de transferencia y los valores que importan para el enrutamiento y la identificación. No decodifica los datos de píxeles y nunca lo hará en lo que respecta a este paquete - un motor de integración mueve imágenes, no las muestra, y un decodificador es un gran cuerpo de trabajo específico del formato cuyo único propósito aquí sería hacer que el binario sea más grande.
Leer metadatos sin tocar píxeles también es lo que lo hace rápido en el tráfico que importa: una tomografía computarizada de tórax es cientos de megabytes de píxeles detrás de dos kilobytes de la información en la que alguien se basa para enrutar.
Por qué las representaciones de valor se manejan explícitamente
Un elemento DICOM lleva un valor de representación de dos letras que indica cómo leer su valor, y las reglas difieren por VR de maneras que no se pueden adivinar: algunos tienen una longitud de dos bytes y otros de cuatro, algunos están rellenos con un espacio y otros con un nulo, y las cadenas numéricas pueden contener varios valores separados por barras invertidas. Obtener uno incorrecto no produce un error, produce un valor incorrecto con apariencia plausible: un nombre de paciente leído desde el medio de una fecha o un identificador de estudio desplazado un byte.
Source: github.com/biodream-llc/perfuse/internal/dicom
26.32 dicomweb
El paquete dicomweb habla DICOMweb (DICOM PS3.18): QIDO-RS para buscar, WADO-RS y WADO-URI para recuperar, STOW-RS para almacenar, sobre HTTP.
Es la forma en que los visores, los servicios de inteligencia artificial y los archivos en la nube acceden a las imágenes ahora: OHIF, Google Cloud Healthcare, Azure Health Data Services, AWS HealthImaging, interfaces DICOMweb de Orthanc y dcm4chee. Un motor de integración que solo habla el protocolo de red de 1993 (DIMSE: C-STORE, C-FIND) puede alimentar un PACS pero no un navegador.
El paquete tiene cuatro partes: un lector de conjunto de datos que recorre todo el objeto, incluidas las secuencias, y sabe dónde está el dato de píxel sin cargarlo; el modelo JSON DICOM (PS3.18 F.2); multipart/related, que es la forma en que DICOMweb mueve varios objetos en una solicitud; y un archivo con un índice, que es lo que QIDO y WADO responden.
Source: github.com/biodream-llc/perfuse/internal/dicomweb
26.33 direct
El paquete direct es el mensajería segura Direct como un sistema de borde: un mensaje a una dirección Direct se firma con el certificado de esta dirección y se cifra para el destinatario, como S/MIME, y se envía a un HISP por SMTP; un mensaje recibido del HISP (por SMTP o recopilado sobre IMAP) se descifra y se verifica su firma contra los anclajes de confianza antes de que nada lo lea.
Sigue la Declaración de aplicabilidad del proyecto Direct para el transporte de salud seguro 1.2 y la especificación XDM (IHE ITI TF-1 Anexo 15) para documentos con metadatos. No es un HISP: la acreditación del HISP, su política de certificado y su directorio permanecen con el HISP.
Source: github.com/biodream-llc/perfuse/internal/direct
26.34 drug
El paquete drug maneja los códigos y cantidades que identifican un medicamento.
Dividido de los estándares de farmacia que los llevan, porque el mismo código de medicamento aparece en una reclamación NCPDP, una prescripción SCRIPT, un segmento HL7 RXE y una solicitud de medicamento FHIR, y una conversión que vive en uno de esos lugares es una conversión que los otros tres no necesitan.
Source: github.com/biodream-llc/perfuse/internal/drug
26.35 dsdr
El paquete dsdr firma y verifica documentos CDA como describe la Guía de implementación de HL7 para CDA Release 2: Firmas digitales y delegación de derechos, Release 1 (octubre de 2014) - el estándar de firma electrónica que HIPAA adopta para adjuntos de reclamaciones en CMS-0053-F, 45 CFR 162.2002(e), con cumplimiento a partir del 26 de mayo de 2028.
Lo que pide la guía y dónde se hace aquí:
- El resumen es sobre todo el documento, omitiendo cada elemento legalAuthenticator y authenticator (sección 3.1.2), para que los firmantes posteriores no rompan las firmas anteriores. Expresado en la firma como una transformación de filtro XPath 2.0, que cualquier implementación de XML-DSig puede evaluar. - La firma es XAdES-X-L (3.1.1, ESMD-1 a ESMD-6): el certificado de firma, la hora de firma UTC, un rol de la taxonomía de proveedores NUCC, un propósito de firma de ASTM E1762, la ruta de certificación en CertificateValues y una respuesta OCSP o CRL en RevocationValues, con las marcas de tiempo X-L en las que se basa. - Se lleva en base64 en sdtc:signatureText en el participante que firmó, con una miniatura de texto plano (3.3, ESMD-7 a ESMD-16), dentro de digitalSignature/authorizedSigner.
Donde la guía es silenciosa, las opciones tomadas se nombran: no proporciona un espacio de nombres para sus propios elementos (digitalSignature, authorizedSigner, SignaturePurpose), por lo que se colocan en urn:hl7-org:sdtc; y el propósito también se expresa como una indicación de tipo de compromiso XAdES con el OID ASTM, que las herramientas XAdES leen y el elemento de la guía no.
No se produce la delegación de derechos (un firmante delegado con una aserción SAML); una firma que lleva uno se informa como tal.
Source: github.com/biodream-llc/perfuse/internal/dsdr
26.36 e4x
El paquete e4x hace que los scripts de JavaScript de Mirth Connect se ejecuten sin cambios.
Los scripts de Mirth están escritos para Rhino, que implementa E4X: XML es un tipo nativo con su propia sintaxis. El motor aquí es goja, que no lo hace, y la brecha no es solo de tiempo de ejecución.
Tres piezas de E4X son *sintaxis*, por lo que el analizador de goja rechaza el archivo directamente antes de que cualquier modelo de objeto pueda ayudar: for each (var seg in msg..OBX) { } // for-each, y el operador descendiente var z = <ZZZ><ZZZ.1>valor</ZZZ.1></ZZZ>; // un literal XML var id = node.@root; // una referencia de atributo
Este archivo reescribe esos en ECMAScript ordinario que goja analiza, dejando la semántica al objeto de tiempo de ejecución en xml.go. Todo lo demás sobre E4X - acceso a propiedades por nombre de elemento, indexación de repeticiones, asignación, toString - ya es sintaxis de JavaScript válida y no necesita reescritura.
La reescritura se realiza con un analizador léxico real en lugar de expresiones regulares, porque la alternativa corrompe los scripts que funcionan. Un patrón para el operador descendiente encuentra uno dentro de "http://example.org", un patrón para literales XML encuentra uno en "a < b && c > d", y un script que cambia silenciosamente de significado es mucho peor que uno que se niega a cargar.
Source: github.com/biodream-llc/perfuse/internal/e4x
26.37 edifact
El paquete edifact lee y escribe intercambios UN/EDIFACT (ISO 9735): la cadena de servicio UNA, el sobre de intercambio UNB/UNZ, grupos funcionales UNG/UNE opcionales y mensajes UNH/UNT, con el carácter de lanzamiento y, para la versión de sintaxis 4, el separador de repetición.
Verifica los recuentos y referencias del sobre, dirige elementos de datos para filtros, divide un intercambio en uno por mensaje y construye un reconocimiento de CONTRL.
Lee cualquier tipo de mensaje (ORDERS, INVOIC, IFTSTA, MEDRPT...); no mantiene los directorios que indican qué segmentos puede contener un tipo de mensaje, por lo que verifica la estructura, no el contenido.
Source: github.com/biodream-llc/perfuse/internal/edifact
26.38 salida
El paquete de salida decide si este motor puede conectarse hacia afuera a una dirección.
Existe porque un destino puede apuntar a cualquier lugar, y una combinación lo convierte en una forma de leer la red interna: un destino SOAP con un transformador de respuesta devuelve la respuesta del receptor a un script, por lo que un editor puede crear un canal que recupere una URL interna y escriba el cuerpo en un registro o un mensaje.
Demostrado en lugar de teorizado: un destino SOAP dirigido a un servidor HTTP local puso el cuerpo de la respuesta del servidor en una línea de registro de canal.
La respuesta proporcional no es prohibir las conexiones salientes - entregar mensajes a puntos de conexión configurados es el propósito completo del software, y un editor está destinado a configurarlos. Es rechazar las direcciones específicas que nunca son un destino de mensaje y siempre son un almacén de credenciales.
Source: github.com/biodream-llc/perfuse/internal/egress
26.39 motor
El paquete motor ejecuta canales configurados.
Un canal es una fuente, un filtro y un conjunto de destinos. El trabajo del tiempo de ejecución es ser honesto sobre lo que sucedió con cada mensaje: un reconocimiento debe significar lo que dice la configuración, un mensaje que se filtra debe ser distinguible de uno que se entregó, y un mensaje que no se pudo entregar en ningún lugar no debe informarse como aceptado.
Source: github.com/biodream-llc/perfuse/internal/engine
26.40 epa
El paquete epa es la autorización electrónica previa de NCPDP SCRIPT, del lado del pagador: PAInitiationRequest, PARequest, PAAppealRequest y PACancelRequest de entrada, y sus respuestas de salida.
La propuesta CMS-0062-P sugiere que los pagadores afectados lo admitan a partir del 1 de octubre de 2027, y Medicare Part D ya lo requiere. Una solicitud se decide de la misma manera que toda autorización previa en este servidor: se convierte en una solicitud de PAS de Da Vinci (un reclamo, la solicitud de medicación y las respuestas del prescriptor como una respuesta de cuestionario) y va al mismo archivo de reglas, servicio de decisión y cola de revisión. El formulario dice qué medicamentos necesitan uno y qué preguntas hacer, del mismo cuestionario FHIR que Da Vinci DTR completa.
Los esquemas de NCPDP están licenciados. Los nombres y el orden de los elementos aquí siguen la guía de acompañamiento pública de Surescripts ePA y sus ejemplos; dos elementos que el material público nunca muestra (la razón dentro de Closed y dentro de Denied) siguen el patrón SCRIPT utilizado en otros lugares y están nombrados en el manual para que un pagador pueda verificarlos contra su propia copia del esquema.
Source: github.com/biodream-llc/perfuse/internal/epa
26.41 eprescribe
El paquete eprescribe lee y escribe NCPDP SCRIPT, el estándar en el que viajan las recetas.
Nombrado por lo que hace en lugar de por el estándar, porque internal/script ya es el motor de JavaScript y dos paquetes llamados script en un árbol es un error esperando ser hecho en una línea de importación.
SCRIPT es XML, a diferencia del Estándar de Telecomunicación que transporta el reclamo resultante. Un prescriptor envía un NEWRX a una farmacia; la farmacia envía de vuelta RXFILL para decir qué sucedió, REFREQ para pedir un rellenado, o RXCHG para pedir un cambio. Cada uno de ellos es un mensaje aquí.
Donde está el peligro
Dos campos en una receta son peligrosos cuando se malinterpretan en lugar de cuando faltan.
Las sustituciones son las primeras. Un cero significa que la sustitución está permitida y un uno significa dispensar como escrito. Lee al revés para cualquiera que espere una bandera donde uno signifique sí, y obtenerlo mal dispensa un genérico donde el prescriptor requirió la marca - o se niega a una sustitución que estaba permitida, que es el mismo error que cuesta al paciente dinero en lugar de eso. Entonces, este paquete se niega a adivinar: un elemento de sustituciones ausente es un error en lugar de un valor predeterminado, porque ambos valores predeterminados son incorrectos.
La cuenta de rellenado es la segunda. Una receta de la lista II no se puede rellenar en absoluto, y un sistema que transporta una cuenta de rellenado a través de una plantilla no controlada produce una receta con aspecto válido que una farmacia debe rechazar.
Lo que no ha sido verificado
La estructura del mensaje sigue los nombres de elementos SCRIPT publicados. Ningún mensaje de este paquete ha sido enviado a través de una red de enrutamiento real como Surescripts, y la certificación por uno es un proceso comercial en lugar de técnico. Esto analiza y construye el formato; no afirma la certificación.
Source: github.com/biodream-llc/perfuse/internal/eprescribe
26.42 equivalente
El paquete equiv prueba que dos motores están de acuerdo, o indica exactamente dónde no lo están.
Esta es la respuesta a la única pregunta que en realidad bloquea una migración. Un equipo de interfaz no rechaza un nuevo motor porque le falten características; lo rechazan porque no pueden estar seguros de que hará lo mismo con el tráfico que ya tienen, y estar equivocado significa que los resultados de los laboratorios dejarán de llegar a los médicos. Ninguna cantidad de comparación de características responde a eso. La evidencia sí.
El uso previsto es una ejecución paralela. Ambos motores reciben los mismos mensajes de producción; el motor existente sigue entregando y Perfuse entrega a nadie, lo que es lo que ya son los canales de sombra. Después de dos semanas, se comparan las salidas aquí, y la conversación cambia de "confíe en esto" a "aquí están los tres mensajes de cuatrocientos mil donde difirimos, y por qué".
El agrupamiento es toda la característica
Una comparación ingenua de 400.000 mensajes produce 400.000 resultados y se cierra sin leer. Casi todas las diferencias reales tienen una causa: un formato de fecha, un código de instalación, un paso de transformación que nadie llevó a cabo. Por lo tanto, las diferencias se agrupan por causa - la ruta en el mensaje y el tipo de diferencia - y se informan una vez con un recuento. Tres mil diferencias de una causa son un resultado, no tres mil. Esa sola decisión es lo que hace que el informe sea legible, y un informe legible es la diferencia entre una migración que procede y una que se estanca.
Los valores se retienen por defecto
El tráfico real es datos de pacientes, por lo que un informe de comparación es un lugar donde la información de identificación personal (PHI) podría filtrarse en un ticket, un correo electrónico o una diapositiva. Por defecto, este informe informa rutas, tipos y recuentos, nunca valores. Eso es suficiente para encontrar la causa: "MSH-7 difiere en 3.000 de 3.000 mensajes" le dice a un analista que es el formato de fecha sin mostrar una fecha.
Los valores se pueden habilitar deliberadamente, porque eventualmente alguien necesita ver uno, y pretender lo contrario solo los empujaría a una herramienta peor. Es una opción explícita y el informe dice en su propia cara que contiene contenido de mensaje.
Source: github.com/biodream-llc/perfuse/internal/equiv
26.43 expr
El enlace HL7 v2.
Esta es la lógica que solía vivir en línea en eval.go. No ha cambiado en comportamiento - deliberadamente, porque el filtro v2 es lo que ejecuta cada canal HL7 y el punto de la gramática genérica era agregar formatos, no alterar este.
Source: github.com/biodream-llc/perfuse/internal/expr
26.44 fdlimit
El paquete fdlimit descubre cuántos descriptores de archivos puede abrir este proceso y aumenta el límite si puede.
Esto existe debido a la forma de la falla que previene. Cada conexión de entrada es un descriptor, y también lo es cada entrega de salida en vuelo. Un receptor que ha dejado de responder mantiene un descriptor durante todo el presupuesto de reintentos - con los valores predeterminados, cinco intentos con un tiempo de espera de treinta segundos más quince segundos de retroceso, por lo que casi tres minutos. Unas pocas conexiones de cientos contra un receptor colgado son, por lo tanto, unos pocas conexiones de cientos de descriptores mantenidos durante minutos.
Perderlos no produce un error ordenado en el lugar que se agotó. Produce "demasiados archivos abiertos" en algún lugar completamente diferente: la base de datos no puede abrir su diario, la interfaz web deja de aceptar, un archivo de registro no se puede escribir. El motor parece haberse roto en tres lugares no relacionados al mismo tiempo, y la causa real es un sistema de socios que se calló. Esa es la llamada de soporte que este paquete existe para evitar.
Dos cosas suceden al inicio. El límite suave se eleva al límite duro, lo que generalmente elimina el problema de inmediato - el límite suave predeterminado suele ser de unos pocos cientos, mientras que el límite duro es de decenas de miles. Luego, se informa lo que está disponible, para que el motor pueda limitar su propia concurrencia dentro de él en lugar de descubrirla al golpearla.
Source: github.com/biodream-llc/perfuse/internal/fdlimit
26.45 fhirpath
El paquete fhirpath evalúa FHIRPath (HL7 FHIRPath N1, 2.0.0) sobre recursos FHIR mantenidos como JSON decodificado.
No tiene modelo FHIR: el tipo de un elemento se conoce cuando el JSON de FHIR lo dice (un sufijo de elemento de elección, como valueQuantity, un resourceType de recurso) o se infiere del valor JSON de lo contrario. Eso cubre lo que SQL en vistas FHIR y medidas de calidad libres de CQL necesitan; la navegación que depende del modelo (el tipo propio de un primitivo cuando no es una elección) se responde a partir de la forma del valor.
Source: github.com/biodream-llc/perfuse/internal/fhirpath
26.46 fhirserver
El paquete fhirserver implementa un servidor FHIR REST.
Esto existe porque el extremo útil de un mapeador v2-a-FHIR es algún lugar para poner el resultado. Ser capaz de convertir un mensaje es una demostración; ser capaz de convertirlo, almacenarlo y luego encontrar al paciente de nuevo por identificador es un producto.
Es un servidor FHIR real en las partes que importan para eso: una declaración de capacidad, crear, leer, actualizar, eliminar, buscar en los parámetros que la gente realmente usa y paquetes de transacción aplicados atómicamente. No es un servidor FHIR completo - no hay historia, no hay búsqueda encadenada, no _include - y lo dice en su propia declaración de capacidad en lugar de dejar que un cliente descubra las brechas.
Source: github.com/biodream-llc/perfuse/internal/fhirserver
26.47 formulary
El paquete formulary es un formulario de un pagador de medicamentos: qué medicamentos cubre cada plan, en qué nivel y con qué límites (autorización previa, terapia de paso, límites de cantidad), y qué paga el miembro.
Un archivo es la única fuente para todo lo que responde a una pregunta sobre un medicamento: la API de formularios de medicamentos de Da Vinci US (publicada desde ella como FHIR), respuestas de beneficios de recetas en tiempo real de NCPDP y autorización electrónica de priorización de NCPDP SCRIPT (si un medicamento la necesita y qué preguntas hacer). Un formulario que viviera en tres lugares respondería la misma pregunta de tres maneras.
Source: github.com/biodream-llc/perfuse/internal/formulary
26.48 encuadernación
El encuadre de paquetes lee y escribe mensajes en una secuencia de bytes que no es MLLP.
Por qué esto existe por separado de mllp
MLLP es un encuadre: 0x0B antes, 0x1C 0x0D después. Es lo que utiliza HL7 v2 sobre TCP y Perfuse siempre lo ha hablado.
Una gran cantidad de equipos no lo hace. Un analizador de laboratorio, una báscula, un monitor de cabecera, un sistema de facturación antiguo: cada uno tiende a tener su propia convención, y las convenciones son todas variaciones simples de las mismas tres ideas: un delimitador en algún lugar, una longitud de registro fija o una longitud escrita al frente de la carga útil.
El conector TCP de Mirth cubre esto y Perfuse no tenía nada, por lo que un sitio con una alimentación de socket que no era HL7 no podía usarlo en absoluto.
El error que este paquete está escrito para evitar
Leer desde una secuencia y adivinar dónde termina un mensaje es todo el problema. Adivinar corto y un mensaje se corta por la mitad; adivinar largo y dos mensajes llegan como uno. Ambos producen una salida que a menudo parece plausible, lo que es peor que un error, por lo que cada modo aquí es explícito sobre qué termina un mensaje y ninguno de ellos recurre a otro.
Un mensaje sin encuadre en absoluto es una configuración legítima: algunos dispositivos abren una conexión, envían un mensaje y cierran - pero no se puede combinar con nada más y no se puede utilizar en una conexión que permanece abierta, por lo que se nombra y se valida en lugar de tratarlo como predeterminado.
Source: github.com/biodream-llc/perfuse/internal/framing
26.49 ftpconn
El paquete ftpconn carga archivos a través de FTP y FTPS utilizando solo la biblioteca estándar.
FTP es antiguo, verboso y no es un buen protocolo. También es la forma en que un gran número de sistemas hospitalarios aceptan archivos, generalmente porque el extremo remoto es un analizador de laboratorio o un servicio de buró cuyo software no ha sido modificado desde que funcionó. Negarse a admitirlo no hace que esos sistemas sean modernos; hace que Perfuse sea inutilizable en esos sitios.
Implementado aquí en lugar de tomado como una dependencia por la misma razón que S3: la lista de materiales de ocho dependencias es un argumento real con quien revise el software antes de que un hospital lo instale. El canal de control de FTP es texto orientado a línea sobre TCP, por lo que esto es un par de cientos de líneas de red y crypto/tls.
Qué se admite y qué se rechaza
Solo modo pasivo. El modo activo requiere que el servidor abra una conexión de regreso al cliente, lo que bloquea cada firewall de hospital construido este siglo, y ofrecerlo significaría que las personas lo configurarían y luego depurarían un firewall.
Se admite FTPS explícito (AUTH TLS) y es el predeterminado cuando se solicita TLS. También se admite FTPS implícito en el puerto 990 porque algunos analizados solo hablan eso.
Se admite FTP plano y advierte. Envia la contraseña en texto plano, y un sitio que tenga que usarlo debe ser informado una vez en lugar de dejar que lo descubra desde una captura de paquetes.
Source: github.com/biodream-llc/perfuse/internal/ftpconn
26.50 gcp
El paquete gcp habla con Google Cloud Pub/Sub y Cloud Storage a través de sus API de REST, con OAuth 2.0 de Google realizado aquí: una clave de cuenta de servicio firma un JWT (RS256, crypto/rsa) que se intercambia por un token de acceso, o, en Google Cloud mismo, el servidor de metadatos entrega uno.
Ninguna SDK de Google.
Los emuladores (el emulador de Pub/Sub, fake-gcs-server) no toman credenciales: establezca Endpoint y deje las credenciales vacías.
Source: github.com/biodream-llc/perfuse/internal/gcp
26.51 generate
El paquete generate crea mensajes sintéticos HL7 v2.
Dos usos. Alguien que intenta Perfuse necesita tráfico para mirar, y una prueba de canal necesita un accesorio que sea lo suficientemente realista como para ejercitar la cosa que se está probando. Un generador es una extensión de pago en Mirth, lo que te dice que la gente lo quiere.
Todo aquí es inventado. Los nombres provienen de una lista fija de nombres obviamente ficticios, los identificadores son secuenciales dentro de una ejecución, y las direcciones son inventadas. Eso no es un descargo de responsabilidad, es el diseño: un generador que produjera nombres que parezcan reales eventualmente pondría uno en un accesorio de prueba, un informe de errores y luego un repositorio.
El realismo se gasta donde importa para probar y en ninguna otra parte. Las marcas de tiempo llevan desplazamientos, los campos repetidos realmente se repiten, un nombre a veces tiene una inicial del segundo nombre y a veces no, y aproximadamente uno de cada doce mensajes es intencionalmente incómodo: un campo opcional que falta, un separador inusual pero legal, un componente vacío. Alimentar un canal con nada más que mensajes perfectamente formados te dice muy poco.
Source: github.com/biodream-llc/perfuse/internal/generate
26.52 hl7dict
El paquete hl7dict nombra los campos de los segmentos HL7 v2.
Esto existe para una sola pantalla: un visor de mensajes que puede decirte que PID-5.1 es el apellido del paciente en lugar de dejarte que cuentes los caracteres de cuidado, y cualquiera que haya depurado una interfaz ha hecho ese recuento manual, y es la fuente más común de errores de desplazamiento que colocan un valor en el campo incorrecto.
No es todo el estándar. HL7 v2 tiene cientos de segmentos y miles de campos, y los que están aquí son los que aparecen en el tráfico ADT, ORU, ORM y SIU. Un campo desconocido se informa como desconocido en lugar de adivinado.
Source: github.com/biodream-llc/perfuse/internal/hl7dict
26.53 hl7v3
El paquete hl7v3 lee mensajes HL7 versión 3.
La versión 3 es algo diferente de la versión 2 con el mismo nombre. La versión 2 es un flujo de segmentos delimitados por tuberías cuyo significado es posicional; la versión 3 es XML generada a partir de un modelo de información de referencia, donde el significado se lleva por los nombres de los elementos y por un pequeño conjunto de tipos de datos que se repiten en todas partes. Casi nada se transfiere entre los dos más allá del vocabulario clínico.
En la práctica, v3 llega en una de dos formas. O es un documento CDA, que es derivado de v3 y manejado por el paquete cda, o es una transacción IHE: PIX y PDQ v3 en particular, que es la forma en que muchos hospitales resuelven identificadores de pacientes entre sistemas. Este paquete se dirige al segundo.
Por qué los tipos de datos vienen primero
La cosa más importante sobre v3 es que un elemento puede estar presente y ser explícitamente nulo, con una razón dada. Una fecha de nacimiento que lleva nullFlavor="ASKU" significa que al paciente se le preguntó y se negó a responder. Una que lleva nullFlavor="NAV" significa que el valor existe pero no está disponible en este momento, así que preguntar de nuevo más tarde puede funcionar. Una que simplemente está ausente significa que nadie grabó nada en absoluto.
Colapsar esas en una cadena vacía es el error que este paquete está diseñado para prevenir. No es una pérdida cosmética: un sistema descendente que recibe una fecha de nacimiento vacía donde la fuente dijo "el paciente se negó" generalmente la tratará como datos que faltan para perseguir, y una coincidencia demográfica que debería haberse dejado sin resolver se resuelve mediante una suposición humana. Así que cada tipo de datos aquí distingue ausente, nulo-con-razón y presente.
Source: github.com/biodream-llc/perfuse/internal/hl7v3
26.54 hl7xml
El paquete hl7xml convierte mensajes HL7 v2 a y desde la forma XML que utiliza Mirth Connect, y de regreso a la codificación ER7.
La forma no es una elección de diseño. Los scripts de Mirth están escritos contra la salida de su propio serializador ER7-XML, así que contienen expresiones como
msg['PID']['PID.5']['PID.5.1'].toString()
y la promesa de que un script existente se ejecuta sin cambios solo se mantiene si el árbol que recorre tiene exactamente los mismos nombres de elementos, anidación y comportamiento de repetición. Cada decisión a continuación es, por lo tanto, "¿qué hace Mirth?", no "¿qué sería más ordenado?".
Los dos que más importan:
Cada campo está envuelto en elementos de componentes numerados incluso cuando el campo tiene solo un componente, así que PID-5 con el valor "Doe" todavía produce <PID.5><PID.5.1>Doe</PID.5.1>. Los scripts indexan directamente a .1 constantemente y un árbol que colocara el texto en el elemento del campo rompería casi todos ellos.
MSH mantiene los números de campo HL7 estándar, donde MSH-1 es el separador de campo y MSH-9 es el tipo de mensaje. El analizador en internal/hl7 indexa MSH con un desplazamiento porque el separador ocupa una posición que ningún delimitador divide, y ese desplazamiento se detiene aquí: un script que dice MSH.9 significa el tipo de mensaje.
Source: github.com/biodream-llc/perfuse/internal/hl7xml
26.55 ibmmq
El paquete ibmmq envía a y lee desde colas IBM MQ a través de la API de mensajería REST del administrador de colas (mqweb), que IBM MQ 9.1.5 y posterior envían en cada plataforma, z/OS incluido.
No necesita una biblioteca de cliente MQ: el protocolo MQI nativo es de IBM y no está documentado, y la API de REST es la forma admitida de llegar a MQ sin uno. (IBM MQ también habla AMQP 1.0 en un canal AMQP; el conector amqp de Perfuse cubre esa ruta.)
La lectura es al menos una vez: el primer mensaje se examina, se le da al llamador y se elimina por su ID de mensaje solo una vez que el llamador lo ha manejado. Un mensaje que no se pudo manejar permanece en la cola, el primero en la línea.
Source: github.com/biodream-llc/perfuse/internal/ibmmq
26.56 identidad
Encontrar un mensaje por lo que sabes sobre él, en cualquier formato en que haya llegado.
La búsqueda de contenido existente coincide con los metadatos, ofrece una coincidencia de subcadena contra la carga útil y puede evaluar una expresión de filtro - pero la expresión se evalúa solo contra HL7 v2, y cada expresión requiere conocer la ruta. Así, encontrar el tráfico de un paciente significaba saber que la identidad vive en PID-3 en una admisión HL7 v2, en Patient.identifier en un recurso FHIR, en (0010,0020) en una instancia DICOM y en NM109 de un bucle NM1 * IL en una reclamación 837. Cuatro formatos, cuatro vocabularios, y durante un incidente el mensaje que se busca puede estar en cualquiera de ellos.
Este paquete asigna un pequeño conjunto de conceptos - quién es el mensaje, qué visita, qué estudio, qué reclamación - al lugar donde cada formato los mantiene, para que una búsqueda pueda ser por "MRN 12345" en lugar de por una ruta. Los conceptos son deliberadamente pocos: son las cosas que las personas realmente tienen en la mano cuando necesitan encontrar un mensaje, que es un número de una llamada telefónica o un nombre de una queja.
Por qué la extracción sucede una vez, en el momento del registro
La alternativa es analizar las cargas útiles en el momento de la consulta, lo que hace la búsqueda de expresiones. Eso funciona y es honesto sobre ser una exploración limitada, pero no puede responder "cada mensaje sobre este paciente en cuatro fuentes y dos meses" sin leer cada fila. Extraer una vez en una tabla indexada convierte eso en una búsqueda de índice.
Esto es una escalada de privacidad, y es opcional
Los nombres y identificadores de los pacientes actualmente se encuentran dentro de una columna de carga útil opaca. Indexarlos los hace consultables, lo cual es el punto, y también los hace enumerables, lo que es un cambio real en la exposición: una base de datos que solo podía ser buscada ahora puede ser preguntada "listar cada paciente". Esa es una decisión para el sitio, no para este paquete, así que la extracción es conmutable y cada búsqueda a través de ella se registra en el registro de auditoría de PHI.
Source: github.com/biodream-llc/perfuse/internal/identity
26.57 iis
El paquete iis habla el transporte que cada registro de inmunización de EE. UU. comparte: el servicio web SOAP de IIS de los CDC.
En 2011, un panel de expertos convocado por los CDC y la Asociación de Registros de Inmunización Americanos eligió una interfaz SOAP para enviar mensajes HL7 v2 a los Sistemas de Información de Inmunización, y los registros los adoptaron. El WSDL es pequeño - dos operaciones, prueba de conectividad y envío de mensaje único - y el mensaje HL7 (un VXU para informar un disparo, un QBP ^ Q11 para preguntar por la historia de un paciente) viaja como texto escapado dentro del cuerpo SOAP. La respuesta regresa de la misma manera: un ACK, o un RSP ^ K11 que lleva la historia y el pronóstico.
El destino SOAP genérico podría estar configurado para hacer esto, pero cada sitio tendría que escribir el mismo sobre, obtener el espacio de nombres correcto, colocar las credenciales en el cuerpo en lugar de en la autenticación HTTP, y luego leer el reconocimiento HL7 de la respuesta para aprender si el registro mantuvo el registro. Un registro responde a un disparo rechazado con HTTP 200 y un AR dentro; tratar el 200 como éxito es cómo las inmunizaciones fallan silenciosamente al ser informadas. Así que este paquete hace el sobre de los CDC, y juzga el reconocimiento, una vez.
Lo que no hace: las credenciales de aplicación del WSDL son las credenciales de aplicación del registro y se envían como dice el WSDL, en el cuerpo. El TLS mutuo, donde un registro lo requiere, es la configuración de TLS del destino como para cualquier llamada HTTPS. Nada de esto es específico de un estado; donde los estados difieren (identificador de instalación requerido o no, SOAP 1.1 aceptado o no, advertencias enviadas como AE) la configuración del destino lo dice.
Source: github.com/biodream-llc/perfuse/internal/iis
26.58 kafka
Kafka, como cliente en lugar de algo para reemplazar.
Un sistema de salud que tiene una columna vertebral de eventos tiene una de Kafka, y los sistemas construidos en la última década publican en ella en lugar de en un broker JMS. Así que este conector existe para hacer que Perfuse sea el borde consciente de la salud de la plataforma de eventos de alguien más: lee mensajes clínicos de temas y los publica en temas, y deliberadamente no intenta ser una plataforma de procesamiento de flujo. La ventana, las uniones, el procesamiento de flujo y los registros de esquemas son el ecosistema de Kafka y se quedan allí.
Por qué una dependencia, cuando STOMP y MLLP se escribieron a mano
La regla establecida en este repositorio es que una dependencia tiene que comprar capacidad en lugar de conveniencia, y debe ser puro Go para que un binario estático todavía se compile en cada objetivo. franz-go es puro Go y no lleva cgo, lo que se verificó contra los seis objetivos.
El protocolo de Kafka no es el de STOMP. STOMP es un protocolo de texto que cabe en unas pocas centenas de líneas. Kafka es un protocolo binario con versión a través de aproximadamente setenta tipos de solicitud, y la parte que más importa aquí - los grupos de consumidores - es un protocolo de coordinación distribuida con fases de unión, sincronización, latido y compromiso de desplazamiento y un baile de reequilibrio entre ellos. Obtener eso sutilmente mal no produce un fallo obvio: produce una partición que nadie está leyendo, o dos consumidores leyendo la misma. En una fuente clínica, esas son un resultado de laboratorio perdido y una orden duplicada. Esa es la capacidad, y no vale la pena hacerla a mano para ahorrar una dependencia.
Source: github.com/biodream-llc/perfuse/internal/kafka
26.59 ldap
El paquete ldap habla suficiente LDAP para autenticar a una persona y leer sus grupos.
Los hospitales ejecutan Active Directory casi universalmente, y AD habla LDAP. OpenID Connect es la mejor respuesta donde es accesible - consulte internal/oidc, y la nota allí sobre Mirth que no tiene inicio de sesión único a ningún precio - pero un motor de integración a menudo se encuentra en una VLAN aislada donde un navegador no puede llegar a un proveedor de identidad en absoluto. Es para eso que esto existe.
BER personalizado en lugar de una dependencia, por la misma razón que DICOM y STOMP: la codificación es pequeña, y lo que llega a este socket decide si alguien puede entrar en un sistema que mueve datos de pacientes.
encoding/asn1 en la biblioteca estándar no es utilizable aquí. Se dirige a DER y rechaza las etiquetas contextuales y la etiquetado implícito que LDAP utiliza en todas partes, por lo que las partes que importan necesitarían ser personalizadas de todos modos.
Source: github.com/biodream-llc/perfuse/internal/ldap
26.60 buzón
El paquete mailbox lee correo de servidores IMAP4rev1 (RFC 3501) y POP3 (RFC 1939), iniciando sesión con una contraseña o con OAuth 2.0 (SASL XOAUTH2, como Microsoft 365 y Gmail requieren), y desmonta los mensajes (RFC 5322 y MIME) para que un archivo adjunto o el cuerpo de texto pueda convertirse en un mensaje en un canal.
Solo lo que una fuente de entrada necesita: seleccionar un buzón, encontrar correo no leído, recuperarlo completo, luego mover, marcar o eliminarlo. Solo biblioteca estándar; TLS es crypto/tls.
Source: github.com/biodream-llc/perfuse/internal/mailbox
26.61 asignador
El paquete asignador sugiere asignaciones de campos entre sistemas con puntuaciones de confianza.
Este es un motor de mapeo inteligente basado en reglas. No LLM, no llamadas de red, no dependencias externas. Se ejecuta sin conexión en el mismo binario y nunca envía datos de pacientes a ninguna parte.
El motor combina múltiples señales - coincidencia de cadenas difusas en los nombres de los campos, reconocimiento de patrones de identificadores de atención médica, búsquedas en sistemas de códigos y datos de aceptación histórica - en una sola puntuación de confianza por sugerencia. Cuando la confianza está por debajo de un umbral configurable, el asignador se abstiene en lugar de adivinar mal, porque un mapeo incorrecto en la atención médica puede enviar datos al campo incorrecto y eso es un defecto de seguridad del paciente.
La abstención es la decisión de diseño central. Un sistema que siempre produce una respuesta entrena a los usuarios a dejar de verificar, y la única vez que está mal nadie se da cuenta hasta que un resultado de laboratorio aterriza en un campo de notas. Un sistema que dice "No lo sé" fuerza una decisión humana exactamente cuando se necesita un humano.
Source: github.com/biodream-llc/perfuse/internal/mapper
26.62 medida
El paquete medida evalúa Medidas FHIR (medidas de calidad digitales) cuyos criterios de población son FHIRPath, y escribe el resultado como un Informe de Medida FHIR: resumen, individual o lista de sujetos, con estratificadores y una puntuación de proporción.
Cada criterio se evalúa una vez por paciente, con el enfoque en un Paquete del registro del paciente (su compartimento) y estas variables: %sujeto (el Paciente), %periodoInicio y %periodoFin (el período de medición, como DateTimes) y %"Período de medición" (lo mismo que un Período). Los criterios en CQL se rechazan con la razón: Perfuse no tiene un motor CQL.
Source: github.com/biodream-llc/perfuse/internal/measure
26.63 métricas
El paquete métricas recopila lo que el motor está haciendo y mantiene suficiente historial para dibujarlo.
Un motor de interfaz se vigila, no solo se ejecuta. Cuando una sala dice que los resultados dejaron de llegar hace veinte minutos, la pregunta es qué cambió hace veinte minutos, y eso no puede ser respondido por los contadores actuales solos. Entonces, esto mantiene series en lugar de totales, en memoria, a una resolución fija.
Tres decisiones dan forma a esto.
La latencia se mantiene como un histograma en lugar de una media. Un tiempo de entrega promedio es casi inútil en este dominio: el número que importa es si los pocos por ciento más lentos de los mensajes son lo suficientemente lentos como para violar un compromiso de respuesta, y una media oculta exactamente eso. Los cubos son exponenciales y fijos en el momento de la compilación, por lo que registrar una duración es una comparación y un incremento sin asignación.
Las series son búferes de anillo acotados, dimensionados cuando se crea el colector. Nada aquí crece sin límite, porque un motor de interfaz se ejecuta durante meses a la vez y un sistema de métricas que necesita reiniciarse es peor que ninguno.
La cardinalidad está limitada. Un valor de etiqueta tomado de los datos del mensaje - una aplicación de envío, por ejemplo - puede tomar miles de valores si alguien configura incorrectamente una fuente, y las etiquetas no limitadas son la forma estándar en que la supervisión derriba lo que supervisa. Más allá del límite, los valores se colapsan en un cubo "otros" y el límite se informa él mismo.
Source: github.com/biodream-llc/perfuse/internal/metrics
26.64 mirth
Análisis de Java para canales Mirth importados.
Por qué esto existe
Mirth ejecuta Rhino en la JVM, por lo que un script Mirth puede llamar a cualquier Java en la ruta de clases, incluyendo las clases del servidor Mirth. Perfuse ejecuta goja y no tiene tiempo de ejecución de Java. Rechaza esas llamadas con un error que nombra la clase - pero ese rechazo ocurre cuando se ejecuta el script, lo que para un conector de fuente significa cuando llega el primer mensaje real.
Entonces, una migración solía ir: la importación tiene éxito en silencio, el canal se implementa y el primer mensaje desde el cable falla. Una migración que parece limpia y se rompe en el primer tráfico es peor que una que se niega en la puerta, porque la persona que lo hizo ya le ha dicho a sus colegas que funcionó.
Esto encuentra esas llamadas en el momento de la importación y dice qué hacer con cada una de ellas.
¿Por qué cuatro veredictos y no dos
"Usa Java" es un bit de información y el incorrecto, porque el Java que un canal Mirth real utiliza cae en grupos con cantidades de trabajo completamente diferentes. Aproximadamente la mitad de lo que los sitios escriben en realidad es SimpleDateFormat y HashMap, que JavaScript maneja de forma nativa. Parte de ello es la API de script documentada de Mirth, que Perfuse ya implementa con los mismos nombres. Un poco de ello quiere iniciar y detener canales, lo que Perfuse puede hacer a través de su API de REST, pero no expone a los scripts. Y parte de ello es un jar de proveedor que nunca se ejecutará aquí.
Informar sobre ellos como un solo número no dice nada a un migrador sobre si su tarde o su trimestre están en juego.
Source: github.com/biodream-llc/perfuse/internal/mirth
26.65 mirthlive
El paquete mirthlive encuentra los motores Mirth en ejecución que las pruebas de interoperabilidad se comunican.
Mirth Connect dejó de ser de código abierto en la versión 4.6 (marzo de 2025). Los sitios que lo dejan van a uno de tres lugares: Mirth 4.5.2, el último lanzamiento de código abierto, que siguen ejecutando; el Open Integration Engine, el fork alojado en Eclipse; o BridgeLink, el fork de Innovar, que numera sus lanzamientos por año (26.9.0). Un canal que Perfuse exporta debe cargarse en todos ellos, y un canal que cualquiera de ellos exporta debe importarse aquí, por lo que cada prueba en vivo se ejecuta una vez por motor que responde.
Los motores se encuentran mediante una solicitud, no se configuran: ./scripts/interop-up.sh los inicia en puertos fijos, y un motor que no se está ejecutando se informa como omitido en lugar de pasar silenciosamente. PERFUSE_MIRTH_ENGINES anula la lista, como pares nombre=URL separados por comas.
Solo las pruebas importan esto. Vive fuera de un archivo _test.go porque dos paquetes lo comparten.
Source: github.com/biodream-llc/perfuse/internal/mirth/mirthlive
26.66 mpi
El paquete mpi es un índice de pacientes maestros: toma registros de pacientes de cada sistema de origen (cada uno identificado por su autoridad de asignación y número), decide cuáles pertenecen a la misma persona y asigna a cada persona un identificador de empresa (EUID).
Ciertas coincidencias se vinculan por sí solas; las inciertas esperan en una cola de revisión para que una persona tome una decisión; cada enlace, desenlace, fusión y decisión se registra con quién lo hizo.
La coincidencia es determinista en primer lugar (un identificador de un sistema que el sitio confía en que sea único por persona, como un identificador nacional o un número de empresa) y probabilística en caso contrario: pesos de Fellegi-Sunter sobre nombre, fecha de nacimiento, sexo, código postal, teléfono y dirección, con Jaro-Winkler para los nombres. No decide nada clínico: dice qué registros son la misma persona, con sus razones, para que una persona lo confirme cuando no esté seguro.
Source: github.com/biodream-llc/perfuse/internal/mpi
26.67 mqtt
El paquete mqtt es un cliente MQTT 3.1.1: conectar (con TLS, un nombre de usuario y una contraseña, o un certificado de cliente), publicar a QoS 0 o 1, suscribirse y confirmar cada mensaje QoS 1 recibido solo una vez que el llamador dice que se ha manejado.
Escrito contra el estándar OASIS MQTT 3.1.1 en lugar de importado, como los demás protocolos de cable de Perfuse. 3.1.1 es la versión que habla cada broker: Mosquitto, EMQX, HiveMQ, VerneMQ, el plugin MQTT de RabbitMQ, el canal de telemetría de IBM MQ, AWS IoT Core y Azure Event Grid.
Entrega: un mensaje recibido a QoS 1 se confirma (PUBACK) solo cuando se llama a Ack. Un llamador que no puede manejar un mensaje cierra la conexión en su lugar; con una sesión persistente (CleanSession false) el broker envía el mensaje nuevamente, marcado como duplicado, cuando el cliente se vuelve a conectar. MQTT 3.1.1 no tiene un reconocimiento negativo, por lo que esta es la única forma de solicitar un mensaje nuevamente.
Source: github.com/biodream-llc/perfuse/internal/mqtt
26.68 msgstore
Encontrar un mensaje almacenado por lo que alguien sabe sobre él.
La búsqueda que una persona necesita en realidad es "aquí está un número de una llamada telefónica, muéstrame todos los mensajes sobre él". Antes de esto, significaba una coincidencia de subcadena sobre las cargas útiles, que es grep y lento, o una expresión de filtro, que requiere saber que la identidad vive en PID-3 en HL7 v2 y en algún otro lugar en los otros tres formatos en el mismo servidor.
La identidad se extrae una vez cuando se registra el mensaje y se indexa, por lo que esta es una búsqueda en lugar de un análisis. El costo se paga en la ruta de grabación, una vez por mensaje, en lugar de en cada búsqueda.
Source: github.com/biodream-llc/perfuse/internal/msgstore
26.69 natid
El paquete natid conoce los identificadores nacionales de pacientes y personas del Reino Unido y Europa: sus formatos, sus dígitos de verificación, el sistema de identificador que las guías FHIR de cada país los nombran, y cuánto de uno puede mostrarse.
Existe para el índice de pacientes. Un número nacional es la prueba más fuerte de que dos registros son una persona, y también la más fácil de equivocar: un dígito tecleado dos veces da un número que pertenece a otra persona, y una fuente envía el número de NHS bajo un OID mientras que otra lo envía bajo su URL de FHIR. Por lo tanto, cada identificador que el índice almacena pasa por aquí: se prueba su dígito de verificación (un dígito de verificación incorrecto nunca se usa para vincular), se elimina su espaciado y se escribe su sistema de una sola manera.
Qué se verifica y qué no se verifica se indica por esquema. Los algoritmos son públicos; las URIs del sistema de identificador son las del país donde una guía FHIR nacional las nombra (SystemVerified), y un valor predeterminado documentado de otra manera, que un sitio cambia a su valor nacional en Configuración → Índice de pacientes. Nada de esto decide nada clínico.
Source: github.com/biodream-llc/perfuse/internal/natid
26.70 ncpdp
Escribir un campo en una transmisión NCPDP.
Por qué una escritura rechaza una ruta ambigua
La lectura devuelve todos los valores que una ruta dirige, porque un filtro que pregunta si existe algún conflicto de DUR debe ver todos ellos. Una escritura no puede funcionar de esa manera. Si D7 dirige los códigos de producto de dos reclamaciones en una transmisión, establecer D7 no tiene un solo significado: escribir ambos cambiaría un medicamento que nadie solicitó, y escribir el primero silenciaría el segundo.
Por lo tanto, una escritura rechaza una ruta que dirige más de un campo, y el error nombra la forma calificada que sería inequívoca. Esta es la misma lógica que da a los pasos de transformación ninguna acción de eliminación: donde la respuesta honesta es que la configuración es ambigua, decirlo es mejor que elegir uno.
Source: github.com/biodream-llc/perfuse/internal/ncpdp
26.71 oidc
El paquete oidc verifica las identidades de OpenID Connect.
Escrito contra la biblioteca estándar en lugar de una dependencia de JWT. La autenticación es el lugar donde una dependencia no auditada es menos aceptable, la verificación de firma para los algoritmos que importan es de aproximadamente cien líneas de crypto/rsa y crypto/ecdsa, y Perfuse ya lleva suficiente carga como para que agregar más necesite una razón en lugar de un hábito.
El alcance es deliberadamente estrecho: verificar que un proveedor de identidad avaló a una persona, e informar lo que dijo sobre ellos. Todo lo demás - lo que pueden hacer, cuánto dura la sesión - se mantiene con Perfuse, porque un proveedor de identidad sabe quién es alguien y no qué están autorizados a hacer en un motor de integración.
Source: github.com/biodream-llc/perfuse/internal/oidc
26.72 parity
El paquete parity compara lo que produce Perfuse con lo que produjo el motor que se está reemplazando.
Por qué esto existe
Perfuse puede importar un canal de Mirth y puede rastrear lo que haría con un mensaje. Ninguno de esos dos responde a la pregunta que alguien realmente tiene antes de mover una fuente de alimentación en vivo, que es: ¿producirá la misma salida que la cosa que ha estado funcionando durante seis años?
Ninguna cantidad de calidad de diseño responde a eso. La única respuesta honesta es la evidencia, y la evidencia tiene que provenir de la propia salida del motor antiguo en el tráfico del sitio.
Por qué los pares tienen que ser suministrados
Esto no puede llegar a Mirth, y pretender lo contrario sería el peor tipo de función. Lo que se necesita son pares: un mensaje como llegó, y el mensaje que el motor antiguo produjo a partir de él. Ambos están en la propia tienda de mensajes de Mirth y se pueden exportar desde su navegador de mensajes, que es algo que un administrador ya sabe cómo hacer.
Esa restricción se establece en lugar de ocultarla. Una herramienta que afirmara verificar una migración sin haber visto nunca la salida del motor antiguo estaría verificando nada.
Por qué las diferencias se agrupan en lugar de listarse
Una ejecución de más de cincuenta mil mensajes que informa cuatro mil diferencias no ha dicho nada que se pueda actuar. La misma ejecución que informa que PID-8 difiere en cada mensaje porque el motor antiguo mapeó M a Masculino le ha dicho exactamente una cosa a decidir.
Por lo tanto, los hallazgos se agrupan por campo, con un recuento, un ejemplo y los pares de valores distintos vistos. Una decisión por hallazgo es la unidad que alguien puede trabajar.
Source: github.com/biodream-llc/perfuse/internal/parity
26.73 pas278
El paquete pas278 mapea entre la autorización previa de Da Vinci y la transacción X12 278.
Por qué esto existe
CMS-0057-F requiere que los pagadores expongan una API de autorización previa de FHIR a partir del 1 de enero de 2027. No reemplaza la transacción X12 278, y nunca lo haría: el sistema de gestión de utilidad de un pagador decide las autorizaciones y habla 278. Así que un pagador que cumple con la regla está traduciendo FHIR a 278 y viceversa, y un proveedor que envía 278 hoy enviará FHIR mañana a los mismos pagadores que aún aceptan ambos.
Esa traducción es la cosa que vale la pena poseer. Un motor que mantiene ambos modelos y no puede convertir entre ellos deja la parte más difícil del trabajo - y la parte donde los errores son costosos - a quien esté integrándolo.
Las reglas que sigue este paquete, establecidas en lugar de implicadas
Escritas para la misma razón por la que v2fhir escribe las suyas: el mapeo es donde los proyectos de interoperabilidad fallan, y los fallos son silenciosos.
- Una decisión nunca se actualiza. La certificación parcial no se convierte en aprobación, y una solicitud que el pagador se negó a considerar no se convierte en una denegación. Ambas de esas caídas están disponibles en el mapeo obvio y ambas están equivocadas en la dirección que cuesta a alguien semanas: una apelación presentada contra una decisión que nadie tomó, o seis sesiones reservadas contra tres aprobadas. - Nada se inventa para llenar un campo requerido. Donde el formato de destino exige algo que el origen no lleva, el mapeo informa como una nota en lugar de suministrar un valor predeterminado plausible. Un número de autorización fabricado es peor que uno ausente. - Cada juicio se informa. Las notas regresan con el resultado, para que una persona pueda leer el puñado que necesitó una decisión en lugar de toda la salida. - Los viajes de ida y vuelta se prueban, no se asumen. Un mapeo que pierde un campo en una dirección se encuentra convirtiendo hacia atrás y comparando, que es la única manera de ver un campo que nadie pensó en afirmar.
Source: github.com/biodream-llc/perfuse/internal/pas278
26.74 pdf
El paquete pdf escribe documentos de texto simples, utilizando solo la biblioteca estándar.
El escritor de documentos de Mirth renderiza HTML a PDF, lo que necesita un motor de diseño. Eso no es lo que esto es. Esto produce un documento de texto plano y paginado con una fuente monoespaciada, y existe porque el requisito real en casi todos los sitios que utilizan ese conector es "poner este informe en una carpeta compartida para que alguien pueda imprimirlo" - un resumen de alta, un informe de resultados, una reconciliación diaria. Esos son textos.
Rechazar la función por completo dejaría un bloqueador de migración de nivel 2 en su lugar. Incorporar un motor de renderizado de HTML costaría más que todo el resto del binario y traería un flujo de CVE con él, lo que anularía el argumento de la lista de materiales. Así que: texto, descrito honestamente como texto, y un sitio que realmente necesita una salida de composición tipográfica está mejor servido por un destino de archivo y una herramienta diseñada para ello.
¿Por qué escribir un PDF en lugar de un archivo de texto
Porque el extremo receptor suele ser una persona con una impresora y una carpeta compartida de Windows, y un archivo .txt impreso desde una carpeta compartida pierde su paginación, sus márgenes y a menudo sus finales de línea. Un PDF se imprime de la misma manera en todas partes, lo que es la razón completa por la que alguien pidió uno.
Source: github.com/biodream-llc/perfuse/internal/pdf
26.75 pgp
El paquete pgp cifra, firma, descifra y verifica mensajes OpenPGP (RFC 9580, y RFC 4880 para pares más antiguos), para pagadores, casas de compensación y agencias estatales que intercambian archivos sobre SFTP o correo electrónico bajo PGP.
La criptografía es github.com/ProtonMail/go-crypto, la bifurcación mantenida de golang.org/x/crypto/openpgp; nada aquí implementa un primitivo. Este paquete solo carga claves y decide qué debe transportar un mensaje.
Source: github.com/biodream-llc/perfuse/internal/pgp
26.76 pixv3
El paquete pixv3 es la cara de HL7 versión 3 del índice de pacientes: los perfiles de intercambio de identificadores de paciente y consulta de datos demográficos de paciente de IHE, ya que la mayoría de los hospitales fuera del mundo FHIR aún los hablan.
- ITI-44, Alimentación de identidad de paciente HL7 V3: una fuente agrega un paciente (PRPA_IN201301UV02), revisa uno (PRPA_IN201302UV02) o dice que dos registros son la misma persona (PRPA_IN201304UV02). Respondido con MCCI_IN000002UV01. - ITI-45, Consulta PIXV3: los demás identificadores de la persona que posee uno (PRPA_IN201309UV02 -> PRPA_IN201310UV02). - ITI-47, Consulta PDQV3: las personas que coinciden con algunas características demográficas (PRPA_IN201305UV02 -> PRPA_IN201306UV02), con continuación (QUQI_IN000003UV01) y cancelación (QUQI_IN000003UV01_Cancel).
Las respuestas provienen del mismo índice de pacientes que PIXm, PDQm, Patient/$match y la pantalla del índice, por lo que un registro alimentado en v3 se encuentra a través de FHIR y viceversa. Los mensajes se escriben a mano, en orden de esquema, por la razón hl7v3.Ack.
Source: github.com/biodream-llc/perfuse/internal/pixv3
26.77 priorauth
El paquete priorauth implementa el flujo de trabajo de autorización previa del lado del proveedor requerido por CMS-0057. Presenta solicitudes de autorización previa a las API FHIR de los pagadores, rastrea las respuestas y convierte entre tipos internos y recursos FHIR.
Source: github.com/biodream-llc/perfuse/internal/priorauth
26.78 privacidad
El paquete privacidad responde a las solicitudes de un sujeto de datos bajo el GDPR y el GDPR del Reino Unido: encuentra todo lo que este servidor mantiene sobre una persona, se lo da (Artículos 15 y 20), lo borra (Artículo 17) y mantiene lo que cubre una retención legal.
Alcanza todos los lugares donde Perfuse mantiene datos de pacientes: el almacén de mensajes (a través de su índice de identidad), la cola de entrega, el almacén FHIR (los recursos de Paciente de la persona y todo lo que se refiere a ellos, con su historial), solicitudes de autorización previa almacenadas, y el índice de pacientes, cuyos enlaces amplían un identificador en todos los números de la persona. Lo que no puede alcanzar lo nombra: trazas enviadas a un coleccionador, el registro de auditoría (mantenido, como el registro de quién hizo qué), y copias de seguridad, que mantienen lo que se borró hasta que caducan - así que cada borrado se escribe en un libro de registro, como hashes con sal de los identificadores en lugar de los identificadores, y se puede aplicar nuevamente a una copia restaurada.
No decide nada en nombre de nadie. Si una solicitud debe ser concedida, y si se aplica una exención, es la decisión del controlador; el borrado necesita la aprobación de un segundo administrador, y una retención legal se lo niega directamente.
Source: github.com/biodream-llc/perfuse/internal/privacy
26.79 saludpública
El paquete saludpública implementa informes de salud pública bidireccionales: informes de casos electrónicos (eCR), informes de laboratorio electrónicos (ELR) y consultas de registros de inmunización.
Cada hospital está obligado por ley a informar ciertas condiciones - tuberculosis, sarampión, hepatitis, COVID-19, infecciones de transmisión sexual, enfermedades transmitidas por los alimentos - a las agencias de salud pública estatales y locales. La informe se produce a través de tres interfaces distintas:
- ELR: mensajes ORU^R01 de HL7 2.5.1 que transportan resultados de laboratorio a las agencias de salud pública (elr.go). - eCR: informes de caso electrónicos iniciales basados en FHIR (eICR) cuando se diagnostica una condición informable. - Registro de inmunización: VXU^V04 para informar vacunaciones administradas, y VXQ^V01 para consultar el historial de inmunización de un paciente.
El paquete es bidireccional: informa hacia afuera (ELR, eCR, VXU) y consulta hacia atrás (VXQ/VXR). Todas las operaciones son en tiempo real en lugar de lotes nocturnos.
Seguridad de subprocesos: SubmissionLog es seguro para uso concurrente. Las funciones de creación son puras y seguras para llamar desde cualquier subproceso.
Source: github.com/biodream-llc/perfuse/internal/publichealth
26.80 publicado
El paquete publicado mantiene las protecciones para los paquetes importables de Perfuse, en un solo lugar.
hl7 fue el primer paquete publicado y lleva su propia copia de estas comprobaciones. fhir y pas se publicaron para que un servicio de decisión como Vaglio lea solicitudes FHIR y PAS con exactamente el código que utiliza Perfuse, en lugar de una copia que se desvía: cuatro lectores de los códigos de acción X12 en este repositorio una vez estuvieron en desacuerdo, y uno leyó una denegación como una aprobación. Las protecciones se mantienen aquí en lugar de copiarse en cada paquete, por la misma razón.
Source: github.com/biodream-llc/perfuse/internal/published
26.81 cola
El paquete cola es una cola de entrega durable y por destino.
Sin ello, un destino que esté inactivo durante dos minutos pierde todo lo enviado durante esos dos minutos: las reintentadas se realizan en memoria, y cuando se agotan, el mensaje se registra como fallido y se elimina. Con ello, el mensaje se almacena en disco y mantiene su lugar hasta que el destino vuelva a estar disponible.
Dos decisiones de diseño dan forma a todo lo demás.
El orden se mantiene por destino, y eso significa que un destino en cola detiene el camino rápido. HL7 es una secuencia de eventos sobre los mismos pacientes: un A01 admite, un A03 da de alta. Si un A01 fallido va a la cola mientras el siguiente A03 se entrega directamente, el sistema receptor ve una alta para un paciente al que nunca admitió. Así que una vez que algo esté en cola para un destino, todo para ese destino se coloca en cola detrás de él hasta que la cola se vacíe. Eso cuesta rendimiento durante una interrupción y no es opcional, porque la alternativa es un sinsentido clínico silencioso. También es la cosa específica que sale mal en otros motores cuando alguien activa hilos de cola concurrentes para hacer que un atraso se drene más rápido.
Un mensaje en cola es un mensaje aceptado. El remitente recibe AA, porque los bytes están en disco y fsynced antes de que se envíe el acuse de recibo. Decir AE haría que una cola de almacenamiento y reenvío funcione correctamente se parezca a una falla e invite al remitente a reenviar lo que ya tenemos.
Source: github.com/biodream-llc/perfuse/internal/queue
26.82 residencia
La residencia del paquete indica dónde puede ir la información de este servidor: las regiones de la nube que un sitio permite (la EEA, el Reino Unido, Suiza o regiones con nombre individual), y si la región de un conector es una de ellas.
Perfuse verifica lo que puede saber. Un conector de AWS nombra su región (S3, SQS, SNS), y un hostname de AWS lleva uno (sqs.eu-west-2.amazonaws.com); se verifican cuando se guarda y carga un canal, así que un canal no puede enviar datos de paciente a un bucket en Virginia desde un servidor cuyos datos deben permanecer en Fráncfort. Un hostname simple (el sistema de un hospital, el sistema de un socio, un oyente MLLP) no dice nada sobre dónde está, y no se juzga aquí: el registro de procesamiento enumera todos los hosts a los que un canal envía, para que el controlador lo verifique.
Source: github.com/biodream-llc/perfuse/internal/residency
26.83 rtpb
El paquete rtpb responde a solicitudes de beneficios de prescripción en tiempo real de NCPDP: qué costará un medicamento para este miembro en esta farmacia, si necesita una autorización previa o un paso primero, y qué alternativas cuestan menos.
CMS-0062-P propone RTPB para pagadores afectados a partir del 1 de octubre de 2027. El XML es NCPDP RTPB v13 (PBv13). El esquema de NCPDP está licenciado, así que los nombres de los elementos y el orden siguen los ejemplos públicos de Surescripts RTPB y las Recomendaciones de implementación de RTPB de NCPDP (códigos de restricción de cobertura y el estado de cobertura al que cada uno se asigna); una respuesta no se verifica contra el XSD aquí, y una certificación de red es donde eso sucede.
Source: github.com/biodream-llc/perfuse/internal/rtpb
26.84 s3put
El paquete s3put escribe objetos en S3 utilizando solo la biblioteca estándar.
La forma obvia de hablar con S3 es el SDK de AWS, y el SDK es enorme. Este proyecto vende un binario estático con ocho dependencias directas y una lista de materiales que cabe en una pantalla - eso es un argumento real con la persona que revisa el software antes de que un hospital lo instale, y cambiarlo por un conector sería un mal negocio.
Lo que S3 realmente necesita es un HTTP PUT y una firma. La versión 4 de la firma está completamente especificada, es determinista y AWS publica vectores de prueba para ella. Así que se implementa aquí: alrededor de doscientas líneas contra crypto/hmac y net/http, sin ninguna nueva dependencia y nada que parchear cuando el SDK tenga un aviso.
Lo que esto deliberadamente no hace
No se permite la carga de varios partes, no se reintenta por sí mismo, no hay cadena de credenciales más allá de claves estáticas y variables de entorno estándar. Un mensaje HL7 es de kilobytes, así que la carga de varios partes resuelve un problema que este no tiene; el motor ya reintenta; y una cadena de credenciales de instancia-rol significaría llamadas a IMDS, lógica de caché y refresco, que es donde una implementación pequeña deja de ser pequeña. Si alguien necesita esas cosas, la respuesta honesta es un destino de archivo y una herramienta de sincronización, no un SDK a medias construido.
Funciona con cualquier cosa que hable el API de S3 - MinIO, Ceph, Wasabi, Backblaze - porque es solo HTTP firmado. Eso es un efecto secundario que vale la pena tener, ya que un almacén de objetos de un hospital a menudo no es AWS.
Source: github.com/biodream-llc/perfuse/internal/s3put
26.85 saml
Canonicalización de XML exclusiva (exc-c14n) según https://www.w3.org/TR/xml-exc-c14n/
Esta es la forma canónica exacta de bytes que una firma de XML firma. Obtenerlo mal significa que las firmas válidas parecen inválidas y - peor - una implementación que lo obtiene en su mayoría correcto pasará en las pruebas contra sus propias firmas mientras rechaza respuestas de IdP reales.
Las reglas que importan para SAML: - Las declaraciones de espacios de nombres se representan solo si se utilizan visiblemente por el elemento o sus atributos. - Los espacios de nombres heredados que un elemento utiliza pero no declara se representan como si se declararan en ese elemento. - Los atributos se ordenan: declaraciones de espacios de nombres primero (ordenados por prefijo), luego otros atributos (ordenados por nombre expandido: URI de espacio de nombres y luego nombre local). - Los elementos vacíos utilizan la etiqueta de inicio/fin, nunca la etiqueta de autocierre. - Los nodos de texto se emiten tal como están; CDATA se reemplaza por su contenido con escape de entidad. - Las instrucciones de procesamiento y los comentarios fuera del elemento del documento se excluyen.
Source: github.com/biodream-llc/perfuse/internal/saml
26.86 scim
El paquete scim implementa SCIM 2.0, el protocolo que los proveedores de identidad utilizan para aprovisionar cuentas.
Okta, Entra ID, Google Workspace y OneLogin todos hablan el mismo lenguaje. Un administrador agrega a alguien a un grupo en el proveedor de identidad, y dentro de un minuto aparece una cuenta aquí con el rol correcto. Si lo eliminan, la cuenta desaparece.
La desaprovisionamiento es el punto
La provisionamiento es una comodidad. La desaprovisionamiento es la razón por la que esto vale la pena construir, y es la mitad que sale mal.
Cuando alguien deja una organización, el proveedor de identidad es el sistema que lo sabe primero - generalmente porque RRHH lo da de baja y el proveedor se basa en el registro de RRHH. Luego le informa a todas las aplicaciones conectadas que desactiven la cuenta. Si la llamada falla, o tiene éxito sin eliminar realmente el acceso, un ex empleado mantiene el acceso a un motor de integración clínica y nadie se entera, porque nada está vigilando algo que no sucedió.
Entonces, las partes de este paquete que más importan son las que hacen que la desaprovisionamiento sea real:
- Establecer active en falso termina todas las sesiones de inmediato, no en la próxima expiración. Una pestaña del navegador que se deja abierta en una laptop que se ha llevado a casa con un empleado despedido debe dejar de funcionar, y una sesión que sobrevive hasta que expira es una sesión que sobrevive al despido. - Un error es un error. SCIM no tiene semántica de reintento en la que una aplicación pueda confiar, por lo que devolver 200 por algo que no sucedió significa que el proveedor de identidad registra la desaprovisionamiento como completa y nunca vuelve a intentarlo. - Eliminar un usuario se trata de la misma manera que deshabilitarlo: el acceso termina. El historial de auditoría se mantiene independientemente, porque un registro que nombra a alguien que ya no existe es peor que uno marcado como retirado.
Por qué es un paquete separado
Las formas de SCIM no son de Perfuse. Tiene su propio formato de error, su propio sobre de lista, su propio lenguaje de parches y sus propias opiniones sobre el caso de los atributos. Mantener la traducción en un solo lugar significa que los controladores pueden centrarse en lo que sucede con una cuenta en lugar de centrarse en JSON.
Source: github.com/biodream-llc/perfuse/internal/scim
26.87 script
El script del paquete ejecuta JavaScript de Mirth Connect.
El objetivo es que los scripts de un canal se muevan sin cambios. Eso es un ejercicio de compatibilidad, no un ejercicio de diseño, por lo que la forma de todo aquí está dictada por lo que Mirth expone: msg y tmp como árboles E4X, seis mapas con métodos de Map de Java, la familia de funciones de búsqueda $, logger y un puñado de objetos de utilidad cuyos nombres de método y cadenas de formato de fecha son de Java.
Tres decisiones son nuestras en lugar de Mirth, y cada una es una desviación deliberada:
Un script se ejecuta con un plazo y se interrumpe cuando pasa. Mirth dejará que un script se ejecute indefinidamente, lo que hace que el canal se detenga; una interfaz que deja de aceptar admisiones debido a un error tipográfico en un transformador no es aceptable, por lo que siempre hay un tiempo de espera.
La interoperabilidad de Java se rechaza con un error que nombra la clase, en lugar de devolver undefined. Un script que se extiende a Packages.java.sql no se puede hacer que funcione aquí, y el resultado útil es un mensaje que dice exactamente eso en la línea donde sucede, no un NullPointerException tres líneas más tarde.
El acceso a archivos y bases de datos está desactivado a menos que un canal lo active. Mirth otorga ambos a cada script por defecto. Un transformador que puede leer cualquier archivo que el proceso pueda leer es algo razonable que se quiera y un valor predeterminado poco razonable.
Source: github.com/biodream-llc/perfuse/internal/script
26.88 ajustes
El paquete de ajustes es la descripción única de todo lo relacionado con un servidor Perfuse que un operador puede cambiar.
Por qué esto existe
Antes de este paquete, aproximadamente treinta cosas eran indicadores de línea de comandos y nada más. Cambiar la cantidad de tiempo que se mantienen los mensajes significaba editar una definición de servicio y reiniciar, lo que en un hospital significa un ticket de cambio - así que en la práctica nadie los cambió, y un valor predeterminado elegido en la primera tarde se convirtió en el ajuste para siempre.
Por qué un registro en lugar de un formulario
La forma obvia de construir una página de ajustes es escribir el formulario. Eso produce dos descripciones de cada ajuste - una en el servidor que lo valida y una en la interfaz que lo representa - y se desvían. La deriva es silenciosa: un campo que el servidor dejó de aceptar todavía tiene un control, así que alguien lo establece, ve que se guarda y no hace nada.
Entonces, un ajuste se declara una vez, aquí, con todo lo necesario para verificarlo y dibujarlo: qué tipo de valor es, qué control se adapta, a qué grupo pertenece, qué significa y si cambiarlo tiene efecto ahora. La interfaz solicita esa descripción y representa lo que se le dice. Agregar un ajuste es una entrada en este paquete y no requiere trabajo de interfaz en absoluto.
TestEverySettingIsFullyDescribed hace cumplir las partes que un ser humano debe suministrar, porque un ajuste sin texto de ayuda es un ajuste que alguien adivina.
Por qué un archivo en lugar de la base de datos
Los ajustes viven en un archivo YAML que la interfaz edita en su lugar, coincidiendo con la forma en que los canales y las reglas de alerta ya funcionan. Tres razones, en orden de peso: el control de cambios de un hospital quiere una configuración que se pueda diferenciar y poner en control de versiones; un operador con la máquina pero no la aplicación puede leer un archivo y no puede leer una base de datos; y restaurar los ajustes de ayer no debe requerir que la aplicación esté funcionando.
Por qué solo las banderas lo siembran
El archivo gana una vez que existe, y las banderas proporcionan el contenido inicial. La alternativa, es decir, que las banderas siempre ganen, haría que la interfaz sea engañosa, ya que cualquier cosa que se pase en la línea de comandos podría ser editada y guardada, y silenciosamente ignorada.
Una bandera que no esté de acuerdo con el archivo se informa al inicio, nombrando ambos valores, porque de lo contrario la bandera permanece en la definición del servicio durante años y la próxima persona que la lea cree que es así.
Source: github.com/biodream-llc/perfuse/internal/settings
26.89 sftpconn
El paquete sftpconn abre conexiones SFTP.
Separado de los conectores porque la parte interesante es la autenticación y la verificación de la clave del host, y ambas son idénticas ya sea que un canal esté recopilando archivos o escribiéndolos. Dos copias se desviarían, y la mitad que se desvió sería la mitad que dejó de verificar.
Source: github.com/biodream-llc/perfuse/internal/sftpconn
26.90 sombra
El paquete shadow compara un canal candidato con el canal en vivo.
La comparación en sí es la parte fácil. Todo el valor está en dos garantías, y ambas se aplican mediante la construcción en lugar de la configuración:
- Una sombra no puede entregar. Contiene una tubería de transformación y no hay remitentes, por lo que no hay camino de código desde aquí hasta un receptor. Un ajuste no puede crear uno y un error no puede habilitar uno. - Una sombra no puede afectar el canal en vivo. Se llama después de que el mensaje en vivo se ha entregado y confirmado, sus pánicos se recuperan y sus tiempos de espera son propios.
Cualquier cosa menos que eso y el modo sombra se convierte en una forma de romper la producción mientras se intenta evitar romper la producción.
Source: github.com/biodream-llc/perfuse/internal/shadow
26.91 shl
El paquete shl implementa enlaces y tarjetas de salud SMART: los códigos QR que CMS pide a los proveedores que acepten en el check-in y que devuelvan con el registro de la visita.
Un enlace de salud SMART (Enlaces de salud SMART STU 1) es shlink:/ seguido de JSON en base64url: una URL de manifiesto, una clave de 256 bits y, opcionalmente, una fecha de caducidad, una etiqueta y banderas (P contraseña, L a largo plazo, U un solo archivo por GET). El receptor publica en la URL del manifiesto y obtiene archivos, cada uno cifrado con esa clave - alg dir, enc A256GCM, opcionalmente comprimido con DEFLATE - que contiene JSON de FHIR, tarjetas de salud SMART o un token de acceso SMART. El servidor que hospeda los archivos nunca ve la clave: solo viaja en el enlace, que es del paciente.
Una tarjeta de salud SMART (Marco de tarjetas de salud SMART 1.4) es un paquete de FHIR firmado: un JWS compacto, ES256, con una carga útil comprimida con DEFLATE que nombra a su emisor, cuya clave pública está en <iss>/.well-known/jwks.json. Viaja como un código QR numérico shc:/ o un archivo .smart-health-card.
Solo la biblioteca estándar: crypto/aes, crypto/cipher, crypto/ecdsa y compress/flate cubren todo, y una biblioteca JOSE traería una superficie mucho más grande para dos algoritmos.
Source: github.com/biodream-llc/perfuse/internal/shl
26.92 smartauth
El paquete smartauth es un servidor de autorización de lanzamiento de aplicaciones SMART: emite los tokens de acceso que el punto de conexión de FHIR de Perfuse acepta.
Escrito contra la biblioteca estándar, al igual que internal/oidc, que verifica lo que este firma. Los tokens son JWT firmados RS256, el único algoritmo en el que todos los clientes SMART y las reglas de token de ID de la especificación pueden confiar.
Source: github.com/biodream-llc/perfuse/internal/smartauth
26.93 snmp
El paquete snmp es un agente SNMPv2c de solo lectura pequeño: GET, GETNEXT y GETBULK sobre UDP contra una tabla de valores que el llamador llena, y trampas SNMPv2.
Los mensajes están codificados en BER (X.690) aquí; nada de ello es criptografía (la cadena de comunidad de v2c es una contraseña de texto plano, por lo que el agente solo responde a las redes a las que se le indica que responda).
Source: github.com/biodream-llc/perfuse/internal/snmp
26.94 soap
El paquete soap envía solicitudes SOAP y lee fallos SOAP, utilizando solo la biblioteca estándar.
SOAP es una gran cantidad de ceremonial alrededor de una publicación HTTP, y todavía es la forma en que una gran cantidad de software de middleware hospitalario acepta datos: sistemas de administración de pacientes, repositorios de documentos, servicios de registros regionales, cualquier cosa especificada en la década en que esto era la respuesta. Mirth tiene un conector de servicio web, por lo que esto es un bloqueador de migración de nivel 2 en lugar de un elemento opcional.
¿Por qué no un cliente generado
El enfoque habitual es tomar un WSDL y generar tipos de Go a partir de él. Esa es la respuesta correcta para una aplicación que habla con un servicio conocido, y la respuesta incorrecta aquí: el destino de un motor de integración se configura mediante alguien que edita un canal, no se recompila, por lo que el sobre debe ensamblarse en tiempo de ejecución a partir de una plantilla que pueda ver y cambiar. También significa que no hay un paso de generación de código y nada que regenerar cuando el extremo remoto publica un nuevo WSDL.
Lo que esto deliberadamente no hace
No se analiza WSDL, no WS-Security, no MTOM, no WS-Addressing. Cada uno es una especificación por derecho propio, y pretender soportar uno mientras se obtiene un detalle incorrecto es peor que no ofrecerlo: una firma que casi verifica es indistinguible de un ataque en el extremo opuesto. WS-Security en particular es donde una implementación parcial haría un daño real.
Lo que está aquí: sobrescrituras SOAP 1.1 y 1.2, un cuerpo con plantilla, el encabezado SOAPAction y la detección de errores que lee la cadena de error en lugar de informar "500".
Source: github.com/biodream-llc/perfuse/internal/soap
26.95 sof
El paquete sof ejecuta SQL en FHIR v2 ViewDefinitions: una vista convierte cada recurso FHIR de un tipo en filas de columnas con nombre, mediante FHIRPath, para que los datos FHIR puedan leerse como tablas por motores SQL, hojas de cálculo y medidas de calidad.
Sigue el algoritmo de procesamiento de ViewDefinition (columna, selección, forEach, forEachOrNull, repetición, unionAll, donde, constantes, %rowIndex, getResourceKey y getReferenceKey) y se verifica contra los fixtures de prueba oficiales de sql-on-fhir.
Source: github.com/biodream-llc/perfuse/internal/sof
26.96 sqldb
El paquete sqldb abre conexiones de base de datos para la fuente y el destino de la base de datos.
Los controladores están registrados aquí y en ningún otro lugar, por lo que hay exactamente un lugar que decide a qué puede conectarse Perfuse.
Source: github.com/biodream-llc/perfuse/internal/sqldb
26.97 sqlitedb
El paquete sqlitedb abre SQLite de la manera que necesita un servidor: una conexión para escrituras y varias para lecturas.
La razón es un modo de fallo que solo aparece una vez que una implementación ha estado en ejecución durante un tiempo. SQLite en modo WAL permite que los lectores ejecuten junto a un escritor, pero el grupo de conexiones de Go se sitúa por encima de SQLite, por lo que un grupo de uno serializa todo, independientemente de lo que permita la base de datos. Un navegador de mensajes de búsqueda que escanea la columna de carga entonces mantiene la única conexión, y cada mensaje que llega espera detrás de él.
Medido en esta máquina, una escritura tarda 104µs con el almacén inactivo y 2,6 ms mientras una búsqueda escanea cuatro mil mensajes: veinticinco veces más lento. Eso se escala con la tabla, por lo que en unos pocos cientos de miles de mensajes almacenados, un informe se convierte en segundos de latencia de entrega, y una escritura está en el camino de reconocimiento. El remitente ve un tiempo de espera, vuelve a enviar y alguien pasa una mañana buscando un problema en la red.
Dividir las piscinas no cuesta nada y elimina la interacción: los informes obtienen sus propias conexiones y el escritor mantiene una para sí mismo porque SQLite permite exactamente un escritor de todos modos.
Source: github.com/biodream-llc/perfuse/internal/sqlitedb
26.98 steps
Pasos de transformación declarativos, para cualquier formato de mensaje.
Por qué esto es compartido
El vocabulario de pasos - establecer, copiar, borrar, asignar, reemplazar, recortar, caso, cada uno con una descripción y una condición opcional - no tiene nada que ver con un formato particular. Solo tres cosas difieren entre formatos: cómo se escribe una ruta, cómo se lee un valor y cómo se escribe de nuevo.
Esta es la misma conclusión a la que llegó la gramática de filtro en internal/expr, y por la misma razón. X12 tenía un motor de pasos y NCPDP necesitaba uno; copiarlo habría producido un segundo motor de cuatrocientas líneas para mantener a mano, y luego un tercero para delimitado. El comportamiento que vale la pena tener es lo suficientemente sutil como para que una copia no lo mantenga - la razón por la que el resultado se vuelve a leer después de cada escritura, por ejemplo, tomó un defecto real para descubrir.
Qué suministra un formato
Un Accesor: un compilador de rutas, un compilador de condiciones y un veto opcional sobre los pasos que no puede honrar. Nada más.
Source: github.com/biodream-llc/perfuse/internal/steps
26.99 stomp
El paquete stomp habla STOMP 1.2 a un agente de mensajes.
Esto es lo que "soporte JMS" tiene que significar en un programa que no es Java. JMS es una API en lugar de un protocolo, por lo que no hay nada que implementar: lo que un sitio realmente necesita es que Perfuse hable con el agente que se encuentra detrás de sus aplicaciones JMS.
STOMP en lugar de OpenWire, y la razonamiento vale la pena grabar porque la respuesta obvia es incorrecta. OpenWire es el protocolo nativo de ActiveMQ y una serialización binaria de comandos JMS - semanas de trabajo, específico de una familia de agentes, y sin documentar excepto por su implementación. ActiveMQ detecta automáticamente STOMP, AMQP y MQTT en el mismo puerto que sirve OpenWire, por lo que un protocolo de texto alcanza los mismos agentes. STOMP cubre ActiveMQ Classic, Artemis y RabbitMQ; AMQP 1.0 agregaría Azure Service Bus y IBM MQ, y es el siguiente sensato en lugar de OpenWire.
Escrito contra la biblioteca estándar. Un cliente de agente no es el lugar para una dependencia cuyos modos de fallo son de alguien más para explicar, y el encuadre aquí es lo suficientemente pequeño como para que poseerlo cueste menos que leer su seguimiento de problemas.
Source: github.com/biodream-llc/perfuse/internal/stomp
26.100 tienda
La tienda de paquetes almacena los datos que son genuinamente estatales: usuarios, sesiones y un registro de auditoría.
Los canales están deliberadamente fuera de aquí. Viven en archivos, porque eso es lo que da git, diffs, revisión de código y promoción de entornos de forma gratuita. Colocar canales en una base de datos es la decisión que obliga a comprar cada uno de ellos por separado, y es la razón por la que existe un producto de terceros puramente para trasladar canales de Mirth entre su base de datos y un repositorio git.
Así que la división es: configuración en archivos, identidad e historia en SQLite.
Source: github.com/biodream-llc/perfuse/internal/store
26.101 tefca
El paquete tefca implementa las partes de la participación en TEFCA que no requieren una conexión QHIN.
¿Qué es real
Validación de propósito de uso, validación de configuración y el registro de auditoría - incluyendo su persistencia, su interfaz de consulta y su obligación de registrar intentos fallidos así como exitosos. Esa última parte no es una cuestión técnica: una serie de entregas fallidas a un destinatario es cómo se muestra un socio mal configurado, y una serie de consultas fallidas para diferentes pacientes es cómo se muestra la exploración.
¿Qué no es
La red. No hay transporte QHIN en esta versión, por lo que no se produce intercambio, y cada función de intercambio se niega con ErrExchangeNotImplemented en lugar de devolver éxito.
Esa distinción no siempre se dibujó, y la forma en que salió mal es digna de mantener. Las cuatro funciones de intercambio solían validar sus entradas, no hacer llamadas de red y devolver éxito; Deliver devolvía una respuesta Aceptada con un identificador de seguimiento construido desde el reloj. La capa de auditoría registraba cada uno como un intercambio completado - en el registro que existe para probar qué se intercambió cuando alguien pregunta meses después. Cuatro pruebas afirmaban que comportamiento y pasaban.
¿Qué se requiere para completarlo
Incorporación a una Red de Información de Salud Calificada, autenticación de TLS mutua utilizando certificados emitidos a través de ese proceso, y either los perfiles IHE que el Acuerdo Común comenzó con o FHIR facilitado según el procedimiento operativo estándar del Proyecto Sequoia, vigente a partir del 8 de marzo de 2026. Nada de eso se puede escribir de forma especulativa: se prueba contra una QHIN real o no se prueba, y una implementación de intercambio no probada es el mismo defecto que no tener implementación con un disfraz mejor.
Source: github.com/biodream-llc/perfuse/internal/tefca
26.102 tenant
El paquete tenant proporciona las reglas de identidad y aislamiento para la operación multi-tenant.
Un HIE, una cámara de compensación o un proveedor de servicios administrados ejecuta un motor en nombre de muchas organizaciones. Mirth no puede hacer esto - una instancia sirve a una organización, y un MSP termina ejecutando cincuenta JVM y cincuenta bases de datos.
¿Qué posee un tenant
Sus canales, como un directorio de archivos. Sus usuarios. Sus mensajes, cola, registro de auditoría y recursos FHIR. Sus etiquetas de métricas. Nada se comparte excepto el proceso, los puertos de escucha que se le asignaron y los administradores de la plataforma.
El aislamiento es estructural, no recordado
El fallo peligroso aquí no es un bloqueo. Es el tenant A que ve los mensajes del tenant B, lo que es una violación informable en lugar de un error. Un diseño donde cada consulta toma un parámetro tenant_id falla la primera vez que alguien escribe una consulta y se olvida de uno, y esa consulta parece perfectamente correcta en la revisión.
Así que el tenant no es un parámetro. El acceso a los datos se realiza a través de un controlador que ya lo lleva, y no hay forma de llegar a las tablas sin uno. Olvidar no está disponible.
Source: github.com/biodream-llc/perfuse/internal/tenant
26.103 terminología
El paquete terminología mantiene los sistemas de código publicados (LOINC, SNOMED CT, RxNorm, ICD-10-CM, CPT y locales) para buscar códigos, y las tablas de valores de asignación que no tenían, para que alguien los asigne.
Los sistemas de código se importan desde los archivos que sus editores distribuyen. Varios están licenciados (LOINC necesita una cuenta gratuita, SNOMED CT una licencia de afiliado, RxNorm una licencia UMLS, CPT una licencia AMA), por lo que ninguno se envía con Perfuse: un administrador que posee la licencia carga el archivo, y Perfuse registra quién lo importó y cuándo.
Source: github.com/biodream-llc/perfuse/internal/terminology
26.104 tlsconf
El paquete tlsconf construye la configuración de TLS a partir de archivos y describe certificados.
Esta es la equivalencia gratuita de una extensión de Mirth que ahora cobra. La mayor parte del valor no reside en el cifrado, que son solo unas líneas, sino en la parte que evita que alguien implemente algo que cree que es seguro y no lo es: rechazar silenciosamente la omisión de verificación, insistir en que se verifique un certificado de cliente cuando se solicita uno y anunciar claramente cuando un certificado está a punto de expirar.
Un certificado expirado en una interfaz de alimentación es un cierre real de un hospital, y sucede porque nadie fue informado. La parte descriptiva de este paquete existe por esa razón.
Source: github.com/biodream-llc/perfuse/internal/tlsconf
26.105 tomirth
El paquete tomirth escribe un canal de Perfuse como XML de canal de Mirth.
Por qué existe esto. La objeción a adoptar un nuevo motor de integración rara vez es "¿es bueno?", sino "¿qué pasa si estamos equivocados y nos quedamos atascados?", y la respuesta más barata a eso es poder retroceder. Perfuse ha leído los canales de Mirth desde el principio; hasta ahora no podía escribir uno, lo que hacía que la garantía fuera unidireccional y, por lo tanto, no fuera una gran garantía.
Cómo evita la trampa que hizo que el primer intento fuera inútil. El XML de Mirth es la salida de XStream, y XStream lee los atributos de clase y versión como instrucciones: un elemento que no reconoce se descarta sin una palabra, y un canal que no puede ensamblar se almacena con su descripción reemplazada por "Este canal es inválido. Verifique que todas las extensiones requeridas estén cargadas correctamente" y sus destinos descartados. Nada en esa respuesta nombra el elemento en falta. Un exportador que compone bloques de propiedades a partir de una lectura del formato, por lo tanto, falla de la manera que es costosa de diagnosticar, lo que es lo que sucedió: este repositorio llevaba un exportador con nueve pruebas que pasaron cuya salida Mirth descartó, porque cada prueba la comparó con nuestro propio analizador en lugar de con un servidor.
Así que ninguno de los bloques de propiedades se compone aquí. scripts/mirth-dump-connector-templates.sh tiene el ObjectXMLSerializer de Mirth serializar los valores predeterminados de cada clase de propiedades de conector, y esos documentos se confirman en templates/. Este paquete sustituye valores en ellos, y mirth.SetRawProperty rechaza cualquier ruta que el modelo no contenga ya - así que un error es un error de nombrar la ruta, en el momento de exportación, en lugar de un canal que desaparece al importar.
Qué no hará. Un transporte sin equivalente en Mirth se rechaza por nombre en lugar de aproximarse. Una exportación que convirtiera silenciosamente un destino S3 en un escritor de archivos sería peor que no tener exportación en absoluto, porque el canal se importaría limpiamente y se entregaría al lugar equivocado.
Source: github.com/biodream-llc/perfuse/internal/tomirth
26.106 trace
El paquete trace produce trazas distribuidas para mensajes que se mueven a través de un canal y las exporta a través de OTLP/HTTP con codificación JSON.
Por qué existe esto en lugar del SDK de OpenTelemetry: el SDK más un exportador de OTLP atrae aproximadamente sesenta módulos y agrega tanto a la binary como todo el resto de este programa. Lo que realmente se necesita aquí es estrecho - propagación de contexto de traza W3C, modelo de trazo, muestreo de cabeza y POST de HTTP por lotes - y OTLP/JSON es una codificación estable documentada que se puede producir con la biblioteca estándar sola. Same comercio que elegir lib/pq sobre pgx: tomar lo pequeño cuando solo se quiere una parte de lo grande.
La consecuencia que vale la pena mencionar: no hay autoinstrumentación y no hay exportador específico de proveedor. Los trazos son los que este programa crea deliberadamente, y van a cualquier punto final de OTLP - un coleccionador, Jaeger, Grafana Tempo, Honeycomb, ingestión de OTLP de Datadog.
La propiedad que importa más que los trazos
Un mensaje llega con frecuencia llevando un encabezado de traceparent desde el sistema que lo envió, y sale hacia un sistema que grabará uno propio. Continuar ese trazo en lugar de comenzar uno fresco es lo que hace posible una sola vista de "el EHR lo envió, el motor lo transformó, el registro lo rechazó" en absoluto. Los problemas de interfaz casi siempre se informan como "lo enviamos y no sucedió nada", y esa pregunta es incontestable sin un identificador que sobreviva al salto.
El trazado nunca debe afectar la entrega
Cada operación aquí es de mejor esfuerzo. Los trazos se descartan cuando el búfer está lleno, los fallos de exportación se cuentan y nunca se reintenta en la ruta del mensaje, y ninguna llamada bloquea en la red. Un trazo descartado es una incomodidad. Un mensaje retrasado o perdido porque el backend de trazado era lento es un incidente clínico, así que el comercio nunca está en cuestión.
Source: github.com/biodream-llc/perfuse/internal/trace
26.107 transform
El paquete transform aplica cambios declarativos a un mensaje.
Esta es la capa destinada a llevar el trabajo ordinario, con la保tura de scripts como una escapada marcada. La distinción no es estilística. Un paso declarado en YAML se puede validar antes de que se ejecute, mostrar en una diferencia, revisar por alguien que no lee JavaScript, y informar; un script solo se puede ejecutar y esperar. La mayor parte de lo que los transformadores de Mirth realmente hacen es aburrido - copiar un campo, rellenar un identificador, mapear un código a través de una tabla, borrar algo que no debería salir del edificio - y todo eso pertenece aquí.
Cada paso nombra su objetivo explícitamente y cada paso es reversible en el sentido de que dice qué hizo, porque el fallo más costoso en una interfaz es una transformación que nadie sabía que estaba sucediendo.
Source: github.com/biodream-llc/perfuse/internal/transform
26.108 translate
El paquete translate convierte un canal de Mirth en una definición de canal de Perfuse.
perfuse explain informa qué bloques una migración. Esto realiza la mayor parte de la migración, que es una afirmación diferente y mucho más grande, por lo que es deliberadamente conservador sobre lo que está dispuesto a afirmar.
Tres reglas dan forma a todo el proceso.
Nada se descarta silenciosamente. Cada parte del canal de origen se traduce, se transporta literalmente como un script o se informa como necesitando intervención humana. Un traductor que omitiera silenciosamente un paso produciría un canal que comienza, se ejecuta y pierde datos: el peor resultado posible y la razón por la que la gente desconfía de las herramientas de migración.
Declarativo cuando es seguro, script cuando no lo es. Un paso de Mapper que copia un campo se convierte en un paso declarativo, porque es legible y verificable. Un paso de JavaScript se convierte en un script, porque Perfuse ejecuta el JavaScript de Mirth sin cambios y reescribirlo en algo que parezca equivalente es exactamente donde un traductor introduciría una diferencia que nadie nota.
La salida está diseñada para ser leída. Lleva comentarios que explican de dónde proviene cada cosa y qué se dejó para una persona. Una migración es revisada por alguien que tiene que aprobarla, y una pared no anotada de YAML no puede ser revisada.
Source: github.com/biodream-llc/perfuse/internal/translate
26.109 udap
El paquete udap implementa la mitad del cliente de la seguridad HL7 para el registro, la autenticación y la autorización a escala - la especificación en la que se basa el intercambio de FHIR facilitado por TEFCA.
UDAP es una infraestructura de clave pública conectada a OAuth 2.0. Una comunidad de confianza emite certificados X.509 a sus miembros; un miembro prueba quién es firmando JWT con la clave privada y adjuntando la cadena de certificados en el encabezado JWT. Eso reemplaza el secreto de cliente compartido de OAuth ordinario, que no se escala a miles de organizaciones que nunca se han reunido.
Por qué existe este paquete en absoluto. El intercambio TEFCA no se puede verificar completamente sin un QHIN real: unirse a uno requiere incorporación y certificados emitidos, y no se puede falsificar. Pero el perfil de seguridad subyacente es una guía de implementación publicada de HL7 con servidores de referencia públicos, y uno de ellos responde en Internet abierto. Entonces, la parte que se puede verificar se verifica aquí, y la parte que no se puede se rechaza en el límite en lugar de adivinarla.
La distinción es importante debido a lo que sucedió con SAML en este repositorio: mil líneas de pruebas pasaron, cinco de ellas específicamente sobre la canonicalización de firmas, mientras que ningún proveedor de identidad real podía autenticar a nadie. Ambas mitades se habían escrito aquí y se habían puesto de acuerdo entre sí. La lección aprendida de ello es que una implementación de seguridad no vale nada hasta que algo que alguien más escribió la haya aceptado o rechazado.
Referencias de especificación: - HL7 UDAP Security STU 1: http://hl7.org/fhir/us/udap-security/STU1/ - TEFCA SOP Facilitated FHIR Implementation v2.0, vigente a partir del 8 de marzo de 2026
Source: github.com/biodream-llc/perfuse/internal/udap
26.110 enlace umlink
El paquete umlink transporta X12 278 a un sistema de gestión de utilización de un pagador y sus respuestas, las formas en que esos sistemas se alcanzan en la práctica: X12 en el cuerpo de un POST HTTPS; el sobre SOAP de CAQH CORE (Regla de Conectividad vC2.2.0, la que la mayoría de los pagadores y CMS ejecutan esMD, y vC4.0.0); el sobre MIME multipart de CAQH CORE por HTTP (vC2.2.0); y archivos por lotes a través de SFTP.
Escrito a partir de las Reglas de Conectividad publicadas por CAQH (vC2.2.0 y vC4.0.0), su Esquema XML y WSDL, y la Guía de Companion X12N 278 de esMD de CMS. No está certificado por CORE: la certificación la realizan los proveedores de prueba autorizados por CAQH.
Los enlaces en tiempo real devuelven la respuesta que dio el sistema UM, que puede ser una respuesta 278 o, cuando no pudo leer la solicitud, un 999 o TA1: el llamador lee cuál es. El enlace por lotes devuelve ErrAnswerLater una vez que se entrega el archivo, y Poll recopila lo que se devuelve.
Source: github.com/biodream-llc/perfuse/internal/umlink
26.111 v2fhir
El paquete v2fhir asigna mensajes HL7 v2 a recursos FHIR.
El mapeo es donde los proyectos de interoperabilidad realmente fallan, por lo que las reglas que sigue este paquete se establecen en lugar de implicar:
- Un valor que no se puede asignar se conserva como texto, nunca se adivina. Un código local convertido en un código estándar plausible es peor que un valor no codificado, porque el receptor lo cree. - Cada decisión de mapeo que involucró juicio se informa como una nota, para que un humano pueda verificar las que importan en lugar de leer toda la salida. - Las fechas y horas se convierten, no se copian. Las fechas y horas HL7 v2 no son ISO 8601, y una fecha y hora que llevan una hora local sin desplazamiento es la razón más común por la que un recurso convertido es rechazado. - Las unidades conservan su texto original y ganan un código UCUM solo cuando el mapeo es seguro. Un código de unidad incorrecto es un defecto de seguridad del paciente, no un problema de formato.
Source: github.com/biodream-llc/perfuse/internal/v2fhir
26.112 vcs
El paquete vcs lee el historial de archivos de canal desde git.
El historial del canal es una extensión de pago en otro lugar, y es una extensión de pago porque esos motores mantienen los canales en una base de datos, por lo que realizar un seguimiento de los cambios significa construir un sistema de seguimiento de cambios. Perfuse mantiene los canales en archivos, por lo que el historial ya existe y ha existido desde el primer compromiso. Este paquete solo tiene que leerlo.
Se ejecuta en git en lugar de incrustar una biblioteca git. La biblioteca agregaría una gran dependencia a un binario cuyo punto de venta es tener cuatro, y no compraría nada: una implementación que mantiene sus canales bajo control de versiones tiene git instalado, y una que no obtiene un claro "no es un repositorio git" en lugar de una implementación ligeramente diferente de lo mismo.
Source: github.com/biodream-llc/perfuse/internal/vcs
26.113 vfs
El paquete vfs es el pequeño conjunto de operaciones de archivo que un conector de sondeo necesita.
Por qué una interfaz en lugar de cuatro conectores
Perfuse recopila archivos de un disco local, un servidor SFTP, un servidor FTP, un recurso compartido de Windows y un servidor WebDAV. La transferencia difiere en cada caso. Todo lo que realmente decide si los mensajes sobreviven no lo hace:
- Un archivo que se está escribiendo y un archivo que ha terminado de escribirse son indistinguibles. No hay bloqueo, no hay indicador y no hay notificación en cualquiera de estos transportes - hay un tamaño y una hora de modificación, y ambos son verdaderos para un archivo medio escrito. Leer demasiado pronto recopila medio mensaje, y HL7 no tiene terminador, por lo que medio mensaje es muy a menudo aún parseable. El MSH está intacto, los segmentos que llegaron están bien formados, los que no están simplemente ausentes. Se acepta, se confirma, se almacena, se entrega y nada mencionará nunca. - Un archivo no debe ser eliminado hasta que todos los mensajes en él hayan sido aceptados, porque el archivo es la única copia. - Un archivo que falló no debe ser recogido de nuevo en la próxima encuesta para siempre, y no debe ser eliminado silenciosamente.
Escritos una vez por transporte, esas reglas serían ligeramente diferentes cinco veces, y las diferencias solo aparecerían como mensajes que desaparecieron en un sitio. Así que se escriben una vez, aquí, sobre la interfaz más pequeña que los admite.
Qué no está aquí deliberadamente
No se escribe nada más allá de lo que necesita la eliminación. Enviar archivos es un problema diferente con modos de falla diferentes - un archivo parcialmente escrito en el extremo lejano, que los remitentes existentes manejan escribiendo en un nombre temporal y renombrando. Combinar ambas direcciones en una interfaz haría que cada una fuera más difícil de razonar.
No hay recursividad. Un recopilador que desciende en subdirectorios recopila el directorio de archivo que acaba de mover a un archivo.
Source: github.com/biodream-llc/perfuse/internal/vfs
26.114 vpn
El paquete vpn informa el estado de los túneles de VPN de sitio a sitio por los que pasa el tráfico de los socios - AWS Site-to-Site VPN, conexiones de Azure VPN Gateway y strongSwan - y mantiene la hoja de conexión de cada socio: la configuración del túnel de ambos lados y si están de acuerdo.
Un túnel que está abajo se ve, desde un motor de interfaz, exactamente como un socio cuyo motor está detenido: las conexiones agotan el tiempo de espera. Decir "el túnel está abajo" en lugar de eso es la diferencia entre llamar al equipo de red y llamar al socio.
Sólo lectura. Nada aquí cambia un túnel, y nunca se lee, mantiene o muestra una clave precompartida.
Source: github.com/biodream-llc/perfuse/internal/vpn
26.115 web
El paquete web sirve el front-end construido desde dentro del binario.
Los activos están incrustados en lugar de enviarse junto con ellos, por lo que la implementación sigue siendo un archivo. Esa es la misma razón por la que el motor evita cgo: un operador debe poder copiar un binario en un servidor y ejecutarlo, sin JVM, sin instalador y sin directorio de activos para salir del paso con el ejecutable.
La salida construida se confirma para que go build funcione en una comprobación sin Node instalado. Vuelva a compilar con make web.
Source: github.com/biodream-llc/perfuse/internal/web
26.116 webauthn
El paquete webauthn verifica las passkeys.
Una passkey es un par de claves que el autenticador mantiene - Touch ID, Windows Hello, un YubiKey, un teléfono - donde la mitad privada nunca sale del dispositivo. Iniciar sesión significa que el servidor envía un desafío aleatorio, el autenticador lo firma después de verificar que el usuario esté presente y el servidor verifica la firma contra la clave pública que almacenó en el registro.
La propiedad que importa no es que sea conveniente. Es que no hay nada que phish. Una contraseña se puede escribir en una copia convincente de una página de inicio de sesión; una firma de passkey está vinculada al origen para el que se creó, por lo que la copia obtiene una firma que su servidor no puede usar. Para un motor de integración que mantiene las interfaces de un hospital, esa es la diferencia que vale la pena tener.
Qué se niega a hacer esta implementación
La especificación es permisiva en lugares donde ser permisiva es cómo se rompen las implementaciones. Esta no lo es:
- El algoritmo de firma está fijo en el registro y se almacena con la clave. Nada en el inicio de sesión lee un algoritmo del cliente, porque eso es confusión de algoritmo y no hay función aquí que pueda hacerlo. - El origen se compara exactamente, nunca por prefijo o sufijo. Una comprobación que acepta cualquier cosa que termine en el dominio correcto acepta un subdominio de un atacante. - Un desafío es de un solo uso y caduca. Se elimina cuando se consume, por lo que una afirmación capturada no se puede reproducir. - La atestación no se confía para nada. La declaración de atestación se analiza para extraer la credencial y luego su firma se ignora, porque verificarla correctamente necesita una tienda de raíz mantenida y pretender verificarla es peor que no reclamarlo. - El recuento de firma se comprueba cuando el autenticador proporciona uno, y su ausencia no se trata como sospechosa. Muchos autenticadores dejan legítimamente en cero.
Source: github.com/biodream-llc/perfuse/internal/webauthn
26.117 winservice
El paquete winservice ejecuta Perfuse como un servicio de Windows.
Este archivo es la mitad de todos los demás lugares. Existe para que cmd/perfuse pueda llamar a este paquete sin envolver cada sitio de llamada en una etiqueta de compilación: el subcomando siempre se compila, y en Linux o macOS explica que los servicios son una idea de Windows en lugar de fallar al existir.
Rechazar con una oración clara es mejor que ocultar el subcomando. Alguien que sigue las instrucciones de Windows en la máquina equivocada se le dice qué está sucediendo en realidad, y lo equivalente para su plataforma.
Source: github.com/biodream-llc/perfuse/internal/winservice
26.118 x12
Leyendo y escribiendo valores en una ruta.
Por qué esto está separado de Path
path.go decide qué significa una expresión de ruta. Este archivo resuelve una contra un intercambio real. Se mantiene aparte porque las reglas de análisis son dignas de leerse sin el manejo de bytes, y porque el analizador es utilizado por el validador de configuración, que no tiene mensaje para resolver.
Por qué una escritura vuelve a analizar en lugar de editar bytes en su lugar
Segment.elements son subslices de Segment.raw, y Message.segs indexa en Message.raw. Editar un valor en su lugar cambia la longitud de todo lo que lo sigue, por lo que todos esos slices necesitarían ajustarse, y cualquier que se perdiera leería correctamente hasta el mensaje donde no lo hiciera.
Así que una escritura reconstruye el segmento, reconstruye el intercambio y vuelve a analizarlo. Eso cuesta un análisis por mensaje cambiado y compra la garantía de que un Message devuelto desde aquí es exactamente lo que produce el análisis de sus propios bytes. No hay estado donde los índices y los bytes no estén de acuerdo, porque ese estado es irrepresentable.
Source: github.com/biodream-llc/perfuse/internal/x12
26.119 x12xml
El paquete x12xml convierte X12 (y otros delimitados EDI) en el XML que Mirth Connect y Open Integration Engine escriben para él, y viceversa.
Por qué ese XML y no uno de los nuestros
El punto de una forma XML de X12 es los sistemas que ya la utilizan. Un sitio que se mueve de Mirth tiene receptores construidos contra lo que emite el tipo de datos EDI/X12 de Mirth, scripts que recorren msg['CLM']['CLM.05']['CLM.05.1'], y motores upstream que envían X12 como ese XML. Una forma más ordenada de la nuestra no serviría a ninguno de ellos. Así que esto escribe exactamente lo que escribe Open Integration Engine 4.6, y lee exactamente lo que lee, y las pruebas comparan los dos byte a byte contra el serializador propio de OIE.
Una versión anterior de este paquete inventó su propia forma (una raíz X12Interchange, elementos con nombre CLM05) y dijo que coincidía con Mirth. No lo hacía; nunca se había comparado con Mirth, y nada en Perfuse lo utilizaba.
The format
La raíz es X12Transaction cuando el mensaje comienza con un segmento ISA y EDIMessage en caso contrario, llevando los tres delimitadores como atributos (segmentDelimiter, elementDelimiter, subelementDelimiter). Cada segmento es un elemento nombrado por su identificador. Cada elemento de datos es SEG.NN, numerado desde 01 (ISA.01, CLM.05), y contiene sus componentes como SEG.NN.1, SEG.NN.2 y así sucesivamente: siempre al menos SEG.NN.1 para un elemento con un valor, ninguno para uno vacío.
Los delimitadores se leen desde el ISA cuando existe uno: el separador de elementos desde la posición 3, el separador de componentes desde la posición 104 y el terminador de segmento desde la posición 105, con un salto de línea después de él contado como parte de él. De lo contrario, son ~ * y:. El separador de repetición (ISA11) no se interpreta, lo cual es también el comportamiento de OIE: un elemento repetido mantiene su ^ dentro de un valor.
Esta es una reimplementación del comportamiento, escrita a partir de la observación de la salida de OIE y de su formato documentado; no hay código de Mirth en ella.
Source: github.com/biodream-llc/perfuse/internal/x12xml
26.120 xds
El paquete xds es el intercambio de documentos empresariales entre empresas de IHE: un Registro y Repositorio de Documentos XDS.b (ITI-41 Proporcionar y registrar, ITI-18 Consulta de almacenamiento de registro, ITI-43 Recuperar conjunto de documentos), una Puerta de respuesta XCA sobre la misma tienda (ITI-38, ITI-39), y el lado cliente de cada uno, por lo que un canal puede publicar un documento en el repositorio de otra organización y la consola puede consultar un registro o una comunidad.
Los metadatos son ebXML Registry 3.0 (ebRIM), como lo define XDS.b en IHE ITI TF-3 capítulo 4. Se modelan aquí como structs de Go cuyas etiquetas XML llevan los espacios de nombres, por lo que un mensaje se lee de la misma manera independientemente de los prefijos que el remitente elija, y se escribe namespace-correcto.
Source: github.com/biodream-llc/perfuse/internal/xds
26.121 xmldsig
El paquete xmldsig implementa firmas digitales XML y el perfil XAdES de ellas.
Por qué esto se escribe en lugar de prestado
Un documento clínico firmado es un artefacto legal. Afirmación de que una persona con nombre, en un momento con nombre, asumió la responsabilidad de un conjunto específico de bytes. Obtener eso mal en cualquier dirección es serio: una firma que no se puede verificar hace que un documento válido parezca falsificado, y un verificador que es demasiado generoso acepta un documento que alguien alteró.
La canonicalización es el problema completo
XML tiene muchas secuencias de bytes que significan lo mismo. El orden de los atributos no es significativo, los prefijos de espacio de nombres se pueden cambiar, los elementos vacíos se pueden escribir de dos maneras, y el espacio en blanco dentro de una etiqueta es gratuito. Una firma es sobre bytes, por lo que antes de hash cualquier cosa, ambos extremos deben estar de acuerdo en exactamente una secuencia de bytes para un documento determinado. Ese acuerdo es la canonicalización, y es donde las implementaciones van mal, porque cada error produce una firma que verifica contra su propia salida y falla contra la de todos los demás.
Así que este archivo es la canonicalización, por sí solo, con sus propias pruebas. No hay nada sobre la firma que aparezca aquí.
Exclusivo en lugar de inclusivo, por defecto
La canonicalización inclusiva extrae todas las declaraciones de espacio de nombres que están en el alcance, incluidas las que el elemento firmado nunca usa. Eso hace que una firma se rompa cuando el documento se incrusta más tarde dentro de algo que declara un espacio de nombres no relacionado en un antepasado, lo cual es precisamente lo que sucede cuando un CDA se coloca dentro de un sobre SOAP o un wrapper de metadatos IHE. La canonicalización exclusiva incluye solo las declaraciones que el elemento realmente usa, y es lo que los perfiles de intercambio de documentos especifican por esa razón.
Source: github.com/biodream-llc/perfuse/internal/xmldsig
26.122 xtree
El paquete xtree es un árbol XML mutable.
Tres características muy diferentes necesitan modificar XML: las transformaciones HL7, que Mirth modela como XML porque eso es lo que operan sus scripts; los documentos clínicos, que son XML de forma nativa; y los pasos de transformación declarativos. Comparten este árbol en lugar de cada uno crecer el suyo propio, porque la alternativa es tener tres modelos que no estén de acuerdo sobre lo que significa un elemento vacío.
El objetivo de diseño es la fidelidad a la semántica E4X, ya que los scripts de Mirth son E4X y el punto de ejecutarlos sin cambios es que se comporten de la misma manera. Esto conduce a dos decisiones que de otra manera parecerían extrañas: un nodo mantiene un puntero a su padre, porque E4X expone parent(), y los hijos están ordenados con duplicados permitidos, porque una repetición de campo HL7 es literalmente un elemento repetido y los scripts indexan en ellos por posición.
Source: github.com/biodream-llc/perfuse/internal/xtree
26.123 paquete pas
El paquete pas lee los paquetes de solicitud de soporte de autorización previa (PAS) de Da Vinci.
Es el lector que utiliza el propio servidor PAS de Perfuse, publicado para que lo que decida una autorización previa - las reglas de un pagador, un sistema de gestión de utilización, un servicio de decisión como Vaglio - lea una solicitud con el mismo código, y así las mismas respuestas sobre lo que contiene una solicitud. Dos lectores de un formato de mensaje son dos opiniones sobre él.
No tiene dependencias fuera de la biblioteca estándar. Una solicitud se mantiene como JSON decodificado (map[string]any) en lugar de como recursos tipados, porque un paquete PAS lleva cualquier recurso de soporte que el proveedor elija enviar, y un lector que descartara lo que no modelara ocultaría pruebas para la decisión.
req, problemas := pas.Read(bundle, false) if len(problemas) > 0 { // rechazar: la solicitud no se puede responder correctamente } for _, svc := range req.Services() { // svc.Codes, svc.Item, y req.Resolve para cualquier cosa que el elemento haga referencia }
Source: github.com/biodream-llc/perfuse/pas
27 Índice de claves de configuración
Cada clave en orden alfabético, con los bloques en los que puede aparecer. Una clave que aparece en varios bloques significa lo mismo en cada uno; donde no es así, los bloques se documentan por separado y la diferencia se establece allí.
27.1 Todas las claves
| Clave | Aparece en |
|---|---|
access_key_id | Acceso de AWS, Autenticación de FHIR, Destino de S3 |
account | Acceso a Azure Blob |
ack | Fuente de HTTP, Fuente de SOAP, Fuente |
ack_sender_id | Opciones de X12 |
ack_sender_qualifier | Opciones de X12 |
acknowledge | Opciones de EDIFACT, Opciones de HL7v3, Opciones de X12 |
acks | Destino de Kafka |
action | Destino de SOAP |
addr | Conexión de AMQP, Destino de Broker, Fuente de Broker, Conexión de MQTT |
address | Conexión de AMQP, Destino de DICOM, Fuente de consulta de DICOM, Destino, Dirección directa, Destino de TCP |
ae_title | Fuente de DICOM |
after_query | Fuente de base de datos |
after_read | Fuente de Azure Blob, IMAP directo, Fuente de correo electrónico, Sondeo de archivo, Fuente de GCS, Fuente de S3, Fuente de SFTP |
allow | Scripts |
allow_clear_password | Destino de FTP |
allow_unencrypted | Socio de AS2, Recepción de PGP |
allow_unsigned | Socio de AS2 |
allowed_calling_ae | Fuente de DICOM |
allowed_senders | Fuente de correo electrónico |
amqp | Destino, Fuente |
anchors_file | Dirección directa |
api_version | Conexión de IBMMQ |
append | Destino, Destino de SFTP |
application | Ack |
armor | Envío de PGP |
arrives_as | Opciones de X12 |
as2 | Destino, Fuente |
attach | Destino de SMTP |
attach_name | Destino directo, Destino de SMTP |
attachment_names | Fuente de correo electrónico |
attachments | Canal |
attempts | Reintento |
auth | Destino de FHIR, Destino de PAS |
authorities | Destino de MPI |
azure_blob | Destino, Fuente |
backoff | Configuración de cola, Reintento |
base64 | Fuente de SOAP |
batch_size | Fuente de base de datos, Sondeo de archivo |
baud | Fuente de serie |
bcc | Destino de SMTP |
bearer_token | Destino de CDA, Destino de FHIR, Destino de HTTP, Destino de PAS |
big_endian | Encuadernación de TCP |
blob | Destino de Azure Blob |
body | Destino de SMTP, Destino de SOAP |
broker | Destino, Fuente |
brokers | Destino de Kafka, Fuente de Kafka |
bucket | Acceso de GCS, Destino de S3, Fuente de S3 |
bundle_script | Destino de FHIR |
called_ae | Destino de DICOM, Fuente de consulta de DICOM |
calling_ae | Destino de DICOM, Fuente de consulta de DICOM |
cc | Destino de SMTP |
cda | Destino |
cert_file | Destino de AS2, Socio de AS2, Fuente de AS2, Dirección directa |
channel | Destino, Sombra |
check_every | Referencia de contrato |
city | Instalación de ECR |
claim_us_core | Destino de FHIR |
class | Destino de XDS |
client_ca_file | XDR directo |
client_id | Autenticación de correo electrónico, Autenticación de FHIR, Conexión de MQTT |
client_secret | Autenticación de correo electrónico, Autenticación de FHIR |
code | Código de XDS |
column | Fuente de base de datos |
columns | Delimitado |
comment | Delimitado |
commit_after_delivery | Fuente de Kafka |
compare | Sombra |
compress | Destino de AS2 |
compression | Destino de Kafka |
config | Destino de ELR |
container | Acceso a Azure Blob |
content_type | Destino de AMQP, Destino de AS2, Destino de Azure Blob, Destino de Broker, Destino de GCS, Destino de HTTP, Destino de S3, Destino de XDS |
contract | Canal |
credentials_file | Acceso de GCP |
dataType | Canal |
data_bits | Fuente de serie |
database | Destino, Fuente |
decrypt_with | Recepción de PGP |
default_identifier_system | Destino de FHIR |
delimited | Canal |
delimiter | Delimitado, Encuadernación de TCP |
deploy | Scripts |
description | Canal |
destination | Destino de Broker, Fuente de Broker |
destinations | Canal |
dicom | Canal, Destino, Fuente |
dicom_query | Fuente |
dicomweb | Destino, Fuente |
dir | Destino de CDA, Destino, Destino de documento, Destino de FTP, Sondeo de archivo, Destino de SFTP, Fuente de SFTP |
direct | Destino, Fuente |
direct_from | Destino de XDS |
direct_to | Destino de XDS |
display | Código de XDS |
dns | Destino directo |
document | Destino |
domain | Fuente de SMB |
driver | Destino de base de datos, Fuente de base de datos |
dsn | Destino de base de datos, Fuente de base de datos |
duplicate_window | Fuente de AS2 |
ecr | Destino de FHIR |
edifact | Canal |
element | Fuente de SOAP |
elr | Destino |
email | Fuente |
emit_on_first_poll | Fuente de consulta de DICOM |
enabled | Canal, Destino, Configuración de cola |
encrypt_to | Envío de PGP |
endpoint | Acceso de AWS, Acceso de Azure Blob, Acceso de GCP, Destino de S3 |
envelope | Opciones de EDIFACT, Opciones de X12 |
error_dir | Sondeo de archivo, Fuente de SFTP |
error_folder | IMAP directo, Fuente de correo electrónico |
error_prefix | Fuente de Azure Blob, Fuente de GCS, Fuente de S3 |
expect_reply | Destino de TCP |
extract | Adjuntos |
facility | Ack, Destino de ECR |
facility_id | Destino de SOAP |
facility_type | Destino de XDS |
fail_on_body | Destino de HTTP |
fault_is_success | Destino de SOAP |
fault_on_nak | Fuente de SOAP |
fhir | Destino |
file | Referencia de contrato, Fuente |
file_name | Destino de AS2, Destino, Destino de documento, Destino de FTP, Envío de PGP, Destino de SFTP |
file_roots | Scripts |
filter | Canal, Delimitado, Destino, Opciones de HL7v3, Scripts |
flow_control | Fuente de serie |
follow_redirects | Destino de HTTP |
follow_symlinks | Fuente de archivo |
font_size | Destino de documento |
format | Destino de documento, Destino de S3 |
framed | Fuente de Azure Blob, Destino de FTP, Sondeo de archivo, Fuente de GCS, Destino de S3, Fuente de S3, Destino de SFTP, Fuente de SFTP |
framing | Encuadernación de TCP |
from | Destino de AS2, Destino de SMTP |
from_beginning | Fuente de Kafka |
ftp | Destino, Fuente |
gcs | Destino, Fuente |
group | Canal, Fuente de Kafka |
group_by | Destino de SNS, Destino de SQS |
has_header | Delimitado |
header | Destino de SOAP |
headers | Destino de Broker, Destino de CDA, Destino de DICO Mweb, Destino de FHIR, Destino de HTTP, Destino de Kafka, Destino de SOAP, Destino de XDS |
heartbeat | Fuente de Broker |
hl7v3 | Canal |
host | Destino de Broker, Fuente de Broker, IMAP directo, Fuente de correo electrónico, Destino de FTP, Fuente de FTP, Destino de SFTP, Fuente de SFTP, Fuente de SMB, Destino de SMTP |
hostname | Conexión de AMQP |
http | Destino, Fuente |
ibm_mq | Destino, Fuente |
id | Socio de AS2, Fuente de AS2 |
id_type | Destino de MPI |
identifier_systems | Destino de CDA, Destino de FHIR |
idle_timeout | Fuente de DICOM, Fuente, Fuente de TCP |
ignore | Sombra |
imap | Fuente directa |
include | Scripts |
include_trigger_event | Ack |
insecure_skip_host_key_check | Destino de SFTP, Fuente de SFTP |
insecure_skip_verify | Destino de FTP, Fuente de FTP, Destino de SMTP, Fuente de Web DAV |
interval | Fuente de consulta de DICOM |
javascript | Destino, Fuente |
kafka | Destino, Fuente |
keep_alive | Destino de TCP |
keep_blank_lines | Delimitado |
keep_delimiter | Encuadernación de TCP |
key | Acceso de Azure Blob, Destino de Kafka, Destino de S3 |
key_column | Fuente de base de datos |
key_file | Destino de AS2, Fuente de AS2, Dirección directa, Destino de SFTP, Fuente de SFTP |
key_passphrase | Destino de SFTP, Fuente de SFTP |
known_hosts_file | Destino de SFTP, Fuente de SFTP |
landscape | Destino de documento |
language | Scripts |
ldap | Destino directo |
length_bytes | Encuadernación de TCP |
length_includes_header | Encuadernación de TCP |
level | Fuente de consulta de DICOM |
limit | Fuente de consulta de DICOM |
line | Instalación de ECR |
listen | Fuente de AS2, Fuente de DICO Mweb, Fuente de DICOM, Fuente directa, XDR directo, Fuente de HTTP, Fuente de SOAP, Fuente, Fuente de TCP |
login | Destino de Broker, Fuente de Broker |
mailbox | IMAP directo, Fuente de correo electrónico |
match | Fuente de consulta de DICOM |
max_attempts | Fuente de base de datos, Configuración de cola |
max_backoff | Configuración de cola, Reintento |
max_bytes | Fuente directa |
max_connections | Fuente, Fuente de TCP |
max_depth | Configuración de cola |
max_differences | Sombra |
max_file_size | Sondeo de archivo, Fuente de SFTP |
max_message_size | Fuente de AS2, Fuente de correo electrónico, Fuente de HTTP, Fuente de Kafka, Fuente de SOAP, Fuente de serie, Fuente, Destino de TCP, Fuente de TCP |
max_messages | Fuente de Pub Sub, Fuente de SQS |
max_object_bytes | Fuente de DICO Mweb, Fuente de DICOM |
max_object_size | Fuente de Azure Blob, Fuente de GCS, Fuente de S3 |
max_open_conns | Destino de base de datos |
mechanism | SASL de Kafka |
method | Destino de HTTP |
move_to | Fuente de Azure Blob, IMAP directo, Fuente de correo electrónico, Sondeo de archivo, Fuente de GCS, Fuente de S3, Fuente de SFTP |
mpi | Destino |
mqtt | Destino, Fuente |
name | Canal, Destino de canal, Destino, Instalación de ECR |
ncpdp | Canal |
no_compress | Envío de PGP |
no_merge | Destino de MPI |
non_persistent | Destino de IBMMQ |
npi | Instalación de ECR |
oauth | Fuente de correo electrónico |
object | Destino de GCS |
on_no_document | Destino de CDA |
order_by | Destino de Pub Sub |
over | Referencia de contrato |
overlap | Fuente de consulta de DICOM |
params | Destino de base de datos |
parity | Fuente de serie |
partner_cert_file | Destino de AS2 |
partners | Fuente de AS2 |
pas | Destino |
pass_plain | Recepción de PGP |
passcode | Destino de Broker, Fuente de Broker |
passphrase | Recepción de PGP, Envío de PGP |
password | Conexión de AMQP, Destino de AS2, Destino de DICO Mweb, Fuente de DICO Mweb, Destino directo, IMAP directo, Fuente directa, Fuente de correo electrónico, Destino de FTP, Fuente de FTP, Destino de HTTP, Conexión de IBMMQ, SASL de Kafka, Conexión de MQTT, Destino de SFTP, Fuente de SFTP, Fuente de SMB, Destino de SMTP, Destino de SOAP, Fuente de SOAP, Fuente de Web DAV |
path | Fuente de AS2, Fuente de DICO Mweb, Fuente de HTTP, Fuente de SOAP |
path_style | Destino de S3, Fuente de S3 |
patient_domain | Destino de XDS |
patient_root | Fuente de consulta de DICOM |
pattern | Sondeo de archivo, Fuente de SFTP |
payer | Destino de PAS |
persistent | Destino de Broker |
pgp | Destino, Fuente |
phone | Instalación de ECR |
poll_interval | Fuente de Azure Blob, Fuente de base de datos, IMAP directo, Fuente de correo electrónico, Sondeo de archivo, Fuente de GCS, Fuente de IBMMQ, Fuente de Java Script, Fuente de S3, Fuente de SFTP |
port | IMAP directo, Fuente de correo electrónico, Fuente de serie |
postal_code | Instalación de ECR |
postprocessor | Scripts |
practice_setting | Destino de XDS |
prefetch | Fuente de AMQP |
prefix | Fuente de Azure Blob, Fuente de GCS, Fuente de S3 |
preprocessor | Scripts |
profile | Destino de SOAP, Fuente de SOAP |
project | Acceso de GCP |
protocol | Fuente de correo electrónico |
provider | Autenticación de correo electrónico |
pubsub | Destino, Fuente |
purpose | Destino de TEFCA |
qos | Conexión de MQTT |
query | Fuente de base de datos |
query_timeout | Fuente de base de datos |
queue | Destino, Conexión de IBMMQ |
queue_manager | Conexión de IBMMQ |
queue_url | Destino de SQS, Fuente de SQS |
quiet_after | Fuente de serie |
quote | Delimitado |
raw | Sondeo de archivo |
rctc | Destino de ECR, Destino de ELR |
read_timeout | Fuente de HTTP, Fuente de SOAP |
reassemble | Adjuntos |
receipt | Destino de AS2 |
receipt_url | Destino de AS2 |
recipient_certs_dir | Destino directo |
reconnect | Fuente de AMQP, Fuente de Broker, Fuente de MQTT, Fuente de Pub Sub |
record_length | Encuadernación de TCP |
refresh_token | Autenticación de correo electrónico |
region | Acceso de AWS, Autenticación de FHIR, Destino de S3 |
reject_on_warning | Destino de FHIR |
relaxed | Delimitado |
reopen_after | Fuente de serie |
reply | Fuente de serie, Fuente de TCP |
reply_text | Fuente de serie, Fuente de TCP |
request_mdn | Destino directo |
require_agreement | Destino de CDA |
require_signature | Recepción de PGP |
response_element | Fuente de SOAP |
response_namespace | Fuente de SOAP |
response_transformer | Destino |
results_to | Destino de PAS, Destino de TEFCA |
retain | Destino de MQTT |
retain_hours | Destino, Configuración de cola |
retry | Destino |
return | Fuente de consulta de DICOM |
root | Fuente de FTP, Fuente de archivo, Fuente de SMB |
s3 | Destino, Fuente |
sample | Sombra |
sas | Acceso de Azure Blob |
sasl | Destino de Kafka, Fuente de Kafka |
scheme | Código de XDS |
scope | Autenticación de correo electrónico, Autenticación de FHIR |
script | Canal, Destino de Java Script, Fuente de Java Script |
scripts | Canal |
secret_access_key | Acceso de AWS, Autenticación de FHIR, Destino de S3 |
security | IMAP directo, Fuente de correo electrónico, Destino de FTP, Fuente de FTP |
selector | Fuente de Broker |
sender_device | Opciones de HL7v3 |
sender_oid | Opciones de HL7v3 |
serial | Fuente |
server_side_encryption | Destino de S3 |
session_timeout | Fuente de Kafka |
session_token | Acceso de AWS, Autenticación de FHIR, Destino de S3 |
sftp | Destino, Fuente |
shadow | Canal |
share | Fuente de SMB |
sign_with | Envío de PGP |
smb | Fuente |
smtp | Destino |
smtp_host | Destino directo, Fuente directa |
sns | Destino |
soap | Destino, Fuente |
sop_classes | Fuente de DICOM |
sort_by | Sondeo de archivo |
source | Canal, Destino de ECR |
source_id | Destino de XDS |
split | Delimitado, Opciones de EDIFACT, Opciones de X12 |
sqs | Destino, Fuente |
ssn_system | Destino de MPI |
stable_for | Sondeo de archivo, Fuente de SFTP |
start_block | Encuadernación de TCP |
starttls | Destino de SMTP |
state | Instalación de ECR |
statement | Destino de base de datos |
stop_bits | Fuente de serie |
storage_class | Destino de GCS, Destino de S3 |
subject | Destino de AS2, Destino directo, Destino de SMTP, Destino de SNS |
subscription | Fuente de Pub Sub |
subscription_id | Fuente de Broker |
success_on_undefined | Destino de Java Script |
success_status | Destino de HTTP |
suffix | Fuente de Azure Blob, Fuente de GCS, Fuente de S3 |
tables | Canal |
take | Fuente de correo electrónico |
tcp | Destino, Fuente |
tefca | Destino |
temp_suffix | Destino, Destino de documento, Destino de FTP, Destino de SFTP |
template | Fuente de base de datos, Destino de documento |
tenant | Autenticación de correo electrónico |
tenant_id | Autenticación de FHIR |
text | Destino directo |
tier | Destino de Azure Blob |
timeout | Conexión de AMQP, Destino de Broker, Fuente de Broker, Destino de DICO Mweb, Destino de DICOM, Fuente de consulta de DICOM, Destino de base de datos, Destino, Destino directo, Destino de documento, Destino de FTP, Fuente de FTP, Conexión de IBMMQ, Destino de Java Script, Fuente de Java Script, Destino de Kafka, Fuente de Kafka, Conexión de MQTT, Destino de S3, Destino de SFTP, Fuente de SFTP, Fuente de SMB, Destino de SMTP, Destino de SOAP, Scripts, Sombra, Destino de TCP, Fuente de Web DAV, Destino de XDS |
timezone | Destino de FHIR |
title | Destino de documento |
tls | Conexión de AMQP, Destino de AS2, Fuente de AS2, Destino de DICO Mweb, Fuente de DICO Mweb, Destino de DICOM, Fuente de consulta de DICOM, Fuente de DICOM, Destino, IMAP directo, Fuente de correo electrónico, Destino de HTTP, Fuente de HTTP, Conexión de IBMMQ, Destino de Kafka, Fuente de Kafka, Conexión de MQTT, Destino de SOAP, Fuente de SOAP, Fuente, Destino de TCP, Fuente de TCP, Destino de XDS |
tls_cert | Fuente directa, XDR directo |
tls_key | Fuente directa, XDR directo |
to | Destino de AS2, Destino de SMTP |
token | Destino de DICO Mweb, Fuente de DICO Mweb, Fuente de HTTP, Fuente de SOAP, Destino de XDS |
token_url | Autenticación de correo electrónico, Autenticación de FHIR |
topic | Destino de Kafka, Destino de MQTT, Fuente de MQTT, Destino de Pub Sub |
topic_arn | Destino de SNS |
topics | Fuente de Kafka |
transfer_syntaxes | Destino de DICOM, Fuente de DICOM |
transformations | Canal, Opciones de DICOM, Delimitado, Opciones de HL7v3, Opciones de NCPDP, Opciones de Script, Opciones de X12 |
transformer | Scripts |
trim_padding | Encuadernación de TCP |
trim_space | Delimitado |
type | Destino, Autenticación de FHIR, Fuente |
undeploy | Scripts |
unencrypted | Destino de AS2 |
unsigned | Destino de AS2 |
unsigned_receipt | Destino de AS2 |
url | Destino de AS2, Destino de CDA, Destino de DICO Mweb, Destino de FHIR, Destino de HTTP, Conexión de IBMMQ, Destino de SOAP, Fuente de Web DAV, Destino de XDS |
user | Destino de FTP, Fuente de FTP, Destino de SFTP, Fuente de SFTP, Fuente de SMB, Fuente de Web DAV |
username | Conexión de AMQP, Destino de AS2, Destino de DICO Mweb, Fuente de DICO Mweb, Destino directo, IMAP directo, Fuente directa, Fuente de correo electrónico, Destino de HTTP, Conexión de IBMMQ, SASL de Kafka, Conexión de MQTT, Destino de SMTP, Destino de SOAP, Fuente de SOAP, Destino de XDS |
validate_before_send | Destino de FHIR |
verify_with | Recepción de PGP |
version | Destino de CDA, Destino de FHIR, Destino de SOAP, Fuente de SOAP |
visibility_timeout | Fuente de SQS |
wait_seconds | Fuente de SQS |
warnings_are_errors | Destino de SOAP |
webdav | Fuente |
when | Ack |
window | Fuente de consulta de DICOM |
write | Destino de CDA |
wsdl | Fuente de SOAP |
x12 | Canal |
x12_as | Destino |
xdm | Destino directo |
xdm_source_id | Destino directo |
xdr | Fuente directa |
xds | Destino |