跳转至

客户端宿主与绑定生产完成度 Delta(2026-08-23)

设计意图

  • qim-desktop 与 Tauri 登录入口只接收显式的 token、device_id 与 TLS 参数;开发取 token 和明文传输必须由显式开发开关共同开启。
  • Tauri bridge 把 SDK 事件先放进有界 FIFO,再尝试 emit;emit 失败保留队头、停止越过并可由前端拉取,避免静默丢失和乱序。
  • Rust 到前端的消息正文和会话预览保持 Vec<u8> / JSON number[];显示层独自决定 UTF-8 或十六进制展示,不能把有损字符串写回状态。
  • C ABI 保持 V1 布局和符号不变,以 V2 函数、V2 事件附加字段及成对释放函数补齐宿主所需命令与本地查询;头文件由 cbindgen 可复现生成和校验。

变更摘要

现有核心 SDK 已负责持久化、对账、重连、REBUILD 和分页,但三个宿主仍把开发认证、明文、device_id = user_id、有损正文转换和部分 ABI 缺口暴露为产品路径。本 delta 只改宿主、Tauri bridge、C ABI 与生成脚本:既不改 qim-sdk 状态机,也不假设服务端已实现同用户多设备路由。

受影响模块与所有权

模块 文件 责任
CLI 宿主 crates/qim-desktop/src/main.rs 显式生产连接参数、开发模式隔离、ACK/未读展示语义。
Tauri Rust bridge crates/qim-gui/src-tauri/src/{lib.rs,sdk_bridge.rs} 登录参数校验、端点解析、事件 FIFO/poll、字节 JSON 映射。
Tauri 前端 crates/qim-gui/src/{App.tsx,types.ts,client_protocol.ts,client_protocol.test.ts} 输入生产登录配置、轮询 bridge、以无损 bytes 状态渲染正文和预览。
C ABI crates/qim-sdk-ffi/src/lib.rs、examples/demo.c、cbindgen 配置/脚本 V2 命令、事件字段、查询结果及内存释放、生成头校验。
设计 本文件 决策、边界与验证记录。

接口 Delta

登录

  1. CLI 增加 --token、必填 --device-id、--tls-ca、--tls-server-name、--dev-token 与 --insecure-plaintext。
  2. 正常模式必须提供 token,默认 TLS(系统根或显式 CA);开发 token 与明文均只能由相应显式 flag 开启。
  3. Tauri login 增加同等的 token/device/TLS/开发模式参数;前端显示这些输入并默认不填 token、device 或地址。
  4. IPv4、DNS 和 [IPv6]:port 都由各宿主的端点 host 解析器校验;不再用 split(':')。
  5. 不生成或替代 device_id;宿主要求调用者提供独立值。服务端以 (user_id, device_id) 路由仍是外部前置条件。

Tauri 事件与正文

  1. UiMessage.payload 和会话预览 payload 改为字节数组;body_included=false 不注入伪正文。
  2. 前端状态保存原始数组;显示函数仅对可逆 UTF-8 呈现文本,其他内容显示长度与十六进制摘要。
  3. bridge 的 FIFO 容量固定且在满时停止消费 SDK 下一事件形成反压。emit 成功才出队;失败保留队头并按退避重试。
  4. drain_sdk_events 以 FIFO 顺序返回未 emit 的事件,前端启动后和定时器调用它。一次 pull 成功即消费该批,这是 Tauri invoke 的交付确认边界。

C ABI V2

  1. V1 类型和导出符号不改。V2 事件尾部增加 custom type 与会话预览字段,并由 qim_event_v2_free 释放。
  2. 新增 V2 发送(含 custom_type)、resend、history、members、room join/leave 函数。
  3. 新增 V2 本地消息/会话查询结构、查询函数和对应 free 函数;查询结构包含 payload、custom type、正文随行和预览所需字段。
  4. C 指针输入一律校验 NUL/UTF-8/空指针组合;所有由 ABI 分配的字节和字符串必须由配对 free 函数释放。

数据流

显式登录表单/CLI 参数
  -> LoginConfig { token, device_id, TLS, dev flags }
  -> ClientConfig + endpoint/user/tenant 隔离 SQLite
  -> SDK ordered Event
  -> Tauri FIFO (enqueue -> emit 成功才 pop;失败则 retry/poll)
  -> JSON bytes[] -> TS 原始 bytes[] 状态 -> 可逆文本或十六进制显示

C caller
  -> V2 command/query -> qim-sdk Handle
  -> V2 flat event/query result + explicit free

保持不变与边界

  • 不修改 qim-sdk core、服务端认证、服务端多设备连接注册、dist 或根 Cargo。
  • 不实现服务端尚未提供的媒体上传、公开加群、E2EE 或实时跨设备 READ_SYNC。
  • SEND_ACK 仍只表示提交确认;不增加“对端送达/已读”含义。
  • 有界 bridge 不会自行丢事件:下游持续不可用时反压 SDK 事件消费;进程退出后的恢复仍由 SDK SQLite 查询/同步负责。

风险与缓解

风险 缓解
修改 Tauri command 参数造成前后端脱节 同时变更 Rust 命令注册、TS 调用、类型检查和命令级测试。
V2 C 结构字段新增造成内存所有权错误 仅尾部扩展 V2;V1 不动;每个新结果拥有独立 free;Rust ABI 测试和 C -fsyntax-only。
emit 持续失败导致无限内存增长或乱序 固定 FIFO、队头失败即停止越过、定时 retry 与 pull 共同排空。
UTF-8 显示误判二进制 用 UTF-8 解码后再编码的逐字节相等性判定;原始数组始终保留。
生产参数被开发回退绕过 在 bridge/CLI 入口强制 token 与 device;dev token/明文各需显式 flag。

验收与测试计划

Must Have 验证
H1 CLI 只把 Committed 显示为“已提交”,且 MessagesAdded 不自行累加未读。
H2 GUI/CLI 正常模式以显式 token/device/TLS 建配置;未给 token/device 失败;IPv4/DNS/IPv6 host 解析单测通过。
H3 GUI 开发 token 和明文只能在显式开发选项下启用。
H4 payload=[0xff,0x00] 与会话预览经 event_to_json 保持完全相同字节;前端对非 UTF-8 显示十六进制而状态未变。
H5 bridge FIFO 在 emit 失败时保留顺序,drain_sdk_events 依序交付,容量满时拒绝继续接收。
H6 C V2 可发送 custom type、调用 history/member/resend/room、查询本地消息/会话并释放;V1 ABI layout 测试仍通过。
H7 cargo fmt --check、各宿主/FFI cargo test 与 clippy、C 语法检查、TS tsc --noEmit 均通过;Vite 用临时输出目录构建,绝不触及 dist。

无可用真实服务环境时,真实 token 登录和图形 E2E 作为未执行的外部环境验证,不以本地单测替代。