// 文档 · 服务端docs · server

服务端 omtsServer · omts

部署(单机 / 多节点)、nginx 配置、启动参数全表。参数与默认值以各版本随附的 --help 为最终依据。 Deployment (single-node / multi-node), nginx configuration, and the full CLI flag reference. Your installed --help output is the final word.

omts 是 API 与 WebSocket 中继服务:浏览器用户认证(密码/TOTP/OAuth)、设备注册与会话管理、浏览器与 omtc 之间的密文中继,以及 P2P 信令与 STUN 响应。它不接触明文载荷——加密发生在两端。库表启动时自动迁移,无需手工 schema。 omts is the API and WebSocket relay: browser authentication (password/TOTP/OAuth), device registry and session management, ciphertext relay between browsers and omtc, plus P2P signaling and a STUN responder. It never touches plaintext payloads — encryption happens at the endpoints. Schema migrations run automatically at startup.

单机部署Single-node deployment

一台 Linux 主机:宿主机 nginx 终止 TLS,omts 在容器里监听 loopback,后接本机 MySQL。绝大多数自托管场景从这里开始。 One Linux host: host nginx terminates TLS, omts listens on loopback inside a container, backed by local MySQL. Where most self-hosted setups start.

浏览器 / omtc ──https──▶ nginx :443 ──▶ omts 127.0.0.1:6444 ──▶ MySQL 127.0.0.1:3306
  • 前置:Linux 主机(glibc 动态链接,Alpine 跑不起来)、Docker + docker compose、宿主机 nginx(或其他能配 WebSocket upgrade 的反代)、编译好的 omts 二进制(源码仓 cargo build --release -p omts,或从 Releases 取)。Prerequisites: a Linux host (glibc, not Alpine), Docker + compose, host nginx (or any proxy that can pass WebSocket upgrades), and the omts binary (cargo build --release -p omts from source, or from Releases).
  • MySQL:docker 起一个 MySQL 8(数据落卷、绑定 127.0.0.1),创建 omts 库与用户。建表由 omts 启动时自动完成。MySQL: run MySQL 8 in docker (data on a volume, bound to 127.0.0.1) and create the omts database and user. Tables are created automatically at startup.
  • 目录:/opt/omt/bin/omts(二进制,volume 挂进容器)、/opt/omt/data/(运行状态,属主 65532:65532)、docker-compose.yml 与 .env。Layout: /opt/omt/bin/omts (the binary, mounted read-only into the container), /opt/omt/data/ (state, owned 65532:65532), plus docker-compose.yml and .env.

