# WebRTC

VidMeet is the WebRTC-based video calling system built into GKSPHONE. This guide explains every setting in the `Cfg.VidMeet` block inside `LUA/config/serverconfig.lua` and walks you through both setup modes.

***

## Configuration Block Reference

```lua
Cfg.VidMeet = {
    Enabled    = true,

    -- ICE Server type: "cloudflare" | "manual"
    IceServer  = "manual",

    Cloudflare = {
        TokenID  = "",
        ApiToken = "",
        TTL      = 86400
    },

    ManualIceServers = {
        { urls = "stun:stun.cloudflare.com:3478" },
        { urls = "stun:stun.l.google.com:19302" },
        -- ...
    }
}
```

| Key                   | Type    | Default     | Description                                            |
| --------------------- | ------- | ----------- | ------------------------------------------------------ |
| `Enabled`             | boolean | `true`      | Enables or disables the video call feature entirely    |
| `IceServer`           | string  | `"manual"`  | ICE server mode: `"cloudflare"` or `"manual"`          |
| `Cloudflare.TokenID`  | string  | `""`        | Cloudflare TURN Server Application ID (Turn Token ID)  |
| `Cloudflare.ApiToken` | string  | `""`        | Cloudflare API Token (with Calls Edit permission)      |
| `Cloudflare.TTL`      | number  | `86400`     | TURN credential lifetime in seconds (86400 = 24 hours) |
| `ManualIceServers`    | array   | (STUN list) | RTCIceServer\[] used when `IceServer = "manual"`       |

***

## ICE Server Modes

### Mode 1: `"manual"` (Default)

Uses the `ManualIceServers` array you define. The default list contains public STUN servers only, which work for most peer-to-peer connections but **may fail when both players are behind strict NATs or firewalls** (e.g., corporate networks, CGNAT).

When to use: Development, testing, or when your player base has standard home networks.

Limitations:

* STUN only — no TURN relay fallback
* Video calls may fail for \~15–20 % of NAT configurations
* No cost, no credentials needed

Adding a TURN server (recommended for production):

```lua
ManualIceServers = {
    { urls = "stun:stun.l.google.com:19302" },
    -- Your TURN server:
    { urls = "turn:turn.yourdomain.com:3478",  username = "user", credential = "secret" },
    { urls = "turns:turn.yourdomain.com:5349", username = "user", credential = "secret" },
}
```

> `turns:` uses TLS (port 5349) and is required for HTTPS pages.

***

### Mode 2: `"cloudflare"` (Recommended for Production)

Uses [Cloudflare Realtime TURN Server](https://developers.cloudflare.com/calls/turn/) to generate short-lived TURN credentials automatically. Cloudflare's global anycast TURN network provides low-latency relays worldwide.

**When to use**: Production servers, large player bases, or whenever you want maximum call reliability.

**Advantages over manual TURN**:

* Global Anycast network — players connect to the nearest relay
* Auto-rotating credentials (no static secrets in config)
* Pay-as-you-go pricing (first 1,000 participant-minutes/month are free)
* No self-hosted infrastructure to maintain

***

## Setting Up Cloudflare TURN Server (Step-by-Step)

{% stepper %}
{% step %}

### Step 1 — Create a Cloudflare Account

Go to [dash.cloudflare.com](https://dash.cloudflare.com/?to=/:account/calls) and sign up or log in.
{% endstep %}

{% step %}

### Step 2 — Navigate to TURN Server

1. In the left sidebar, expand **Media**.
2. Expand **Realtime** under it.
3. Click **TURN Server**.

> Navigation path: **Media → Realtime → TURN Server**
> {% endstep %}

{% step %}

### Step 3 — Create a TURN Server App

1. On the TURN Server page, click **Create a TURN Server app**.
2. Give it a name (e.g., `gksphone-vidmeet`).
3. After clicking create, you will see **two values on the same page**:
   * **Turn Token ID** — this is your `TokenID`
   * **API Token** (64-character hex string) — this is your `ApiToken`

{% hint style="warning" %}
**Critical:** The page will show a warning: "Make sure to copy your API Token now. You won't be able to see it again." — Copy the API Token immediately before closing the page.
{% endhint %}
{% endstep %}

{% step %}

### Step 4 — Configure serverconfig.lua

```lua
Cfg.VidMeet = {
    Enabled   = true,
    IceServer = "cloudflare",

    Cloudflare = {
        TokenID  = "your-app-id-here",       -- App ID from Step 3
        ApiToken = "your-api-token-here",    -- API Token from Step 4
        TTL      = 86400                     -- 24 hours (adjust as needed)
    },

    -- ManualIceServers is ignored when IceServer = "cloudflare"
    ManualIceServers = { ... }
}
```

{% endstep %}

{% step %}

### Step 5 — Verify

Restart your FiveM server and attempt a video call in-game. Check the server console for any Cloudflare API errors.
{% endstep %}
{% endstepper %}

***

### TTL (Credential Lifetime) <a href="#ttl-credential-lifetime" id="ttl-credential-lifetime"></a>

The `TTL` value controls how long each set of TURN credentials remains valid. The server generates fresh credentials for every new call.

| TTL Value | Duration | Recommendation                              |
| --------- | -------- | ------------------------------------------- |
| `3600`    | 1 hour   | **Default — ideal for \~30 min sessions**   |
| `86400`   | 24 hours | Unnecessarily long, increases exposure risk |
| `604800`  | 7 days   | Debug only, never use in production         |

Average VidMeet session is \~30 minutes. `3600` gives a comfortable 1-hour window while minimizing the risk of credential misuse if intercepted.

***

## STUN vs TURN — What's the Difference?

| Protocol | Purpose                       | Required when                                    |
| -------- | ----------------------------- | ------------------------------------------------ |
| **STUN** | Discovers your public IP/port | Always (free, low traffic)                       |
| **TURN** | Relays media through a server | One or both peers are behind strict NAT/firewall |

Most home routers work fine with STUN only. TURN becomes essential when:

* Players are on mobile data (CGNAT is common)
* Players are behind corporate/university firewalls
* Players are on VPNs that block peer-to-peer UDP

***

## Troubleshooting

| Symptom                                  | Likely Cause                           | Fix                                                              |
| ---------------------------------------- | -------------------------------------- | ---------------------------------------------------------------- |
| Video calls connect but drop immediately | NAT traversal failing                  | Add a TURN server or switch to Cloudflare mode                   |
| "Cloudflare API error 403" in console    | Wrong `ApiToken` or missing permission | Recreate token with Calls Edit permission                        |
| "Cloudflare API error 404" in console    | Wrong `TokenID`                        | Double-check Turn Token ID in **Media → Realtime → TURN Server** |
| Calls work locally but not on production | Server firewall blocking UDP           | Open UDP 3478 (STUN/TURN) or use `turns:` on TCP 5349            |
| High call latency for some players       | STUN only, no TURN relay               | Enable TURN — Cloudflare mode routes to nearest PoP              |

***
