Apple 开发者账号迁移 Runbook

Pangolin 客户端 · 中国「岩美北京技术」→ 美国「Yanmei AI LLC」· 目标:产物与仓库无中国开发者账号任何痕迹 · 2026-09-06

一句话:这是一次干净切割——启用全新 Bundle ID(com.yanmeiai.*)、全新 Team、全新证书/描述文件/公证凭据,与旧中国账号零依赖(无需清理旧 App ID)。 分三块:你做 Apple 后台建资产 + gitea 传新 secret;我做 仓库代码里 Team ID / Bundle ID / App Group / 实体名的全量替换;我做 本机钥匙串 + 公证凭据配置。 顺序很重要:先建 Apple 资产 → 我改代码 → 传 gitea → 验证首个构建

✅ 进度:代码侧已完成(Team ID = 44WULXM6SV

仓库代码(§3)已全量迁到美国身份并收敛成单一真相源 scripts/signing.env + codegen。剩下的是你在 Apple 后台建资产(§1)、本机公证凭据(§2)、gitea 换 secret(§4)、首个构建验证(§5)。建资产时把两张证书精确全名 + ASC Key 的 Key/Issuer ID 发我,我做 §2。

0 · 命名总表(新账号下的目标身份)

旧(中国,待清除)新(美国 Yanmei AI LLC)
Team IDBYL4KQHMTN44WULXM6SV(你提供)
法人实体(证书 CN 内含)Yanmei (beijing) Technology Co., LtdYanmei AI LLC
macOS 主 app Bundle IDcom.pangolin.pangolincom.yanmeiai.pangolin
macOS 扩展 Bundle IDcom.pangolin.pangolin.PacketTunnelcom.yanmeiai.pangolin.PacketTunnel
iOS 主 app Bundle IDcom.pangolin.pangolinVpncom.yanmeiai.pangolin
iOS 扩展 Bundle IDcom.pangolin.pangolinVpn.PacketTunnelcom.yanmeiai.pangolin.PacketTunnel
iOS App Groupgroup.com.pangolin.pangolinVpngroup.com.yanmeiai.pangolin
macOS App Group(原生 <Team>. 格式)BYL4KQHMTN.com.pangolin.pangolin44WULXM6SV.com.yanmeiai.pangolin
macOS NEMachServiceNameBYL4KQHMTN.com.pangolin.pangolin.PacketTunnel44WULXM6SV.com.yanmeiai.pangolin.PacketTunnel

iOS App Group 不含 Team ID(group. 前缀,跨账号可移植);macOS 用原生 <TeamID>.<name> 格式,Team 一换前缀必变——这也是必须改代码而非纯换证书的根本原因。旧 macOS 安装的登录态存在旧 App Group 容器里,切换后清空、用户需重登(重签本就如此,可接受)。

1 · Apple 后台建资产 你做

全部在 developer.apple.com/account(美国账号)完成。按顺序:

1.1 记下 Team ID

Membership details → Team ID → 发我。

1.2 注册 2 个 App ID(Identifiers)

Certificates, IDs & Profiles → Identifiers → + → App IDs → App。因 iOS/macOS 现共用同一 Bundle ID 字符串,只需 2 个唯一 App ID(各覆盖两端):

DescriptionBundle ID(Explicit)勾选的 Capabilities
Pangolin(主 app)com.yanmeiai.pangolinApp Groups、Network Extensions、System Extension
Pangolin PacketTunnel(扩展)com.yanmeiai.pangolin.PacketTunnelApp Groups、Network Extensions

主 app 三个能力缺一不可——尤其 System Extension:macOS 主 app 靠 com.apple.developer.system-extension.install 装载 sysext,少了它 Developer ID profile 不含该权限,构建报「Provisioning profile doesn't include the System Extension capability」。改了 App ID 能力后,相关 profile 会变 Invalid,须去 Profiles 重新 Generate + Download。扩展 App ID 只需 Network Extensions + App Groups。

1.3 创建 App Group

Identifiers → 右上下拉切到 App Groups → + → 填 group.com.yanmeiai.pangolin(Description 随意)。回到上面 4 个 App ID,编辑其 App Groups capability → Configure → 勾上这个 group。

macOS 端说明:macOS 用的是原生 44WULXM6SV.com.yanmeiai.pangolin(team 作用域容器,不带 group.),不需要在后台单独注册——只要 App ID 勾了 App Groups capability、且用该 Team 的证书签名即可生效。后台注册的 group.com.yanmeiai.pangolin 供 iOS 使用。

1.4 生成 2 张证书(Certificates)

用「钥匙串访问 → 证书助理 → 从证书颁发机构请求证书」生成一个 CertificateSigningRequest.certSigningRequest(存到磁盘),后台上传它换证书;下载后双击导入 login 钥匙串

导入后,两张证书的主体会是 Developer ID Application: Yanmei AI LLC (44WULXM6SV)Apple Distribution: Yanmei AI LLC (44WULXM6SV)请把这两串完整名字发我security find-identity -v -p codesigning 可打印),我要把它们精确写进 local_test.sh

