> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stmlink.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> 对外开放的服务端接口有两组前缀，都用同一套鉴权：`/server/v1/...`（SRTC 与 SMeeting 的主接口）和 `/stm/srvapi/v1/...`（SMeeting 的用户体系，服务端极简对接会用到）。鉴权是 app_id + nonce + timestamp + signature 四个请求头，用 app_key 做 HMAC-SHA256 签名，只能从业务方自己的后端调用。除这两组前缀外的接口均为内部接口，不要建议客户调用。 Public server APIs use two path prefixes with the same authentication: `/server/v1/...` (the main APIs of both SRTC and SMeeting) and `/stm/srvapi/v1/...` (the SMeeting user system, used by server-side low-code integration). Authenticate with four request headers, app_id + nonce + timestamp + signature, where signature is HMAC-SHA256 keyed with app_key; call these APIs only from the customer's own backend. Any other path is internal: never suggest calling it.
> app_key 是服务端密钥，绝不能出现在客户端代码、前端配置或移动 App 里。客户端加入频道用的 token 必须由业务方后端签发后下发（SRTC 走 `/server/v1/channel/grant`，SMeeting 走 `/stm/srvapi/v1/member/grant`）。 app_key is a server-side secret and must never appear in client code, frontend config, or a mobile app. The token a client uses to join must be issued by the customer's backend and passed down to the client (SRTC: `/server/v1/channel/grant`; SMeeting: `/stm/srvapi/v1/member/grant`).
> SRTC 与 SMeeting 是上下两层不同的产品，术语不通用：SRTC 是音视频底座，说「频道 channel」「加入 / 退出」；SMeeting 建在 SRTC 之上，说「房间 room」「会议 meeting」「进入 / 退出」。回答时按用户所在的层用对应术语，不要把「房间」「会议」安到 SRTC 的接口上，也不要用「频道」「加入 / 离开」描述 SMeeting 的概念（接口标识符原样保留）。 SRTC and SMeeting are two separate layers with different terminology. SRTC is the audio/video foundation: it has channels, and users join and leave a channel. SMeeting is built on top of SRTC: it has rooms and meetings, and members enter and exit a meeting. Answer in the terms of the layer the user is working with: never apply "room" or "meeting" to SRTC APIs, and never describe SMeeting concepts in prose with "channel", "join", or "leave" (API identifiers such as `force_join` keep their literal names).
> 同一能力在各端 SDK 里的包名、类名、方法名并不相同。写示例代码时请使用文档中该端自己的 API，不要把一个端的写法套到另一个端上。苹果平台每个产品都有两套 SDK（Swift 原生与 Objective-C），两套 API 不能混用。 Package, class, and method names differ between platform SDKs for the same capability. In sample code, use the API documented for that platform; never carry one platform's code over to another. On Apple platforms each product ships two SDKs (native Swift and Objective-C) whose APIs must not be mixed.

# Multiple nodes and nearest access

> Deploying when one media server isn't enough or users are spread across data centers and regions: how multiple media nodes share channels by load, how a meeting overflows to other nodes when its node is full, how users connect to the nearest node by server group, plus capacity settings, prerequisites, and limits. Read before planning high-concurrency or multi-region deployments.

