23 auxiliary services across compute, storage, messaging, AI, and infrastructure layers
Overview
MedOS employs a rich sidecar architecture — auxiliary services that run alongside the core 18 Moleculer microservices. These sidecars handle data ingestion, AI orchestration, infrastructure, storage, and external integrations.
No service mesh (Istio/Envoy/Linkerd) is deployed. Routing is handled by Nginx reverse proxy + NATS message bus.
Architecture Diagram
┌──────────────────────────────────────────────────────────────────────────────┐
│ EXTERNAL TRAFFIC │
│ │
│ Browsers / Mobile / API Consumers / Metriport / EHR Systems │
└───────────────────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 1: INGRESS & ROUTING │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ NGINX REVERSE PROXY │ │
│ │ docker/reverse-proxy/ — Alpine Nginx 1.22 │ │
│ │ Workers: 4 | Body: 100MB | Timeout: 600s | SSL: LetsEncrypt │ │
│ │ │ │
│ │ Routing: │ │
│ │ / → Next.js (3000) │ │
│ │ /api/, /api/v2/→ API Gateway (3001) │ │
│ │ /filestore/ → Filestore (8083) │ │
│ │ /e-form → eForm (8085) [WebSocket] │ │
│ │ /socket.io → WebSocket (3003) [WebSocket] │ │
│ │ /reporting/ → Jasper (8088) │ │
│ │ /openapi/ → Public API (8082) │ │
│ │ │ │
│ │ 3 server blocks: sandbox, UAT, test │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ │
│ │ CERTBOT SIDECAR │ certbot/certbot:latest │
│ │ Auto-renew SSL certs│ ACME challenge via Nginx .well-known/ │
│ └──────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 2: MESSAGING & SERVICE BUS │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ NATS MESSAGE BROKER │ │
│ │ docker/nats/ — NATS 2-alpine │ │
│ │ │ │
│ │ Ports: 4222 (client) | 8222 (monitor) | 6222 (server) | 5222 (route)│ │
│ │ Max Payload: 64MB | Write Deadline: 40s │ │
│ │ │ │
│ │ All 18 Moleculer microservices connect via TRANSPORTER=nats://nats:4222│
│ └────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 3: CORE MICROSERVICES (18 services) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ api-gateway │ │ api-aaa │ │ api-admin │ │ api-clinical│ │
│ │ :3001,:3210 │ │ Auth/AuthZ │ │ :4002 │ │ Clinical │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ api-diag │ │ api-meds │ │ api-finance │ │ api-found │ │
│ │ Lab/Imaging │ │ Pharmacy │ │ Billing │ │ Foundation │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ api-filestore│ │ api-eform │ │ api-interop │ │ api-migrate │ │
│ │ :8083 │ │ E-Forms │ │ FHIR/HL7 │ │ Migration │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ api-messaging│ │ api-printing│ │ api-prm │ │ api-task │ │
│ │ :8084,:3003 │ │ :8088 (Java)│ │ PRM │ │ Scheduling │ │
│ ├─────────────┤ ├─────────────┤ │ │ │ │ │
│ │ public-api │ │ global-seq │ │ │ │ │ │
│ │ :8082 │ │ ID Gen │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 4: DATA PERSISTENCE │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ ┌─────────────────┐ │
│ │ MongoDB (Primary) │ │ PostgreSQL (eForm) │ │ Supabase │ │
│ │ ────────────────── │ │ ────────────────── │ │ (Read Models) │ │
│ │ mongo:5 replica set │ │ postgres:14.5 │ │ Cloud Postgres │ │
│ │ Port: 27017 │ │ Port: 5432 │ │ + Realtime │ │
│ │ Pool: 100-500 conn │ │ DB: eform │ │ │ │
│ │ ulimits: 64000 fd │ │ │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ MongoDB Setup │ │ PgAdmin │ │
│ │ (Init Helper) │ │ dpage/pgadmin4:6.10 │ │
│ │ Runs mongo_setup.sh │ │ Port: 5050 │ │
│ │ then exits │ │ DB admin console │ │
│ └──────────────────────┘ └──────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 5: FILE STORAGE │
│ │
│ ┌──────────────────────────────────────────┐ ┌──────────────────────────┐ │
│ │ IPFS Node │ │ IPFS Cluster Manager │ │
│ │ ipfs/go-ipfs:master │ │ ipfs/ipfs-cluster │ │
│ │ Port: 4001 (swarm) │ │ Port: 9094 (REST API) │ │
│ │ Port: 5001 (API) │ │ Port: 9096 (Proxy) │ │
│ │ Port: 8080 (gateway) │ │ CRDT consensus │ │
│ └──────────────────────────────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ FTP Server (Legacy) │ │
│ │ stilliard/pure-ftpd │ │
│ │ Port: 21 (control) │ │
│ │ Ports: 30000-30009 (passive) │ │
│ │ User: ever / ftpPassEver │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 6: TERMINOLOGY & REFERENCE DATA │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SNOMED CT Terminology Server (Snowstorm) │ │
│ │ docker/snowstorm/docker-compose.snowstorm.yml │ │
│ │ │ │
│ │ ┌──────────────────────┐ ┌───────────────────────────────────┐ │ │
│ │ │ Elasticsearch 7.17 │────▶│ Snowstorm 10.2.0 │ │ │
│ │ │ snowstorm-es │ │ SNOMED CT Browser + FHIR API │ │ │
│ │ │ Single-node cluster │ │ Port: 8085 │ │ │
│ │ │ 2GB JVM heap │ │ 1-2GB JVM heap │ │ │
│ │ └──────────────────────┘ └───────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 7: AI & AUTOMATION SIDECARS │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ HEALTH-DATA-PLATFORM SIDECAR │ │
│ │ his-vajira/packages/health-data-platform/src/sidecar/ │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ │
│ │ │ Webhook │ │ FHIR API │ │ CSV │ │ Transformer │ │ │
│ │ │ Collector │ │ Collector │ │ Collector │ │ + Forwarder │ │ │
│ │ │ (push) │ │ (pull/60s) │ │ (batch) │ │ → Data Lake │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ → Search Idx │ │ │
│ │ │ → Event Bus │ │ │
│ │ └───────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ PRM (Patient Relationship Management) SIDECARS │ │
│ │ his-vajira/medbase/functions/prm-sidecars/ │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌──────────────────────────────────────┐ │ │
│ │ │ Dify AI Personas │ │ n8n Workflow: care-gap-reactivation │ │ │
│ │ │ ───────────────── │ │ ────────────────────────────────── │ │ │
│ │ │ Protocol Scout │ │ 1. Webhook: prm.opportunity.created │ │ │
│ │ │ (Clinical Analyst) │ │ 2. Consent checker │ │ │
│ │ │ Brand Concierge │ │ 3. HubSpot contact upsert │ │ │
│ │ │ (VIP Liaison) │ │ 4. HubSpot deal creation │ │ │
│ │ │ Thai/EN WhatsApp │ │ 5. Dify AI outreach generation │ │ │
│ │ └─────────────────────┘ └──────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ OpenClaw Insurance Verification Sidecar │ │ │
│ │ │ openclaw/skills/insurance-liaison/verify_coverage.py │ │ │
│ │ │ Providers: AIA, Bupa, Allianz, etc. │ │ │
│ │ │ Input: patient_id + package_code + provider_name │ │ │
│ │ │ Output: coverage status + out-of-pocket calculation │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ REPORTING SIDECAR (Jasper) │ │
│ │ ever-api-printing (Spring Boot, Java) │ │
│ │ Port: 8088 │ │
│ │ PDF generation, Jasper Reports, IPFS storage │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ LAYER 8: FRONTEND SIDECARS │
│ │
│ ┌──────────────────────┐ ┌──────────────────────────────────────────────┐ │
│ │ Next.js Web App │ │ eForm Builder (Toolpad) │ │
│ │ Port: 3000 │ │ Port: 8085 │ │
│ │ SSR + React frontend│ │ Postgres-backed form builder │ │
│ └──────────────────────┘ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
Sidecar Inventory (23 services)
Infrastructure Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 1 |
Nginx Reverse Proxy |
nginx:1.22-alpine |
80, 443 |
API gateway, SSL termination, routing |
| 2 |
Certbot |
certbot/certbot |
— |
LetsEncrypt certificate automation |
| 3 |
NATS |
nats:2-alpine |
4222, 8222, 6222, 5222 |
Inter-service message bus |
Data Persistence Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 4 |
MongoDB |
mongo:5 |
27017 |
Primary OLTP database (replica set) |
| 5 |
MongoDB Setup |
mongo:5 |
— |
Replica set initialization (run-once) |
| 6 |
PostgreSQL |
postgres:14.5 |
5432 |
eForm relational database |
| 7 |
PgAdmin |
dpage/pgadmin4:6.10 |
5050 |
Database admin console |
File Storage Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 8 |
IPFS Node |
ipfs/go-ipfs |
4001, 5001, 8080 |
Distributed file storage |
| 9 |
IPFS Cluster |
ipfs/ipfs-cluster |
9094, 9096 |
Cluster coordination (CRDT) |
| 10 |
FTP Server |
stilliard/pure-ftpd |
21, 30000-30009 |
Legacy file transfer |
Terminology Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 11 |
Elasticsearch |
elasticsearch:7.17.18 |
9200 |
Snowstorm storage backend |
| 12 |
Snowstorm |
snowstorm:10.2.0 |
8085 |
SNOMED CT browser + FHIR API |
AI & Automation Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 13 |
Health-Data Sidecar |
TypeScript (in-process) |
— |
FHIR data collect → transform → forward |
| 14 |
Dify Protocol Scout |
Dify YAML persona |
— |
Clinical telemetry analysis |
| 15 |
Dify Brand Concierge |
Dify YAML persona |
— |
VIP patient outreach (Thai/EN) |
| 16 |
n8n Care Gap Workflow |
n8n JSON workflow |
— |
PRM opportunity → HubSpot → Dify |
| 17 |
OpenClaw Insurance |
Python skill |
— |
Insurance coverage verification |
Application Sidecars
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 18 |
Jasper Reporting |
Spring Boot (Java) |
8088 |
PDF generation + Jasper Reports |
| 19 |
Next.js Frontend |
Node.js |
3000 |
SSR web application |
| 20 |
eForm Builder |
Toolpad (React) |
8085 |
Electronic form builder |
Migration Box Sidecars (portable deployment)
| # |
Service |
Image/Tech |
Port(s) |
Purpose |
| 21 |
migration-api |
Moleculer + Claude |
— |
Portable migration engine |
| 22 |
medbase-db |
PostgreSQL |
— |
Migration metadata store |
| 23 |
Migration Gateway |
API Gateway |
— |
Minimal Box UI gateway |
Inter-Sidecar Communication
┌─────────────┐ TCP 4222 ┌──────────────┐
│ Microservice │──────────────▶│ NATS │◀──── All 18 services
│ (any) │ └──────────────┘
└──────┬───────┘
│ TCP 27017
▼
┌──────────────┐ ┌──────────────┐
│ MongoDB │ │ PostgreSQL │◀──── eForm, PgAdmin
│ (replica set)│ └──────────────┘
└──────────────┘
│
│ HTTP 5001 ┌──────────────┐
│ │ IPFS Node │◀──── Filestore service
│ └──────┬───────┘
│ │ HTTP 5001
│ ┌──────▼───────┐
│ │ IPFS Cluster │
│ └──────────────┘
│
│ ┌──────────────┐
│ │ Elasticsearch│
│ └──────┬───────┘
│ │ HTTP 9200
│ ┌──────▼───────┐
│ │ Snowstorm │ SNOMED CT + FHIR
│ └──────────────┘
│
│ HTTP :5001 ┌──────────────┐
└──────────────────────▶│ Dify API │◀──── n8n workflow
└──────────────┘
OAuth2 ┌──────────────┐
──────────────────────▶│ HubSpot │◀──── n8n workflow
└──────────────┘
Docker Compose Files
| File |
Purpose |
Services |
docker-compose.yml |
Primary development |
All core services + sidecars |
docker-compose.migration-box.yml |
Portable migration |
migration-api, medbase-db, mongo, nats, gateway |
docker/snowstorm/docker-compose.snowstorm.yml |
Terminology server |
Elasticsearch + Snowstorm |
Multiple docker-compose.*.yml |
Environment variants |
sandbox, test, UAT configurations |
Resource Requirements
| Service |
CPU |
Memory |
Storage |
Notes |
| MongoDB |
— |
— |
Persistent volume |
64,000 file descriptors |
| Elasticsearch |
— |
2GB JVM heap |
Persistent volume |
Memory lock enabled |
| Snowstorm |
— |
1-2GB JVM heap |
— |
120s startup period |
| NATS |
— |
— |
— |
64MB max payload |
| Nginx |
4 workers |
— |
— |
100MB body, 600s timeout |
| IPFS |
— |
— |
Persistent volume |
Swarm networking |
Key Design Decisions
- NATS over Kafka/RabbitMQ — Lightweight, low-latency pub/sub for Moleculer microservices. 64MB payload supports large clinical documents.
- IPFS for file storage — Content-addressed, distributed storage with cluster replication. Supports large medical images and documents.
- Nginx over API Gateway service — Simple, battle-tested reverse proxy with SSL termination. No need for API gateway features like rate limiting (handled at app level).
- Snowstorm self-hosted — SNOMED CT terminology server avoids external API dependency. Critical for clinical coding workflows.
- PRM sidecars as plugins — Dify personas + n8n workflows + OpenClaw skills are loosely coupled to the core HIS. Easy to swap or disable.
- FTP for legacy — Pure-FTPd supports legacy system integrations that can’t use modern APIs.
- No service mesh — Complexity avoided in favor of NATS + Nginx. May revisit with Istio when multi-cluster deployment is needed.