tturn 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md CHANGED
@@ -1,108 +1,8 @@
1
1
  # Tturn (English)
2
2
 
3
- [English](./README.en.md) | [中文](./README.zh-CN.md)
3
+ [English (default)](./README.md) | [中文](./README.zh-CN.md)
4
4
 
5
- `tturn` is a high-performance TURN server package for Node.js.
6
- It uses an embedded native core (Rust + N-API), so you can install and run it directly from npm without Docker and without an external `turnserver.exe` runtime dependency.
5
+ The primary English documentation is now in `README.md`.
7
6
 
8
- ## 1) What this project is
9
-
10
- - Embedded TURN server for Node.js applications.
11
- - Native data plane implemented in Rust for better throughput and lower overhead than pure JavaScript TURN implementations.
12
- - Programmatic API and CLI are both provided.
13
- - Time-limited TURN credentials are generated with HMAC-SHA1 (standard TURN REST style).
14
-
15
- ## 2) Current capabilities
16
-
17
- - Start / stop TURN service from Node.
18
- - Issue short-lived credentials.
19
- - Return WebRTC ICE `urls/username/credential` directly.
20
- - Health check (`running: boolean`).
21
-
22
- ## 3) Install
23
-
24
- ```bash
25
- npm i tturn
26
- ```
27
-
28
- ## 4) Quick start (Node API)
29
-
30
- ```ts
31
- import { Tturn } from "tturn";
32
-
33
- const turn = new Tturn({
34
- realm: "turn.example.com",
35
- authSecret: "replace-with-your-secret",
36
- publicIp: "1.2.3.4",
37
- listenPort: 3478
38
- });
39
-
40
- await turn.start();
41
-
42
- const ice = turn.issueCredential({ ttlSec: 600, userId: "user-1001" });
43
- console.log(ice);
44
-
45
- // stop when exiting process
46
- await turn.stop();
47
- ```
48
-
49
- ## 5) CLI usage
50
-
51
- ```bash
52
- # Start embedded TURN service
53
- TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn start
54
-
55
- # Print one ICE credential payload
56
- TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn credential
57
- ```
58
-
59
- ## 6) API options
60
-
61
- - `realm` (required): TURN realm/domain.
62
- - `authSecret` (required): shared secret for dynamic credentials.
63
- - `listenPort` (default `3478`): TURN listening port.
64
- - `publicIp` (optional, recommended): public relay IP exposed to clients.
65
- - `listeningIp` (default `0.0.0.0`): bind address.
66
- - `minPort` / `maxPort`: reserved for relay port range control in next iterations.
67
-
68
- ## 7) Build from source
69
-
70
- Requirements:
71
-
72
- - Node.js >= 18
73
- - Rust toolchain (stable)
74
-
75
- Commands:
76
-
77
- ```bash
78
- npm install
79
- npm run build:native
80
- ./node_modules/.bin/tsc -p tsconfig.json
81
- ```
82
-
83
- Then run demo app:
84
-
85
- ```bash
86
- node dist/app.js
87
- ```
88
-
89
- Edit fixed config in `src/app.ts` before running.
90
-
91
- ## 8) Verify with Google WebRTC tool
92
-
93
- 1. Start service (`node dist/app.js`).
94
- 2. Copy printed `urls`, `username`, `credential`.
95
- 3. Open Google Trickle ICE tool and paste the ICE server config.
96
- 4. Gather candidates and confirm relay candidates are returned.
97
-
98
- ## 9) Publish to npm
99
-
100
- ```bash
101
- npm login
102
- npm publish --access public
103
- ```
104
-
105
- Notes:
106
-
107
- - npm package names must be lowercase, so publish as `tturn`.
108
- - For production users across multiple platforms, publish prebuilt `.node` artifacts for each target platform/arch.
7
+ - Main English doc: `README.md`
8
+ - Chinese doc: `README.zh-CN.md`
package/README.md CHANGED
@@ -1,14 +1,144 @@
1
1
  # Tturn
2
2
 
3
- [English](./README.en.md) | [中文](./README.zh-CN.md)
3
+ [English (default)](./README.md) | [中文](./README.zh-CN.md)
4
4
 
5
- `tturn` is a high-performance TURN server package for Node.js with an embedded native core (Rust + N-API).
5
+ `tturn` is a high-performance TURN server package for Node.js.
6
+ It embeds a native Rust core via N-API, so you can install from npm and run directly.
6
7
 
