Skip to content

RFC(plugin): Host API 1.1 enablers validated by an SSH/SFTP plugin - #5794

Draft
Ezreal-byte wants to merge 3 commits into
t8y2:dev/plugin-framework-currentfrom
Ezreal-byte:codex/plugin-host-api-1.1-review-ready
Draft

RFC(plugin): Host API 1.1 enablers validated by an SSH/SFTP plugin#5794
Ezreal-byte wants to merge 3 commits into
t8y2:dev/plugin-framework-currentfrom
Ezreal-byte:codex/plugin-host-api-1.1-review-ready

Conversation

@Ezreal-byte

Copy link
Copy Markdown

定位

这是一个供插件框架讨论和真实项目验证的 Draft/RFC 参考实现,不以当前形态直接合并为目标。

本 PR 基于 dev/plugin-framework-current,用一个真实的 SSH/SFTP 插件验证 Host API 1.1 所需的安全边界、生命周期、长连接和大文件传输能力。重点是把“插件适配遇到的问题”映射为可运行、可审查的代码方案,供维护者决定 API 形态、拆分方式和最终实现。

真实插件遇到的问题与本 PR 的方案

插件适配问题 本 PR 提供的参考方案 主要代码位置
SSH 五种认证方式需要条件显示/必填,私钥和 Agent 需要路径字段 Manifest 字段增加 visible_whenrequired_whenpath,仅校验当前可见字段 manifest.rsmanifest.schema.jsonPluginConnectionFields.vue
同一连接需要多个相互隔离的 SSH 标签 增加 multiple-workbenches,普通打开复用,显式“新建连接”创建独立 workbenchId SidebarTreeRuntimeHost.vuequeryStore.tsdatabase.ts
iframe 因 KeepAlive 淘汰重挂载时不能丢 SSH 会话和终端输出 注入稳定 workbenchIdrestored、最多 64 KiB 的 workbenchState;提供 set/get 和 acknowledgeRestore() openTabsPersistence.tsPluginWorkbenchTab.vuepluginHostBridge.ts
每个工作台监听 Tauri 全局拖放会导致多标签/多插件重复接收 将插件拖放并入 DBX 全局 useFileDrop(),通过活动工作台 Bridge 注册表只投递给唯一活动标签 useFileDrop.tspluginWorkbenchBridgeRegistry.tsContentArea.vue
iframe 直接取得本机路径会突破插件隔离;Tauri fs allow-open 也过宽 桌面选择/保存/拖放下沉 Rust,插件只持有不透明句柄;Rust 重复校验 owner、offset、块大小、总量、状态和超时 plugin_file_transfer.rspluginHostBridge.ts
Web 与桌面都需要流式上传下载,不能在 iframe/Host 聚合 1 GiB 文件 公共 API 保持 pick/read/beginSave/write/finish/cancel,新增 release();256 KiB 分块,桌面 16 GiB,OPFS 16 GiB,无 OPFS 内存回退 1 GiB pluginHostBridge.tsplugin_file_transfer.rs
未知 SSH 主机指纹必须在发送凭据前由 Host 确认,iframe confirm() 无法形成可信边界 增加全局挑战队列和 DBX 模态框,以 pluginId + operationId + challengeId 绑定并单次消费 host.rspluginConnectionChallenge.tsPluginConnectionChallengeDialog.vue
disconnect/notify 复用操作上下文可能造成挑战重放或错误授权 disconnect 使用独立 operationId;notify 生命周期禁止挑战;活动操作完成/超时后清理 host.rsruntime.rspluginHostBridge.ts
终端复制粘贴需要宿主能力,但剪贴板读取不能静默发生 增加 host.clipboard;首次读取按插件进行进程内授权,写入仍做权限检查 pluginClipboardPermission.tspluginHostBridge.ts
插件需要随 DBX 明暗模式、色彩令牌、终端字体变化 注入 allow-list 外观上下文并支持变化通知 frontendPlugin.tsPluginWorkbenchHost.vuepluginHostBridge.ts
known_hosts 和安全临时文件需要跨版本、按插件隔离的数据目录 Runtime 注入 DBX_PLUGIN_DATA_DIR,目录与插件 ID 绑定 runtime.rsplugins.rs
标签真正关闭与 iframe 暂时卸载语义不同 增加可选 workbench/close 生命周期;缓存淘汰只卸载 iframe,真正关标签才释放会话/传输 PluginWorkbenchHost.vuePluginWorkbenchTab.vuehost.rs

