ApiBorne
Sommaire du guide

Modes de connectivité

Votre serveur n'est pas exposé sur Internet ? Le contrat prévoit deux modes d'appel (borne en direct ou relais chiffré du serveur ApiBorne) et deux sources de référentiels (routes HTTP ou JSON statique). Cette page explique quand et comment les utiliser.

Le problème : un serveur non exposé

Certains établissements refusent d'exposer leur système de gestion médical sur Internet. Les bornes, elles, sont sur le même réseau local que ce serveur. Deux besoins symétriques en découlent :

  • le parcours patient doit pouvoir partir de la borne directement vers une adresse locale de votre serveur — une URL différente de celle que voit le serveur ApiBorne ;
  • inversement, si c'est la borne qui ne peut pas vous joindre, elle peut passer par le serveur ApiBorne en relais.

Ces réglages se font dans l'admin ApiBorne, page Intégration — rien à déclarer côté éditeur : vos routes restent identiques dans les deux modes.

Les deux modes d'appel du contrat

Le réglage « Le contrat est interrogé par » de la licence détermine qui appelle vos routes de communication pendant le parcours patient :

  • Borne en direct (défaut, comportement historique) : la borne appelle votre serveur directement. Un second réglage optionnel, « URL contrat côté borne », permet de donner à la borne une adresse locale (ex. http://192.168.1.50:8080) différente de l'URL contrat connue du serveur ApiBorne — le serveur peut alors vous joindre par un autre chemin, ou pas du tout (voir l'architecture bastion ci-dessous).
  • Relais serveur : la borne appelle le serveur ApiBorne, qui fait un passthrough vers l'URL contrat qu'il connaît. Utile quand la borne ne peut pas joindre votre serveur mais que le serveur ApiBorne le peut.
Rétro-compatibilité stricte : sans ces réglages, le comportement est identique à l'historique (borne en direct, référentiels par routes HTTP) — aucune migration des licences existantes.

Architecture recommandée : un bastion sur le réseau local

En mode direct, quand l'établissement veut garder son RIS totalement isolé, l'architecture de déploiement recommandée est un bastion : une machine dédiée sur le réseau du site (DMZ ou VLAN technique), la seule habilitée à dialoguer en dehors du VLAN métier. Elle héberge un programme dédié qui :

  • expose le Kiosk Integration Contract — les routes du parcours uniquement — aux bornes, à travers le tunnel OpenVPN ;
  • traduit en interne chaque appel vers le système du client dans son protocole natif : ressources FHIR (Patient, Appointment, DocumentReference…) ou messages HL7 v2 (ADT, SIU…). Le RIS n'a aucune modification à subir : il continue de parler son protocole.

Les bornes restent en mode direct, avec « URL contrat côté borne » pointant vers l'adresse du bastion dans le tunnel : chaque borne et le bastion embarquent un certificat OpenVPN et montent leur tunnel vers le serveur VPN ApiBorne — une infrastructure OpenVPN dédiée, fournie et opérée par ApiBorne, distincte du serveur applicatif. Le parcours patient ne quitte jamais le tunnel (borne → serveur VPN ApiBorne → bastion), et comme le chiffrement de bout en bout est obligatoire, le contrat peut y circuler en http simple. Le serveur applicatif ApiBorne, lui, reste hors VPN — pas de certificat, jamais dans le tunnel : les bornes le joignent en HTTPS classique (configuration au démarrage, numérotation des tickets), et il ne joint jamais le bastion. Dans ce pattern, les référentiels sont fournis en JSON statique dans l'admin et le bastion n'expose que les routes du parcours : l'isolement est total.

Architecture bastion : chaque borne et le bastion embarquent un certificat OpenVPN et montent un tunnel vers le serveur VPN ApiBorne ; le parcours patient circule dans ce tunnel jusqu'au bastion, qui traduit le contrat vers le RIS en FHIR ou HL7 v2. Le serveur applicatif ApiBorne, hors VPN, n'est joint qu'en HTTPS pour la configuration et les tickets, et ne joint jamais le bastion.Site clientVLAN bornesBornes ApiBorneapplication navigateurURL contrat = bastioncertificat OpenVPNDMZ / VLAN techniqueBastionprogramme dédiéexpose le contrat kiosque⇄ traduit en FHIR / HL7 v2certificat OpenVPNVLAN métierRIS / système métierprotocole natifaucune modificationFHIR / HL7 v2Patient, Appointment · ADT, SIUCloud ApiBorneServeur ApiBornehors VPN — ne joint jamais le bastionconf des bornes · numérotation des ticketsréférentiels : JSON statique dans ce patternServeur VPN ApiBorneOpenVPN — fourni et opéré par ApiBornetermine les tunnels des bornes et du bastionHTTPS (hors VPN)conf au démarrage · ticketstunnel OpenVPN — parcours patientcontrat chiffré E2E · http simple possible dans le tunnel
Le parcours patient ne quitte jamais le tunnel OpenVPN (borne → serveur VPN ApiBorne → bastion) : le chiffrement de bout en bout obligatoire s'applique par-dessus, l'http simple suffit dans le tunnel — défense en profondeur : VPN + chiffrement E2E + clés. Le serveur applicatif ApiBorne reste hors VPN (HTTPS classique pour la configuration et les tickets) et ne joint jamais le bastion : les référentiels sont fournis en JSON statique. Bastion → RIS dans son protocole natif.

Ce que ce découpage apporte en sécurité :

  • le RIS n'est jamais exposé sur Internet, ni même joignable depuis les bornes : seul le bastion lui parle, dans son VLAN ;
  • le VLAN métier est conservé tel quel ; le bastion est l'unique point de passage, avec une surface d'attaque minimale (un seul programme, un seul port) ;
  • le chiffrement de bout en bout obligatoire s'applique par-dessus : même en http dans le tunnel, le trafic bornes → bastion ne transporte que des corps chiffrés — défense en profondeur : tunnel OpenVPN + chiffrement applicatif E2E + authentification par clé de marque et device.
Le VPN est fourni et opéré par ApiBorne : serveur OpenVPN dédié dans le cloud ApiBorne, certificats déployés avec la solution sur chaque borne et sur le bastion. La DSI de l'établissement n'a aucun VPN site-à-site à monter.

Le relais en détail

En mode relais, la borne préfixe chaque appel du contrat par le chemin du relais :

chemin du relais
# Mode relais : la borne appelle le serveur ApiBorne, qui transmet à votre serveur
{serveur-apiborne}/api/contractRelay/api/apiborneIntegrationService/v1/patients/identify
# → passthrough vers {votre-serveur}/api/apiborneIntegrationService/v1/patients/identify
  • Passthrough zero-knowledge : les corps chiffrés de bout en bout transitent intacts — le serveur ApiBorne ne peut pas les lire (voir le guide Chiffrement) ;
  • les headers X-Kiosk-* sont transmis tels quels à votre serveur : X-Kiosk-Device-Id reste celui de la borne réelle, votre implémentation ne voit aucune différence ;
  • le relais authentifie la borne (device + clé de marque) avant de transmettre, et chaque hit est tracé côté serveur ApiBorne ;
  • votre serveur injoignable depuis le relais → 502 RELAY_UPSTREAM_UNREACHABLE renvoyé à la borne.

La source des référentiels

Indépendamment du mode d'appel du contrat, le réglage « Les référentiels sont servis par » choisit la source des référentiels :

  • Routes HTTP (défaut) : les 6 routes GET /config/*, appelées par le serveur ApiBorne — comportement historique ;
  • JSON statique : un JSON uploadé dans l'admin remplace les routes de configuration. Plus aucun appel HTTP de config — ces routes peuvent ne jamais être implémentées. Une section absente du JSON vaut liste vide.

Grammaire du JSON (validée à la sauvegarde, 400 sinon) : toutes les sections sont optionnelles, les ids acceptent string ou number (normalisés en string). Chaque section est un tableau d'objets id + name — sauf documentTypes : code + label.

référentiels statiques
{
  "officePlaces":  [{ "id": "1", "name": "Site A" }],
  "examTypes":     [{ "id": "1", "name": "RADIO", "ticketPrefix": "RA" }],
  "practitioners": [{ "id": "1", "name": "Dr MARTIN", "rppsId": "10101010101" }],
  "rooms":         [{ "id": "1", "name": "Salle 1" }],
  "exams":         [{ "id": "1", "name": "Thorax", "examTypeId": "1" }],
  "documentTypes": [{ "code": "prescription", "label": "Ordonnance" }]
}
En source JSON, ne vous étonnez pas de ne jamais voir arriver de requête GET /config/* sur votre serveur : ce n'est pas un bug, c'est le mode choisi. Les ids du JSON doivent rester exactement ceux portés par vos RDV (locationId, examId, examTypeId, practitionerId, roomId).

Incidence sur les sondes de connectivité

La sonde config-check s'adapte aux réglages de la licence :

  • Borne en direct : la sonde « Endpoint contrat » est sautée (réputée OK) — le parcours ne passe jamais par le serveur ApiBorne, votre serveur peut être injoignable depuis le cloud. En mode relais, elle reste active : le serveur ApiBorne DOIT vous joindre.
  • Source JSON : les référentiels sont validés par la présence de la section dans le JSON — aucune sonde HTTP.
  • Source HTTP : les sondes des routes de configuration restent actives quel que soit le mode contrat — le serveur ApiBorne consomme ces routes pour les pages d'admin (référentiels, lieux…).

Le banc de test « Test du contrat » de l'admin est remplacé, en borne directe, par un message explicatif : l'appel de test partirait du serveur ApiBorne, qui n'atteint pas votre serveur.

Incidence sur le CORS

  • Mode direct : la borne est une application navigateur qui vous appelle — le CORS reste obligatoire sur les routes du parcours (inchangé) ;
  • Mode relais : vous recevez des appels serveur → serveur — plus de contrainte CORS navigateur pour le parcours. Les headers X-Kiosk-* restent transmis.

Chiffrement de bout en bout : obligatoire dans les deux modes

Le chiffrement de bout en bout est obligatoire quel que soit le mode. C'est lui qui rend acceptable une URL http sur un réseau local en mode direct, et c'est lui qui garantit qu'en mode relais le serveur ApiBorne ne voit passer que des corps chiffrés (zero-knowledge).

Ce qui reste côté serveur ApiBorne

Quel que soit le mode, le serveur ApiBorne reste requis pour deux choses : le chargement de la configuration de la borne à son démarrage et la numérotation des tickets (/api/kioskTicket). Ces appels se font en HTTPS classique, hors de tout VPN. Et si vos référentiels sont servis par les routes HTTP, le serveur ApiBorne doit pouvoir joindre votre serveur pour les charger — dans l'architecture bastion (référentiels en JSON statique), ce chemin n'existe pas : le serveur ne joint jamais le bastion.