7
8
  - No Docker runtime required.
8
9
  - No external `turnserver.exe` runtime dependency.
9
- - Programmatic API and CLI included.
10
+ - Programmatic API and CLI are both included.
10
11
 
11
- Please read full docs:
12
+ ## What this project provides
12
13
 
13
- - English: `README.en.md`
14
- - 中文:`README.zh-CN.md`
14
+ - Embedded TURN service for Node.js applications.
15
+ - Native data plane implemented in Rust.
16
+ - Dynamic TURN credentials using HMAC-SHA1 (TURN REST style).
17
+ - Direct ICE payload output (`urls`, `username`, `credential`).
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm i tturn
23
+ ```
24
+
25
+ ## Quick start (Node API)
26
+
27
+ ```ts
28
+ import { Tturn } from "tturn";
29
+
30
+ const turn = new Tturn({
31
+ realm: "turn.example.com",
32
+ password: "replace-with-your-password",
33
+ publicIp: "1.2.3.4",
34
+ listenPort: 3478,
35
+ username: "user-1001",
36
+ disableCredentialExpiry: true
37
+ });
38
+
39
+ const ice = await turn.start();
40
+
41
+ console.log(ice);
42
+ await turn.stop();
43
+ ```
44
+
45
+ `start()` now returns one ICE payload directly, so bootstrap can be only `new Tturn(...)` + `start()`.
46
+
47
+ For long-running sessions, set `disableCredentialExpiry: true` to issue non-expiring credentials.
48
+
49
+ With static password mode (`password` + `username`), what you set is what clients use:
50
+
51
+ - output `username` is exactly your configured username
52
+ - output `credential` is exactly your configured password
53
+
54
+ ## Credential options
55
+
56
+ `issueCredential(options)` supports:
57
+
58
+ - `ttlSec` (optional, default `3600`, minimum `60`)
59
+ - `username` (optional): pass a custom TURN username
60
+ - `userId` (optional): legacy prefix mode (`userId:expiresAt`)
61
+
62
+ Username behavior:
63
+
64
+ - If `username` already ends with a future unix timestamp (for example `alice:1730000000`), it is used as-is.
65
+ - Otherwise, `tturn` appends `:<expiresAt>` automatically so TURN auth remains valid.
66
+ - If both `username` and `userId` are provided, `username` takes priority.
67
+
68
+ ## CLI usage
69
+
70
+ ```bash
71
+ # Start embedded TURN service
72
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn start
73
+
74
+ # Print one ICE credential payload
75
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn credential
76
+
77
+ # Optional: provide custom username
78
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret TTURN_USERNAME=alice tturn credential
79
+
80
+ # Optional: disable credential expiry (long-lived credentials)
81
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret TTURN_USERNAME=alice TTURN_DISABLE_CREDENTIAL_EXPIRY=1 tturn credential
82
+
83
+ # Static account/password (exact value, no rewrite)
84
+ TURN_REALM=turn.example.com TURN_USERNAME=alice TURN_PASSWORD=alice-pass TTURN_DISABLE_CREDENTIAL_EXPIRY=1 tturn start
85
+ ```
86
+
87
+ Required env:
88
+
89
+ - `TURN_REALM`
90
+ - `TURN_SECRET` or `TURN_PASSWORD`
91
+
92
+ Optional env:
93
+
94
+ - `TURN_PUBLIC_IP`
95
+ - `TURN_PORT`
96
+ - `TTURN_TTL_SEC`
97
+ - `TTURN_USER_ID`
98
+ - `TTURN_USERNAME` (or `TURN_USERNAME`)
99
+ - `TTURN_DISABLE_CREDENTIAL_EXPIRY` (`1` or `true`)
100
+
101
+ ## API options
102
+
103
+ - `realm` (required): TURN realm/domain.
104
+ - `authSecret` (optional): shared secret for dynamic credentials.
105
+ - `password` (optional): static TURN password (when set, returned credential stays fixed).
106
+ - `listenPort` (default `3478`): TURN listening port.
107
+ - `publicIp` (optional, recommended): public relay IP exposed to clients.
108
+ - `listeningIp` (default `0.0.0.0`): bind address.
109
+ - `username` / `userId` (optional): default credential username seed. `username` has higher priority.
110
+ - `ttlSec` (optional): default credential TTL used by `start()` and `issueCredential()`.
111
+ - `disableCredentialExpiry` (optional): disable timestamp expiry check and issue non-expiring credentials.
112
+ - `minPort` / `maxPort`: reserved for relay port range control in next iterations.
113
+
114
+ At least one of `authSecret` or `password` must be provided.
115
+
116
+ ## Quick verify
117
+
118
+ ```bash
119
+ npm install
120
+ npm run build
121
+
122
+ TURN_REALM=turn.example.com TURN_PUBLIC_IP=1.2.3.4 TURN_USERNAME=alice TURN_PASSWORD=alice-pass TTURN_DISABLE_CREDENTIAL_EXPIRY=1 node dist/cli.js credential
123
+ ```
124
+
125
+ The returned JSON should keep `username = "alice"` and `credential = "alice-pass"`.
126
+
127
+ ## Build from source
128
+
129
+ Requirements:
130
+
131
+ - Node.js >= 18
132
+ - Rust toolchain (stable)
133
+
134
+ ```bash
135
+ npm install
136
+ npm run build:native
137
+ npm run build:ts
138
+ ```
139
+
140
+ Run demo:
141
+
142
+ ```bash
143
+ node dist/app.js
144
+ ```
package/README.zh-CN.md CHANGED
@@ -1,107 +1,150 @@
1
- # Tturn(中文)
2
-
3
- [English](./README.en.md) | [中文](./README.zh-CN.md)
4
-
5
- `tturn` 是一个高性能的 Node.js TURN 服务包。
6
- 它使用内嵌原生核心(Rust + N-API),可以直接通过 npm 安装并运行,不依赖 Docker,也不需要额外安装 `turnserver.exe` 作为运行时。
7
-
8
- ## 1)项目介绍
9
-
10
- - `tturn` 是一个可直接在 Node.js 中运行的 TURN 服务包。
11
- - 底层是 Rust + N-API 原生实现,性能和资源效率优于纯 JS TURN 实现。
12
- - 不依赖 Docker,也不需要额外安装 `turnserver.exe` 作为运行时。
13
- - 同时提供 Node API 和 CLI 两种使用方式。
14
-
15
- ## 2)当前功能
16
-
17
- - 在 Node 中启动 / 停止 TURN 服务。
18
- - 生成短时效 TURN 凭证(HMAC-SHA1)。
19
- - 直接输出 WebRTC 所需的 ICE 参数(`urls/username/credential`)。
20
- - 基础健康状态检查(`running: boolean`)。
21
-
22
- ## 3)安装
23
-
24
- ```bash
25
- npm i tturn
26
- ```
27
-
28
- ## 4)Node API 快速使用
29
-
30
- ```ts
31
- import { Tturn } from "tturn";
32
-
1
+ # Tturn(中文)
2
+
3
+ [English(主文档)](./README.md) | [中文](./README.zh-CN.md)
4
+
5
+ `tturn` 是一个高性能的 Node.js TURN 服务包。
6
+ 它使用内嵌原生核心(Rust + N-API),可以直接通过 npm 安装并运行,不依赖 Docker,也不需要额外安装 `turnserver.exe` 作为运行时。
7
+
8
+ ## 1)项目介绍
9
+
10
+ - `tturn` 是一个可直接在 Node.js 中运行的 TURN 服务包。
11
+ - 底层是 Rust + N-API 原生实现,性能和资源效率优于纯 JS TURN 实现。
12
+ - 不依赖 Docker,也不需要额外安装 `turnserver.exe` 作为运行时。
13
+ - 同时提供 Node API 和 CLI 两种使用方式。
14
+
15
+ ## 2)当前功能
16
+
17
+ - 在 Node 中启动 / 停止 TURN 服务。
18
+ - 生成短时效 TURN 凭证(HMAC-SHA1)。
19
+ - 直接输出 WebRTC 所需的 ICE 参数(`urls/username/credential`)。
20
+ - 基础健康状态检查(`running: boolean`)。
21
+
22
+ ## 3)安装
23
+
24
+ ```bash
25
+ npm i tturn
26
+ ```
27
+
28
+ ## 4)Node API 快速使用
29
+
30
+ ```ts
31
+ import { Tturn } from "tturn";
32
+
33
33
  const turn = new Tturn({
34
34
  realm: "turn.example.com",
35
- authSecret: "replace-with-your-secret",
35
+ password: "replace-with-your-password",
36
36
  publicIp: "1.2.3.4",
37
- listenPort: 3478
37
+ listenPort: 3478,
38
+ username: "user-1001",
39
+ disableCredentialExpiry: true
38
40
  });
39
41
 
40
- await turn.start();
41
-
42
- const ice = turn.issueCredential({ ttlSec: 600, userId: "user-1001" });
42
+ const ice = await turn.start();
43
43
  console.log(ice);
44
44
 
45
45
  await turn.stop();
46
46
  ```
