Celium 客户端:多平台落地路径

Celium 的核心是 Go:引擎(WireGuard、打洞、中继、用户态协议栈)来自 Tailscale 的 开源实现,控制面客户端、账号流程、状态机是 Celium 自己的。桌面端直接把它编译成原生 二进制;移动端把同一份核心打包进 App,而不是重写一遍协议栈——这样"手机登录 Celium 和电脑登录 Celium 能互联"是自然结果,而不是两套实现互相对齐。

各平台现状

平台形态状态
Linuxceliumd + celium CLI,真实 TUN 接口可用
macOS同上(utun)可用
Windows同上核心可交叉编译,TUN 后端待接
Androidgomobile 绑定核心 + VpnService待建
iOSgomobile 绑定核心 + NEPacketTunnelProvider待建
鸿蒙见下需单独评估

交叉编译已经验证过(CGO_ENABLED=0,含 Tailscale 引擎的完整依赖闭包):

GOOS=linux   GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/celiumd
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/celiumd
GOOS=android GOARCH=arm64 CGO_ENABLED=0 go build ./cmd/celiumd

移动端的真正难点不是 Go,是数据面的入口

这一点必须在写代码之前说清楚,否则会低估工作量:手机不允许普通 App 创建 TUN 设备。系统只把接口交给一个受管组件:

  • Android:App 必须实现 VpnService,由系统弹出授权,然后通过

Builder.establish() 拿到一个文件描述符(ParcelFileDescriptor),把它交给 Go 核心。 若要做"按应用分流",则用 addAllowedApplication / addDisallowedApplication

  • iOS:App 必须带一个 NEPacketTunnelProvider 扩展,系统把

packetFlow 的读写句柄交给扩展;Mesh 的核心逻辑跑在扩展进程里(不是主 App)。

也就是说,移动端的"壳"要提供的是:一个 TUN 文件描述符进程生命周期Keychain / Keystore 里的密钥存储,以及 UI。协议栈、打洞、加密、账号流程都在 Go 核心里,三个平台共用同一份。

因此核心需要暴露一个平台无关的入口,形状大致是:

// 三种数据面入口,平台壳负责选一个并提供所需句柄
type DataPath interface{ /* ... */ }

// 1) 桌面:自己建 TUN(需要 root)
// 2) Android/iOS:由系统把 fd 交进来
func NewFromTUNFD(fd int, cfg Config) (*Node, error)
// 3) 无特权:用户态协议栈 + 本地 SOCKS5/HTTP 代理
func NewUserspace(cfg Config) (*Node, error)

第 3 种已经可用,而且它本身就是移动端的一个合理降级路径:在没有 VPN 授权、或在容器 与 CI 里,核心以用户态运行并提供本地代理,流量照样能进 mesh。

Android

# 前置:Android SDK + NDK、gomobile
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

# 绑定核心(产出 .aar)
gomobile bind -target=android -androidapi 24 -o celium.aar ./mobile/celiumcore

App 侧(Kotlin):登录界面 → 调 celiumcore.login(...) → 启动前台服务 → 申请 VpnService 授权 → 把 ParcelFileDescriptor.fd 交给 celiumcore.startWithFD(...)

iOS

gomobile bind -target=ios -o CeliumCore.xcframework ./mobile/celiumcore

App 侧(Swift):主 App 负责登录与 UI,NEPacketTunnelProvider 扩展负责把 packetFlow 的 fd 交给核心。注意扩展有严格的内存上限(通常几十 MB),核心必须按需 初始化,而不是在扩展启动时就把所有子系统拉起。

鸿蒙

需要单独评估,先把事实说清楚:

  • HarmonyOS NEXT 的应用层以 ArkTS 为主,没有官方支持的 Go 运行时,因此不能像

Android/iOS 那样把 Go 核心直接编译进去。

  • 可行的路径有两条:一是把核心做成一个独立进程/服务(跑在设备上的 Go 二进制或

通过方舟的原生能力调用),App 通过本地 IPC 驱动它;二是用 ArkTS 重写客户端, 但只能复用协议,不能复用代码——这就违背了"同一份核心"的初衷。

  • 如果目标设备是仍兼容 Android 应用的 HarmonyOS 版本,则 Android 方案直接适用。

结论:鸿蒙要等核心的平台无关入口(上面的 DataPath 抽象)稳定之后再评估。

账号在客户端上的位置

账号流程与数据面无关,三个平台完全一样:登录(用户名+密码)→ 拿到会话令牌 → 用它 签发本机的节点密钥 → 节点入网。

  • 会话令牌是 bearer 凭据:桌面存 0600 文件,Android 存 Keystore,iOS 存 Keychain。
  • 令牌只用于管理(签发节点密钥、查看自己的机器、邀请他人);真正的数据面认证是

节点密钥与机器密钥,即使会话过期,已经入网的机器也继续工作。

  • 邀请码让一个已有用户把别人带进来,这是自托管部署在没有外部身份提供方时扩张的

唯一方式。

现在就能做的和还不能做的

:Linux/macOS 上跑真实 mesh;无特权环境下跑用户态 mesh + 本地代理;用自有账号 注册、登录、邀请、签发节点密钥;celium status 看每台机器的直连/中继路径。

不能:Android/iOS 的 App 与 VPN 授权壳(需要 NDK / Xcode,本机都没有);Windows 的 TUN 后端;鸿蒙客户端。这些不是设计问题,是还没做的工作,其中移动端的入口(fd 交接 + 扩展生命周期)是主要工作量。