方案结构

1. Rust Host:文件句柄与可信生命周期

  • PluginFileTransferState 维护 handleId → pluginId/workbenchId/kind/path/offset/expectedSize/lastAccess/status
  • 桌面文件对话框、拖放路径、临时文件和最终原子替换均留在 Rust;iframe 只见随机句柄。
  • 每块上限 256 KiB;offset 必须连续;单工作台最多 32 个句柄;15 分钟闲置失效。
  • 工作台关闭、完成、取消和应用退出时释放句柄并清理临时文件。
  • Desktop/OPFS 单文件上限为 16 GiB;无 OPFS 内存回退强制 1 GiB,避免未声明大小时聚合 16 GiB。
  • operationId 与挑战生命周期由 Host 管理;challengeId 成功 resolve 后只能消费一次。

2. 前端 Host:单一入口路由与沙箱桥

  • 全局拖放入口优先路由到当前活动插件工作台,不影响原有 SQL/SQLite/DuckDB 文件拖放。
  • Bridge 注册表只保存当前活动、仍存在的工作台桥;缓存标签不再各自注册 WebView 全局监听。
  • Bridge 暴露外观、剪贴板、文件句柄、挑战和 workbenchState 能力,同时保持 Host API 1.0 插件兼容。
  • restored 不再永久写入持久数据;DBX 启动恢复标签时重新置 true,新会话成功后由插件显式 acknowledge。
  • 全局 KeepAlive 恢复原上限 4,要求真实插件通过 attach/replay 处理淘汰后的重挂载。

3. Manifest/SDK/文档:把试验接口写成可讨论契约

  • Host API 版本保持未发布的 1.1.0
  • Schema、Rust manifest 类型、中文入门文档和 README 同步描述新增能力。
  • Smoke 示例不固定 DBX 包版本,读取当前构建元数据。
  • SSH/SFTP 插件作为外部真实消费者,不把插件业务实现合入 DBX 主仓库。

修改文件明细