1.5 导出 .p12(给 CI 用)

钥匙串里分别右键两张证书(连带私钥)→ 导出为 .p12,设一个导出密码(记进 Bitwarden,别写文件)。得到 developerID.p12ios_dist.p12——第 4 节转 base64 传 gitea。

1.6 创建 4 个描述文件(Profiles)

Profiles → +:

类型关联 App ID证书用途
Developer ID(macOS,含 Network Extensions)com.yanmeiai.pangolinDeveloper ID ApplicationmacOS 主 app
Developer ID(macOS)com.yanmeiai.pangolin.PacketTunnelDeveloper ID ApplicationmacOS sysext
App Store(iOS)com.yanmeiai.pangolinApple DistributioniOS 主 app
App Store(iOS)com.yanmeiai.pangolin.PacketTunnelApple DistributioniOS 扩展

各自下载 .provisionprofile(macOS)/ .mobileprovision(iOS)。CI 会用它们;本机 local_test.sh 的 macOS 流程也从固定目录读这两个 macOS profile(第 3 节代码替换里会核对路径)。

1.7 App Store Connect API Key

App Store Connect → Users and Access → Integrations → App Store Connect API → 生成一个 Team Key(Role: Developer 或 App Manager)→ 记下 Key IDIssuer ID,下载 AuthKey_XXXX.p8(只能下一次,妥存 Bitwarden)。用于公证(notarytool)与 CI 上传。

2 · 本机配置 我做(你把证书/凭据准备好后)

  1. 确认新证书已在 login 钥匙串:我跑 security find-identity -v -p codesigning 核对 Developer ID Application: Yanmei AI LLCApple Distribution: Yanmei AI LLC 在列。
  2. 建公证凭据 profile(local_test.sh 用的 pangolin-notary)——用 1.7 的 ASC Key:
    xcrun notarytool store-credentials pangolin-notary \
      --key AuthKey_XXXX.p8 --key-id <KEY_ID> --issuer <ISSUER_ID>
    这一步把凭据存进本机钥匙串,不入 git。(此命令需交互,可能由你在终端用 ! … 跑,我给你现成命令。)
  3. (可选)清理旧账号残留:删除 login 钥匙串里旧的 … Yanmei (beijing) Technology 两张证书,避免签名时误选旧身份。

3 · 仓库代码替换 已完成(单一真相源 + codegen)

已落地,且不再是「散落硬编码」。签名身份收敛成单一真相源,今后迁账号/改 Bundle ID 只改一个文件跑一次 codegen,不用再全仓找替换。

3.1 单一真相源 scripts/signing.env

Team ID / 实体名 / Bundle ID / App Group 全在这里(当前已是美国身份:Team 44WULXM6SVYanmei AI LLCcom.yanmeiai.pangolin)。三个签名脚本(local_test.sh / ci/compile-macos.sh / ci/compile-ios.sh)直接 source 它。

3.2 原生文件由 codegen 回填

Xcode/Swift 侧的标识符无法 source 变量、只能字面量,故由 node scripts/gen-signing.mjs 从 signing.env 结构化回填 16 个原生文件(两端 project.pbxprojDEVELOPMENT_TEAM/Bundle ID、各 *.entitlements 的 App Group/app-identifier、macOS Info.plistNEMachServiceName、各 Swift 的 appGroup/subsystem/loginHelper 等)。codegen 用「形状」匹配、不含任何旧字面值——自身无痕,未来再迁仍生效。

