<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Oslo的博客</title><description>写点最近在做的东西</description><link>https://oslo254804746.github.io/personal-blog/</link><language>zh_CN</language><item><title>用 Rust 和 Tauri 做一个 Windows 进程性能监视器</title><link>https://oslo254804746.github.io/personal-blog/posts/building-perfcat-windows/</link><guid isPermaLink="true">https://oslo254804746.github.io/personal-blog/posts/building-perfcat-windows/</guid><description>从独立采样器、统一时间轴到可恢复的 JSONL 会话，记录 perfcat-windows 的几个关键设计取舍。</description><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近在做的 &lt;a href=&quot;https://github.com/oslo254804746/perfcat-windows&quot;&gt;perfcat-windows&lt;/a&gt;，是一款面向 Windows 的进程性能监视器。它使用 Rust 采集本机数据，通过 Tauri 把能力交给 React 界面，再将 CPU、内存、GPU 显存、FPS、磁盘 I/O、网络、句柄和线程等指标放到同一条时间线上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/oslo254804746/perfcat-windows/master/docs/images/perfcat-overview.jpg&quot; alt=&quot;perfcat 主监控界面&quot; /&gt;&lt;/p&gt;
&lt;p&gt;项目当前处于 &lt;code&gt;0.1.0&lt;/code&gt;。功能还在继续完善，但实现过程中有几个问题很值得单独记录：来自不同系统接口的数据如何对齐、采集失败应当如何表达，以及怎样让一次意外退出不至于毁掉整段记录。&lt;/p&gt;
&lt;h2&gt;先确定数据的语义&lt;/h2&gt;
&lt;p&gt;性能监控最容易出现的错误，不一定是数值算错，而是把不同含义的数据画在了一起。&lt;/p&gt;
&lt;p&gt;perfcat 中的各类采样器彼此独立：CPU 和内存来自进程相关的 Windows API，GPU 显存依赖性能计数器，网络使用 ETW 与 IP Helper，FPS 则可以来自 RTSS 或实验性的 ETW Present。它们的启动条件、权限要求和返回节奏都不相同。&lt;/p&gt;
&lt;p&gt;因此，前端不会直接接收一组没有关联的散乱事件。核心层先用 &lt;code&gt;FrameAssembler&lt;/code&gt; 按采样序号聚合数据，再输出版本化的 &lt;code&gt;SampleFrame&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Windows 数据源
    ↓
独立 Sampler
    ↓
MetricSample(sequence, kind, value)
    ↓
FrameAssembler
    ↓
SampleFrame + FrameQuality
    ↓