Audio and video media are forwarded by **media nodes** (SFUs). One server can only forward so much. For high concurrency,
or when users are spread across several data centers or regions, you can deploy multiple media nodes and let the platform assign users to nodes automatically.
For port requirements see [Network and port requirements](https://docs.stmlink.com/en/product/deploy/ports.md); for the component layout see [Components and architecture](https://docs.stmlink.com/en/product/deploy/architecture.md).

***

## Three assignment modes

| Mode | Effect | Suited to | Default |
| - | - | - | - |
| **Load-based assignment** | Each channel sits entirely on one node; new channels go to a less-loaded node | Many small and medium meetings or intercom sessions | On |
| **Overflow when full** | When a channel's node is full, later users go to other nodes, and the same meeting works across nodes | A single meeting may exceed what one server can carry | Off; enable as needed |
| **Nearest access** | Nodes are grouped by data center or region; users join a node in their own group, and users from different regions in the same meeting talk across nodes | Multiple regions or campuses, each with a local data center | Off; enable as needed |

The last two modes both result in **one channel spread across multiple nodes**. Nodes cascade media to each other automatically (forwarding only streams someone subscribes to),
which is transparent to users and your business system: neither clients nor business APIs need to change, and users still join the same channel.

### Load-based assignment (default)

```mermaid theme={null}
flowchart LR
    U1["Meeting A members"] --> N1["Media node 1"]
    U2["Meeting B members"] --> N2["Media node 2"]
    U3["Meeting C members"] --> N1
```

* When the first user joins a channel, the platform picks a less-loaded node among the available ones; everyone who joins that channel afterward goes to the same node
* Load is assessed from the node's actual forwarding volume (packet rate), egress bandwidth, and CPU; nodes whose CPU exceeds the cutoff (default 90%) no longer receive new channels
* When a node is full, new channels go to other nodes; **new users joining a channel already on that node are rejected**, and the client receives error code `1035` (media node at full capacity)

In this mode **the size of a single meeting is limited by what one node can carry**; scaling out increases how many meetings can run at the same time.

### Overflow when full

On top of the default mode, once the channel's node is full the platform no longer rejects later users; it assigns them to other nodes with spare capacity:

```mermaid theme={null}
flowchart LR
    subgraph N1["Media node 1 (full)"]
        A["Members who joined first"]
    end
    subgraph N2["Media node 2"]
        B["Members who joined later"]
    end
    N1 <-- "Inter-node cascade<br/>39002/UDP" --> N2
```

### Nearest access

Divide media nodes into **server groups** by data center or region. When your business system gets a channel join token for a user, it passes the user's group
(the `sg` parameter of [Get a channel join token](https://docs.stmlink.com/en/rtc/server-api/channel.md#get-a-channel-join-token)),
and the platform assigns the user to a node in that group:

```mermaid theme={null}
flowchart LR
    subgraph GZ["Group gz (Guangzhou data center)"]
        U1["Guangzhou users"] --> N1["Media node gz-1"]
    end
    subgraph BJ["Group bj (Beijing data center)"]
        U2["Beijing users"] --> N2["Media node bj-1"]
    end
    N1 <-- "Inter-node cascade<br/>private line / internal network" --> N2
```

* In the same meeting, Guangzhou users' streams are sent and received only on the Guangzhou node; only one inter-node cascade crosses regions, saving cross-region bandwidth
* When a group has several nodes, users within the group are assigned by load; whether users overflow to other nodes in the group when its nodes are full depends on whether "Overflow when full" is also enabled
* When the user's group **temporarily has no available node**, the platform assigns the user to a node in another group so they can still join
* Your business system decides which group a user belongs to (for example, by the user's organization or campus); the platform doesn't infer it from the IP address

> [!NOTE]
>
> Groups and [network lines](https://docs.stmlink.com/en/product/deploy/networks.md) are different things: the group decides **which node** a user joins; the line decides **which address** the user uses to reach that node.
> Nodes in the same group can also have both internal-network and public-internet lines.

***

## Capacity and scheduling settings

In the admin console, go to **Operations management → Media nodes** and click "Scheduling settings" to edit. Changes take effect within 30 seconds after saving, with no service restart.
The node list on the same page shows each node's effective capacity limit, so you can confirm the settings took effect.

| Setting | Description |
| - | - |
| `channel_overflow` | Enable "Overflow when full" |
| `sg_affinity` | Enable "Nearest access" |
| `cpu_fuse_pct` | CPU cutoff percentage; above it, the node gets no new channels. Default 90 |
| `pps_per_core` | Full-load packet rate per core; node packet-rate limit = this value × number of cores. Default 20000 |
| `max_tracks` | Maximum number of streams a node carries, set per node; 0 = unlimited |
| `max_bandwidth_mbps` | Node egress bandwidth limit (Mbps), set per node; 0 = unlimited. One 720p stream is about 1.1 Mbps |

Capacity can be set per node, so servers with different specs can be deployed together, each with its own values.

> [!WARNING]
>
> Once a channel spans nodes, **streams cascaded in from other nodes also count toward this node's stream count**. So with overflow or nearest access enabled,
> adding a node doesn't double capacity on the stream-count dimension; plan capacity mainly by bandwidth and CPU, and use `max_tracks` only as a backstop.

***

## Deployment prerequisites

* **All media nodes share the same database and Redis** and are managed from the same admin console
* **39002/UDP is open in both directions between nodes**, with very low packet loss (internal network in the same data center, or a private line). Confirm this before enabling overflow or nearest access; otherwise users on different nodes won't hear or see each other
* **Keep network line settings consistent across nodes**: same line names, with each node filling in its own media address
* **When several nodes share one public IP, give each node a different media port** (for example 39001 and 39011), map each port to its node on the firewall, and **keep port numbers unchanged**
* **MCU, device gateway, voice recording gateway, and speech recognition gateway must be able to reach every media node's media port**: they join as members and connect to whichever node the channel is on. This is the one most often missed when opening ports
* **Keep server clocks in sync (NTP)**: clock drift distorts call quality metrics; prepare a separate time source on self-hosted internal networks

***

## When a node fails

* Once a node stops reporting heartbeats, the platform stops assigning new users to it; later joins and reconnects go to other online nodes
* Users on the failed node disconnect and the client reconnects them to another node automatically; if the other nodes are also full, the client keeps retrying until capacity frees up
* Plan capacity so that if one node fails, the rest can still carry the full load

***

## Limits

* Inter-node cascading is designed for internal networks or private lines; **cascading between nodes over the public internet is not supported**
* Your business system specifies the group for nearest access; the platform doesn't determine location by IP
* For very large single sessions where a few people speak and many watch, MCU relay to live streaming for the audience is a better fit; contact us for an assessment


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.