分层
整条链路只有一件事在流动:把浏览器的一个动作,送到那台具体的电台上,再把结果送回来。下面五层是这件事在物理上的五个落点;证书、身份与门户横穿所有层,单独列为一条。
8888)。CAT/CI-V
控制、双向音频、频谱、以及 PTT 释放的权威。
:8989 → Hub
上属于你的回环端口 188xx。客户侧零入站。
443;历史:
9988 / 8899)。终结 TLS、按 Host
取呼号、查表得端口、按实例名校验上游证书(信任包里钉着
各实例的公钥)、透明转发数据通道。呼号自助注册是另一个
vhost。
每层职责与边界
边界比职责更重要 —— 明确写出「这层不做什么」,才能知道该在哪一层找问题。
失效表现:本地页打不开;局域网就 502。
0600,装成常驻服务,带 KeepAlive
失效表现:Hub 侧 502;日志里
start proxy success消失。
失效表现:未知呼号 →
404(不猜测别的实例);上游证书与钉住的不符 →
502。
失效表现:注册/状态查询不可用;已接入的实例不受影响(门户不在数据路径上)。
关键决策
每条决策的价值不在结论,而在当时排除了什么。
| 决策 | 内容 | 为什么不选另一种 |
|---|---|---|
AD-H01实例主动出站 |
客户侧只出站,零入站 | 反过来的「Hub 连进来」需要在每家路由器上做端口映射 —— 家庭宽带做不到,4G/5G 更做不到。 |
AD-H02通配子域 |
一条通配 vhost + 注册表映射 | 每实例一个 vhost 是上一版的做法:加一个实例就要改中心配置,还要处理正则与前缀的优先级。规模一上来就不可维护。 |
AD-H03控制面 / 数据面分离 |
无状态入口 ×N,有状态隧道网关 ×≥2 | 合成一个进程后,入口的横向扩容会牵着会话归属一起搬 —— 等于把无状态的部分也变成了有状态。 |
AD-H05单写者租约 |
每实例同时最多一个写者 | 允许多写者会让两个人同时按下 PTT。这个冲突在电台侧无法仲裁 —— 电台只知道「有人在发射」。 |
AD-H06PTT 权威在实例本地 |
Hub 只能关流通知,从不写下 PTT | 依赖云端超时释放,意味着云端一断网或一延迟,电台就一直发射。释放的权威必须和电台在同一台机器上。 |
AD-H07会话凭据不进 URL |
走 Cookie 与握手,不进查询串 | 查询串必然落进代理与实例的访问日志 —— 这个失效模式在本项目早前的形态里真实发生过,不是理论风险。 |
AD-H08监听者语义 |
可听、可看频谱、可调频换模式、禁发射 | 简单做成「只读」会让人没法帮忙找台;而调频并不改变「谁在发射」这个根本约束,所以放开它没有代价。 |
AD-H11一实例一证 |
逐实例证书校验名(目标态是设备 mTLS) | 共享静态 token 一旦泄露就是全 fleet 泄露;先把逐实例的校验名与信任包做绝,隧道自身的设备证书留到阶段 2,是有意分两步走。 |
AD-H13复用而不新建 |
遥测、升级、诊断都复用产品侧既有实现 | 为 Hub 单造一套升级/诊断通道,等于把同一件事维护两遍,两边会先分叉再打架。 |
AD-H15呼号即身份 |
租户名 = 呼号;入口 = 呼号.域名 | 另建一套用户名体系,会把「这是谁家的电台」变成一个要查表的问题,而它本来是可以从地址里直接读出来的。 |
三条安全不变量
这三条是不能被「优化掉」的。任何实现如果违反它们,就是错的,不管跑得多快。
Hub 与隧道只能发送「关流」这类通知并回收授权,不得直接下发「切到接收」的指令。多一个写者,就会出现两条互相矛盾的释放路径,实例侧的仲裁逻辑随之失效。
包括 Hub、实例、以及链路中间任何代理的访问日志。这一条同时约束前端(不拼查询串)与运维侧(日志格式不得记录查询串)。
每一跳都校验对端证书。关掉它(哪怕「只是为了先跑通」)等于把中间人攻击变成默认配置 —— 而且这类关闭一旦上线,通常不会有人再回头打开。
现状与目标态
这张表区分「已经跑通」和「设计好了但还没做」。两者不能混着看。
| 能力 | 状态 | 说明 |
|---|---|---|
| 实例出站隧道 | 已跑通 | 阶段 1 用成熟反向隧道验证通路;目标态是内置的实例代理进程 |
| 通配子域接入 + 真证书 | 已跑通 | 一条通配 vhost + 注册表;证书为真发证书,每日自动续期 |
| 数据通道透明转发 | 已跑通 | 控制 / 频谱 / 收音频 / 发音频四路固定,天调(ATR-1000)可选;握手、长连接与关闭语义与直连一致 |
| 逐跳证书校验 | 已开启 | Hub 到实例这一跳也校验,没有一处是关闭校验换来的 |
| 会话凭据不进 URL | 已实现 | Hub 侧日志格式不记查询串(已实测 0 命中);实例侧不再拼查询串的修复已随之发布(mrrc_modern v1.25.4:令牌走 Cookie / Bearer,query 仅作兼容并告警)—— 现状见概览 · 能力现状表 |
| (历史)海外边缘入口 | 已取消 |
2026-10-01 实测 6 次中 3 次 20 秒无响应;2026-10-02
定根因(边缘
proxy_pass用裸主机名、选中当时不可达的
IPv6)。该路径已随 V0.21 整体删除。
|
|
PTT 半开连接释放 (远程会话活性) |
实例侧已有 隧道层待做 | 「TCP 还活着但对端已失联」的情形(拔网线、NAT 掉表)。实例侧已有心跳闸门:经应用「接入云端」上线的实例自动设为 5 秒,手动配置默认关闭;隧道层的主动关流还没做 —— 在此之前释放靠实例本地多层兜底,会慢一些。 |
| 自助注册 Portal(呼号核验四步) | 公网入口已上线 |
代码在 portal/(零第三方依赖,标准库
HTTP),服务为
mrrc-portal.service,本体只绑回环;对外经 nginx
暴露为
https://portal.mrrc.vlsc.net并在该层限流。自助取回凭证、换机后的证书指纹判据、远程协助的端口分配与强制结束都在这里;授予后
由 30 s 的
mrrc-hub-routes.timer自动重生成路由与信任包;需人工的只有核验与批准那一步。
|
| 远程协助通道(客户发起) | 已实现 |
实例侧
help_session.py:本机生成凭据、只监听回环,经隧道开一条限时
SSH;门户负责端口分配、心跳与强制结束。凭据不落盘、不进日志、结构上不进门户请求体。v1.26.0
起随产品发布。
|
| 实例证书链(hub → 实例这一跳的一机一证) | 机制就位 |
make_instance_cert.sh签一张签给实例自己入口名的自签证书,公钥钉进
trust-bundle.pem,nginx 按
$mrrc_tls_name逐实例校验 ——
校验始终开着,自签靠「钉住它」通过。2026-10-16
复核:在册实例的校验名已逐台生成(注册表第三列可选,缺省即自己的入口名);换机/重签之后由路由
timer 自动收录。
|
| 实例安装器 | 脚本就位 |
deploy/install_instance_tunnel.{sh,ps1}:自取
frpc 并 校验 SHA-256、与 frps 版本 pin
死、三平台常驻。仍是仓内脚本,无公开发布的安装包下载。
|
| 一机一证(隧道自身的设备证书) | 阶段 2 | 隧道认证仍是 frp 的单个共享凭证 —— 这是它被定位为过渡通道的原因。与上面那条证书链是两件事:一个是「谁能接入隧道」,一个是「这台实例是不是它声称的那台」。 |
| 独占写者租约 | 阶段 2 | 现阶段由实例侧仲裁谁在发射 |
| 成员管理与单独撤销 | 阶段 2 | 现阶段只能改监听口令,会影响所有持有它的人 |
边界声明
本页只讲接入层:隧道、入口、路由、证书、身份。(海外边缘一层已随 V0.21 取消。)
电台控制、音频编解码、频谱、以及 PTT 安全释放的全部细节,权威在 MRRC Modern 自己的设计记录里 —— 本页不复制它们,只定义与它们相邻的接口。
- 要到产品站看 MRRC Modern 的设计与操作指南 → MRRC Modern
-
要看本项目的完整设计记录(需求、用例、决策、风险) →
mrrc_hub 仓库的
SDD/目录 -
实测的运维事实(主机、端口、证书流程、排障) →
SDD/12 §12.8
本页所有事实来自 2026-10-01 现场取证、2026-10-16 复核 对现网实际配置、证书与注册表的现场读取。如果你发现页面说的和实际不符,是页面该改 —— 欢迎提 issue 指出。
← 回到 概览 ·
照做走一遍
接入 ·
换机/退出/远程协助
维护 ·
遇到问题 排障
本页事实基线:2026-10-01 现场取证 / 2026-10-16 复核(现网 nginx
配置、证书有效期、注册表与信任包、延迟实测)。