Tauri 事件 / JSONL 会话 / React 图表
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个 &lt;code&gt;SampleFrame&lt;/code&gt; 除了指标值，还包含会话 ID、序号、采集时间、目标进程、采样间隔和 schema 版本。&lt;code&gt;FrameQuality&lt;/code&gt; 则明确记录这一帧是否完整，以及缺失了哪些指标。这样一来，UI、存储和之后可能出现的导出工具都在消费同一种数据语义。&lt;/p&gt;
&lt;h2&gt;“没有采到”不等于零&lt;/h2&gt;
&lt;p&gt;这是我在这个项目里最想坚持的一条规则。&lt;/p&gt;
&lt;p&gt;假设 RTSS 没有挂接目标进程，或者某个采样周期中没有新的 Present 事件，此时把 FPS 写成 &lt;code&gt;0&lt;/code&gt; 看似方便，实际上会制造一条错误结论：程序真的运行到了 0 FPS。更准确的表达应该是“这个时间点没有可信测量结果”。&lt;/p&gt;
&lt;p&gt;所以 perfcat 会把对应指标保留为空，并在帧质量中标记缺失。图表上呈现的是间隙，界面也会告诉用户不可用的原因。其他正常工作的采样器仍然继续运行，而不是因为一个指标失败就结束整个会话。&lt;/p&gt;
&lt;p&gt;这种设计不只适用于 FPS。网络 ETW 可能缺少管理员权限，GPU 性能计数器也可能没有目标进程实例。与其用整齐但虚假的曲线掩盖问题，不如忠实保留数据的不确定性。&lt;/p&gt;
&lt;h2&gt;两种 FPS 来源，两种测量含义&lt;/h2&gt;
&lt;p&gt;当前默认方案是读取 RTSS 共享内存。这条路径成熟、结果直观，但要求本机已经安装并运行 RTSS，并且它成功挂接目标图形进程。&lt;/p&gt;
&lt;p&gt;另一个实验选项是 ETW Present。perfcat 会启动独立命名的实时 ETW 会话，筛选目标 PID 的 DXGI/D3D9 Present 起始事件，并保留原始 QPC 时间戳。它不依赖 RTSS，但通常需要管理员权限，而且测到的是应用提交 Present 的节奏，不应直接等同于画面最终显示到屏幕的帧率。&lt;/p&gt;
&lt;p&gt;既然语义不同，FPS 来源在一次会话开始后就会锁定。否则同一条曲线的前半段和后半段可能来自两套口径，数值看起来连续，实际却无法比较。&lt;/p&gt;
&lt;p&gt;帧时间除了平均值，还会计算 P95、标准差、卡顿次数、大卡顿次数和卡顿占比。平均值可以描述总体速度，但定位偶发卡顿时，尾部指标通常更有价值。&lt;/p&gt;
&lt;h2&gt;为什么会话存成追加式 JSONL&lt;/h2&gt;
&lt;p&gt;每次开始监控，应用都会在 &lt;code&gt;%APPDATA%\perfcat\sessions&lt;/code&gt; 中创建一个会话文件。文件按行依次写入：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;会话头，保存目标进程、指标配置和开始时间；&lt;/li&gt;
&lt;li&gt;一个个完整的 &lt;code&gt;SampleFrame&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;正常停止时写入结束标记。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;每写入一帧就刷新到磁盘。应用如果异常退出，之前已经完成的行仍然可以读取；解析时会忽略损坏的尾行，没有结束标记的会话则显示为“异常中断”。&lt;/p&gt;
&lt;p&gt;这里没有引入数据库。对于一条主要按时间追加、很少随机更新的性能会话，JSONL 足够透明，也方便排查和迁移。以后若增加索引、复杂查询或跨会话统计，再评估更重的存储方案会更合适。&lt;/p&gt;
&lt;h2&gt;Rust、Tauri 和 React 的职责边界&lt;/h2&gt;
&lt;p&gt;仓库目前分为三层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;crates/perfcat-core&lt;/code&gt; 负责进程发现、本机采样、帧聚合和监控会话；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src-tauri&lt;/code&gt; 负责桌面应用状态、命令、事件与本地会话存储；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src&lt;/code&gt; 负责 React 界面、国际化、图表和采样详情。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rust 适合承接 Windows API、并发采样和明确的数据类型；React 适合处理信息密度较高的交互界面；Tauri 则让两者之间保持一条相对克制的边界。浏览器开发模式还能使用确定性的模拟数据调整 UI，不必每次改样式都启动真实采集。&lt;/p&gt;
&lt;p&gt;这个组合并不会自动让桌面应用变简单。真正减少复杂度的，是先把数据模型稳定下来，让前端只负责展示，而不是偷偷承担采样对齐和缺失值推断。&lt;/p&gt;
&lt;h2&gt;当前边界&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;0.1.0&lt;/code&gt; 只构建和测试 Windows amd64。RTSS 仍然是推荐的 FPS 数据源；ETW Present 尚未覆盖所有图形 API、显示模式和游戏。高权限或受保护进程也可能拒绝访问，GPU 和网络指标的可用性则会受到系统环境与运行权限影响。&lt;/p&gt;
&lt;p&gt;这些限制会直接显示在界面和文档中。性能工具首先要让人相信数据，然后才是把图表做得更满。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/oslo254804746/perfcat-windows&quot;&gt;https://github.com/oslo254804746/perfcat-windows&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Hop：用 Rust 做一台轻量但不草率的 SSH 跳板机</title><link>https://oslo254804746.github.io/personal-blog/posts/hop-rs-lightweight-ssh-bastion/</link><guid isPermaLink="true">https://oslo254804746.github.io/personal-blog/posts/hop-rs-lightweight-ssh-bastion/</guid><description>单二进制、原生 SSH、SQLite 和渐进式权限管理，Hop 如何在轻量与安全之间划定边界。</description><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;跳板机经常出现在这样一个尴尬的位置：个人服务器和小团队确实需要统一入口，但完整堡垒机带来的数据库、缓存、消息队列和部署维护成本，又明显超过了实际需求。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/oslo254804746/hop-rs&quot;&gt;Hop&lt;/a&gt; 想解决的就是这个区间。它是一个 Rust 编写的 SSH 跳板机，以单二进制、SQLite、公钥认证和原生 SSH 工作流为基础；需要管理时提供 Web 页面，不需要时，它看起来仍然只是一台可以直接 &lt;code&gt;ssh&lt;/code&gt; 的服务器。&lt;/p&gt;
&lt;p&gt;本文基于当前 &lt;code&gt;0.1.5&lt;/code&gt; 版本，记录这个项目最重要的几条设计取舍。&lt;/p&gt;
&lt;h2&gt;轻量不是删掉安全能力&lt;/h2&gt;
&lt;p&gt;Hop 的“轻”主要来自更少的运行时部件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hop-server&lt;/code&gt; 一个二进制同时提供 SSH、TUI、Admin Web 和本机管理命令；&lt;/li&gt;
&lt;li&gt;SQLite 随程序内嵌，不要求额外部署 Postgres；&lt;/li&gt;
&lt;li&gt;管理页面由服务端提供，日常使用无需安装专属客户端；&lt;/li&gt;
&lt;li&gt;发布构建启用 LTO、单 codegen unit 和 &lt;code&gt;panic = &quot;abort&quot;&lt;/code&gt;，目标是一个容易分发的产物。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但减少组件不代表省略关键边界。Hop 的入口只接受白名单中的 SSH 公钥，托管凭证使用 ChaCha20-Poly1305 加密，Admin 密码使用 Argon2 哈希，管理页面默认只监听 &lt;code&gt;127.0.0.1:8080&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;远程打开 Admin Web 时，更推荐通过现有 SSH 通道转发：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -N -L 8080:127.0.0.1:8080 root@hop-host
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这让默认部署不会平白增加一个暴露在公网的管理入口。需要反向代理或管理网络时可以显式调整，但风险是部署者主动选择的，而不是项目替用户默默选择的。&lt;/p&gt;
&lt;h2&gt;让 SSH 保持 SSH&lt;/h2&gt;
&lt;p&gt;Hop 没有发明新的连接协议。用户仍然使用 OpenSSH、SFTP、SCP 和 &lt;code&gt;ProxyJump&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 打开 TUI，模糊搜索资产
ssh -p 2222 hop-host


