# Commilitia Drop 全客户端构建与安装手册 本文记录 Web、服务端、Windows、macOS、iPhone 与 iPad 的正式构建、签名、安装和验证流程。 命令默认从仓库根目录执行。项目内部目录名和稳定系统身份可以继续使用 `cdrop`;所有用户可见名称、 协议和诊断界面统一使用 “Commilitia Drop” 或 `commilitia-drop` 命名空间。 ## 1. 正式客户端矩阵 | 平台 | 唯一正式实现 | 架构/设备 | 正式产物 | |---|---|---|---| | Web | `web/` React SPA / PWA | 现代浏览器 | `web/dist/`,嵌入服务端 | | Windows | `desktop/` Wails | x86-64 | `Commilitia Drop.exe` | | macOS | `desktop/` Wails | Apple Silicon ARM64 | `Commilitia Drop.app`/`.dmg` | | iPhone | `ios/CDrop` SwiftUI | 已登记真机 | `CommilitiaDrop.app` | | iPad | `ios/CDrop` SwiftUI | 已登记真机 | 与 iPhone 共用 target | `ios/CDrop` 中的原生 macOS target 只是迁移候选。它在功能对等、三语界面、签名扩展和真机验收 全部完成前,不得替代 Wails,也不得作为第二个 macOS 客户端发布。macOS 只考虑 ARM64。 ## 2. 命名与兼容边界 - 用户可见产品名:`Commilitia Drop`,桌面应用名中不再出现 `Desktop`。 - iOS/iPadOS 登录回调:`commilitia-drop://auth-callback`。 - WebRTC DataChannel 与 iOS 文件流 Scheme:`commilitia-drop-file`。 - Bonjour 服务类型:`_commilitia-drop._tcp`。 - Auth Broker application key:`commilitia-drop`。它会出现在 OAuth 参数和 JWT scope 中,属于 用户可见/可诊断协议文本。 - Bundle ID、App Group、Keychain service、LaunchAgent label、环境变量、数据库和配置目录不直接 决定显示名,保持稳定以保护系统权限、钥匙串数据和既有配置。 - 每个平台只保留上表的一份正式客户端;迁移验证成功后删除同平台旧名称副本。 命名复扫时应区分用户可见文本和内部身份。建议至少检查: ```sh rg -n -i 'commilitia drop desktop|cdrop desktop|cdrop://|_cdrop\._tcp|cdrop-file' \ README.md docs auth desktop ios web ``` 允许保留的典型内部值包括 `CDROP_*` 环境变量、`net.commilitia.cdrop` LaunchAgent/Bundle ID、 源码 package 和存储目录。若某个内部值直接、不可更改地成为系统显示名,则必须单独迁移。 ## 3. 通用前置与密钥规则 本地需要 Go、Node.js、Wails v2、Just、Xcode Command Line Tools、XcodeGen,以及 iOS 依赖所需的 Swift Package Manager。环境配置放在仓库根目录、已被 Git 忽略的 `.env`;变量说明见 `.env.example`。 签名和密钥遵循以下规则: - 不在仓库记录 `.p8`、私钥、Broker internal key、VAPID 私钥或证书导出文件。 - iPhone/iPad 必须复用钥匙串中既有 Apple Development 证书。把其 SHA-1 写入 `CDROP_APPLE_DEVELOPMENT_IDENTITY`;recipe 会在构建前检查身份并把该值固定传给 `xcodebuild`。不得用 Xcode GUI,不得新建证书或修改私钥 ACL。 - macOS 正式分发必须复用既有 Developer ID Application 证书;完整身份名写入 `CDROP_DEVID_IDENTITY`。 - App Store Connect API Key 只通过 `CDROP_ASC_KEY_PATH`、`CDROP_ASC_KEY_ID` 和 `CDROP_ASC_ISSUER_ID` 引用。 - 登录钥匙串必须已经解锁。`errSecInternalComponent` 通常表示签名进程不能使用私钥;先在用户的 普通 Terminal 解锁登录钥匙串,再重试同一证书,不要创建替代证书。 构建前可做基础检查: ```sh git status --short go version node --version just desktop-doctor security find-identity -v -p codesigning xcrun devicectl list devices ``` ## 4. Web 与服务端 ### 4.1 构建和验证 Web ```sh just typecheck-front just build-front ``` 正式 Web 构建输出在 `web/dist/`。服务端通过嵌入静态文件提供 SPA、PWA manifest、service worker 和 iOS 原生引擎页,因此发布 Web 改动时必须重新构建服务端镜像,不能只替换单个 HTML 文件。 ```sh just test just docker-image ``` 部署新镜像后至少验证: ```text /healthz /api/auth/config /site.webmanifest /engine.html ``` `/api/auth/config` 应返回 `broker_app=commilitia-drop`;manifest 的 `name` 与 `short_name` 均应为 “Commilitia Drop”。登录、扫码批准、设备续期和传输必须各做一次实际冒烟。 ### 4.2 Auth Broker 原位迁移 Broker application 不创建并行副本,直接把既有 `cdrop` 项改为: ```text key: commilitia-drop callback: commilitia-drop://auth-callback ``` 只需在修改前备份一次 Broker 配置;客户端和服务端源码由 Git 历史恢复,不另做文件备份。迁移后旧 session 可以全部失效,客户端重新登录。Broker、Caddy 和服务端的 application key 必须同时一致; 否则批准可以完成,但客户端会在兑换或 scope 校验阶段显示“登录失败”。 若 Caddyfile 以单文件 bind mount 进入容器,宿主机上的 `sed -i`/原子替换会生成新 inode,而运行 容器仍可能读旧 inode。修改后必须热加载,并从 Caddy admin API 或容器内实际挂载内容验证生效; 不能只查看宿主机路径。 ## 5. macOS 正式客户端(Wails ARM64) ### 5.1 开发构建 ```sh just desktop-build-mac ``` 任务先构建最新 Web 前端,再执行 `wails build -clean -s -platform darwin/arm64`。输出为: ```text desktop/build/bin/Commilitia Drop.app ``` `-clean` 是必要的:macOS 与 Windows 共用 `desktop/build/bin/`,另一平台的残留产物可能被 Wails/Go 误判为构建输入。不要把原生 macOS 候选 target 的产物放进正式分发目录。 ### 5.2 Developer ID 签名、公证与 DMG 在 `.env` 配齐以下变量: ```text CDROP_DEVID_IDENTITY CDROP_ASC_KEY_PATH CDROP_ASC_KEY_ID CDROP_ASC_ISSUER_ID ``` 然后运行: ```sh just desktop-dist-mac ``` 该 recipe 会依次完成: 1. ARM64 Wails 构建; 2. 使用既有 Developer ID Application 身份、hardened runtime 和 secure timestamp 签名 `.app`; 3. 将 `.app` 以 `ditto` 打包提交 Apple 公证,等待 `Accepted`,再 staple/validate; 4. 生成含 `.app` 和 `/Applications` 链接的压缩 DMG; 5. 对 DMG 签名、单独提交公证并 staple/validate; 6. 用 Gatekeeper `spctl` 分别评估 app 和 DMG。 最终文件为 `desktop/build/bin/Commilitia Drop.dmg`。发布前再次检查: ```sh codesign --verify --deep --strict --verbose=2 \ 'desktop/build/bin/Commilitia Drop.app' xcrun stapler validate 'desktop/build/bin/Commilitia Drop.app' xcrun stapler validate 'desktop/build/bin/Commilitia Drop.dmg' spctl --assess --type execute --verbose=2 \ 'desktop/build/bin/Commilitia Drop.app' spctl --assess --type open --context context:primary-signature --verbose=2 \ 'desktop/build/bin/Commilitia Drop.dmg' ``` 2026-07-31 已用既有 Developer ID 证书验证完整链路:app 与 DMG 均获 Apple `Accepted`,并完成 staple 和 Gatekeeper 的 `Notarized Developer ID` 评估。 ### 5.3 本机安装 先验证新 app,再替换 `/Applications` 中的正式客户端;不保留第二个 `Desktop` 副本: ```sh ditto 'desktop/build/bin/Commilitia Drop.app' \ '/Applications/Commilitia Drop.app' ``` LaunchAgent label `net.commilitia.cdrop` 是稳定内部身份,可以保留;但其 `ProgramArguments` 必须指向: ```text /Applications/Commilitia Drop.app/Contents/MacOS/Commilitia Drop ``` 更新磁盘 plist 后需要重新 bootstrap,不能只看文件,因为 launchd 可能仍缓存旧路径。最终检查已安装 app 的签名、公证、架构、进程路径,以及“登录后启动”开关。 ## 6. Windows 正式客户端(Wails x64) ### 6.1 构建 ```sh just desktop-build-win ``` 输出为 `desktop/build/bin/Commilitia Drop.exe`。在 macOS 上交叉构建无需 MinGW;Windows 特有通知 和 WebView2 绑定不依赖 Darwin CGO。当前 EXE 尚无 Authenticode 签名,Windows 会报告 `NotSigned`,并可能显示 SmartScreen 提示;获得 Windows 代码签名证书后应把签名步骤加入 recipe。 ### 6.2 通过 mDNS 无代理直连安装 目标主机使用 `Desktop-C.local`、SSH 端口 `223`、用户 `commilitia`。访问该主机不得使用任何代理。 先确认 shell、系统和 SSH 配置没有代理: ```sh env | rg -i '^(http|https|all|ftp|no)_proxy=' scutil --proxy ssh -G -p 223 commilitia@Desktop-C.local | \ rg -i '^(hostname|user|port|proxycommand|proxyjump) ' ``` mDNS 可能先返回不可达 IPv6,使 SSH 看似超时;继续保留 mDNS 主机名并用 `-4` 强制 IPv4。显式清除 代理变量、禁用 SSH 代理和跳板,并建立任务专用、10 分钟 TTL 的复用连接: ```sh env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \ -u http_proxy -u https_proxy -u all_proxy \ ssh -4 -M -S /tmp/commilitia-drop-windows-%C \ -o ControlPersist=600 -o ProxyCommand=none -o ProxyJump=none \ -p 223 commilitia@Desktop-C.local exit ``` 复用前用 `stat` 检查 socket 创建时间仍在 TTL 内,并执行 `ssh -O check`。安装流程为: 1. 在 Windows 上只读侦查 `D:\Tools`、匹配进程和 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`; 2. 本地记录 EXE 的 SHA-256; 3. 用同一 `-4`、`ProxyCommand=none`、`ProxyJump=none` 连接上传到用户目录暂存; 4. 远端再次校验 SHA-256; 5. 停止旧 `D:\Tools\Commilitia Drop Desktop.exe` 进程; 6. 先复制为 `Commilitia Drop.installing.exe` 并再次校验,再原子移到 `D:\Tools\Commilitia Drop.exe`; 7. 把既有 “Commilitia Drop” Run 项改为 `"D:\Tools\Commilitia Drop.exe" --hidden`; 8. 只有新文件哈希正确后才删除旧 `Desktop` 文件与上传暂存文件; 9. 从 SSH 服务会话直接 `Start-Process` 可能因没有交互桌面而立即退出。使用当前用户完整 Windows 身份注册 `LogonType=Interactive` 的一次性计划任务,启动后立即删除任务; 10. 独立复核新进程在交互 Session、旧文件不存在、Run 项正确且应用日志无新错误。 2026-07-31 在 `Desktop-C.local` 已完成上述流程,正式路径为 `D:\Tools\Commilitia Drop.exe`,旧 `Desktop` 文件已删除,登录启动项和运行进程均指向新路径。 ## 7. iPhone 与 iPad 两种设备共用 `CommilitiaDrop` scheme 和一套签名约束。全程使用 CLI,不打开 Xcode GUI。 ### 7.1 模拟器验证 ```sh just ios-sim-build ``` 该任务会拉取/生成品牌字体、运行 XcodeGen、解析 SPM 的 libwebrtc,并针对通用 iOS Simulator 目标编译。模拟器不需要 Apple 签名,但不能替代真机的 URL Scheme、本地网络、APNs、Share Extension 和后台行为验收。 ### 7.2 复用既有证书真机构建 在 `.env` 配齐: ```text CDROP_TEAM_ID CDROP_ASC_KEY_PATH CDROP_ASC_KEY_ID CDROP_ASC_ISSUER_ID CDROP_APPLE_DEVELOPMENT_IDENTITY=<既有 Apple Development 证书 SHA-1> ``` 确认设备与身份: ```sh just ios-devices security find-identity -v -p codesigning | \ rg -F "$CDROP_APPLE_DEVELOPMENT_IDENTITY" ``` 分别覆盖安装 iPhone 与 iPad: ```sh just ios-device just ios-device ``` `ios-device` 会把 SHA-1 作为 `CODE_SIGN_IDENTITY` 固定传入 `xcodebuild`,ASC API Key 仅用于自动 provisioning 的设备、能力和 profile 更新。若签名测试返回 `errSecInternalComponent`,应解锁当前 登录钥匙串后重试,不得生成新证书或修改私钥 ACL。 安装后验证: - 设备端显示名为“Commilitia Drop”; - Bundle ID 保持 `net.commilitia.Commilitia-Drop`; - `commilitia-drop://auth-callback` 已登记; - 旧 session 失效时能进入登录页,经 Broker 批准后回到 app 并成功建立新 session; - iPhone 与 iPad 各自完成一次发送、接收、本地网络权限和后台/扩展冒烟。 更完整的真机能力清单见 `ios/CDrop/REALDEVICE.md`。 ## 8. 发布顺序与分发目录 Wails 的 macOS 和 Windows recipe 都用 `-clean` 且共享 `desktop/build/bin/`,因此不能假设两个产物 会同时保留。推荐固定顺序: 1. `just desktop-dist-mac`; 2. 复制 `Commilitia Drop.dmg` 到分发目录; 3. `just desktop-build-win`; 4. 复制 `Commilitia Drop.exe` 到分发目录; 5. 检查分发目录只保留这两个正式文件,不保留 `Commilitia Drop Desktop.*`; 6. 记录两者 SHA-256。 当前分发目录约定为: ```text ~/Library/CloudStorage/Dropbox/软件客户端/Commilitia Drop/ ``` 不要把 `.app` bundle 当成 Dropbox 的正式发布物;macOS 使用已签名、公证、装订的 DMG。 ## 9. 提交前验证矩阵 ```sh git diff --check go test ./... (cd desktop && go test ./...) (cd web && npm run typecheck) (cd web && npm run build) just ios-sim-build bash -n desktop/scripts/make-dmg.sh just --dry-run desktop-dist-mac ``` 平台产物还需验证: | 对象 | 必验项 | |---|---| | Web/服务端 | health、auth config、manifest、登录、传输 | | Mac app/DMG | ARM64、Developer ID、hardened runtime、Accepted、staple、Gatekeeper | | Windows EXE | PE32+ x86-64、SHA-256、唯一文件、Run 项、交互 Session 进程 | | iPhone/iPad | 既有证书 SHA-1、签名验证、显示名、URL Scheme、Broker 登录、真机传输 | 提交时不加入 `.env`、`.p8`、构建产物、临时 PowerShell/安装脚本或 Broker 备份。客户端源码和构建 流程由 Git 历史恢复;Auth Broker 配置备份由服务端运维位置单独保留。