Skip to content

Platform Architecture

Version: 4.0.0 (SOAP Adapter Live + Onboarding Redesign) Last Updated: 2026-02-15 Validated: Every fact verified against running production system Status: Production — SOAP adapter active, REST bridge deprecated

This document provides a complete visual representation of the VitaraVox platform architecture across all layers — infrastructure, voice AI, server middleware, EMR integration, and database.


System Overview — Validated Infrastructure

╔══════════════════════════════════════════════════════════════════════════════╗
║                        VITARAVOX PLATFORM — VALIDATED                      ║
║                        2026-02-15 — All facts checked                      ║
╚══════════════════════════════════════════════════════════════════════════════╝

  📱 Patient's Phone
       │  Regular phone call
┌─────────────────────┐
│      VAPI.AI         │   Cloud Service
│  AI Voice Platform   │   - Answers the phone
│                      │   - Listens & speaks
│  +1 236-305-7446     │   - Thinks (GPT-4o)
│                      │
│  ┌───────────────┐   │
│  │ SQUAD v3.0    │   │   ID: 13fdfd19-a2cd-4ca4-8e14-ad2275095e32
│  │ (9 agents)    │   │
│  │               │   │
│  │  Router       │   │   Detects language, routes caller
│  │       │       │   │
│  │  ┌────┴────┐  │   │
│  │  ▼         ▼  │   │
│  │  EN       ZH  │   │   Two parallel tracks
│  │ Track   Track │   │
│  │  │         │  │   │
│  │  ├─Patient ├─Patient     Find caller's medical file
│  │  │  ID     │  ID    │
│  │  ├─Booking ├─Booking     Book new appointments
│  │  ├─Modify  ├─Modify      Reschedule or cancel
│  │  └─Register└─Register    Sign up new patients
│  └───────────────┘   │
└──────────┬────────────┘
           │  HTTPS webhook (tool-calls)
           │  Authenticated with API key (x-api-key header)
┌──────────────────────────────────────┐
│   VITARA PLATFORM SERVER              │
│   Oracle Cloud (OCI)                  │   ca-toronto-1
│   VM.Standard.A1.Flex (ARM64)         │   2 OCPUs, 24GB RAM
│   instance-20251030-1238              │
│   Port 3002 — PM2 process:            │
│     "vitara-admin-api"                │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │  Webhook Handler                  │ │  vapi-webhook.ts
│  │  Routes: /api/vapi (primary)      │ │  /vapi-webhook (legacy)
│  │  Auth: x-api-key verified         │ │
│  └──────────┬───────────────────────┘ │
│             │                          │
│  ┌──────────▼───────────────────────┐ │
│  │  Booking Engine                   │ │  booking.service.ts
│  │  (The Brain)                      │ │
│  │                                   │ │  Checks in order:
│  │  1. Holiday check (from DB)       │ │  ← clinic_holidays table
│  │  2. Day-of-week open/closed       │ │  ← clinic_hours table
│  │  3. Max advance booking days      │ │  ← clinic_config table
│  │  4. Fetch schedule from OSCAR     │ │  ← SOAP call
│  │  5. Fetch existing appointments   │ │  ← SOAP call
│  │  6. Filter: hours, overlap, limit │ │  ← per-slot checks
│  │                                   │ │
│  │  Booking uses advisory locks      │ │  PostgreSQL pg_try_advisory_lock
│  │  to prevent double-booking        │ │
│  └──────────┬───────────────────────┘ │
│             │                          │
│  ┌──────────▼───────────────────────┐ │
│  │  SOAP Adapter (ACTIVE)            │ │  OscarSoapAdapter.ts
│  │  WS-Security: UsernameToken       │ │  passwordType: PasswordText
│  │  (no Timestamp, no Nonce)         │ │  hasTimeStamp: false
│  │  Circuit breaker: 10s timeout     │ │  opossum library
│  │                                   │ │
│  │  Bridge Adapter (DEPRECATED)      │ │  OscarBridgeAdapter.ts
│  │  REST/JSON + X-API-Key            │ │  Dev/fallback only
│  └──────────┬───────────────────────┘ │
│             │                          │
│  ┌──────────────────────────────────┐ │
│  │  PostgreSQL (LOCAL)               │ │  localhost:5432
│  │  Database: vitara_platform        │ │
│  │  Tables: clinic_config,           │ │
│  │    clinic_hours, clinic_holidays, │ │
│  │    call_logs, waitlist, audit_log │ │
│  └──────────────────────────────────┘ │
└──────────┬────────────────────────────┘
           │  SOAP/XML over HTTPS (port 8443)
           │  WS-Security UsernameToken
           │  Direct connection, no middleman
           │  ← Crosses from OCI Toronto to AWS Montreal →