# 直接进入指定资产
ssh -p 2222 web-prod-01@hop-host


# 把 Hop 当作 ProxyJump
ssh -J hop-host:2222 web-prod-01.hop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;未指定资产时，终端中会出现由 &lt;code&gt;ratatui&lt;/code&gt; 构建的选择器；把资产名放在 SSH 用户名位置，则可以跳过选择直接连接。对于已有大量 SSH 配置和脚本的人，这种兼容比一个功能齐全但封闭的 Web Terminal 更重要。&lt;/p&gt;
&lt;p&gt;RDP、VNC、MySQL、PostgreSQL 和 Redis 也没有被分别实现一遍。它们统一落到 SSH 本地端口转发上，资产中的协议只负责提供端口和客户端提示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -p 2222 -N -T \
  -L 127.0.0.1:13389:win-prod-rdp.hop:3389 \
  hop-host
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hop 的核心只转发 TCP，不解析上层协议。这既限定了功能范围，也减少了协议实现和维护成本。&lt;/p&gt;
&lt;h2&gt;把“能访问什么”和“如何登录”拆开&lt;/h2&gt;
&lt;p&gt;这是权限模型中很关键的一层。&lt;/p&gt;
&lt;p&gt;进入 Hop 的 SSH Key 决定用户可以看见并连接哪些资产；资产绑定的托管凭据，则决定 Hop 用什么身份登录目标主机。二者不是同一个概念。&lt;/p&gt;
&lt;p&gt;每把入口 Key 有两种资产访问模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;all&lt;/code&gt;：可以访问当前以及未来新增的全部资产；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;restricted&lt;/code&gt;：只能访问明确分配的资产，空集合表示可以进入 Hop，但不能发现或连接任何资产。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同一套授权检查会覆盖 TUI、直连 SSH、SFTP、ProxyJump 和本地 TCP 转发。否则只在界面中隐藏某个资产，却允许用户通过手写目标地址绕过限制，就只是“看起来有权限控制”。&lt;/p&gt;
&lt;p&gt;托管凭据也有自己的约束：正在被资产引用时不能直接删除；导出功能只迁移名称、用户名和认证类型等元数据，不导出密码、私钥和 passphrase。便捷的迁移功能不应该顺便变成凭据泄露通道。&lt;/p&gt;
&lt;h2&gt;从一个人平滑长到一个小团队&lt;/h2&gt;
&lt;p&gt;很多权限系统一开始就要求设计角色、策略和组织结构。对只有一个维护者的实例来说，这些步骤只是噪音。&lt;/p&gt;
&lt;p&gt;Hop 采用渐进式管理：只有一位有效管理员时，登录页保持简单；添加第二位管理员后才显示账号名。权限级别也只保留三种任务化角色：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;角色&lt;/th&gt;
&lt;th&gt;主要能力&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;完整管理，包括管理员和访问级别&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operator&lt;/td&gt;
&lt;td&gt;管理资产、凭据、SSH 访问与 Known Hosts，并查看审计&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Viewer&lt;/td&gt;
&lt;td&gt;只读查看 Dashboard、库存与审计&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这里没有通用 RBAC 策略编辑器。它牺牲了一部分任意组合能力，换来的是更容易理解和审计的日常操作。&lt;/p&gt;
&lt;h2&gt;两个 crate，一条清晰主线&lt;/h2&gt;
&lt;p&gt;代码按职责拆成两个主要 crate：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hop-core    配置、数据模型、SQLite、凭证加密
hop-server  SSH 服务、TUI、Admin Web、本机 CLI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;技术栈围绕 Tokio 展开：&lt;code&gt;russh&lt;/code&gt; 处理 SSH，&lt;code&gt;ratatui&lt;/code&gt; 提供终端界面，&lt;code&gt;axum&lt;/code&gt; 和 &lt;code&gt;maud&lt;/code&gt; 组成管理页面，&lt;code&gt;sqlx&lt;/code&gt; 管理 SQLite 与 schema 迁移。&lt;/p&gt;
&lt;p&gt;这个结构没有为了“微服务化”而拆进程，但依然把可复用的领域和存储能力从入口层分离出来。对一个目标是单二进制部署的项目来说，模块边界通常比进程边界更实用。&lt;/p&gt;
&lt;h2&gt;备份是安全模型的一部分&lt;/h2&gt;
&lt;p&gt;Hop 的运行数据中有三个文件不能遗漏：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hop.db        资产、入口密钥、会话、审计和加密后的凭证
hop.secret    凭证主密钥
hop_host_key  SSH 服务端身份
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尤其是 &lt;code&gt;hop.secret&lt;/code&gt;：丢失后，数据库里的托管凭证无法恢复。只导出资产 CSV 也不能替代完整备份。升级前应停止服务，对整个数据目录做一致性快照，同时保留旧二进制或镜像和一条不经过 Hop 的备用管理路径。&lt;/p&gt;
&lt;p&gt;轻量工具更容易部署，也更容易让人忘记它已经成为基础设施的一部分。把备份、回滚和主机密钥身份写进项目的正常使用路径，和把程序编译成一个文件同样重要。&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;Hop 并不打算覆盖大型堡垒机的全部场景。它没有复杂审批流，不解析 RDP 或数据库协议，当前审计页的筛选、分页、留存设置和导出也仍待完善。&lt;/p&gt;
&lt;p&gt;它更像是在回答另一个问题：当需求只是可信入口、资产选择、托管凭据、透明转发和基础审计时，能不能让系统保持足够小，同时不牺牲默认安全？&lt;/p&gt;
&lt;p&gt;我认为这正是“轻量”最有价值的含义。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/oslo254804746/hop-rs&quot;&gt;https://github.com/oslo254804746/hop-rs&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>rust-ios-device：用 Rust 串起 iOS 真机工具链</title><link>https://oslo254804746.github.io/personal-blog/posts/rust-ios-device-architecture/</link><guid isPermaLink="true">https://oslo254804746.github.io/personal-blog/posts/rust-ios-device-architecture/</guid><description>从 usbmuxd、lockdown 到 CoreDevice 与 RemoteXPC，梳理 rust-ios-device 的分层、连接路径和多语言封装。</description><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在电脑上列出一台 iPhone、截一张图或者读取一段 syslog，看起来只是几条命令。真正走进实现后，会遇到 usbmuxd、配对记录、lockdown、TLS、AFC、DTX，以及 iOS 17 之后越来越重要的 CoreDevice、RSD 和 RemoteXPC。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/oslo254804746/rust-ios-device&quot;&gt;rust-ios-device&lt;/a&gt; 尝试用 Rust 把这些层次组织成一套可以复用的工具链：底层是 &lt;code&gt;ios-core&lt;/code&gt;，上层提供 &lt;code&gt;ios&lt;/code&gt; CLI、Python 模块与 C ABI。当前版本为 &lt;code&gt;0.1.8&lt;/code&gt;，能力面已经不小，但项目仍明确标记为实验性。&lt;/p&gt;
&lt;h2&gt;两条主要连接路径&lt;/h2&gt;
&lt;p&gt;iOS 设备服务大致可以分成经典路径和 CoreDevice 路径。&lt;/p&gt;
&lt;p&gt;经典服务通常从 usbmuxd 开始：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;host
  → usbmuxd
  → device lockdown port
  → StartService
  → service port
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usbmuxd 负责发现设备，并把主机上的连接转发到 USB 设备端口。lockdown 则处理配对记录、会话建立、TLS 和服务启动。AFC 文件访问、syslog、截图、崩溃报告、SpringBoard 与 diagnostics relay 等能力都可以建立在这条路径上。&lt;/p&gt;
&lt;p&gt;iOS 17+ 的 CoreDevice 服务通常需要另一条链路：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;host
  → usbmuxd 或 remote pairing
  → CoreDeviceProxy / CDTunnel
  → RSD 服务发现
  → RemoteXPC 或原始服务
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的难点不只是“再加一个协议”。隧道建立后，还要通过 Remote Service Discovery 确认设备真正暴露了哪些服务，再通过 HTTP/2 与 XPC 编码和服务通信。不同系统版本、配对状态、Developer Mode 和设备服务面，都会影响最终可用能力。&lt;/p&gt;
&lt;p&gt;因此 CLI 不会只根据 iOS 版本猜测服务一定存在。遇到 CoreDevice fileservice 等功能时，可以先检查实际服务目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ios rsd services --all
ios rsd check com.apple.coredevice.fileservice.control
ios file --coredevice --domain temporary ls /
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果设备没有同时暴露所需的 control/data 服务，工具会报告明确的缺失服务错误，而不是悄悄换一个名字或落到语义不同的实现上。&lt;/p&gt;
&lt;h2&gt;用分层隔离协议复杂度&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ios-core&lt;/code&gt; 内部按照职责继续拆分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;proto&lt;/code&gt; 处理 plist、AFC、DTX、XPC 等线格式和编解码；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mux&lt;/code&gt; 与 usbmuxd 通信，负责设备枚举、监听和端口转发；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lockdown&lt;/code&gt; 管理配对、会话、TLS 与经典服务启动；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tunnel&lt;/code&gt; 实现 CDTunnel 握手以及用户态或内核态的数据转发；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xpc&lt;/code&gt; 实现 RSD、HTTP/2 和 RemoteXPC 传输；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;services&lt;/code&gt; 提供面向具体能力的客户端。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这套分层的价值在于，高层命令不需要重复处理连接细节。CLI 的 &lt;code&gt;screenshot&lt;/code&gt;、&lt;code&gt;syslog&lt;/code&gt; 或 &lt;code&gt;apps&lt;/code&gt; 命令关心的是任务，底层则根据设备与服务选择合适的连接路径。&lt;/p&gt;
&lt;p&gt;同时，内部协议模块不会全部作为稳定 API 暴露。支持给库用户的类型会在 crate 根部重新导出，内部组织仍可以随着协议理解的深入继续调整。对于一个公开 API 尚未稳定的协议项目，这种边界能减少过早承诺。&lt;/p&gt;
&lt;h2&gt;一个核心，三种使用方式&lt;/h2&gt;
&lt;p&gt;整个 workspace 有四个主要 crate：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;面向对象&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ios-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rust 开发者&lt;/td&gt;
&lt;td&gt;设备发现、配对、服务访问、隧道与高层 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ios-cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;终端与自动化脚本&lt;/td&gt;
&lt;td&gt;提供 &lt;code&gt;ios&lt;/code&gt; 命令和 54+ 子命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ios-py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Python 生态&lt;/td&gt;
&lt;td&gt;通过 PyO3 暴露设备列表与隧道工作流&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ios-ffi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;其他语言&lt;/td&gt;
&lt;td&gt;输出动态库、静态库与 &lt;code&gt;ios_rs.h&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;它们共用同一份核心实现。这比在 Rust、Python 和 C 侧分别维护一套设备协议更重要：修复握手、配对或隧道问题时，各入口可以一起受益。&lt;/p&gt;
&lt;p&gt;CLI 默认输出 JSON，方便被脚本继续处理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ios list
ios info
ios -u &amp;lt;UDID&amp;gt; lockdown get --key ProductVersion
ios screenshot --output screenshot.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要给人阅读时可以传入 &lt;code&gt;--no-json&lt;/code&gt;。设备参数省略时使用发现列表中的第一台设备，也可以通过 &lt;code&gt;-u&lt;/code&gt; 或 &lt;code&gt;IOS_UDID&lt;/code&gt; 固定目标。这些看似普通的 CLI 约定，对持续集成和多设备环境很重要。&lt;/p&gt;
&lt;h2&gt;Feature flag 也是架构边界&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ios-core&lt;/code&gt; 默认不启用具体服务，库使用者可以只选需要的能力：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[dependencies]
ios-core = { version = &quot;0.1.8&quot;, features = [&quot;afc&quot;, &quot;syslog&quot;] }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;仓库同时提供 &lt;code&gt;classic&lt;/code&gt;、&lt;code&gt;developer&lt;/code&gt;、&lt;code&gt;management&lt;/code&gt;、&lt;code&gt;ios17&lt;/code&gt; 和 &lt;code&gt;full&lt;/code&gt; 等分组。CLI 为了覆盖完整命令面使用 &lt;code&gt;full&lt;/code&gt;，而嵌入其他应用的库通常应该选择更窄的集合。&lt;/p&gt;
&lt;p&gt;这既减少不必要的依赖和编译范围，也迫使服务模块保持相对清晰的边界。对于包含大量设备服务的项目，feature 设计不只是优化包体积，也是一张可以被 Cargo 检查的能力地图。&lt;/p&gt;
&lt;h2&gt;为什么同时支持用户态与内核态隧道&lt;/h2&gt;
&lt;p&gt;CoreDevice 隧道可以通过内核 TUN 接口建立，但创建网络接口通常需要管理员或 root 权限。为了让普通用户和 Python 工具也能使用，项目还提供基于 &lt;code&gt;smoltcp&lt;/code&gt; 的用户态转发。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 启动单设备用户态隧道
ios tunnel start --userspace

