Ubuntu 22.04 安装 Tinode 0.25.3 指南
Tinode 0.25.3 在 Ubuntu 22.04 上的从零安装实录:发行包下载与 sha256 校验、MySQL 8 数据库准备、keygen 生成 API key、init-db 初始化、前台运行与 systemd 托管两种形态、Web 托管与点对点消息闭环验证、升级与卸载,以及 ROOT 账号 401 与数据库连接失败的排障实录。全部命令与回显来自真实服务器实测。
Ubuntu 22.04 安装 Tinode 0.25.3 指南
目标:在一台 Ubuntu 22.04 服务器上从零安装 Tinode v0.25.3 即时消息服务端:准备 MySQL 数据库、下载并校验官方发行包、初始化数据库、以 systemd 托管服务,最后通过 Web 控制台与原生 REST/长轮询接口完成注册与点对点消息的功能闭环验证。本文所有命令与回显均来自真实服务器实测,可直接照做。
一、环境信息
本文的安装与验证在下列环境中完成,所有回显均可按此环境复核:
| 项目 | 实测值 |
|---|---|
| 主机名 | tinode-sys |
| 操作系统 | Ubuntu 22.04.5 LTS |
| 数据库 | MySQL 8.0.46 |
| 服务端版本 | Tinode v0.25.3(发行包 tinode-alldbs.linux-amd64.tar.gz) |
| 客户端端口 | 6060(HTTP/WebSocket/长轮询 + Web 控制台) |
| gRPC 端口 | 16060 |
环境快照实测回显:
hostname
head -2 /etc/os-release
mysql --version
tinode-sys
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
mysql Ver 8.0.46-0ubuntu0.22.04.4 for Linux on x86_64 ((Ubuntu))
二、版本选型与安装方案
2.1 版本选型
Tinode 服务端以 GitHub Releases 发布预编译二进制,无 apt/yum 官方软件源。2026-10-26 查询 GitHub Releases,最新稳定版为 v0.25.3(发布于 2026-07-04)。本文选用 tinode-alldbs.linux-amd64.tar.gz 发行包:包名中的 alldbs 表示内置全部四种数据库适配器(MySQL、PostgreSQL、RethinkDB、MongoDB),Linux amd64 架构。若只需单一数据库适配器,Releases 页也提供 tinode-mysql.linux-amd64.tar.gz 等单适配器包,安装步骤完全一致。
2.2 运行形态方案对比
Tinode 是单进程二进制,运行形态有两种,本文两种都实测:
| 方案 | 启动方式 | 适用场景 |
|---|---|---|
| 前台直接运行 | ./tinode -config tinode.conf | 快速验证、调试配置 |
| systemd 托管 | systemctl start tinode | 生产常驻、开机自启(本文主线) |
关键差异:前台方式日志直达终端、退出即停;systemd 方式获得崩溃自动拉起(Restart=on-failure)、开机自启与 journald 日志托管,生产环境应使用后者。数据库适配器的选择见 2.3 节。
2.3 数据库适配器选型
💡 技术拓展:Tinode 通过适配器层支持四种存储后端,发行包 alldbs 版本全部内置——
适配器 特点 适用场景 MySQL Ubuntu/Debian 仓库直装,运维生态最普及 中小规模自托管(本文采用) PostgreSQL 功能丰富的开源关系库 已有 PG 运维体系的环境 RethinkDB Tinode 历史默认后端,JSON 文档型 与官方早期文档对齐的存量部署 MongoDB 文档型分布式数据库 已有 Mongo 集群的复用 本文因 Ubuntu 22.04 可用仓库直接安装 MySQL 8 且社区资料最多,实际采用 MySQL 适配器。适配器由配置文件
store_config.use_adapter一项切换,切换后需重新初始化数据库(见第六章)。
三、安装准备
3.1 安装基础依赖
Tinode 二进制无任何运行时依赖,唯一需要预装的是数据库服务器与下载/校验工具。在全新系统上执行:
apt-get update && apt-get install -y mysql-server curl
完成后验证数据库可用(实测回显):
mysql --version
mysql Ver 8.0.46-0ubuntu0.22.04.4 for Linux on x86_64 ((Ubuntu))
3.2 准备数据库账号
初始化程序 init-db 需要一个可建库的 MySQL 账号。以 root 走本机 socket 进入 MySQL(root 免密登录本机 socket 是 Ubuntu 打包默认行为),创建专用账号并授权:
mysql -e "CREATE USER IF NOT EXISTS 'tinode'@'localhost' IDENTIFIED BY 'q1w2e3r4'; GRANT ALL PRIVILEGES ON *.* TO 'tinode'@'localhost'; FLUSH PRIVILEGES;"
mysql -e "SELECT User,Host FROM mysql.user WHERE User='tinode';"
User Host
tinode localhost
说明:GRANT ALL PRIVILEGES ON *.* 是因为 init-db 要创建名为 tinode 的数据库;若遵循最小权限原则,可改为 GRANT ALL PRIVILEGES ON tinode.* TO 'tinode'@'localhost',但需先手工 CREATE DATABASE tinode;。本文为安装流畅采用全库授权。数据库本身不需要预建,init-db 检测到不存在时会自动创建(见 6.1 节回显)。
3.3 生成 API key 与 HMAC salt
Tinode 服务端对客户端请求校验 apikey 参数,密钥对由发行包内的 keygen 工具生成。运行(每次输出一对新值):
cd /opt/tinode-app && ./keygen
API key v1 seq1 [ordinary]: AQAAAAABAABb1lv6-dWoYXC4M6VO5gY9
HMAC salt: XxCVzrVjlX84Urtke1MuW+6DzuqchOYPkYlUgJAz+m8=
用法:把 HMAC salt 写入 tinode.conf 的 api_key_salt,把 API key 用在所有客户端请求的 apikey 参数上。两者必须配对:服务端用 salt 反推校验 key,换 salt 不换 key(或反之)会导致所有请求被拒。本环境部署时生成的 apikey 为 AQAAAAABAAAoeOI7tA3HsYvdzDhYhZJy,对应 conf 第 36 行的 salt(见 5.1 节回显),后文 REST 示例沿用这把 key。修改 salt 后必须重启服务才生效。
四、下载与部署发行包
4.1 下载发行包
从 GitHub Releases 下载 v0.25.3 的 Linux amd64 全适配器包。若服务器出海带宽有限,为 curl 指定 HTTP 代理(-x 参数;直连环境去掉该参数即可):
mkdir -p /opt/dl && cd /opt/dl
curl -sIL --http1.1 -x http://192.168.1.62:7897 https://github.com/tinode/chat/releases/download/v0.25.3/tinode-alldbs.linux-amd64.tar.gz | grep -iE 'HTTP/|content-length|content-type'
Content-Length: 0
HTTP/1.1 200 Connection established
HTTP/1.1 200 OK
Content-Length: 40920300
Content-Type: application/octet-stream
说明:HTTP/1.1 200 Connection established 是代理 CONNECT 阶段的回显,最后一个 Content-Length: 40920300 是真实文件大小(约 39 MB)。执行下载:
curl -x http://192.168.1.62:7897 -L -o tinode-alldbs.linux-amd64.tar.gz https://github.com/tinode/chat/releases/download/v0.25.3/tinode-alldbs.linux-amd64.tar.gz
实测下载约 2.6 秒完成(平均 14.7 MB/s),与上一步校验的 40920300 字节一致。
4.2 校验与解压
校验 sha256 并确认包内容:
sha256sum tinode-alldbs.linux-amd64.tar.gz
70635a0ca75da69708d5887e61fe9210e86be91d831e15828d0871e73c7a3b66 tinode-alldbs.linux-amd64.tar.gz
tar tzf tinode-alldbs.linux-amd64.tar.gz | head -6
./
./bob-128.jpg
./abcdef-128.jpg
./templ/
./xena-128.jpg
./init-db
包内为扁平结构:服务端二进制 tinode、初始化工具 init-db、密钥工具 keygen、示例数据装载脚本 credentials.sh 与 data.json、配置文件 tinode.conf、Web 控制台静态资源目录 static/、模板目录 templ/ 及若干示例头像图片。解压到部署目录:
mkdir -p /opt/tinode-app
tar -xzf tinode-alldbs.linux-amd64.tar.gz -C /opt/tinode-app
ls /opt/tinode-app
abc-128.jpg carol-128.jpg eve-128.jpg static tinode.conf
abcdef-128.jpg chan-128.jpg init-db support-128.jpg uploads
alice-128.jpg credentials.sh keygen templ xena-128.jpg
bf-128.jpg data.json tino-128.jpg tinode
bob-128.jpg dave-128.jpg tinode.tar.gz
💡 说明:
tinode.tar.gz与tinode.conf.bak是本环境调试过程的自留文件,全新解压不会出现;uploads/是文件上传存储目录(首次有文件上传时自动使用)。核心三件套为tinode(服务端)、init-db(数据库初始化)、tinode.conf(唯一配置文件)。
五、配置文件关键项
5.1 必改与默认项速览
tinode.conf 共约 700 行,JSON 格式,注释详尽。安装阶段必须核对的关键项实测如下:
grep -n '"listen"\|"grpc_listen"\|"static_mount"\|"api_key_salt"\|"uid_key"\|use_adapter' /opt/tinode-app/tinode.conf
9: "listen": ":6060",
23: "static_mount": "/",
28: "grpc_listen": ":16060",
36: "api_key_salt": "T713/rYYgW7g4m3vG6zGRh7+FM1t0T8j13koXScOAj4=",
249: "uid_key": "6cTXIXgdxO8qxObF30+1hg==",
256: "use_adapter": "mysql",
MySQL 连接段(本环境仅显式配置了用户与密码):
sed -n '/"mysql": {/,/}/p' /opt/tinode-app/tinode.conf | grep -E 'User|Passwd'
"User": "tinode",
"Passwd": "q1w2e3r4",
逐项说明:
listen(第 9 行):客户端 HTTP 入口监听地址与端口,默认:6060,承载 JSON/WebSocket/长轮询三通道与 Web 控制台静态托管。static_mount(第 23 行):Web 控制台静态资源的挂载路径,默认/,即直接用服务端 6060 端口托管 Web 客户端,无需另起 Nginx。grpc_listen(第 28 行):gRPC 通道监听端口,默认:16060,供 tn-cli 与各语言 gRPC 客户端使用。api_key_salt(第 36 行):apikey 校验盐,替换为 3.3 节 keygen 输出后重启生效。uid_key(第 249 行):用户 UID 生成与解析密钥(16 字节 base64)。全新安装可保持预置值;一旦有用户数据,此值绝不可再改,否则既有 UID 无法解析。use_adapter(第 256 行):数据库适配器选择,改为mysql使用 MySQL。adapters.mysql.User/Passwd:数据库账号,对应 3.2 节创建的tinode/q1w2e3r4;连接地址与库名沿用适配器默认值(本机实例、库tinode)。
5.2 注册验证码的调试默认值
发行包 conf 中邮箱/手机验证器默认启用了调试回执,实测位置:
grep -n -B2 'debug_response' /opt/tinode-app/tinode.conf | head -6
456- // REMOVE IN PRODUCTION!!! Otherwise anyone will be able to register
457- // with fake emails.
458: "debug_response": "123456"
含义:未配置真实邮件/短信出口时,注册阶段下发的验证码固定为 123456(第 498 行为手机号验证的同类默认值)。官方注释明确 REMOVE IN PRODUCTION——生产环境要么配置真实 SMTP 出口,要么删除这两处默认值,否则任何人都能用假邮箱注册。本文第八章的功能验证正是利用该默认值完成邮箱验证闭环。
六、初始化数据库
6.1 执行 init-db 并创建管理账号
init-db 负责建库、建表(schema 版本 116)并写入种子数据。执行:
cd /opt/tinode-app
./init-db -add_root admin:q1w2e3r4
2026/10/26 12:23:25 Database adapter: 'mysql'; version: 116
2026/10/26 12:23:25 Database not found. Creating.
2026/10/26 12:23:26 Database successfully created.
2026/10/26 12:23:26 No data provided, stopping
2026/10/26 12:23:26 ROOT user created: 'admin:q1w2e3r4'
2026/10/26 12:23:26 All done.
回显逐行解读:适配器与 schema 版本(mysql 116)→ 库不存在自动创建 → 未指定 -data 示例数据文件故跳过装载 → 创建 ROOT 特权账号 admin:q1w2e3r4 → 完成。
⚠️ ROOT 账号定位(实测重要发现):
-add_root创建的 admin 是服务端特权种子账号,供 tn-cli 管理宏等 root 级操作识别使用;它没有写入 basic(用户名+密码)登录命名空间,在 Web 控制台与 REST/长轮询接口上用admin:q1w2e3r4登录会返回 401(复现与结论见 10.1 节)。日常使用账号请通过注册接口创建(见 8.3 节),不要把-add_root当作「创建管理员登录账号」。
6.2 升级模式验证
schema 升级由 init-db -upgrade 完成。在当前已是最新 schema 的库上运行,可确认升级机制的判定逻辑(实测回显):
cd /opt/tinode-app && ./init-db -config tinode.conf -upgrade
2026/10/26 12:57:57 Database adapter: 'mysql'; version: 116
2026/10/26 12:57:57 Database exists, version is correct.
2026/10/26 12:57:57 All done.
七、启动与运行
7.1 方案 A:前台直接运行
cd /opt/tinode-app
./tinode -config /opt/tinode-app/tinode.conf
实测启动日志(输出节选):
I2026/10/26 13:00:00 Server v0.25:/opt/tinode-app/tinode:v0.25.3; pid 6193; 6 process(es)
I2026/10/26 13:00:00 DB adapter mysql 116
I2026/10/26 13:00:00 Push handlers configured: [stdout]
I2026/10/26 13:00:00 gRPC/1.82.0 server is registered at [:16060]
I2026/10/26 13:00:00 Serving static content from '/opt/tinode-app/static' at '/'
I2026/10/26 13:00:00 Listening for client HTTP connections on [:6060]
首行即版本自证:Server v0.25:/opt/tinode-app/tinode:v0.25.3。前台方式 Ctrl+C 即退出,验证通过后按 7.2 节改用 systemd 托管。
7.2 方案 B:systemd 托管
创建单元文件:
cat > /etc/systemd/system/tinode.service <<'EOF'
[Unit]
Description=Tinode instant messaging server
Documentation=https://tinode.co
After=network.target mysql.service
Wants=mysql.service
[Service]
Type=simple
WorkingDirectory=/opt/tinode-app
ExecStart=/opt/tinode-app/tinode -config /opt/tinode-app/tinode.conf
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now tinode
Created symlink /etc/systemd/system/multi-user.target.wants/tinode.service → /etc/systemd/system/tinode.service.
逐项说明:After/Wants mysql.service 保证数据库先于 Tinode 启动;Restart=on-failure + RestartSec=5 提供崩溃自愈;LimitNOFILE=65536 抬高句柄上限以支撑长连接数量。验证运行状态与自启(实测回显):
systemctl is-active tinode
systemctl is-enabled tinode
active
enabled
启动日志确认(journald 与前台输出同源,实测节选):
journalctl -u tinode --no-pager -n 10 | grep -E 'Server v0.25|Push handlers|gRPC|Serving static|Listening'
Oct 26 12:23:26 tinode-sys tinode[5744]: I2026/10/26 12:23:26 Push handlers configured: [stdout]
Oct 26 12:23:26 tinode-sys tinode[5744]: I2026/10/26 12:23:26 gRPC/1.82.0 server is registered at [:16060]
Oct 26 12:23:26 tinode-sys tinode[5744]: I2026/10/26 12:23:26 Serving static content from '/opt/tinode-app/static' at '/'
Oct 26 12:23:26 tinode-sys tinode[5744]: I2026/10/26 12:23:26 Listening for client HTTP connections on [:6060]
八、部署验证与功能闭环
8.1 Web 控制台验证
6060 端口同时托管 Web 控制台。浏览器访问 http://<服务器IP>:6060/ 应看到登录页;命令行验证静态托管(实测回显):
curl -sI http://localhost:6060/ | head -6
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: must-revalidate, public, max-age=39600
Content-Length: 2471
Content-Type: text/html; charset=utf-8
Last-Modified: Sat, 04 Jul 2026 14:40:58 GMT
HTTP 200 且 Content-Type: text/html 说明 Web 控制台已随服务端一并上线(Last-Modified 与 v0.25.3 发布日期一致)。若云服务器有防火墙/安全组,放行 TCP 6060(Web 与 JSON/WS/LP 通道)与 16060(gRPC)后即可从外部访问。
8.2 通过长轮询接口建会话
以下用 curl 直接驱动 Tinode 原生长轮询(LP)通道验证服务端 API。空体 POST 即创建会话,先看原始回显:
K="AQAAAAABAAAoeOI7tA3HsYvdzDhYhZJy"
B="http://localhost:6060/v0/channels/lp"
curl -s -X POST "$B?apikey=$K"
{"ctrl":{"params":{"sid":"aX06iiNwxZs"},"code":201,"text":"created","ts":"2026-10-26T13:04:32.448Z"}}
201 created,params.sid 即会话 ID。实际使用时把 sid 提取到变量,后续请求以 &sid=$SID 附加:
SID=$(curl -s -X POST "$B?apikey=$K" | grep -oE '"sid":"[^"]+"' | cut -d'"' -f4)
定义两个辅助函数(发送消息体、轮询服务端响应队列):
send(){ curl -s -X POST "$B?apikey=$K&sid=$SID" -H 'Content-Type: application/json' -d "$1"; }
poll(){ curl -s -X POST "$B?apikey=$K&sid=$SID" -H 'Content-Type: application/json'; echo; }
⚠️ 实测要点:send 必须显式带
Content-Type: application/json,否则服务端返回 400malformed。LP 通道的语义是:不带消息体的 POST 建会话,带消息体的 POST 发包(无回显),空体 POST 逐包取回响应——因此每个请求后都要 poll 一次拿结果。
先发握手包并取回服务端能力描述(实测回显):
send '{"hi":{"id":"1","ver":"0.25","ua":"curl-lp/0.25.3"}}'; poll
{"ctrl":{"id":"1","params":{"build":"mysql:v0.25.3","maxFileUploadSize":8388608,"maxMessageSize":131072,"maxSubscriberCount":128,"maxTagCount":16,"maxTagLength":96,"minTagLength":2,"msgDelAge":600,"reqCred":{"auth":["email"]},"ver":"0.25"},"code":200,"text":"ok","ts":"2026-10-26T12:18:29.394Z"}}
reqCred:{"auth":["email"]} 表示本服务器策略要求注册时提供 email 凭据(与 5.2 节的验证码默认值联动)。
8.3 注册两个用户并完成点对点消息闭环
注册用户 tina。注意 acc 包必须带 "user":"new"(告诉服务端这是建新账号而非改当前账号)、scheme:basic、以及 base64 的 用户名:密码 secret(实测回显):
send '{"acc":{"id":"2","user":"new","scheme":"basic","username":"tina","secret":"'"$(echo -n 'tina:q1w2e3r4' | base64 -w0)"'","login":true,"cred":[{"meth":"email","val":"tina@example.com"}]}}'; poll
{"ctrl":{"id":"2","params":{"authlvl":"auth","cred":["email"],"desc":{"created":"2026-10-26T12:18:29.413Z","updated":"2026-10-26T12:18:29.413Z","defacs":{"auth":"JRWPASD","anon":"N"}},"expires":"2026-11-09T12:18:29.466Z","token":"8Y/EkEKfpugVuvFqFAABAAAAQvhB+rA5MyV/S4WOCW7LA6p1I96hHx0jQAzuu/ZYDt0=","user":"usr8Y_EkEKfpug"},"code":300,"text":"validate credentials","ts":"2026-10-26T12:18:29.41Z"}}
返回 300 validate credentials:账号已建(UID usr8Y_EkEKfpug)但需完成邮箱验证。用 5.2 节的调试验证码 123456 携带在 login 包中完成验证并登录(实测回显):
send '{"login":{"id":"3","scheme":"basic","secret":"'"$(echo -n 'tina:q1w2e3r4' | base64 -w0)"'","cred":[{"meth":"email","resp":"123456"}]}}'; poll
{"ctrl":{"id":"3","params":{"authlvl":"auth","expires":"2026-11-09T12:18:29.531Z","token":"8Y/EkEKfpugVuvFqFAABAAEAnYMLpmuWIaVG4LSgn77ADpt2BgKOpQ2GZXKGV4X7n+k=","user":"usr8Y_EkEKfpug"},"code":200,"text":"ok","ts":"2026-10-26T12:18:29.481Z"}}
200 ok 即登录成功。按同样步骤在第二个会话注册 derek(会话新建后重发 hi 再 acc/login,过程相同),得到 DEREK_UID=usrx-2oWQr-x2w。为 tina 设置公开资料(实测回显):
send '{"set":{"id":"5","topic":"me","desc":{"public":{"fn":"Tina Wang"}}}}'; poll
{"ctrl":{"id":"5","topic":"me","code":200,"text":"ok","ts":"2026-10-26T12:18:29.727Z"}}
tina 订阅 derek 的用户话题并发送点对点消息(实测回显):
send '{"sub":{"id":"7","topic":"usrx-2oWQr-x2w"}}'; poll
send '{"pub":{"id":"8","topic":"usrx-2oWQr-x2w","head":{"mime":"text/plain"},"content":"你好 derek,这是 Tinode 部署完成后的第一条点对点消息"}}'; poll
{"ctrl":{"id":"7","topic":"usrx-2oWQr-x2w","params":{"acs":{"want":"JRWPAD","given":"JRWPASD","mode":"JRWPAD"}},"code":200,"text":"ok","ts":"2026-10-26T12:18:29.758Z"}}
{"ctrl":{"id":"8","topic":"usrx-2oWQr-x2w","params":{"seq":1},"code":202,"text":"accepted","ts":"2026-10-26T12:18:29.77Z"}}
derek 侧订阅 tina 并拉取历史消息,收到 tina 刚发送的消息(实测回显):
send '{"sub":{"id":"4","topic":"usr8Y_EkEKfpug"}}'; poll
send '{"get":{"id":"5","topic":"usr8Y_EkEKfpug","what":"data","data":{"limit":3}}}'; poll
{"ctrl":{"id":"4","topic":"usr8Y_EkEKfpug","code":200,"text":"ok","ts":"2026-10-26T12:18:29.783Z"}}
{"data":{"topic":"usr8Y_EkEKfpug","from":"usr8Y_EkEKfpug","ts":"2026-10-26T12:18:29.77Z","seq":1,"head":{"mime":"text/plain"},"content":"你好 derek,这是 Tinode 部署完成后的第一条点对点消息"}}
{"ctrl":{"id":"5","topic":"usr8Y_EkEKfpug","params":{"count":1,"what":"data"},"code":208,"text":"delivered","ts":"2026-10-26T12:18:29.801Z"}}
至此闭环成立:注册(300→200)→ 资料 → 订阅(200)→ 发送(202 seq:1)→ 对端拉取(data 包 + 208 delivered)。Web 控制台上以 tina/derek 登录同样能看到这条消息,说明 REST 通道与 Web 客户端共享同一数据面。
九、升级、降级与多版本管理
9.1 升级
升级路径:备份 → 下载新版发行包解压到新目录 → init-db -upgrade 迁移 schema → 切换 systemd 指向 → 重启。schema 迁移命令与「已是最新」时的判定回显见 6.2 节(Database exists, version is correct.)。操作顺序上必须先备份 MySQL(mysqldump tinode > tinode-backup.sql)再执行 upgrade,schema 版本由 init-db 自动比对推进。
9.2 降级与多版本
Tinode 官方未提供 schema 降级工具,数据库一旦升级到新版 schema,旧版服务端无法识别,因此降级的唯一安全做法是用升级前的 mysqldump 备份恢复数据库后运行旧版二进制。多版本并存推荐目录隔离 + 软链切换(实测命令与回显):
ln -sfn /opt/tinode-app /opt/tinode-latest
readlink /opt/tinode-latest
/opt/tinode-app
新版本解压到 /opt/tinode-app-<新版本> 后,将软链重新指向新目录并 systemctl restart tinode 即完成切换;回滚时把软链改回旧目录即可(前提是数据库 schema 兼容旧版)。
十、常见问题与排障实录
10.1 init-db 创建的 admin 登录返回 401
现象:./init-db -add_root admin:q1w2e3r4 回显 ROOT user created,但在登录接口用 admin:q1w2e3r4 登录失败。实测复现:
send '{"login":{"id":"2","scheme":"basic","secret":"'"$(echo -n 'admin:q1w2e3r4' | base64 -w0)"'"}}'; poll
{"ctrl":{"id":"2","code":401,"text":"authentication failed","ts":"2026-10-26T12:59:59.234Z"}}
定位:ROOT 是服务端特权种子账号,写入的存储记录不带 basic(用户名+密码)登录命名空间,供服务端内部特权操作识别(如 tn-cli 标注「requires root privileges」的管理宏);客户端 basic 登录查找的是 basic 命名空间记录,自然 401。
解决:ROOT 账号不是给终端登录用的。日常账号通过注册接口创建(8.3 节的 acc+验证流程),或直接在 Web 控制台注册;确需管理特权时,用普通账号执行 tn-cli 管理命令的前提是账号具备 ROOT 权限级别,可由具备权限的账号通过 tn-cli 的 usermod 系列宏调整。
10.2 服务启动失败:Access denied for user
现象:把 MySQL 密码字段名误写成 "Password"(正确为 "Passwd")后,服务反复重启失败。journald 实测回显:
Oct 26 12:52:25 tinode-sys tinode[5975]: I2026/10/26 12:52:25 DB adapter mysql 116
Oct 26 12:52:25 tinode-sys tinode[5975]: E2026/10/26 12:52:25 Failed to connect to DB: Error 1045 (28000): Access denied for user 'tinode'@'localhost' (using password: NO)
Oct 26 12:52:25 tinode-sys systemd[1]: tinode.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 12:52:25 tinode-sys systemd[1]: tinode.service: Failed with result 'exit-code'.
定位:tinode.conf 的 JSON 解析对未知字段名不报错,Password 被静默忽略后等于「密码为空」,于是 MySQL 拒绝连接——错误行的 using password: NO 正是空密码特征。同类问题还有 uid_key 被改成非法 base64(实测回显为 Failed to connect to DB: store: failed to parse config: illegal base64 data at input byte 32({... 后跟整段 store_config 回显)。
解决:字段名逐字符对照发行包原版 conf(Passwd、uid_key 为 16 字节 base64);改正后 systemctl restart tinode,以 systemctl is-active tinode 回显 active 与启动日志中的 Listening for client HTTP connections on [:6060] 为恢复判据。
10.3 LP 请求返回 400 malformed
现象:8.2 节的 send 若漏掉 -H 'Content-Type: application/json',服务端直接返回 400 malformed,且该会话后续包全部被拒。
解决:LP 通道所有带消息体的 POST 都必须携带 JSON Content-Type;空体轮询请求则不要带消息体。这是 curl 手工调试 Tinode 时最高频的坑。
十一、卸载方式
停止并取消自启(实测回显):
systemctl disable --now tinode
systemctl is-active tinode
systemctl is-enabled tinode
Removed /etc/systemd/system/multi-user.target.wants/tinode.service.
inactive
disabled
确认不再需要数据后,删除程序目录、数据目录与数据库(不可逆操作,执行前务必确认已备份):
rm -rf /opt/tinode-app /opt/dl /etc/systemd/system/tinode.service
systemctl daemon-reload
mysql -e "DROP DATABASE IF EXISTS tinode; DROP USER IF EXISTS 'tinode'@'localhost';"
若只是停用而非卸载,本文环境在演示后已按相反顺序恢复(实测回显):
systemctl enable --now tinode
systemctl is-active tinode
systemctl is-enabled tinode
Created symlink /etc/systemd/system/multi-user.target.wants/tinode.service → /etc/systemd/system/tinode.service.
active
enabled
十二、参考链接
- 本系列选型篇:《Tinode 0.25.3 介绍与选型指南》
- Tinode 官方仓库:https://github.com/tinode/chat
- v0.25.3 发布页(含各平台发行包):https://github.com/tinode/chat/releases/tag/v0.25.3
- 官方安装文档 INSTALL.md:https://github.com/tinode/chat/blob/master/INSTALL.md
- 官网与在线演示:https://tinode.co