# 迁账号/改 Bundle ID 的唯一操作:
vim scripts/signing.env          # 改 3 个基值
node scripts/gen-signing.mjs      # 回填全部原生文件

3.3 drift 闸防漂移

ci/check-codegen-drift.shgen-signing.mjs --check:若改了 signing.env 没重生成、或手改了原生标识符,即失败。与 token/l10n 同一套 codegen 单源治理。

3.4 已验证

范围说明:未动 Android 包名 com.pangolin.pangolin_vpn(Play Store 身份、自签名、非 Apple 账号痕迹,改动会破坏 Play 更新,另议)与 Windows/桌面少量元数据串。codesign 层面 Apple 产物已零中国痕迹。

4 · gitea Secrets 换新 你做

gitea → 仓库 wangjia/pangolin → Settings → Actions → Secrets(注意历史经验:Apple 签名 secret 配在 用户级非仓库级,见记忆 pangolin-apple-signing-assets——按你当初配的层级更新)。名字不变,只换值

Secret 名新值来源
DEVELOPER_ID_P12base64 -i developerID.p12(1.5)
P12_PASSWORD导出 developerID.p12 时设的密码
MACOS_APP_PROVISION_PROFILE_BASE64macOS 主 app 的 .provisionprofile base64(1.6)
MACOS_SYSEXT_PROVISION_PROFILE_BASE64macOS sysext 的 .provisionprofile base64
IOS_DIST_P12base64 -i ios_dist.p12
IOS_DIST_PASSWORD导出 ios_dist.p12 时设的密码
IOS_APP_PROVISIONING_PROFILE_BASE64iOS 主 app 的 .mobileprovision base64
IOS_PACKETTUNNEL_PROVISIONING_PROFILE_BASE64iOS 扩展的 .mobileprovision base64
APPSTORE_API_KEY_ID1.7 的 Key ID
APPSTORE_API_ISSUER_ID1.7 的 Issuer ID
APPSTORE_API_KEY_P8_BASE64base64 -i AuthKey_XXXX.p8

macOS 转 base64:base64 -i 文件(默认单行,直接粘 gitea)。Android 的 RELEASE_KEYSTORE/KEY_PASSWORD 是自签名,与 Apple 账号无关,不用动

5 · 验证 + 首个构建 我做 / 你配合

  1. 本机 macOS:bash scripts/local_test.sh macos(build+签名+公证+staple+App Group 一致性闸)。
  2. iOS 装机:iPhone/iPad 连上后 bash scripts/local_test.sh ipad "<设备>"——脚本会核验签名主体,若还是旧证书会 die。
  3. CI 发版:打 tag client-vX.Y.Z 触发 gitea 流水线,用新 secret 构建。首次建议先跑一次看签名步骤绿。
  4. 无痕核验:对产物二进制跑:
    codesign -dvvv # 看 Authority / TeamIdentifier Runner.app 2>&1 | grep -E "Authority|TeamIdentifier"
    # 期望:Yanmei AI LLC + 44WULXM6SV,无 BYL4KQHMTN / beijing
    再全仓 grep -rn "BYL4KQHMTN\|beijing" client scripts .gitea 应为空。

✅ 无痕验收清单

6 · 顺序与责任一览

#步骤依赖
1Apple 后台建 Team/App ID/Group/证书/Profile/ASC Key(§1)
2把 Team ID + 两张证书全名 + ASC Key 信息发我1
3本机钥匙串核对 + 建 pangolin-notary 公证凭据(§2)2
4仓库代码替换 → signing.env 单源 + codegen(§3)已完成
5gitea 12 个 secret 换新值(§4)1
6本机 macOS/iOS 构建验证(§5.1–5.2)3,4
7打 tag 触发 CI 发版 + 无痕核验(§5.3–5.4)+4,5

相关:macOS sysext realize 排障 · iOS/iPad 支持设计 · 签名资产位置见项目记忆 pangolin-apple-signing-assets。本文随迁移推进更新。