┌──────────────────────────────────────┐
│   OSCAR EMR                           │
│   AWS ca-central-1 (Montreal)         │   ec2-15-222-50-48
│   https://15.222.50.48:8443/oscar     │
│                                        │
│  3 SOAP Endpoints:                     │
│  ┌──────────────────────────────────┐ │
│  │  /ws/ScheduleService?wsdl        │ │  Schedules & appointments
│  │  /ws/DemographicService?wsdl     │ │  Patient records
│  │  /ws/ProviderService?wsdl        │ │  Doctor list
│  └──────────────────────────────────┘ │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │  MariaDB 10.5                     │ │  The actual patient data
│  │  (oscar_mcmaster database)        │ │
│  └──────────────────────────────────┘ │
└──────────────────────────────────────┘

Voice Call Data Flow — Validated Booking Sequence

Every method name, argument format, and check order verified against running code.

╔══════════════════════════════════════════════════════════════════════════════╗
║              WHAT HAPPENS WHEN A PATIENT CALLS TO BOOK                     ║
║              Every method name verified against running code               ║
╚══════════════════════════════════════════════════════════════════════════════╝

 Patient dials +1 236-305-7446
       │ 1. "Hi, I'd like to book an appointment"
 ┌─ VAPI ROUTER (assistant: router-v3) ──────────────────────────────────┐
 │  Detects: English                                                      │
 │  Calls: handoff_to_patient_id_en                                       │
 │  Hands off to: vitara-patient-id-en-v3                                 │
 └────┬───────────────────────────────────────────────────────────────────┘
      │ 2. "Let me pull up your file. Can I get your name?"  →  "John Smith"
 ┌─ PATIENT-ID-EN (assistant: patient-id-en) ─────────────────────────────┐
 │                                                                         │
 │  Calls webhook: search_patient({ name: "Smith" })                      │
 │           │                                                             │
 │           ▼                                                             │
 │  ┌─ OCI SERVER (vapi-webhook.ts → booking.service.ts) ────────┐       │
 │  │                                                             │       │
 │  │  SOAP Adapter → DemographicService                          │       │
 │  │  Method: searchDemographicByNameAsync                       │       │
 │  │  Args: { arg0: "Smith", arg1: 0, arg2: 20 }               │       │
 │  │         (search term, offset, limit)                        │       │
 │  │       │                                                     │       │
 │  │       ▼  SOAP/XML over HTTPS to AWS Montreal                │       │
 │  │                                                             │       │
 │  │  OSCAR returns: { demographicNo: 11,                        │       │
 │  │    firstName: "John", lastName: "Smith" }                   │       │
 │  │       │                                                     │       │
 │  │  Returns: { found: true, patient: { id: "11", ... } }      │       │
 │  └─────────────────────────────────────────────────────────────┘       │
 │                                                                         │
 │  "I found John Smith. Is that you?"  →  "Yes!"                        │
 │  "What can I help you with?"  →  "I need an appointment"              │
 │                                                                         │
 │  Calls: handoff_to_booking_en                                          │
 │  Hands off to: vitara-booking-en-v3                                    │
 └────┬───────────────────────────────────────────────────────────────────┘
      │ 3. Silent handoff — patient doesn't notice
 ┌─ BOOKING-EN (assistant: booking-en) ───────────────────────────────────┐
 │                                                                         │
 │  "When would you like to come in?"  →  "Next Tuesday morning"          │
 │                                                                         │
 │  Calls webhook: get_available_slots                                    │
 │    { startDate: "2026-02-17", providerId: "999998" }                   │
 │           │                                                             │
 │           ▼                                                             │
 │  ┌─ OCI SERVER (booking.service.ts → getTrueAvailability) ────┐       │
 │  │                                                             │       │
 │  │  CHECK 1: Is Feb 17 a holiday?                              │       │
 │  │    → Query clinic_holidays table (local PostgreSQL)         │       │
 │  │    → No match → Continue                                    │       │
 │  │                                                             │       │
 │  │  CHECK 2: Is Tuesday open?                                  │       │
 │  │    → Query clinic_hours table                               │       │
 │  │    → Tuesday: open 09:00-14:00 → Continue                   │       │
 │  │                                                             │       │
 │  │  CHECK 3: Is Feb 17 within max advance booking days?        │       │
 │  │    → Yes → Continue                                         │       │
 │  │                                                             │       │
 │  │  SOAP CALL 1: Get doctor's schedule template                │       │
 │  │    → ScheduleService                                        │       │
 │  │    → Method: getDayWorkScheduleAsync                        │       │
 │  │    → Args: { arg0: "999998", arg1: "2026-02-17T00:00:00" } │       │
 │  │    → Returns: time slots the doctor has that day            │       │
 │  │                                                             │       │
 │  │  SOAP CALL 2: Get already-booked appointments               │       │
 │  │    → ScheduleService                                        │       │
 │  │    → Method: getAppointmentsForDateRangeAndProvider2Async   │       │
 │  │    → Args: { arg0: "2026-02-17T00:00:00",                  │       │
 │  │             arg1: "2026-02-17T23:59:59",                    │       │
 │  │             arg2: "999998", arg3: false }                   │       │
 │  │    → Returns: existing bookings                             │       │
 │  │                                                             │       │
 │  │  FILTER: For each template slot:                            │       │
 │  │    → Remove if outside clinic hours (09:00-14:00 Tue)       │       │
 │  │    → Remove if too soon (min advance hours)                 │       │
 │  │    → Remove if overlaps with existing booking               │       │
 │  │    → Remove if slot hit its booking limit                   │       │
 │  │                                                             │       │
 │  │  Returns: slots: ["9:30","9:45","10:00", ...]              │       │
 │  └─────────────────────────────────────────────────────────────┘       │
 │                                                                         │
 │  "I have 9:30, 9:45, or 10:00 AM. What works?"  →  "9:30 please"     │
 │                                                                         │
 │  Calls webhook: create_appointment                                     │
 │    { patientId:"11", providerId:"999998",                              │
 │      appointmentTime:"2026-02-17 09:30", reason:"Checkup" }           │
 │           │                                                             │
 │           ▼                                                             │
 │  ┌─ OCI SERVER (booking.service.ts → bookAppointment) ────────┐       │
 │  │                                                             │       │
 │  │  STEP 1: Acquire advisory lock                              │       │
 │  │    → pg_try_advisory_lock("provider:999998:date:            │       │
 │  │      2026-02-17:slot:09:30")                                │       │
 │  │    → Lock acquired (no one else booking this slot)          │       │
 │  │                                                             │       │
 │  │  STEP 2: FRESH availability check                           │       │
 │  │    → Calls getTrueAvailability() AGAIN                      │       │
 │  │    → Confirms 9:30 is still free                            │       │
 │  │                                                             │       │
 │  │  STEP 3: Book via SOAP                                      │       │
 │  │    → ScheduleService                                        │       │
 │  │    → Method: addAppointmentAsync                            │       │
 │  │    → Args: { arg0: {                                        │       │
 │  │        providerNo: "999998",                                │       │
 │  │        demographicNo: 11,                                   │       │
 │  │        appointmentStartDateTime: "2026-02-17T09:30:00",     │       │
 │  │        appointmentEndDateTime: "2026-02-17T09:45:00",       │       │
 │  │        reason: "Checkup",                                   │       │
 │  │        status: "t"    ("t" = To Do)                         │       │
 │  │      }}                                                     │       │
 │  │                                                             │       │
 │  │  STEP 4: Release advisory lock (always, even on error)      │       │
 │  │                                                             │       │
 │  │  Returns: { success: true, appointmentId: 4527 }            │       │
 │  └─────────────────────────────────────────────────────────────┘       │
 │                                                                         │
 │  "You're all set! Tuesday Feb 17th at 9:30 AM."                       │
 │  "Is there anything else?"  →  "No, thank you!"                       │
 │                                                                         │
 │  Calls webhook: log_call_metadata                                      │
 │    { action:"booking", outcome:"success", patientId:"11" }            │
 │    → Saved to local PostgreSQL (call_logs table)                       │
 │                                                                         │
 └────┬───────────────────────────────────────────────────────────────────┘
      │ 4. Call ends
 ┌────────────────────────────────────────────────────────────────────────┐
 │                                                                        │
 │  Appointment now in OSCAR's MariaDB (AWS Montreal)                    │
 │    → Visible to clinic staff in OSCAR's web interface                 │
 │                                                                        │
 │  Call log saved in PostgreSQL (OCI Toronto)                           │
 │    → Available for analytics in admin dashboard                       │
 │                                                                        │
 │  Patient talked to 3 AI agents (Router → Patient-ID → Booking)       │
 │    → Never knew — all silent handoffs                                 │
 │                                                                        │
 │  Two servers involved:                                                │
 │    OCI Toronto (our code) <-> AWS Montreal (OSCAR EMR)                │
 │    Connected by direct SOAP/XML — no middleman                        │
 │                                                                        │
 └────────────────────────────────────────────────────────────────────────┘

Vapi Squad v3.0 — Agent Routing Map

Validated against /home/ubuntu/vitara-platform/vapi-gitops/resources/squads/vitaravox-v3.yml.

                              +1 236-305-7446
                          ┌─────────────────┐
                          │     ROUTER      │  AssemblyAI Universal STT
                          │   (router-v3)   │  "Hi, this is Vitara Clinic."
                          └────────┬────────┘
                    ┌──────────────┴──────────────┐
                    │                              │
        handoff_to_patient_id_en       handoff_to_patient_id_zh
                    │                              │
                    ▼                              ▼
          ┌─────────────────┐            ┌─────────────────┐
          │  PATIENT-ID-EN  │            │  PATIENT-ID-ZH  │
          │ (patient-id-en) │            │ (patient-id-zh) │
          │ Deepgram EN     │            │ Deepgram ZH     │
          │ "Let me pull up │            │ "请稍等,我帮您   │
          │  your file."    │            │  查一下信息。"    │
          └───────┬─────────┘            └───────┬─────────┘
                  │                              │
     ┌────────────┼────────────┐    ┌────────────┼────────────┐
     │            │            │    │            │            │
     ▼            ▼            ▼    ▼            ▼            ▼
┌─────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ ┌──────────┐ ┌────────┐
│BOOKING  │ │MODIFICA- │ │REGIS-  │ │BOOKING  │ │MODIFICA- │ │REGIS-  │
│   EN    │ │ TION EN  │ │TRATION │ │   ZH    │ │ TION ZH  │ │TRATION │
│(booking │ │(modifica │ │  EN    │ │(booking │ │(modifica │ │  ZH    │
│  -en)   │ │ tion-en) │ │(regis- │ │  -zh)   │ │ tion-zh) │ │(regis- │
│         │ │          │ │tration │ │         │ │          │ │tration │
│Eleven-  │ │          │ │  -en)  │ │Azure    │ │          │ │  -zh)  │
│Labs TTS │ │          │ │        │ │ZH TTS   │ │          │ │        │
└────┬────┘ └────┬─────┘ └───┬────┘ └────┬────┘ └────┬─────┘ └───┬────┘
     │           │           │           │           │           │
     └───────────┴───────────┘           └───────────┴───────────┘
              │                                    │
    Cross-handoffs available:           Cross-handoffs available:
    Booking ↔ Modification              Booking ↔ Modification
    Booking/Mod → Router                Booking/Mod → Router
    Registration → Booking              Registration → Booking

All 9 agents share the same 19 webhook tools — the server handles logic regardless of which agent calls.


Server Architecture

Middleware Chain (execution order)

Request
┌─────────────────┐
│ trust proxy = 1 │  Correct client IP behind NGINX/Caddy
└────────┬────────┘
┌─────────────────┐
│ helmet()        │  Security headers (X-Frame-Options, CSP, HSTS, etc.)
└────────┬────────┘
┌─────────────────┐
│ pino-http       │  Structured JSON request logging with x-request-id
└────────┬────────┘
┌─────────────────┐
│ cors()          │  Origin control for browser requests
└────────┬────────┘
┌─────────────────┐
│ express.json()  │  Body parsing (1MB limit)
└────────┬────────┘
┌─────────────────┐
│ auditMiddleware │  Captures POST/PUT/DELETE → AuditLog table
└────────┬────────┘  (redacts passwords and secrets)
┌─────────────────┐
│ rate-limit      │  Per-IP throttling:
│                 │   Auth:    5 req/min
│                 │   Webhook: 300 req/min
│                 │   API:     100 req/min
└────────┬────────┘
      Routes

Route Layer

┌─────────────────────────────────────────────────────────────────────┐
│                         ROUTE STRUCTURE                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  auth.ts (/api/auth/*)                                              │
│  ├── POST /login          → JWT access + refresh tokens              │
│  ├── POST /refresh        → Renew access token                      │
│  ├── GET  /me             → Current user profile                     │
│  └── POST /logout         → Invalidate session                      │
│                                                                      │
│  vapi-webhook.ts (/api/vapi, /vapi-webhook)                         │
│  ├── vapiWebhookAuth      → API Key verified (x-api-key header)     │
│  └── 19 tool handlers     → Patient, appointment, registration...   │
│                                                                      │
│  api.ts (/api/*) — 87+ endpoints                                    │
│  ├── PUBLIC:  GET /oscar/health                                      │
│  ├── CLINIC:  /clinic/me, /stats, /calls, /settings, /analytics     │
│  ├── ONBOARD: /clinic/onboarding/*, test-slots, test-patient-search │
│  ├── ADMIN:   /admin/clinics, /stats, /health, /audit               │
│  ├── PROVISION: /admin/clinics/:id/activate, pending-activation     │
│  ├── OSCAR:   /api/oscar/patients, providers, appointments          │
│  └── NOTIFY:  /api/notifications/list, read, read-all               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

19 Webhook Tool Handlers

All verified working via E2E test (35/35 pass — 2026-02-14):

Category Tools EMR Path
Patient search_patient, search_patient_by_phone, get_patient SOAP → DemographicService
Provider get_providers SOAP → ProviderService
Schedule get_available_slots, find_earliest_appointment, check_appointments SOAP → ScheduleService
Booking create_appointment, cancel_appointment, update_appointment SOAP → ScheduleService
Registration register_new_patient Returns NOT_SUPPORTED (SOAP has no addDemographic)
Clinic get_clinic_info, checkNewPatientAcceptance Local PostgreSQL only
Utility transfer_call, log_call_metadata, add_to_waitlist Local PostgreSQL only
Error Unknown tool names Returns structured error

EMR Adapter Layer

┌─────────────────────────────────────────────────────────────────────┐
│                      EMR ADAPTER LAYER (v4.0)                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  EmrAdapterFactory.getAdapter(clinicId)                              │
│            │                                                         │
│            ▼                                                         │
│  ┌───────────────────────┐                                          │
│  │   IEmrAdapter         │ (common interface)                       │
│  │   healthCheck()       │                                          │
│  │   searchPatients()    │                                          │
│  │   getPatient()        │                                          │
│  │   createPatient()     │                                          │
│  │   getProviders()      │                                          │
│  │   getScheduleSlots()  │  ← New in v4.0                          │
│  │   getAvailability()   │                                          │
│  │   getAppointment()    │  ← New in v4.0                          │
│  │   bookAppointment()   │                                          │
│  │   cancelAppointment() │                                          │
│  └───────────┬───────────┘                                          │
│              │                                                       │
│    ┌─────────┼─────────┬───────────────┐                            │
│    ▼         ▼         ▼               ▼                            │
│  ┌────────┐ ┌────────┐ ┌────────┐   ┌────────┐                     │
│  │ OSCAR  │ │ OSCAR  │ │ Accuro │   │ Telus  │                     │
│  │ SOAP   │ │ Bridge │ │(Future)│   │(Future)│                     │
│  │ ACTIVE │ │DEPREC. │ │        │   │        │                     │
│  │        │ │        │ │        │   │        │                     │
│  │ WS-Sec │ │REST/   │ │OAuth2.0│   │SAML/   │                     │
│  │ SOAP   │ │X-API-  │ │        │   │OAuth   │                     │
│  │        │ │Key     │ │        │   │        │                     │
│  └────┬───┘ └────┬───┘ └────────┘   └────────┘                     │
│       │          │                                                   │
│       ▼          ▼                                                   │
│  OSCAR SOAP   OSCAR REST Bridge                                     │
│  (Direct)     (15.222.50.48:3000)                                   │
│  Port 8443    Legacy, dev-only                                       │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

SOAP Adapter — Method Inventory

All method names verified against OSCAR WSDLs:

Operation SOAP Service Method Arguments
Search patient DemographicService searchDemographicByNameAsync arg0: name, arg1: offset, arg2: limit
Get patient DemographicService getDemographicAsync arg0: demographicNo
Create patient NOT AVAILABLE OSCAR SOAP has no addDemographic
Get providers ProviderService getProviders2Async arg0: activeOnly (boolean)
Get schedule ScheduleService getDayWorkScheduleAsync arg0: providerNo, arg1: dateTime (ISO)
Get appointments ScheduleService getAppointmentsForDateRangeAndProvider2Async arg0: startDate, arg1: endDate, arg2: providerNo, arg3: useGMT
Book appointment ScheduleService addAppointmentAsync arg0:
Cancel/update ScheduleService updateAppointmentAsync arg0:

Critical SOAP notes:

  • All methods use positional args (arg0, arg1, etc.) — NOT named parameters
  • DateTime values must be ISO format: 2026-02-17T00:00:00
  • WS-Security: UsernameToken with passwordType: PasswordText, hasTimeStamp: false, hasNonce: false
  • OSCAR CXF WSS4J only validates UsernameToken — adding Timestamp causes SecurityError

Database Schema

┌─────────────────────────────────────────────────────────────────────┐
│                   POSTGRESQL — 13 MODELS (Prisma ORM)                │
│                   localhost:5432/vitara_platform                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  CORE                                                                │
│  ┌─────────────────┐                                                │
│  │     Clinic       │─────┬──────────┬──────────┬──────────┐       │
│  │  id, name, slug, │     │          │          │          │       │
│  │  status, timezone│     │          │          │          │       │
│  └────────┬────────┘     │          │          │          │       │
│           │ 1:1          │ 1:7      │ 1:N      │ 1:N      │       │
│           ▼              ▼          ▼          ▼          ▼       │
│  ┌──────────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐ ┌────────┐│
│  │ ClinicConfig │ │ Clinic   │ │ Clinic  │ │Waitlist│ │CallLog ││
│  │              │ │ Hours    │ │ Provider│ │ Entry  │ │        ││
│  │ EMR type     │ │ (7 days) │ │         │ │        │ │        ││
│  │ SOAP creds   │ │          │ │ OSCAR   │ │ Status │ │ Intent ││
│  │ Vapi IDs     │ │ Open/    │ │ mapping │ │ mgmt   │ │ Outcome││
│  │ Privacy      │ │ close    │ │         │ │        │ │ Transcrp││
│  │ BAA status   │ │ times    │ │         │ │        │ │        ││
│  │ Retention    │ └──────────┘ └─────────┘ └────────┘ └────────┘│
│  │ Greeting     │                                                │
│  │ Pharmacy     │                                                │
│  └──────────────┘                                                │
│                                                                      │
│  ┌─────────────┐     ┌───────────────┐     ┌───────────────┐      │
│  │    User      │     │ ClinicHoliday │     │OnboardingProg.│      │
│  │ email, role, │     │ date, name    │     │ 8 boolean     │      │
│  │ clinicId     │     │ (per clinic)  │     │ flags +       │      │
│  │              │     │               │     │ completedAt   │      │
│  └─────────────┘     └───────────────┘     └───────────────┘      │
│                                                                      │
│  COMPLIANCE                          SUPPORT                        │
│  ┌──────────────────┐  ┌─────────────────────────────────┐         │
│  │   AuditLog        │  │ SupportTicket + TicketMessage   │         │
│  │   (append-only)   │  │ + Notification                  │         │
│  └──────────────────┘  └─────────────────────────────────┘         │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Security Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                    SECURITY CONTROLS (6 layers)                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Layer 1: Network                                                    │
│  ├── NGINX reverse proxy with TLS (Let's Encrypt)                   │
│  ├── OCI Security Lists (firewall rules)                            │
│  └── trust proxy = 1 (correct IP for rate limiting)                 │
│                                                                      │
│  Layer 2: Transport                                                  │
│  ├── Helmet security headers (CSP, HSTS, X-Frame-Options)          │
│  └── CORS origin control                                            │
│                                                                      │
│  Layer 3: Rate Limiting                                              │
│  ├── Auth endpoints: 5 req/min per IP                               │
│  ├── Webhook endpoints: 300 req/min per IP                          │
│  └── API endpoints: 100 req/min per IP                              │
│                                                                      │
│  Layer 4: Authentication                                             │
│  ├── JWT access tokens (1h) + refresh tokens (7d)                   │
│  ├── bcrypt password hashing (cost factor 12)                       │
│  ├── Vapi webhook: API key (x-api-key header)                       │
│  ├── OSCAR SOAP: WS-Security UsernameToken (PasswordText)          │
│  └── Password max-length 128 (bcrypt DoS prevention)                │
│                                                                      │
│  Layer 5: Authorization                                              │
│  ├── Role-based: vitara_admin, clinic_manager                       │
│  ├── requireRole() middleware for admin routes                      │
│  ├── requireClinicAccess middleware (tenant isolation)              │
│  └── Advisory locks for concurrent booking protection               │
│                                                                      │
│  Layer 6: Data Protection                                            │
│  ├── AES-256-GCM encryption for EMR credentials                    │
│  ├── ENCRYPTION_KEY required in production (env validation)         │
│  ├── Credential masking in API responses                            │
│  ├── Zod .strict() schema validation (reject unknown fields)        │
│  ├── AuditLog on all mutations (PIPEDA 4.1.4 compliance)           │
│  └── Data retention auto-purge (configurable per clinic)            │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Connection Status (as of 2026-02-15)

Component Status Details
PostgreSQL Running localhost:5432/vitara_platform (local, OCI Toronto)
Express Server Running PM2 vitara-admin-api, port 3002
OSCAR SOAP Active https://15.222.50.48:8443/oscar (AWS Montreal) via WS-Security
OSCAR REST Bridge Deprecated 15.222.50.48:3000 — dev/fallback only, zero production calls
Vapi.ai Deployed v3.0 squad 13fdfd19, phone +1 236-305-7446
E2E Tests 35/35 pass All 19 tools verified, auth enforced, SOAP confirmed

Technology Stack

Layer Technology Purpose
Hosting Oracle Cloud (OCI) ca-toronto-1, ARM64 Canadian data residency
Runtime Node.js + Express 4.18 HTTP server
Language TypeScript Type safety
ORM Prisma 5.22 Database access
Database PostgreSQL 16 (local) Primary data store
EMR Integration node-soap + WS-Security OSCAR SOAP adapter
Validation Zod Request + env validation
Logging Pino Structured JSON logs
Security Helmet HTTP security headers
Rate Limiting express-rate-limit Brute-force protection
Circuit Breaker Opossum (10s timeout) SOAP failure isolation
Scheduling node-cron Data retention jobs
Encryption Node.js crypto AES-256-GCM
Auth jsonwebtoken + bcryptjs JWT + password hashing
Voice AI Vapi.ai 9-agent bilingual squad
LLM OpenAI GPT-4o Intent understanding
STT AssemblyAI Universal / Deepgram Nova-2 Speech recognition
TTS ElevenLabs (EN) / Azure Xiaoxiao (ZH) Speech synthesis
OSCAR EMR AWS ca-central-1 (Montreal) Patient data, MariaDB 10.5

Document validated: 2026-02-14 — 35/35 E2E tests passing, all facts verified against running system