从原生绑定连接(Connect from native bindings)
原生绑定(native bindings)通过 FFI 把共享的 connect 会话核心嵌入宿主语言:纯会话规则 —— peer_id 推导、握手签名/校验、allowlist、nonce 单次使用、sequence 分配、关联校验、dispatch gate —— 集中在一个核心中,传输则留在各宿主语言。原生绑定中,C#(GitHub Packages NuGet)、Kotlin(GitHub Packages Maven)与 Python(PyPI)走注册表承载(registry-backed)分发;Swift(Swift Package Manager)、Go(Go modules)与 C/C++(提交的头文件与平台载体)走基于 git(git-based)分发。注册表软件包携带锁步发布版本;基于 git 的消费方从对应仓库 tag vX.Y.Z 解析。NuGet 与 Maven 共用 GitHub Packages 注册表族。
| 语言 | 渠道 | 软件包 |
|---|---|---|
| C# | GitHub Packages NuGet | 42ch.Spoke.Connect |
| Kotlin | GitHub Packages Maven | dev.42ch:spoke-connect |
| Swift | Swift Package Manager(git + tags) | 产品 SpokeConnect |
| Go | Go modules(git + tags) | github.com/42ch-dev/spoke/crates/spoke-connect/bindings/go |
| Python | PyPI | spoke-connect |
| C / C++ | git(提交的头文件 + 平台载体) | spoke_connect.h + spoke_connect.hpp + native/<rid>/ |
每个绑定暴露相同的同步核心面;golden-parity smoke 从各宿主侧断言字节级一致的行为。每个绑定原生库都由生产构建特性对 ffi,remote-adapter 构建 —— 重新生成的绑定在加载时需要 remote-adapter 符号(RemoteAdapterFFI、MultiPeerRouterFFI、回调 Transport),因此发布构建始终同时携带这两个特性。
C# —— GitHub Packages NuGet
<!-- nuget.config(每个解决方案配置一次) -->
<packageSources>
<add key="github-42ch" value="https://nuget.pkg.github.com/42ch-dev/index.json" />
</packageSources>
<PackageReference Include="42ch.Spoke.Connect" Version="X.Y.Z" />使用带 read:packages 权限的令牌向 GitHub Packages 认证。原生 libspoke_connect 随 NuGet runtimes/<rid>/native/(win-x64、linux-x64、osx-arm64)发布。
using uniffi.spoke_connect;
var peerId = SpokeConnectMethods.DerivePeerIdFromEd25519Pubkey(pubkey);
var version = SpokeConnectMethods.ProtocolVersion(); // 1软件包详情:bindings/csharp/PACKAGE.md。
Kotlin —— GitHub Packages Maven
// settings.gradle.kts 或 build.gradle.kts 的 repository 块
maven {
url = uri("https://maven.pkg.github.com/42ch-dev/spoke")
credentials {
username = providers.gradleProperty("gpr.user").get()
password = providers.gradleProperty("gpr.key").get()
}
}
dependencies {
implementation("dev.42ch:spoke-connect:X.Y.Z")
// JNA 是已发布产物的传递依赖
}在 gradle.properties 或 ~/.gradle/gradle.properties 中设置 gpr.user 与 gpr.key(GitHub 用户名与带 read:packages 的令牌)。JNA 从 jar 加载平台原生库(darwin-aarch64、linux-x86-64、win32-x86-64)。
import uniffi.spoke_connect.derivePeerIdFromEd25519Pubkey
import uniffi.spoke_connect.protocolVersion
val peerId = derivePeerIdFromEd25519Pubkey(pubkey)
val version = protocolVersion() // 1绑定 README:bindings/kotlin/README.md。
Swift —— Swift Package Manager
// Package.swift
dependencies: [
.package(url: "https://github.com/42ch-dev/spoke.git", from: "X.Y.Z"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "SpokeConnect", package: "spoke"),
]
),
]在 vX.Y.Z tag 上,SPM 解析仓库根 Package.swift,得到产品 SpokeConnect(生成的 Swift 代码加 spoke_connectFFI xcframework)。
import SpokeConnect
let peerId = try derivePeerIdFromEd25519Pubkey(pubkey: goldenPubkey)
let version = protocolVersion() // 1绑定 README:bindings/swift/README.md。
Go —— Go modules
go get github.com/42ch-dev/spoke/crates/spoke-connect/bindings/[email protected]在 vX.Y.Z tag 上,仓库根 go.mod(module github.com/42ch-dev/spoke)为模块定版本;导入路径即子目录包。cgo 链接模块树中 native/<goos>_<goarch>/ 下的共享库;消费者需要 C 工具链与 CGO_ENABLED=1(永不需要 Rust 工具链)。
import spokeconnect "github.com/42ch-dev/spoke/crates/spoke-connect/bindings/go"
peerID, err := spokeconnect.DerivePeerIdFromEd25519Pubkey(pubkey)
version := spokeconnect.ProtocolVersion() // 1绑定 README:bindings/go/README.md。
Python —— PyPI
pip install spoke-connect==X.Y.Z平台 wheel(manylinux、macosx_11_0_arm64、win_amd64)经发布工作流上的 Trusted Publishing 发布到 PyPI 项目 spoke-connect。
import spoke_connect
peer_id = spoke_connect.derive_peer_id_from_ed25519_pubkey(pubkey)
version = spoke_connect.protocol_version() # 1绑定 README:bindings/python/README.md。
C 与 C++ —— git
git clone --branch vX.Y.Z --depth 1 https://github.com/42ch-dev/spoke.git
git lfs install # 每台机器执行一次
git lfs pull # 取回载体动态库;全新 clone 会自动 smudgecrates/spoke-connect/bindings/cpp/include/spoke_connect.h—— C99 ABI 头文件crates/spoke-connect/bindings/cpp/include/spoke_connect.hpp—— C++17 便利层头文件crates/spoke-connect/bindings/cpp/native/osx-arm64/libspoke_connect_capi.dylib—— macOS arm64 载体crates/spoke-connect/bindings/cpp/native/win-x64/spoke_connect_capi.dll—— Windows x64 载体
提交的 C/C++ 载体面向 macOS arm64(osx-arm64)与 Windows x64(win-x64)。spoke_connect.h、spoke_connect.hpp 与目标平台的原生文件都取自同一仓库 tag vX.Y.Z。两个载体动态库是 Git LFS 对象(.gitattributes);头文件、Windows 导入库与 provenance.json 是普通 Git 对象。
C++17 头文件为 header-only,并包含 C 头文件。spoke::connect 把同一会话核心包装为可移动的 RAII 句柄、显式的 Result 错误通道、返回缓冲区的借用文本视图,以及传输、ports 与工具面的宿主回调桥。C99 头文件仍是 C 宿主的 ABI 契约 —— 状态值加原始记录与回调表布局。
完整走查 —— 获取、编译、开会话并完成一次调用:从 C 与 C++ 连接。
共享会话核心
每种绑定暴露同一套同步核心面:peer_id 推导、握手签名/校验、allowlist、nonce store、sequence 分配、响应关联、dispatch gate 与协议版本。密钥以原始字节跨 FFI 边界(校验为恰好 32 字节),peer id 以字符串,manifest / 握手信封以 JSON 字符串 —— 传输 adapter 留在宿主语言,按线上契约实现。
TypeScript 语言原生客户端(从 TypeScript 客户端连接)直接用 TypeScript 实现同一套会话核心规则 —— 它是并行的姊妹路径,不是绑定行。Rust 参考实现(crates.io 上的 spoke-connect)是会话核心参考与绑定来源;共享契约见connect 线上参考。RemoteAdapter 契约经同一 FFI 面以同步对象形式交付(RemoteAdapterFFI、MultiPeerRouterFFI、回调 Transport)—— 见从原生绑定使用 RemoteAdapter。同一面还携带工具契约:adapter、路由器与响应方上的 invoke_tool;adapter 与响应方上用于工具服务的 register_tool_handler 与带外 ToolHandler(工具处理器)回调;以及接受侧 ConnectResponderFFI / connect_responder_ffi。同一面还携带可选 port 族:RemoteAdapterFFI 暴露 project / compute / list_fork_timeline_events(JSON 进 / JSON 出),响应方的 ports 参数接受一个可选的带外 PortsHandler,服务基线及可选的 port.* 族 —— 见从原生绑定使用 RemoteAdapter与可选 port 族。同一 PortsHandler 回调面还携带可选的抽取服务面(PortsHandler.extract)与归属门禁 —— 见远程抽取与归属门禁。
下一步
- 开启你的首个 connect 会话 —— 每个绑定都实现的握手流程。
- 从原生绑定使用 RemoteAdapter —— 拨号
Transport、调用 port 方法并在 FFI 上跨多个对等节点路由。 - 暴露并调用远程工具 —— 从原生宿主通告、发现并反向调用工具。
- 从 C 与 C++ 连接 —— 以手写 C 头文件编译,并链接提交的原生载体。
- connect 线上参考 —— 信封字段表与身份绑定。