文件 修改原因
Cargo.lock 记录新增 Rust 依赖解析结果。
apps/desktop/src/components/connection/ConnectionDialog.vue 接入条件字段校验和全局连接挑战流程,移除局部 iframe/连接框确认语义。
apps/desktop/src/components/layout/AppDialogs.vue 挂载唯一的全局插件连接挑战对话框。
apps/desktop/src/components/layout/ContentArea.vue 同步当前活动工作台,为全局拖放路由提供唯一目标。
apps/desktop/src/components/plugins/PluginConnectionChallengeDialog.vue 新增 DBX 风格的全局主机指纹挑战 UI。
apps/desktop/src/components/plugins/PluginConnectionFields.vue 渲染 visible_when/required_when/path,并按可见性执行表单校验。
apps/desktop/src/components/plugins/PluginWorkbenchHost.vue 注入 Host API 1.1 上下文,注册/注销工作台 Bridge,处理外观、拖放和关闭生命周期。
apps/desktop/src/components/plugins/PluginWorkbenchTab.vue 向插件传递稳定 workbenchId、恢复状态和持久化工作台状态。
apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue 为支持能力的插件提供“新建连接/新工作台”入口。
apps/desktop/src/composables/useFileDrop.ts 将插件工作台纳入 DBX 唯一全局文件拖放分发链。
apps/desktop/src/i18n/locales/en.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/es.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/it.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/ja.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/ko.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/pt-BR.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/zh-CN.ts 增加插件连接挑战文案。
apps/desktop/src/i18n/locales/zh-TW.ts 增加插件连接挑战文案。
apps/desktop/src/lib/__tests__/app/openTabsPersistence.spec.ts 验证 restored 的启动时语义和状态大小边界。
apps/desktop/src/lib/app/openTabsPersistence.ts 持久化 workbenchState,但不持久化瞬时 restored 标志。
apps/desktop/src/lib/plugins/frontendPlugin.spec.ts 覆盖 Host API 1.1 前端 Manifest/外观解析。
apps/desktop/src/lib/plugins/frontendPlugin.ts 扩展前端 Manifest、appearance 和新 capability 类型。
apps/desktop/src/lib/plugins/pluginClipboardPermission.spec.ts 验证剪贴板首次读取、允许和拒绝行为。
apps/desktop/src/lib/plugins/pluginClipboardPermission.ts 实现按插件、仅当前进程有效的读取授权缓存。
apps/desktop/src/lib/plugins/pluginConnectionChallenge.spec.ts 验证全局挑战排队和 resolve 生命周期。
apps/desktop/src/lib/plugins/pluginConnectionChallenge.ts 实现全局单例挑战队列。
apps/desktop/src/lib/plugins/pluginHostBridge.spec.ts 覆盖文件句柄、所有权、上限、状态恢复、挑战和剪贴板桥接。
apps/desktop/src/lib/plugins/pluginHostBridge.ts Host API 1.1 的主要 iframe 桥;实现桌面/Web 文件传输、外观、剪贴板、挑战和状态接口。
apps/desktop/src/lib/plugins/pluginWorkbenchBridgeRegistry.spec.ts 验证只有活动且有效的工作台接收拖放。
apps/desktop/src/lib/plugins/pluginWorkbenchBridgeRegistry.ts 新增工作台 Bridge 注册表和活动目标解析。
apps/desktop/src/stores/queryStore.ts 创建独立插件工作台实例,并区分缓存卸载和真实关闭。
apps/desktop/src/types/database.ts 扩展插件标签的 workbenchId/restored/workbenchState 类型。
crates/dbx-core/examples/ssh_plugin_package_smoke.rs 提供真实 SSH/SFTP 插件安装、生命周期、挑战和传输的可复现验证夹具。
crates/dbx-core/src/plugins.rs 导出新增插件 Host/manifest 类型与能力。
crates/dbx-core/src/plugins/host.rs 实现 operation/challenge 绑定、工作台关闭、数据目录与 Host API 1.1 生命周期。
crates/dbx-core/src/plugins/manifest.rs 增加条件字段、path、multiple-workbenches、clipboard/fileTransfer 等 schema 对应类型和验证。
crates/dbx-core/src/plugins/runtime.rs 注入插件数据目录,区分 request/notify 操作并清理操作上下文。
plugins/GETTING_STARTED.zh-CN.md 记录 Host API 1.1、挑战 payload、剪贴板授权、句柄超时和工作台恢复语义。
plugins/README.md 更新插件框架能力摘要和 SSH/SFTP 验证入口。
plugins/manifest.schema.json 同步 Manifest 条件字段、路径类型、能力和权限 JSON Schema。
src-tauri/Cargo.toml 引入 Rust 文件句柄实现所需依赖。
src-tauri/src/commands/mod.rs 注册插件文件传输命令模块。
src-tauri/src/commands/plugin_file_transfer.rs 实现桌面不透明文件句柄、流式 I/O、所有权、offset/块/总量校验、超时与清理。
src-tauri/src/lib.rs 初始化文件传输状态、注册命令并在应用生命周期中清理。

总计:44 个文件,约 +3004/-79。未包含 SSH/SFTP 插件业务代码,也已移除与 SQL Server 无关的格式差异和不再需要的宽泛 Tauri fs 权限。

建议审查顺序

本分支特意整理成三个可独立阅读的提交:

  1. 119b99a73 feat(plugin): harden Host API 1.1 security boundaries
    • Rust 不透明文件句柄、安全边界、挑战绑定、Manifest/Runtime 基础。
  2. 85b2251ab feat(plugin): centralize workbench routing and lifecycle
    • 全局拖放路由、Bridge 注册表、恢复/关闭生命周期、前端 Host API。
  3. 6931bd2e4 test(plugin): document and verify Host API 1.1
    • 回归测试、真实 SSH smoke 示例、Schema 和开发文档。

