跳到主要内容

设计与层层实现

从电台到浏览器,六层。每一层负责什么、边界画在哪、失效时表现什么 —— 以及每条关键决策背后,当时为什么不选另一种。

分层

整条链路只有一件事在流动:把浏览器的一个动作,送到那台具体的电台上,再把结果送回来。下面五层是这件事在物理上的五个落点;证书、身份与门户横穿所有层,单独列为一条。

L0
电台硬件 FT-710 / IC-7300 / IC-7300MK2。产生射频,提供 CAT/CI-V 状态。不参与任何接入逻辑 —— 它不知道有 Hub 这回事。
L1
实例本地 · MRRC Modern 跑在你自己的机器上(默认 8888)。CAT/CI-V 控制、双向音频、频谱、以及 PTT 释放的权威。
L2
隧道 你的机器主动出站 → Hub 的隧道入口 :8989 → Hub 上属于你的回环端口 188xx。客户侧零入站。
L3
Hub 入口 一条通配 vhost 服务所有实例(443;历史: 9988 / 8899)。终结 TLS、按 Host 取呼号、查表得端口、按实例名校验上游证书(信任包里钉着 各实例的公钥)、透明转发数据通道。呼号自助注册是另一个 vhost。
L4
客户端 浏览器 / PWA / 原生 App。负责展示与交互,不是 PTT 释放的依赖 —— 客户端失联时释放由 L1 负责。
横切
证书 · 身份 · 审计 · 遥测 贯穿所有层,不属于任何一层:通配证书(DNS-01 签发、每日自动续期)、呼号即租户名与账号标识、门户(申请/核验/授予/撤销、远程协助端点与信任判据)、访问日志只记路径不记查询串、会话并发遥测。

每层职责与边界

边界比职责更重要 —— 明确写出「这层不做什么」,才能知道该在哪一层找问题。

L1 实例本地
五个数据通道
控制 · 频谱 · 收音频 · 发音频 · 天调
不做什么:不做成员与权限管理(那在 Hub,且尚未实现);不把 PTT 释放托付给云端。

失效表现:本地页打不开;局域网就 502。
L2 隧道
frpc / frps
配置 0600,装成常驻服务,带 KeepAlive
不做什么:不终结 TLS、不做路由决策、不知道「呼号」以外的任何业务语义。

失效表现:Hub 侧 502;日志里 start proxy success消失。
L3 Hub 入口
通配 vhost + 注册表
一条 vhost 服务全部实例;每实例的全部差异只是注册表里的一行
不做什么:不保存会话状态(无状态,可横向扩);永不写 PTT。

失效表现:未知呼号 → 404(不猜测别的实例);上游证书与钉住的不符 → 502。
横切 · 门户与身份
呼号自助 Portal
申请 → 核验 → 授予 → 登记(enroll)→ 撤销;远程协助的端口分配、心跳与强制结束
不做什么:不接触你的会话凭据;不保管协助口令(请求结构里就没有这个字段);核验通过之前不写注册表。

失效表现:注册/状态查询不可用;已接入的实例不受影响(门户不在数据路径上)。

关键决策

每条决策的价值不在结论,而在当时排除了什么。

决策 内容 为什么不选另一种
AD-H01
实例主动出站
客户侧只出站,零入站 反过来的「Hub 连进来」需要在每家路由器上做端口映射 —— 家庭宽带做不到,4G/5G 更做不到。
AD-H02
通配子域
一条通配 vhost + 注册表映射 每实例一个 vhost 是上一版的做法:加一个实例就要改中心配置,还要处理正则与前缀的优先级。规模一上来就不可维护。
AD-H03
控制面 / 数据面分离
无状态入口 ×N,有状态隧道网关 ×≥2 合成一个进程后,入口的横向扩容会牵着会话归属一起搬 —— 等于把无状态的部分也变成了有状态。
AD-H05
单写者租约
每实例同时最多一个写者 允许多写者会让两个人同时按下 PTT。这个冲突在电台侧无法仲裁 —— 电台只知道「有人在发射」。
AD-H06
PTT 权威在实例本地
Hub 只能关流通知,从不写下 PTT 依赖云端超时释放,意味着云端一断网或一延迟,电台就一直发射。释放的权威必须和电台在同一台机器上。
AD-H07
会话凭据不进 URL
走 Cookie 与握手,不进查询串 查询串必然落进代理与实例的访问日志 —— 这个失效模式在本项目早前的形态里真实发生过,不是理论风险。
AD-H08
监听者语义
可听、可看频谱、可调频换模式、禁发射 简单做成「只读」会让人没法帮忙找台;而调频并不改变「谁在发射」这个根本约束,所以放开它没有代价。
AD-H11
一实例一证
逐实例证书校验名(目标态是设备 mTLS) 共享静态 token 一旦泄露就是全 fleet 泄露;先把逐实例的校验名与信任包做绝,隧道自身的设备证书留到阶段 2,是有意分两步走。
AD-H13
复用而不新建
遥测、升级、诊断都复用产品侧既有实现 为 Hub 单造一套升级/诊断通道,等于把同一件事维护两遍,两边会先分叉再打架。
AD-H15
呼号即身份
租户名 = 呼号;入口 = 呼号.域名 另建一套用户名体系,会把「这是谁家的电台」变成一个要查表的问题,而它本来是可以从地址里直接读出来的。

三条安全不变量

这三条是不能被「优化掉」的。任何实现如果违反它们,就是错的,不管跑得多快。

一 · Hub 永远不是 PTT 写者

Hub 与隧道只能发送「关流」这类通知并回收授权,不得直接下发「切到接收」的指令。多一个写者,就会出现两条互相矛盾的释放路径,实例侧的仲裁逻辑随之失效。

二 · 会话凭据不进 URL,也不进日志

包括 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 配置、证书有效期、注册表与信任包、延迟实测)。