Skip to content

Architecture Overview

Give a high-level map of the Get2Dial components and how traffic and data flow between them.

Get2Dial separates a shared control plane from per-tenant edge nodes. The control plane owns state and orchestration; the edge owns real-time media and carrier connectivity. They communicate over NATS (call events, originate requests) and node-authenticated HTTP (registration, config reads, media/recording sync).

flowchart LR
  subgraph Browser
    A[Tenant App]
    M[Admin Console]
    SP[Agent softphone - WebRTC]
  end
  subgraph Control["Control plane (shared)"]
    C[Control API :8080 - Go]
    P[Dialer pacer]
    DB[(PostgreSQL / TimescaleDB)]
    R[(Redis)]
    N{{NATS}}
    S[(MinIO)]
  end
  subgraph Edge["Tenant edge (one per tenant)"]
    O[OpenSIPS :7443 WSS / :5060]
    RE[rtpengine]
    F[FreeSWITCH :5066]
    CE[Call engine]
    NA[Node agent]
  end
  Carrier[(SIP carrier)]

  A --> C
  M --> C
  SP -. WSS .-> O
  C --- DB
  C --- R
  C --- S
  P --- C
  C <--> N
  N <--> CE
  CE -. ESL .-> F
  NA -->|register / heartbeat / sync| C
  O <--> RE
  RE <--> F
  O <--> F
  F <--> Carrier

The real media chain on the edge is Browser (WSS) → OpenSIPS → rtpengine → FreeSWITCH → carrier. OpenSIPS is the only SIP entry point; it authenticates, anchors media in rtpengine, then relays signaling to FreeSWITCH (which listens on 5066 to avoid colliding with OpenSIPS on 5060).

Each layer is configured independently:

A bridged outbound call crosses every layer: the pacer (control plane) publishes an originate to t.<tenant>.node.<NODE_ID>.call.originate, the edge call engine drives FreeSWITCH to dial the carrier, runs AMD, and on a human answer bridges the agent leg back through the local OpenSIPS so the WebRTC softphone rings.

  • The control plane is multi-tenant; each edge is single-tenant.
  • Tenant identity is stamped on the edge (g2d_tenant_id, sourced from a SIP header or the node’s NODE_TENANT_ID) and flows back with CDRs — it is never pinned on the shared control plane.
  • Defense-in-depth tenant isolation: Postgres row-level security keyed on app.tenant_id, plus per-tenant Redis ACLs and NATS subject permissions scoped to t.<tenant>.>.