47
47
 
48
- ## 5)CLI 用法
48
+ `start()` 会直接返回一组 ICE,因此最简流程只需要 `new Tturn(...)` 和 `start()`。
49
49
 
50
- ```bash
51
- # 启动内置 TURN 服务
52
- TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn start
50
+ 如果你需要长时间持续连接,可设置 `disableCredentialExpiry: true`,生成不过期凭证。
53
51
 
52
+ 在静态账号密码模式(`password` + `username`)下:
53
+
54
+ - 输出的 `username` 就是你配置的账号
55
+ - 输出的 `credential` 就是你配置的密码
56
+
57
+ ## 5)CLI 用法
58
+
59
+ ```bash
60
+ # 启动内置 TURN 服务
61
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn start
62
+
54
63
  # 生成一组 ICE 凭证
55
64
  TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret tturn credential
65
+
66
+ # 可选:传入自定义 username
67
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret TTURN_USERNAME=alice tturn credential
68
+
69
+ # 可选:禁用凭证过期(长期凭证)
70
+ TURN_REALM=turn.example.com TURN_SECRET=replace-with-your-secret TTURN_USERNAME=alice TTURN_DISABLE_CREDENTIAL_EXPIRY=1 tturn credential
71
+
72
+ # 静态账号密码(原样输出,不改写)
73
+ TURN_REALM=turn.example.com TURN_USERNAME=alice TURN_PASSWORD=alice-pass TTURN_DISABLE_CREDENTIAL_EXPIRY=1 tturn start
56
74
  ```
57
75
 
58
- ## 6)配置参数说明
76
+ ## 5.1)凭证参数补充(username)
59
77
 
78
+ `issueCredential(options)` 额外支持 `username`(可选)。
79
+
80
+ - 如果 `username` 已经以未来时间戳结尾(例如 `alice:1730000000`),会原样使用。
81
+ - 否则会自动补上 `:<expiresAt>`,保证 TURN 鉴权可用。
82
+ - 同时传 `username` 和 `userId` 时,优先使用 `username`。
83
+
84
+ ## 6)配置参数说明
85
+
60
86
  - `realm`(必填):TURN realm / 域名。
61
- - `authSecret`(必填):动态凭证签名密钥。
87
+ - `authSecret`(可选):动态凭证签名密钥。
88
+ - `password`(可选):静态 TURN 密码(设置后返回值保持固定)。
62
89
  - `listenPort`(默认 `3478`):TURN 监听端口。
63
90
  - `publicIp`(建议配置):客户端访问的公网 IP。
64
91
  - `listeningIp`(默认 `0.0.0.0`):本地绑定地址。
92
+ - `username` / `userId`(可选):默认凭证用户名种子,`username` 优先级更高。
93
+ - `ttlSec`(可选):`start()` 与 `issueCredential()` 的默认凭证时效。
94
+ - `disableCredentialExpiry`(可选):禁用时间戳过期校验,生成不过期凭证。
65
95
  - `minPort` / `maxPort`:预留给后续中继端口范围控制。
66
96
 
67
- ## 7)源码构建
68
-
69
- 环境要求:
97
+ `authSecret` 和 `password` 至少需要提供一个。
70
98
 
71
- - Node.js >= 18
72
- - Rust 稳定版工具链
73
-
74
- 构建命令:
99
+ ## 6.1)快速验证
75
100
 
76
101
  ```bash