.env 最小集合(secrets 用 SC_* 命名,compose 显式映射到进程的 OMT_*):Minimal .env (secrets use SC_* names; compose maps them onto the process's OMT_* explicitly):

SC_REG_TOKEN=<设备注册令牌>
SC_JWT_SECRET=<随机串>
SC_CRYPTO_SECRET=<随机串>
SC_DB_URL=mysql://omts:<password>@127.0.0.1:3306/omts
OMT_ENV_ROLE=production
OMT_ENV_SERVER_URL=https://omt.example.com
OMT_ENV_URL_PREFIX=/omt
OMT_TRUST_PROXY=true
OMT_METRICS_ALLOW_CIDRS=127.0.0.1/32

compose 要点(安全内建,勿弱化):Compose essentials (hardened by default — do not weaken):

  • 容器 network_mode: host + omts 监听 127.0.0.1:6444——只有 nginx 能到达。Container uses network_mode: host while omts binds 127.0.0.1:6444 — only nginx can reach it.
  • 以非 root(65532)运行,cap_drop: [ALL]、pids_limit;二进制只读挂载。Runs as non-root (65532) with cap_drop: [ALL] and a pids_limit; the binary is mounted read-only.
  • secrets 经 显式 environment 列表注入(不要改回 env_file——那会把全部 secret 整包暴露给 docker inspect)。Secrets are injected via an explicit environment list (do not switch back to env_file — it exposes every secret wholesale to docker inspect).

nginx:TLS 终止在 nginx;WebSocket 必须透传 Upgrade/Connection,读超时要放宽到会话级别:nginx: terminate TLS here; WebSocket upgrade headers must pass through, and read timeouts must be session-scale:

location /omt/ {
    proxy_pass http://127.0.0.1:6444;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_read_timeout 3600s;          # 会话长连接
}

要浏览器 UX,还需把官方 dist 静态集(ui/ 页面、metainfo.json、版本化下载物)放到 nginx 静态目录,由 nginx 直接 serve(生产模板随源码仓提供,含 CSP、安全头与缓存策略)。只要 CLI(omtc)用法则完全不需要静态文件。 For the browser UX, also place the official dist static set (ui/ pages, metainfo.json, versioned downloads) in an nginx-served directory (the production template ships with the source repo, covering CSP, security headers, and caching). A CLI-only (omtc) deployment needs no static files at all.

  • 首次启动:docker compose up -d 后 curl http://127.0.0.1:6444/api/v1/version 应返回 200;/opt/omt/data/ 会落一份一次性 bootstrap 管理员密码(0600),用它登录管理页后删除。First boot: after docker compose up -d, curl http://127.0.0.1:6444/api/v1/version should return 200; a one-time bootstrap admin password (0600) lands in /opt/omt/data/ — log in once, then delete it.
  • 升级:新二进制 cp 到 bin/omts(temp+rename,运行中的旧 inode 不受影响)后 docker compose up -d 重建容器。schema 迁移自动且只增列,可回滚到上一个二进制;单机重启会断在线会话(客户端自动重连),要求无感请用多节点。Upgrades: cp the new binary into bin/omts (temp+rename — the running old inode is untouched) and docker compose up -d to recreate the container. Migrations are additive-only and rollback-safe; a single-node restart drops live sessions (clients reconnect) — use multi-node for zero-interruption.

多节点部署Multi-node deployment

同一主机(或多台主机)跑多个 omts 实例,共享同一个 MySQL。价值:滚动升级时在线会话不中断(节点逐个 drain)并扩容。节点之间没有任何直接通信——所有协调(会话归属、epoch、租约)都走共享数据库。 Run several omts instances (same host or many) over one shared MySQL. The payoff: rolling upgrades without dropping live sessions (nodes drain one by one) and more capacity. Nodes never talk to each other directly — all coordination (session ownership, epochs, leases) happens through the shared database.

                ┌─▶ omts1 (--node-id 1, 127.0.0.1:6444) ─┐
浏览器/omtc ──▶ nginx(omts_pool 轮询 + /n1/ /n2/ 钉定) ├─▶ MySQL(同一实例)
                └─▶ omts2 (--node-id 2, 127.0.0.1:6445) ─┘
  • 起点:先完成单机部署。集群 compose 与单机文件并存不替换——回滚就是 multi-node down && 单机 up。Start from a finished single-node deployment. The cluster compose file coexists with (does not replace) the single-node one — rollback is multi-node down && single-node up.
  • compose 差异:两个 service(omts1 --node-id 1 :6444 / omts2 --node-id 2 :6445);同一个 DSN 与 ./data 卷;stop_grace_period: 75s——SIGTERM 后先广播 reconnect_hint、等在线设备离开(最长 60s drain),compose 默认 10s 会在排水中途 SIGKILL。Compose differences: two services (omts1 --node-id 1 :6444 / omts2 --node-id 2 :6445); the same DSN and ./data volume; stop_grace_period: 75s — after SIGTERM the node broadcasts a reconnect hint and waits for online devices to leave (up to 60s drain); the compose default of 10s would SIGKILL mid-drain.
  • nginx 装两段:upstream/map 属 http 作用域、location 属 server 作用域,drop-in 被 include 在 server 里,所以要拆两个文件(upstreams 放 sites-enabled/,路由放 sites-available/):nginx needs two files: upstream/map live in the http scope while location lives in the server scope, and drop-ins are included inside server blocks — so split them (upstreams into sites-enabled/, routing into sites-available/):
# http 作用域:upstreams.conf
upstream omts_pool {
    server 127.0.0.1:6444;
    server 127.0.0.1:6445;
}

# server 作用域:路由 drop-in
location /omt/n1/ { proxy_pass http://127.0.0.1:6444; }   # 钉定节点(排障/灰度)
location /omt/n2/ { proxy_pass http://127.0.0.1:6445; }
location /omt/    { proxy_pass http://omts_pool; }        # 默认轮询
# (同单机:每个 location 都要透传 Upgrade/Connection 与长读超时)
  • 顺序:先装 nginx 再起第二节点(/n2/ 路由在节点注册前就该存在)。备份文件不要放 sites-enabled/——nginx glob 会把它当第二份配置加载(duplicate upstream)。Order: install the nginx config before starting the second node (the /n2/ route must exist before the node registers). Never park backup files in sites-enabled/ — nginx globs them in as a second copy (duplicate upstream).
  • 启动与验证:docker compose -f docker-compose.multi-node.yml up -d;管理页的集群面板应显示两个 epoch 成员。P2P 的 STUN 地址按节点配置(OMT_STUN_LISTEN/OMT_STUN_PUBLIC_ADDR 每节点一对),第二节点缺 STUN 对会静默退化成中继——部署后 ss -ulnp | grep 3478 核对。Start & verify: docker compose -f docker-compose.multi-node.yml up -d; the admin cluster panel should show both epoch members. STUN is per-node (OMT_STUN_LISTEN/OMT_STUN_PUBLIC_ADDR, one pair each); a node missing its STUN pair silently degrades to relay — check with ss -ulnp | grep 3478 after deploying.
  • 滚动升级:替换二进制后逐节点重建——先 n2 后 n1(n1 是默认池成员,最后动)。在线设备收到 reconnect_hint 后换节点重连,会话不中断。Rolling upgrade: swap the binary, then recreate node by node — n2 first, n1 last (n1 is the default pool member). Online devices follow the reconnect hint to the other node; sessions survive.
  • 回滚到单机:docker compose -f docker-compose.multi-node.yml down 然后 docker compose up -d(单机栈从未被修改)。Roll back to single node: docker compose -f docker-compose.multi-node.yml down, then docker compose up -d (the single-node stack was never modified).

启动参数CLI flags

每个参数都有对应的环境变量绑定(表中 env 列);带 secret 的参数只走环境变量。 Every flag has an environment binding (the env column); secret-bearing flags are env-only.

监听与 TLSListen & TLS

flagenv默认Default说明Description
--listen—127.0.0.1:8443监听地址。默认 loopback,裸启动不可能把明文监听暴露到公网。Listen address; loopback by default so a bare start can never expose plaintext on a public interface.
--allow-plaintext-listen—false无 TLS 证书绑定非 loopback 时必须显式开启(同机反代终止 TLS 的场景)。Required to bind non-loopback without TLS certs (same-host reverse proxy terminating TLS).
--tls-cert / --tls-key——TLS 证书与私钥文件;公网部署通常交给 nginx 终止。TLS cert and key files; public deployments usually terminate TLS at nginx instead.

数据库与密钥Database & secrets

flagenv默认Default说明Description
--db-urlOMT_DB_URL—MySQL DSN(mysql://user:pass@host:3306/db)。secret 只经环境变量传入,绝不进命令行。MySQL DSN (mysql://user:pass@host:3306/db). Env-only by design — never a command-line argument.
--reg-tokenOMT_REG_TOKEN自动生成generated设备注册令牌,omtc register -r 使用。Device registration token, consumed by omtc register -r.
--jwt-secretOMT_JWT_SECRET自动生成generatedJWT 签名密钥。JWT signing secret.
--crypto-secretOMT_CRYPTO_SECRET—存储凭据(TOTP、OAuth secret)的加密密钥。Encryption key for stored credentials (TOTP, OAuth secrets).
--bootstrap-password-dir—.一次性 bootstrap 管理员密码文件的落盘目录;容器部署指到挂载卷。Directory for the one-time bootstrap admin password file; point it at a mounted volume in containers.

对外地址与多节点 / P2PURL, multi-node & P2P

flagenv默认Default说明Description
--server-url——对外可达的 HTTPS 地址(生成绝对链接用)。Externally reachable HTTPS URL (used for absolute links).
--url-prefixSC_URL_PREFIX—整站挂载的子路径(如 /omt);必须以 / 开头、不以 / 结尾,nginx 侧同样配置。Sub-path the site is mounted under (e.g. /omt); must start with / and not end with it — mirrored on the nginx side.
--node-idOMT_NODE_ID1(WARN)多节点身份。单节点不填也行;多节点必须钉死——节点段 /n<id>/ 是可收藏 URL 的一部分。Multi-node identity. Fine to omit on a single node; mandatory in clusters — the /n<id>/ segment is part of bookmarkable URLs.
--stun-listenOMT_STUN_LISTEN空=禁用empty=offSTUN UDP 绑定地址(如 0.0.0.0:3478)。留空时 P2P 走 host candidates(同网直连仍可用)。STUN UDP bind (e.g. 0.0.0.0:3478). Empty disables the responder; P2P then relies on host candidates (same-network direct still works).
--stun-public-addrOMT_STUN_PUBLIC_ADDR—STUN 公网 host:port;与 --stun-listen 成对出现,多节点共享公网 IP 时各用不同端口。Public host:port for the STUN responder; paired with --stun-listen, distinct ports when nodes share a public IP.

限流、观测与不活跃清理Limits, observability & inactivity

flagenv默认Default说明Description
--trust-proxyOMT_TRUST_PROXYfalse信任反代的 X-Real-IP(限流与审计 IP)。omts 能被不可信客户端直连时必须为 false;开启时反代必须逐请求覆写该头。Trust the proxy's X-Real-IP (rate-limit and audit IPs). Must stay false if untrusted clients can reach omts directly; when true the proxy MUST overwrite the header on every request.
--metrics-allow-cidrsOMT_METRICS_ALLOW_CIDRS空=禁用empty=off/metrics 的 CIDR 白名单,逗号分隔;留空对远端全部关闭。Comma-separated CIDR allowlist for /metrics; empty disables it for all remote callers.
--ws-max-frame-sizeOMT_WS_MAX_FRAME_SIZE1 MiB单个 WebSocket 帧上限。Max size of a single WebSocket frame.
--ws-max-message-sizeOMT_WS_MAX_MESSAGE_SIZE4 MiB完整 WebSocket 消息上限。Max size of a complete WebSocket message.
--inactivity-enabledOMT_INACTIVITY_ENABLEDfalse开启不活跃账号的「禁用 → 删除」生命周期。开放注册的公共实例建议开启;私有部署默认关。Enable the inactivity disable→delete lifecycle. Recommended for public open-registration instances; off by default for private deployments.
--inactivity-disable-daysOMT_INACTIVITY_DISABLE_DAYS180连续 N 天无「完成登录」即自动禁用(页面一直开着不算)。Auto-disable after N days without a completed login (a page kept open does not count).
--inactivity-delete-daysOMT_INACTIVITY_DELETE_DAYS90被禁用账号再过 N 天可恢复,之后连同数据删除。A disabled account stays recoverable for N more days, then is deleted with its data.
--inactivity-sweep-interval-secsOMT_INACTIVITY_SWEEP_INTERVAL_SECS3600清扫周期(秒)。Sweep interval in seconds.

登录与企业集成Auth & integrations

flag说明Description
--feishu-app-id / --feishu-app-secret / --feishu-redirect-uri / --feishu-allowed-user / --feishu-webhook飞书 OAuth 登录与通知 webhook。DingTalk / GitHub / Google 登录经管理页配置(存库),无需命令行参数。Feishu OAuth login and notification webhook. DingTalk / GitHub / Google sign-in is configured in the admin UI (stored in the DB) — no flags needed.
--legacy-web-ui保留旧版 HTML 界面路由,默认开启;传 --legacy-web-ui=false 显式关闭。Keep the legacy HTML UI routes (default on); pass --legacy-web-ui=false to disable explicitly.
--test-token / --enable-test-auth仅测试环境(E2E 冒烟用)。生产一律不开。Test environments only (E2E smoke). Never enable in production.

子命令:omts config set <key> <value> [--db-url …] [--dry-run] 绕过 HTTP 管理 API、直改库内配置项(按 key 类型校验)。Subcommand: omts config set <key> <value> [--db-url …] [--dry-run] sets a config key directly in the DB, bypassing the HTTP admin API (type-checked per key).

网络要求Networking

  • P2P 直连需要 STUN 端口(默认 UDP 3478)可达:宿主机 iptables 与云防火墙/安全组两层都要放行,只开一层会静默退化为中继。集群每个节点要独立的公网 STUN 端口。P2P direct needs the STUN port (UDP 3478 by default) reachable: open it in BOTH host iptables and the cloud firewall/security group — one layer alone silently degrades to relay. Each cluster node needs its own public STUN port.
  • 中继走 HTTPS/WSS 443,无额外端口;弱网下自动回落。The relay rides HTTPS/WSS on 443 with no extra ports, and is the automatic weak-network fallback.