# 3CX

The `3cx` plugin runs on a 3CX appliance and checks the things that break a phone system. It watches the 3CX systemd services, the SIP ports, the management console, and the tunnels between the PBX and its Session Border Controllers. It reads everything without root and without a 3CX API token. Turn it on for any 3CX PBX or SBC you are responsible for.

## Enable it

Add `3cx` to the `plugins` array and install the agent on the 3CX machine.

A PBX with one branch SBC watched from the PBX side:

```json
{
  "plugins": ["3cx"],
  "3cx": {
    "refreshInterval": 60000,
    "name": "pbx-01",
    "role": "pbx",
    "sbcs": [
      { "name": "branch-office", "peer": "198.51.100.25" }
    ]
  }
}
```

An SBC host:

```json
{
  "plugins": ["3cx"],
  "3cx": {
    "name": "sbc-01",
    "role": "sbc"
  }
}
```

For more than one instance on the same agent, list them under `instances`. Each entry takes the same keys as the flat form.

| Option | Default | What it does |
| --- | --- | --- |
| `refreshInterval` | `60000` | Milliseconds between polls. Section level only. |
| `instances` | none | Array of instances. Without it, the flat keys describe one instance. |
| `name` | the machine's hostname | Display name. A flat `name` of exactly `3cx` is ignored and the hostname is used. |
| `role` | `pbx` | `pbx` or `sbc`. |
| `host` | `127.0.0.1` | Where to probe the SIP and console ports. |
| `unitGlob` | `3CX*`, or `3cxsbc*` for an SBC | systemd unit pattern to check. |
| `versionPkg` | `3cxpbx`, or `3cxsbc` for an SBC | Debian package whose version is reported. |
| `sipPorts` | `[5060, 5061]` | PBX. Ports that must accept a TCP connection. |
| `consolePort` | `443` | PBX. Management console port. |
| `pbxTunnelPort` | `5090` | Tunnel port. An SBC connects out to it, a PBX accepts on it. |
| `sbcs` | none | PBX. SBCs to watch through their inbound tunnels. Each has a `name` and an optional `peer`, the SBC's public IP as the PBX sees it. Without `peer`, any inbound tunnel counts as connected. |
| `systemctlBin` | `systemctl` | systemctl binary. |
| `uniqueId` | `3cx-<name>` | Unique id for the service. |

## What it reports

Every payload has `kind: "3cx"`, `role`, `name`, `host`, `reachable` and `version`.

- `services`, every unit matching `unitGlob`, each with `unit`, `active`, `sub` and `ok`. `servicesTotal`, `servicesOk`, and `down` with the failed units.
- PBX: `sip`, each port with `listening`, and `sipOk` when all of them listen. `console` with `port` and `listening`.
- PBX with `sbcs`: `sbcs`, each with `name`, `peer` and `connected`, and `sbcTunnelCount`, the number of distinct peers on the tunnel port.
- SBC: `tunnelUp`, `tunnelPeer` and `tunnelPort`, from an established outbound connection to the tunnel port.
- `error`, when systemd could not be read or no matching units exist.

## When it alerts

The server checks these rules in order and the first match sets the status:

| Severity | Trigger |
| --- | --- |
| error | The payload has `error`. This covers a failed systemd read and an instance with no matching units. |
| error | `reachable` is false. |
| error | Any unit in `down`. The reason lists them. |
| error | SBC role and `tunnelUp` is false. |
| error | PBX and `sipOk` is false. The reason lists the dead ports. |
| warning | A configured SBC is not connected. The PBX is fine but phones behind that SBC are cut off. |
| warning | The console port is not listening. |

These alerts use the normal path. A problem notifies only after a second consecutive bad report, and the alert resolves when the instance reports healthy again.

## On the dashboard

The card is labelled "3CX · PBX" or "3CX · SBC" with the major and minor version.

- SERVICES shows healthy over total. Red when any is down or the instance is unreachable.
- SBC role: TUNNEL shows UP in green or DOWN in red.
- PBX role: SIP shows UP or DOWN, red when down. CONSOLE shows UP, or DOWN in amber. SBC shows connected over configured, amber when any is missing.
- VER shows the package version.
- The SERVICES list has one row per unit, red when not active.

A 3CX service with no report for 300 seconds shows OFFLINE.

## Requirements

- The agent on the 3CX machine itself. It is Debian-based.
- `systemctl` that supports `-o json` for `list-units`, `dpkg-query`, and `ss` from iproute2. None need root. 
## Gotchas

- The unit list includes inactive units. Any unit matching `unitGlob` that is meant to be stopped counts as down and raises an error. Narrow `unitGlob` if your install has such a unit.
- A oneshot unit that ran and exited reports `active`, so it counts as healthy.
- A PBX with no SBCs configured does not check the tunnel port at all.
- An SBC does not listen for SIP itself, so its health is the service plus the outbound tunnel. The tunnel check looks for any established connection to the tunnel port and does not compare it to a PBX address.