77
102
  npm install
78
- npm run build:native
79
- ./node_modules/.bin/tsc -p tsconfig.json
80
- ```
81
-
82
- 运行示例:
83
-
84
- ```bash
85
- node dist/app.js
86
- ```
87
-
88
- 运行前请先修改 `src/app.ts` 中的固定配置项。
89
-
90
- ## 8)使用 Google WebRTC 工具验证
103
+ npm run build
91
104
 
92
- 1. 启动服务:`node dist/app.js`
93
- 2. 复制控制台打印的 `urls`、`username`、`credential`
94
- 3. 打开 Google Trickle ICE 页面,填入 ICE Server
95
- 4. 执行采集并确认出现 relay candidate
96
-
97
- ## 9)发布到 npm
98
-
99
- ```bash
100
- npm login
101
- npm publish --access public
105
+ TURN_REALM=turn.example.com TURN_PUBLIC_IP=1.2.3.4 TURN_USERNAME=alice TURN_PASSWORD=alice-pass TTURN_DISABLE_CREDENTIAL_EXPIRY=1 node dist/cli.js credential
102
106
  ```
103
107
 
104
- 说明:
105
-
106
- - npm 包名必须小写,所以发布名为 `tturn`。
107
- - 生产环境建议按平台预编译并发布对应 `.node` 二进制文件。
108
+ 返回 JSON 中应保持 `username = "alice"`、`credential = "alice-pass"`。
109
+
110
+ ## 7)源码构建
111
+
112
+ 环境要求:
113
+
114
+ - Node.js >= 18
115
+ - Rust 稳定版工具链
116
+
117
+ 构建命令:
118
+
119
+ ```bash
120
+ npm install
121
+ npm run build:native
122
+ ./node_modules/.bin/tsc -p tsconfig.json
123
+ ```
124
+
125
+ 运行示例:
126
+
127
+ ```bash
128
+ node dist/app.js
129
+ ```
130
+
131
+ 运行前请先修改 `src/app.ts` 中的固定配置项。
132
+
133
+ ## 8)使用 Google WebRTC 工具验证
134
+
135
+ 1. 启动服务:`node dist/app.js`
136
+ 2. 复制控制台打印的 `urls`、`username`、`credential`
137
+ 3. 打开 Google Trickle ICE 页面,填入 ICE Server
138
+ 4. 执行采集并确认出现 relay candidate
139
+
140
+ ## 9)发布到 npm
141
+
142
+ ```bash
143
+ npm login
144
+ npm publish --access public
145
+ ```
146
+
147
+ 说明:
148
+
149
+ - npm 包名必须小写,所以发布名为 `tturn`。
150
+ - 生产环境建议按平台预编译并发布对应 `.node` 二进制文件。
package/dist/app.js CHANGED
@@ -3,20 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const index_1 = require("./index");
4
4
  const APP_CONFIG = {
5
5
  realm: "turn.example.com",
6
- authSecret: "replace-with-your-secret",
7
- publicIp: "1.2.3.4",
8
- listenPort: 3478,
9
- ttlSec: 600
6
+ publicIp: "192.168.3.66",
7
+ listenPort: 22224,
8
+ username: "metadigiee",
9
+ password: "22222",
10
+ ttlSec: 600,
11
+ disableCredentialExpiry: true
10
12
  };
11
13
  async function main() {
12
14
  const service = new index_1.Tturn({
13
15
  realm: APP_CONFIG.realm,
14
16
  authSecret: APP_CONFIG.authSecret,
17
+ password: APP_CONFIG.password,
15
18
  publicIp: APP_CONFIG.publicIp,
16
- listenPort: APP_CONFIG.listenPort
19
+ listenPort: APP_CONFIG.listenPort,
20
+ username: APP_CONFIG.username,
21
+ ttlSec: APP_CONFIG.ttlSec,
22
+ disableCredentialExpiry: APP_CONFIG.disableCredentialExpiry
17
23
  });
18
- await service.start();
19
- const ice = service.issueCredential({ ttlSec: APP_CONFIG.ttlSec, userId: "google-test" });
24
+ const ice = await service.start();
20
25
  console.log("[tturn] started.");
21
26
  console.log("[tturn] use this ICE server in Google WebRTC tool:");
22
27
  console.log(JSON.stringify(ice, null, 2));
package/dist/cli.js CHANGED
@@ -5,27 +5,40 @@ const index_1 = require("./index");
5
5
  async function run() {
6
6
  const command = process.argv[2];
7
7
  if (command === "credential") {
8
- const secret = mustGetEnv("TURN_SECRET");
9
8
  const realm = mustGetEnv("TURN_REALM");
10
- const service = (0, index_1.createTurnService)({ realm, authSecret: secret, publicIp: process.env.TURN_PUBLIC_IP });
9
+ const authOptions = resolveAuthOptions();
10
+ const service = (0, index_1.createTurnService)({
11
+ realm,
12
+ authSecret: authOptions.authSecret,
13
+ password: authOptions.password,
14
+ publicIp: process.env.TURN_PUBLIC_IP,
15
+ disableCredentialExpiry: readBoolEnv("TTURN_DISABLE_CREDENTIAL_EXPIRY") ?? Boolean(authOptions.password)
16
+ });
11
17
  const ice = service.issueCredential({
12
18
  ttlSec: process.env.TTURN_TTL_SEC ? Number(process.env.TTURN_TTL_SEC) : 3600,
13
- userId: process.env.TTURN_USER_ID
19
+ userId: process.env.TTURN_USER_ID,
20
+ username: readUsernameEnv()
14
21
  });
15
22
  process.stdout.write(`${JSON.stringify(ice, null, 2)}\n`);
16
23
  return;
17
24
  }
18
25
  if (command === "start") {
19
- const secret = mustGetEnv("TURN_SECRET");
20
26
  const realm = mustGetEnv("TURN_REALM");
27
+ const authOptions = resolveAuthOptions();
21
28
  const service = (0, index_1.createTurnService)({
22
29
  realm,
23
- authSecret: secret,
30
+ authSecret: authOptions.authSecret,
31
+ password: authOptions.password,
24
32
  publicIp: process.env.TURN_PUBLIC_IP,
25
- listenPort: process.env.TURN_PORT ? Number(process.env.TURN_PORT) : 3478
33
+ listenPort: process.env.TURN_PORT ? Number(process.env.TURN_PORT) : 3478,
34
+ ttlSec: process.env.TTURN_TTL_SEC ? Number(process.env.TTURN_TTL_SEC) : 3600,
35
+ username: readUsernameEnv(),
36
+ userId: process.env.TTURN_USER_ID,
37
+ disableCredentialExpiry: readBoolEnv("TTURN_DISABLE_CREDENTIAL_EXPIRY") ?? Boolean(authOptions.password)
26
38
  });
27
- await service.start();
39
+ const ice = await service.start();
28
40
  process.stdout.write("tturn started\n");
41
+ process.stdout.write(`${JSON.stringify(ice, null, 2)}\n`);
29
42
  await waitForSignal();
30
43
  await service.stop();
31
44
  process.stdout.write("tturn stopped\n");
@@ -48,11 +61,33 @@ function printUsage() {
48
61
  " tturn credential # prints one ICE server credential JSON",
49
62
  "",
50
63
  "required env:",
51
- " TURN_REALM, TURN_SECRET",
64
+ " TURN_REALM",
65
+ " TURN_SECRET or TURN_PASSWORD",
52
66
  "optional env:",
53
- " TURN_PUBLIC_IP, TURN_PORT, TTURN_TTL_SEC, TTURN_USER_ID"
67
+ " TURN_PUBLIC_IP, TURN_PORT, TTURN_TTL_SEC, TTURN_USER_ID, TTURN_USERNAME (or TURN_USERNAME), TTURN_DISABLE_CREDENTIAL_EXPIRY"
54
68
  ].join("\n") + "\n");
55
69
  }
70
+ function readUsernameEnv() {
71
+ return process.env.TTURN_USERNAME ?? process.env.TURN_USERNAME;
72
+ }
73
+ function resolveAuthOptions() {
74
+ const authSecret = process.env.TURN_SECRET;
75
+ const password = process.env.TURN_PASSWORD;
76
+ if (!authSecret && !password) {
77
+ throw new Error("Missing required env: TURN_SECRET or TURN_PASSWORD");
78
+ }
79
+ return {
80
+ authSecret: authSecret ?? "",
81
+ password
82
+ };
83
+ }
84
+ function readBoolEnv(name) {
85
+ const value = process.env[name];
86
+ if (!value) {
87
+ return undefined;
88
+ }
89
+ return value === "1" || value.toLowerCase() === "true";
90
+ }
56
91
  function waitForSignal() {
57
92
  return new Promise((resolve) => {
58
93
  const keepAlive = setInterval(() => {
@@ -6,8 +6,18 @@ const DEFAULT_TTL_SEC = 3600;
6
6
  function createTurnCredential(authSecret, options = {}) {
7
7
  const ttlSec = Math.max(60, options.ttlSec ?? DEFAULT_TTL_SEC);
8
8
  const expiresAt = Math.floor(Date.now() / 1000) + ttlSec;
9
- const userPrefix = options.userId ? `${options.userId}:` : "";
10
- const username = `${userPrefix}${expiresAt}`;
9
+ const username = resolveUsername(options, expiresAt);
11
10
  const password = (0, node_crypto_1.createHmac)("sha1", authSecret).update(username).digest("base64");
12
11
  return { username, password, ttlSec, expiresAt };
13
12
  }
13
+ function resolveUsername(options, expiresAt) {
14
+ if (options.username) {
15
+ const tail = options.username.split(":").pop();
16
+ if (tail && /^\d+$/.test(tail) && Number(tail) > Math.floor(Date.now() / 1000)) {
17
+ return options.username;
18
+ }
19
+ return `${options.username}:${expiresAt}`;
20
+ }
21
+ const userPrefix = options.userId ? `${options.userId}:` : "";
22
+ return `${userPrefix}${expiresAt}`;
23
+ }
@@ -8,7 +8,7 @@ interface NativeCredential {
8
8
  interface NativeTurnService {
9
9
  start(detached?: boolean): void;
10
10
  stop(): void;
11
- issueCredential(ttlSec?: number, userId?: string): NativeCredential;
11
+ issueCredential(ttlSec?: number, userId?: string, username?: string): NativeCredential;
12
12
  getIceUrls(): string[];
13
13
  health(): {
14
14
  running: boolean;
@@ -6,9 +6,9 @@ const node_path_1 = require("node:path");
6
6
  function loadNativeBinding() {
7
7
  const root = (0, node_path_1.join)(__dirname, "..");
8
8
  const candidates = [
9
+ (0, node_path_1.join)(root, binaryName()),
9
10
  (0, node_path_1.join)(root, "index.node"),
10
- (0, node_path_1.join)(root, "native", "index.node"),
11
- (0, node_path_1.join)(root, binaryName())
11
+ (0, node_path_1.join)(root, "native", "index.node")
12
12
  ];
13
13
  for (const filePath of candidates) {
14
14
  if (!(0, node_fs_1.existsSync)(filePath)) {
package/dist/service.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { IceServer, IssueCredentialOptions, StartOptions, TurnServiceOptions } from "./types";
2
2
  export declare class TurnService {
3
3
  private readonly native;
4
+ private readonly defaultIssueOptions;
4
5
  constructor(options: TurnServiceOptions);
5
- start(startOptions?: StartOptions): Promise<void>;
6
+ start(startOptions?: StartOptions): Promise<IceServer>;
6
7
  stop(): Promise<void>;
7
8
  issueCredential(issueOptions?: IssueCredentialOptions): IceServer;
8
9
  getIceUrls(): string[];
package/dist/service.js CHANGED
@@ -4,23 +4,36 @@ exports.TurnService = void 0;
4
4
  const native_binding_1 = require("./native-binding");
5
5
  class TurnService {
6
6
  constructor(options) {
7
+ const { username, password, userId, ttlSec, ...nativeOptions } = options;
8
+ const disableCredentialExpiry = options.disableCredentialExpiry ?? Boolean(password);
7
9
  this.native = new native_binding_1.binding.NativeTurnService({
8
- ...options,
9
- listenPort: options.listenPort ?? 3478,
10
- minPort: options.minPort ?? 49152,
11
- maxPort: options.maxPort ?? 65535,
12
- publicIp: options.publicIp ?? options.realm,
13
- listeningIp: options.listeningIp ?? "0.0.0.0"
10
+ ...nativeOptions,
11
+ authSecret: nativeOptions.authSecret ?? "",
12
+ listenPort: nativeOptions.listenPort ?? 3478,
13
+ minPort: nativeOptions.minPort ?? 49152,
14
+ maxPort: nativeOptions.maxPort ?? 65535,
15
+ publicIp: nativeOptions.publicIp ?? nativeOptions.realm,
16
+ listeningIp: nativeOptions.listeningIp ?? "0.0.0.0",
17
+ username,
18
+ password,
19
+ disableCredentialExpiry
14
20
  });
21
+ this.defaultIssueOptions = {
22
+ ttlSec,
23
+ username,
24
+ userId
25
+ };
15
26
  }
16
27
  async start(startOptions = {}) {
17
28
  this.native.start(Boolean(startOptions.detached));
29
+ return this.issueCredential();
18
30
  }
19
31
  async stop() {
20
32
  this.native.stop();
21
33
  }
22
34
  issueCredential(issueOptions = {}) {
23
- const out = this.native.issueCredential(issueOptions.ttlSec, issueOptions.userId);
35
+ const merged = { ...this.defaultIssueOptions, ...issueOptions };
36
+ const out = this.native.issueCredential(merged.ttlSec, merged.userId, merged.username);
24
37
  return {
25
38
  urls: this.native.getIceUrls(),
26
39
  username: out.username,
package/dist/types.d.ts CHANGED
@@ -11,16 +11,22 @@ export interface IceServer {
11
11
  }
12
12
  export interface IssueCredentialOptions {
13
13
  ttlSec?: number;
14
+ username?: string;
14
15
  userId?: string;
15
16
  }
16
17
  export interface TurnServiceOptions {
17
18
  realm: string;
18
- authSecret: string;
19
+ authSecret?: string;
19
20
  listenPort?: number;
20
21
  minPort?: number;
21
22
  maxPort?: number;
22
23
  publicIp?: string;
23
24
  listeningIp?: string;
25
+ username?: string;
26
+ password?: string;
27
+ userId?: string;
28
+ ttlSec?: number;
29
+ disableCredentialExpiry?: boolean;
24
30
  }
25
31
  export interface StartOptions {
26
32
  detached?: boolean;
package/index.node CHANGED
Binary file
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
- {
2
- "name": "tturn",
3
- "version": "0.1.0",
4
- "description": "High-performance TURN server for Node.js with embedded native core",
5
- "keywords": [
6
- "turn",
7
- "webrtc",
8
- "napi",
9
- "rust",
10
- "stun",
11
- "relay"
12
- ],
13
- "license": "MIT",
14
- "author": "",
15
- "homepage": "https://github.com/txzh007/ttrun",
16
- "repository": {
17
- "type": "git",
18
- "url": "https://github.com/txzh007/ttrun.git"
19
- },
20
- "bugs": {
21
- "url": "https://github.com/txzh007/ttrun/issues"
22
- },
23
- "main": "dist/index.js",
24
- "types": "dist/index.d.ts",
25
- "bin": {
26
- "tturn": "dist/cli.js"
27
- },
28
- "files": [
29
- "dist",
30
- "index.node",
31
- "tturn.*.node",
32
- "README.md",
33
- "README.en.md",
34
- "README.zh-CN.md",
35
- "LICENSE"
36
- ],
37
- "engines": {
38
- "node": ">=18"
39
- },
40
- "scripts": {
41
- "build:native": "node scripts/build-native.js",
42
- "build": "npm run build:native && tsc -p tsconfig.json",
43
- "build:ts": "tsc -p tsconfig.json"
44
- },
45
- "dependencies": {},
46
- "devDependencies": {
47
- "@types/node": "^22.13.10",
48
- "typescript": "^5.8.2"
49
- }
50
- }
1
+ {
2
+ "name": "tturn",
3
+ "version": "0.1.2",
4
+ "description": "High-performance TURN server for Node.js with embedded native core",
5
+ "keywords": [
6
+ "turn",
7
+ "webrtc",
8
+ "napi",
9
+ "rust",
10
+ "stun",
11
+ "relay"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "",
15
+ "homepage": "https://github.com/txzh007/ttrun",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/txzh007/ttrun.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/txzh007/ttrun/issues"
22
+ },
23
+ "main": "dist/index.js",
24
+ "types": "dist/index.d.ts",
25
+ "bin": {
26
+ "tturn": "dist/cli.js"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "index.node",
31
+ "tturn.*.node",
32
+ "README.md",
33
+ "README.en.md",
34
+ "README.zh-CN.md",
35
+ "LICENSE"
36
+ ],
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "scripts": {
41
+ "build:native": "node scripts/build-native.js",
42
+ "build": "npm run build:native && tsc -p tsconfig.json",
43
+ "build:ts": "tsc -p tsconfig.json"
44
+ },
45
+ "dependencies": {},
46
+ "devDependencies": {
47
+ "@types/node": "^22.13.10",
48
+ "typescript": "^5.8.2"
49
+ }
50
+ }
Binary file