# 启动本地隧道管理服务
ios tunnel serve --userspace --host 127.0.0.1 --port 49151
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用户态模式暴露一个本地 TCP 代理。客户端先发送目标 IPv6 地址和端口，再开始转发数据。Python 绑定进一步提供 &lt;code&gt;asyncio_proxy()&lt;/code&gt; 上下文，让现有的 &lt;code&gt;asyncio.open_connection()&lt;/code&gt; 可以在作用域内通过这条隧道路由。&lt;/p&gt;
&lt;p&gt;内核模式更接近系统网络接口，用户态模式则降低权限和集成门槛。两者并存，不是重复功能，而是为桌面工具、自动化脚本和开发环境提供不同的取舍。&lt;/p&gt;
&lt;h2&gt;对不稳定设备环境保持诚实&lt;/h2&gt;
&lt;p&gt;真实设备工具的测试矩阵很难穷举：主机可能是 Windows、macOS 或 Linux，设备系统版本不同，信任、配对、监督和 Developer Mode 状态也不同，部分服务还依赖开发者磁盘镜像。&lt;/p&gt;
&lt;p&gt;因此当前项目强调两件事：&lt;/p&gt;
&lt;p&gt;第一，错误信息尽量指向缺失的前置条件，例如服务未暴露、设备未配对或权限不足；第二，对会改变设备状态的命令保持明确警告。&lt;code&gt;erase&lt;/code&gt;、&lt;code&gt;restore&lt;/code&gt;、监督配置、描述文件安装与定位修改等操作，应优先在测试设备上验证。&lt;/p&gt;
&lt;p&gt;pair record 和监督证书同样属于敏感凭据，不应提交到仓库或写进共享日志。协议工具越接近设备管理能力，越需要把安全边界当作正常功能，而不是文档末尾的一句免责说明。&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;rust-ios-device 现在已经能覆盖从设备发现、文件访问和日志，到 Instruments、TestManager、WebInspector 与 iOS 17+ 隧道的一条很长链路。但比命令数量更值得记录的，是它如何把复杂度放回合适的位置：协议层理解字节，传输层理解连接，服务层理解任务，CLI 和语言绑定只组合公开能力。&lt;/p&gt;
&lt;p&gt;项目距离稳定 API 还有路要走。也正因为如此，先守住这些层次，会比急着把所有内部模块公开更有价值。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;oslo254804746/rust-ios-device&quot;}&lt;/p&gt;
</content:encoded></item></channel></rss>