Infrastructure Topology¶
This page documents the full infrastructure layout for the VitaraVox platform, including network topology, service placement, request flows, and operational commands.
Network Topology¶
INTERNET
|
+-----------------+-----------------+
| | |
v v v
+------------+ +-------------+ +---------------+
| Cloudflare | | Let's | | Telnyx |
| WAF | | Encrypt | | (Phone SIP) |
| (Kai only) | | (OCI certs) | +-------+-------+
+-----+------+ +------+------+ |
| | v
v | +-----------+
+------------------+ | | Vapi.ai |
| fbh.kai-oscar.com| | | (Voice |
| Kai OSCAR Pro | | | AI) |
| (WELL Health) | | +-----+-----+
| OAuth 1.0a REST | | |
+------------------+ | | HTTPS POST
| | (webhooks)
+--------+--------+--------+ |
| | | | |
v v v v v
+------------------------------------------------+
| OCI ARM Instance (Toronto Region) |
| Ubuntu on ARM64 |
| |
| +------------------------------------------+ |
| | nginx (host-level) | |
| | :80 (HTTP -> HTTPS redirect) | |
| | :443 (HTTPS, Let's Encrypt) | |
| | | |
| | Virtual Hosts: | |
| | +------------------------------------+ | |
| | | dev.vitaravox.ca | | |
| | | static -> client/dist/ | | |
| | | /api/* -> localhost:3002 | | |
| | +------------------------------------+ | |
| | | vitdocs.vitaravox.ca | | |
| | | static -> vitaravox-docs/site/ | | |
| | +------------------------------------+ | |
| | | api-dev.vitaravox.ca | | |
| | | proxy -> localhost:3002 | | |
| | +------------------------------------+ | |
| +------------------------------------------+ |
| | |
| v |
| +------------------------------------------+ |
| | PM2: vitara-admin-api (:3002) | |
| | Express 4 + Prisma ORM + Pino | |
| | Zod validation + opossum (breaker) | |
| | node-soap (OSCAR SOAP client) | |
| +------------------------------------------+ |
| | |
| +-------------+-------------+ |
| | | |
| v v |
| +----------------+ +-----------------------+|
| | SQLite (local) | | /opt/zatuka-stack/ ||
| | (Prisma DB) | | Docker Compose ||
| +----------------+ | (see Zatuka section) ||
| +-----------------------+|
+------------------------------------------------+
|
| SOAP / REST
v
+--------------------------------+
| AWS EC2 (ca-central-1) |
| t3a.medium @ 15.222.50.48 |
| Terraform-managed |
| |
| +---------------------------+ |
| | Docker Compose | |
| | | |
| | +--------+ +-----------+ | |
| | |Tomcat 9| |MariaDB | | |
| | |OSCAR | |10.5 | | |
| | |:8080 | |:3306 | | |
| | +--------+ +-----------+ | |
| +---------------------------+ |
+--------------------------------+
Voice Call Request Flow¶
This diagram traces a patient phone call from the PSTN through to the EMR.
Patient dials +1 236-305-7446
|
v
+---------------+
| PSTN / SIP |
+-------+-------+
|
v
+---------------+
| Telnyx | Phone number provider
| (SIP trunk) | Routes call to Vapi
+-------+-------+
|
v
+-------+-------+
| Vapi.ai | Voice AI platform
| |
| +---------+ | 1. Router agent answers
| | Router | | 2. Detects language (EN/ZH)
| +----+----+ | 3. Identifies intent
| | |
| +----v----+ | 4. Transfers to role agent
| | Booking | | (Booking, Modification,
| | Modif. | | Registration)
| | Regist. | |
| +----+----+ | 5. Agent calls tools via
| | | HTTP POST webhooks
+-------+-------+
|
| HTTPS POST (webhook)
v
+-------+-------+
| api-dev | api-dev.vitaravox.ca
| .vitaravox.ca |
+-------+-------+
|
| SSL termination (Let's Encrypt)
v
+-------+-------+
| nginx | Reverse proxy
| :443 | proxy_pass to :3002
+-------+-------+
|
v
+-------+-------+
| Express API | vitara-admin-api (PM2)
| :3002 | /api/vapi webhook handler
| |
| Zod validate | Validates Vapi payload
| Route handler | Dispatches to EMR adapter
+-------+-------+
|
| REST (OAuth 1.0a) SOAP (WS-Security)
+----------------+ +----------------+
| | | |
v v v v
+---------------+ +---------------------------+
| Kai OSCAR Pro | | Dev OSCAR (EC2) |
| fbh.kai- | | 15.222.50.48:8080 |
| oscar.com | | |
| (production) | | (development/testing) |
+---------------+ +---------------------------+
Protocol Selection
The EMR adapter automatically selects the protocol based on the clinic's preferRest flag:
- Kai-hosted clinics: OAuth 1.0a REST (Cloudflare WAF blocks SOAP
text/xmlPOST) - Self-hosted clinics: SOAP via WS-Security (universal, ships with every OSCAR since v12)
Admin Dashboard Request Flow¶
Browser: https://dev.vitaravox.ca
|
v
+-------+-------+
| nginx | SSL termination (Let's Encrypt)
| :443 | Virtual host: dev.vitaravox.ca
+-------+-------+
|
+------ Static files? ------+
| |
| YES | NO (/api/*)
v v
+---------------+ +---------------+
| client/dist/ | | proxy_pass |
| | | localhost |
| React 19.2 | | :3002 |
| Vite build | +-------+-------+
| Tailwind 4 | |
| | v
| SPA routing: | +---------------+
| try_files | | Express API |
| $uri $uri/ | | :3002 |
| /index.html | | |
| | | +----------+ |
| Asset cache: | | | Prisma | |
| 1 year | | | (SQLite) | |
+---------------+ | +----------+ |
| | |
| +-----v----+ |
| | OSCAR | |
| | Adapter | |
| +----------+ |
+---------------+
SPA Routing
nginx is configured with try_files $uri $uri/ /index.html so that all
client-side routes (React Router) resolve to the index page. The Vite build
output in client/dist/ includes hashed filenames with a 1-year cache header
for optimal performance.
Zatuka Stack Layout¶
/opt/zatuka-stack/ (Docker Compose)
+---------------------------------------------------------+
| |
| +-------------+ +-------------+ +----------------+ |
| | Mattermost | | Outline | | Vikunja | |
| | chat.zatuka | | docs.zatuka | | projects.zatuka| |
| | .ai | | .ai | | .ai | |
| +------+------+ +------+------+ +-------+--------+ |
| | | | |
| +------+------+ +-----+-------+ +-------+--------+ |
| | n8n | | Uptime Kuma | | zatuka-nginx | |
| | n8n.zatuka | | status. | | (CURRENTLY | |
| | .ai | | zatuka.ai | | DOWN - port | |
| +------+------+ +------+------+ | 80 conflict) | |
| | | +-------+--------+ |
| +--------+-------+------------------+ |
| | |
| +--------v--------+ |
| | Shared Infra | |
| | | |
| | +-------------+ | |
| | | PostgreSQL | | |
| | | 16 | | |
| | | :5432 | | |
| | +-------------+ | |
| | | |
| | +-------------+ | |
| | | Redis | | |
| | | :6379 | | |
| | +-------------+ | |
| +-----------------+ |
+---------------------------------------------------------+
Port Conflict
The zatuka-nginx container is currently DOWN due to a port 80 conflict
with the host-level nginx. The Zatuka services still function via their
individual container ports, but external routing through zatuka-nginx is
unavailable. The documentation site (vitdocs.vitaravox.ca) is served
directly by host-level nginx and is unaffected.
SSL / TLS Termination Points¶
INTERNET
|
+-----------------+-----------------+
| | |
v v v
+----------------+ +------------+ +-------------+
| Let's Encrypt | | Cloudflare | | Vapi.ai |
| (OCI nginx) | | (Kai only) | | (managed) |
+--------+-------+ +-----+------+ +------+------+
| | |
TLS terminated at: TLS terminated TLS terminated
nginx :443 at CF edge at Vapi edge
| | |
v v v
+----------------+ +------------+ +-------------+
| localhost:3002 | | Kai OSCAR | | Webhook to |
| (plaintext | | (re-encrypted | api-dev |
| internal) | | by CF) | | .vitaravox |
+----------------+ +------------+ | .ca :443 |
+-------------+
Legend:
~~~~~~~ = TLS encrypted
------- = plaintext (internal only)
| Endpoint | Certificate | Issuer | Termination |
|---|---|---|---|
dev.vitaravox.ca |
Auto-renewed | Let's Encrypt | OCI nginx |
vitdocs.vitaravox.ca |
Auto-renewed | Let's Encrypt | OCI nginx |
api-dev.vitaravox.ca |
Auto-renewed | Let's Encrypt | OCI nginx |
fbh.kai-oscar.com |
Managed | Cloudflare | CF edge |
15.222.50.48:8080 |
None | N/A | No TLS (dev only) |
Dev OSCAR has no TLS
The development OSCAR instance on AWS EC2 (15.222.50.48:8080) does not
use TLS. It is intended for development and testing only. Production EMR
traffic uses the Kai OSCAR Pro endpoint with Cloudflare TLS.
DNS Routing¶
DNS Records (managed externally)
+--------------------------------------------------+
| |
| dev.vitaravox.ca ---------> OCI Public IP |
| (Toronto ARM) |
| |
| vitdocs.vitaravox.ca -----> OCI Public IP |
| (Toronto ARM) |
| |
| api-dev.vitaravox.ca -----> OCI Public IP |
| (Toronto ARM) |
| |
| chat.zatuka.ai ------------> OCI Public IP |
| docs.zatuka.ai ------------> OCI Public IP |
| projects.zatuka.ai --------> OCI Public IP |
| n8n.zatuka.ai --------------> OCI Public IP |
| status.zatuka.ai -----------> OCI Public IP |
| |
| fbh.kai-oscar.com ---------> Cloudflare proxy |
| (WELL Health / |
| Kai managed) |
+--------------------------------------------------+
All *.vitaravox.ca and *.zatuka.ai domains resolve to the
single OCI ARM instance. nginx virtual hosts differentiate
traffic by hostname.
Port Matrix¶
| Port | Protocol | Service | Host | Exposure |
|---|---|---|---|---|
| 80 | TCP | nginx (HTTP redirect) | OCI ARM | Public |
| 443 | TCP | nginx (HTTPS) | OCI ARM | Public |
| 3002 | TCP | vitara-admin-api (Express/PM2) | OCI ARM | localhost only |
| 5173 | TCP | Vite dev server | OCI ARM | Dev only, not production |
| 5432 | TCP | PostgreSQL 16 (Zatuka) | OCI ARM | Docker internal |
| 6379 | TCP | Redis (Zatuka) | OCI ARM | Docker internal |
| 8080 | TCP | OSCAR Tomcat | AWS EC2 | Public (dev only) |
| 3306 | TCP | MariaDB 10.5 | AWS EC2 | Docker internal |
Security Note
Port 3002 (Express API) is not exposed publicly. All external traffic reaches it through nginx reverse proxy on ports 80/443. The Zatuka database ports (5432, 6379) are internal to the Docker network. MariaDB on the dev OSCAR instance is also Docker-internal only.
Deployment Commands¶
Express API Server¶
# Build TypeScript and restart PM2 process
cd /home/ubuntu/vitara-platform/admin-dashboard/server
npx tsc
pm2 restart vitara-admin-api
# View logs
pm2 logs vitara-admin-api --lines 100
# Monitor process
pm2 monit
# Check status
pm2 status
Admin Dashboard Client¶
# Build the React client
cd /home/ubuntu/vitara-platform/admin-dashboard/client
npm run build
# Output goes to client/dist/, served by nginx automatically
# No restart needed — nginx serves static files directly
Documentation Site¶
# Build and deploy docs
source /tmp/mkdocs-env/bin/activate
cd /home/ubuntu/vitaravox-docs
mkdocs build --site-dir /tmp/vitdocs-site
cp -r /tmp/vitdocs-site/* /home/ubuntu/vitaravox-docs/site/
sudo nginx -s reload
Vapi GitOps (Voice Agent Config)¶
# Push voice agent configuration to Vapi
cd /home/ubuntu/vitara-platform/vapi-gitops
npm run push:dev
nginx Management¶
# Test configuration
sudo nginx -t
# Reload (graceful, no downtime)
sudo nginx -s reload
# View access logs
sudo tail -f /var/log/nginx/access.log
# View error logs
sudo tail -f /var/log/nginx/error.log
# Check virtual host configs
ls /etc/nginx/sites-enabled/
PM2 Process Management¶
# List all processes
pm2 list
# Restart with zero-downtime reload
pm2 reload vitara-admin-api
# Save process list (survives reboot)
pm2 save
# Startup script (run once)
pm2 startup
# View detailed process info
pm2 show vitara-admin-api
External Service Dependencies¶
vitara-admin-api (:3002)
|
+-------> Vapi.ai (Voice AI)
| Squads, Assistants, Tool webhooks
| v2.3.0 Squad: 775db28c (prod)
| v3.0 Squad: 13fdfd19 (deployed)
|
+-------> OpenAI
| GPT-4o (all agents, both EN and ZH)
|
+-------> ElevenLabs
| TTS: eleven_multilingual_v2
| English voice synthesis
|
+-------> Azure Cognitive Services
| TTS: zh-CN-XiaoxiaoNeural
| Mandarin voice synthesis
|
+-------> Deepgram
| STT: Nova-2
| Language-specific (en, zh)
|
+-------> AssemblyAI
| STT: Universal Multilingual
| Used by Router for language detection
|
+-------> Telnyx
| Phone number provisioning
| SIP trunking to Vapi
|
+-------> Kai OSCAR Pro (production EMR)
| OAuth 1.0a REST
| fbh.kai-oscar.com
|
+-------> Dev OSCAR (development EMR)
SOAP (WS-Security) + REST
15.222.50.48:8080
Full System Context Diagram¶
+===========================================================================+
| INTERNET |
+===========================================================================+
| | | | |
v v v v v
+--------+ +---------+ +----------+ +---------+ +---------+
| Patient | | Admin | | Docs | | Vapi | | Kai |
| Phone | | Browser | | Browser | | .ai | | OSCAR |
+----+----+ +----+----+ +----+-----+ +----+----+ +----+----+
| | | | ^
v | | | |
+--------+ | | | OAuth 1.0a
| Telnyx | | | | REST
+----+---+ | | | |
| | | | |
v | | | |
+--------+ | | | |
| Vapi | | | | |
| Squad |---------+--------------+--------------+ |
+----+---+ | | |
| | | |
| webhook | | |
v v v |
+===================================================================+
| OCI ARM Instance — Toronto Region |
| |
| +-------------------------------------------------------------+ |
| | nginx (:80/:443) — Let's Encrypt SSL | |
| | | |
| | api-dev. dev. vitdocs. | |
| | vitaravox.ca vitaravox.ca vitaravox.ca | |
| | | | | | |
| | proxy_pass +----+----+ static files | |
| | | | | | | |
| | | static proxy +--------+ | |
| | | files /api/* | site/ | | |
| | | dist/ | +--------+ | |
| | | | | |
| +-------+-----------------+------------------------------------+ |
| | | |
| +--------+--------+ |
| | |
| +--------v--------+ |
| | PM2: vitara- | |
| | admin-api |--------------------------------------+-->
| | Express :3002 | (to Kai OSCAR via HTTPS)
| | |
| | Prisma (SQLite) |
| +---------+-------+
| |
| | SOAP (WS-Security)
| v
| +-----------------+----+ +-------------------------+
| | /opt/zatuka-stack/ | | AWS EC2 ca-central-1 |
| | | | 15.222.50.48 |
| | Mattermost Outline | | |
| | Vikunja n8n | | OSCAR (Docker) |
| | Uptime Kuma | | Tomcat :8080 |
| | PostgreSQL 16 | | MariaDB :3306 |
| | Redis | | |
| +----------------------+ +-------------------------+
+====================================================================+
Architecture Summary
The entire VitaraVox platform runs on two compute instances plus managed external services. The OCI ARM instance in Toronto handles all web traffic, the Express API, documentation, and the Zatuka collaboration stack. The AWS EC2 instance in Montreal (ca-central-1) runs a development OSCAR EMR. Production EMR connectivity goes to Kai OSCAR Pro via OAuth 1.0a REST through Cloudflare.