已完成验证

  • 从清空 target 开始执行完整 pnpm tauri dev,Windows Debug 客户端成功编译并启动。
  • Desktop 前端类型检查通过。
  • Host/插件前端测试共 47 项通过。
  • Rust challenge operation 绑定测试通过。
  • dbx-core check 通过。
  • Tauri desktop library check 通过。
  • 外部插件 0.2.2:前端类型检查、4 项插件测试、生产构建通过。
  • 生成 Windows x64 .dbxp 候选包,SHA-256:1ABB446DF830556CD3187E04B5C096C317D9FD5F165049C2F672B90AFC3B2744
  • 使用真实 SSH 服务器完成:安装包、UI、连接生命周期、未知主机密钥挑战、交互终端、4 MiB 和空文件 SFTP 往返、变化主机密钥拒绝、disconnect。

已知限制 / 希望维护者重点评估

这些问题是本 RFC 不应直接按“可合并完成态”处理的原因:

  1. 挑战连接绑定仍可进一步收紧:当前 Host 在缺少 connectionId 时允许 operation 进入兼容路径。最终协议可考虑强制 connectionId 严格匹配,并校验 challengeId 的格式/来源。
  2. 安全模型依赖 iframe 隔离假设:当前 owner 的 pluginId/workbenchId 由 Host bridge 注入,真正边界依赖插件 iframe 无法直接调用 Tauri。建议增加自动化隔离测试,或进一步采用 Host 签发的不透明 capability token。
  3. 原生拖放错误反馈:非文件、非法路径或超过 16 GiB 的拖放项当前会被过滤,尚未向 UI 返回逐项失败原因。
  4. 剪贴板授权 UI:参考实现使用宿主 confirm 完成进程内授权,正式实现宜替换为可国际化的 DBX 全局模态框。
  5. Smoke 示例归属ssh_plugin_package_smoke.rs 对框架验证很有用,但较偏具体插件;请确认应长期保留在 core、迁到插件仓库,还是改造成通用 fixture。
  6. 尚未完成的自动化:Windows 文件句柄测试目标已补齐缺失的 tempfile dev dependency,但测试二进制链接在本机 5 分钟窗口内未完成;Web 无 OPFS 的 1 GiB 边界和多插件拖放仍需浏览器自动化覆盖。

希望得到的反馈

  • workbenchId/restored/workbenchState/acknowledgeRestore 的生命周期是否符合 DBX 标签模型。
  • 文件传输是否应由 Rust 句柄作为唯一桌面实现,以及 Web/OPFS 与 Desktop 的 API 是否应保持同形。
  • operationId/challengeId 的可信边界应落在 dbx-core、Tauri 还是前端全局队列。
  • multiple-workbenches、条件表单和 path 字段的 Manifest 形态是否适合作为通用能力。
  • 如果后续进入合并阶段,建议怎样拆分成更小 PR(Manifest、生命周期、文件句柄、挑战、剪贴板/外观)。

兼容性与边界

  • Host API 版本保持未发布的 1.1.0;未使用新能力的 Host API 1.0 插件行为不变。
  • 插件 iframe 仍不能直接访问 Tauri;本 PR 不向插件暴露任意本地路径。
  • 桌面/OPFS 上限 16 GiB,无 OPFS 内存回退 1 GiB,块大小 256 KiB,每工作台最多 32 句柄,闲置超时 15 分钟。
  • 本 PR 不包含发布插件、商店元数据或 SSH/SFTP 业务逻辑;真实插件与测试包仅作为外部验证材料。

@github-actions github-actions Bot added area/multiple Touches more than three repository areas dependencies/backend Adds a backend dependency ui-change Changes user-visible interface, text, or visual assets labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/multiple Touches more than three repository areas dependencies/backend Adds a backend dependency ui-change Changes user-visible interface, text, or visual assets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants