diff --git a/.github/workflows/agent-release.yml b/.github/workflows/agent-release.yml index d0deaf58..c0b0a357 100644 --- a/.github/workflows/agent-release.yml +++ b/.github/workflows/agent-release.yml @@ -189,10 +189,10 @@ jobs: } NODE - - name: Preflight production Agent signing identity + - name: Preflight production GoodBuddy signing identity shell: bash env: - GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }} + GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }} run: | set -euo pipefail node build/agent-bundle.cjs preflight @@ -202,8 +202,8 @@ jobs: env: AGENT_ARCH: ${{ matrix.arch }} AGENT_VERSION: ${{ needs.authorize.outputs.version }} - GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }} - GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY }} + GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }} + GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }} NODE_ARCHIVE: ${{ steps.inputs.outputs.node-archive }} OPENCODE_ARCHIVE: ${{ steps.inputs.outputs.opencode-archive }} run: | @@ -344,8 +344,8 @@ jobs: env: AGENT_VERSION: ${{ needs.authorize.outputs.version }} PREVIOUS_ARGUMENTS: ${{ steps.previous.outputs.arguments }} - GOODBUDDY_AGENT_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_AGENT_SIGNING_KEY_ID }} - GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY }} + GOODBUDDY_SIGNING_KEY_ID: ${{ vars.GOODBUDDY_SIGNING_KEY_ID }} + GOODBUDDY_SIGNING_PRIVATE_KEY: ${{ secrets.GOODBUDDY_SIGNING_PRIVATE_KEY }} run: | set -euo pipefail generated_at="$(git show -s --format=%cI "$GITHUB_SHA")" diff --git a/AGENTS.md b/AGENTS.md index be5b80e7..f1c87c85 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -227,12 +227,12 @@ Release note: 修复左上角项目设置与消息通道项目设置不一致的 the checked-in public key registry. - `.github/workflows/agent-release.yml` is the only production compound Agent publication path. It requires an annotated immutable Agent tag, the protected - `agent-signing` Environment, native x64/arm64 builds, one production Agent - signing identity for every layer of the compound package and catalog, a - non-Latest GitHub Agent Release, and synchronized immutable Beijing OSS - objects plus the final signed latest-catalog pointer. + `agent-signing` Environment, native x64/arm64 builds, one production + GoodBuddy signing identity for every layer of the compound package and + catalog, a non-Latest GitHub Agent Release, and synchronized immutable + Beijing OSS objects plus the final signed latest-catalog pointer. - `.github/workflows/packages.yml` and `release:package` build only desktop - products. Missing Agent packages, remote Runtime inputs, or Agent signing + products. Missing Agent packages, remote Runtime inputs, or GoodBuddy signing credentials must never block an ordinary desktop package or release. - Keep electron-builder invocations on `--publish never`. Main-branch builds run validation and build the production bundle without running the native diff --git a/BUILD.md b/BUILD.md index 36ec3ca1..4f8edda8 100644 --- a/BUILD.md +++ b/BUILD.md @@ -197,15 +197,15 @@ Linux x64/arm64 复合包。每个 job 从 `agent-runtime-lock.json` 解析官 发布路径:只接受指向受保护 `main` 历史的 annotated `agent-v${agent-runtime-lock.agentVersion}` 标签,在 `agent-signing` Environment 中 原生构建两种架构,生成并签名累计目录,然后同步到非 Latest 的 GitHub Agent Release -与北京 OSS。该 Environment 只保护一组 GoodBuddy Agent 发布变量与 Secret: +与北京 OSS。该 Environment 只保护一组 GoodBuddy 通用发布变量与 Secret: -- `GOODBUDDY_AGENT_SIGNING_KEY_ID` / - `GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY` +- `GOODBUDDY_SIGNING_KEY_ID` / + `GOODBUDDY_SIGNING_PRIVATE_KEY` -该身份统一签署内部 Agent、固定 Runtime manifest、外层 `.gbagent` 描述符和累计目录, -各层通过既有签名域区分用途,不再要求内部 Runtime 单独配置生产身份。私钥只注入签名 -步骤;预检只读取 key ID 并确认公钥已在 `resources/agent-release-keys.json` -注册为 production 且未撤销。 +该 GoodBuddy 身份统一签署内部 Agent、固定 Runtime manifest、外层 `.gbagent` +描述符和累计目录,各层通过既有签名域区分用途,不再要求内部 Runtime 单独配置生产 +身份。私钥只注入签名步骤;预检只读取 key ID 并确认公钥已在 +`resources/agent-release-keys.json` 注册为 production 且未撤销。 累计目录使用 `agent-catalog.json` 与 `agent-catalog.sig`。每个条目绑定 Agent 版本、 最低桌面版本、Agent 协议、远端 OpenCode 版本/digest、架构、文件名、大小、SHA-256 diff --git a/build/agent-bundle.cjs b/build/agent-bundle.cjs index 888a1034..19c8419d 100644 --- a/build/agent-bundle.cjs +++ b/build/agent-bundle.cjs @@ -47,9 +47,9 @@ const manifestFileName = 'manifest.json' const signatureFileName = 'manifest.sig' const koffiPackageName = 'koffi' const signingPrivateKeyEnvironment = - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' const signingKeyIdEnvironment = - 'GOODBUDDY_AGENT_SIGNING_KEY_ID' + 'GOODBUDDY_SIGNING_KEY_ID' const semanticVersionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+((?:[0-9a-zA-Z-]+)(?:\.[0-9a-zA-Z-]+)*))?$/u @@ -372,7 +372,7 @@ function productionSigningKey(environment = process.env) { const privateKey = environment[signingPrivateKeyEnvironment] if (!keyId || !privateKey) { throw new Error( - `${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required for production Agent signing` + `${signingKeyIdEnvironment} and ${signingPrivateKeyEnvironment} are required for production GoodBuddy signing` ) } return { keyId, privateKey } @@ -385,11 +385,11 @@ function preflightProductionSigningKey(options = {}) { const registry = options.registry ?? readTrustedKeyRegistry(projectRoot) return preflightRegisteredProductionKey({ - component: 'Agent', + component: 'GoodBuddy', keyId, registry, missingKeyIdMessage: - `${signingKeyIdEnvironment} is not configured; provision the production Agent signing key ID before building a release` + `${signingKeyIdEnvironment} is not configured; provision the production GoodBuddy signing key ID before building a release` }) } @@ -427,7 +427,7 @@ function trustedKeyForManifest( (candidate) => candidate.keyId === key.keyId ) if (revocation) { - throw new Error(`Agent signing key is revoked: ${key.keyId}`) + throw new Error(`GoodBuddy signing key is revoked: ${key.keyId}`) } return key } @@ -814,7 +814,9 @@ function buildAgentBundle(options) { publicKeySpkiBase64(signingIdentity.privateKey) !== signingRecord.publicKeySpkiBase64 ) { - throw new Error('Agent signing private key does not match the trusted key') + throw new Error( + 'GoodBuddy signing private key does not match the trusted key' + ) } if ( !options.testSigningIdentity && @@ -1105,7 +1107,7 @@ function parseArguments(argv) { } if (command === 'preflight') { if (Object.keys(options).length > 0) { - throw new Error('Agent signing preflight does not accept arguments') + throw new Error('GoodBuddy signing preflight does not accept arguments') } return { command } } @@ -1118,7 +1120,7 @@ function main(argv = process.argv.slice(2)) { if (options.command === 'preflight') { const signingRecord = preflightProductionSigningKey() console.log( - `Production Agent signing registry preflight passed: ${signingRecord.keyId}` + `Production GoodBuddy signing registry preflight passed: ${signingRecord.keyId}` ) return } diff --git a/build/remote-runtime-bundle.cjs b/build/remote-runtime-bundle.cjs index 0737ddb6..94073d96 100644 --- a/build/remote-runtime-bundle.cjs +++ b/build/remote-runtime-bundle.cjs @@ -44,9 +44,9 @@ const supportedArchitectures = Object.freeze(['x64', 'arm64']) const manifestFileName = 'manifest.json' const signatureFileName = 'manifest.sig' const signingPrivateKeyEnvironment = - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' const signingKeyIdEnvironment = - 'GOODBUDDY_AGENT_SIGNING_KEY_ID' + 'GOODBUDDY_SIGNING_KEY_ID' const signatureDomain = Buffer.from( 'GoodBuddy Remote Runtime Bundle Manifest Signature v1\0', 'utf8' @@ -318,11 +318,11 @@ function preflightProductionSigningKey(options = {}) { const registry = options.registry ?? readTrustedKeyRegistry(projectRoot) return preflightRegisteredProductionKey({ - component: 'Agent', + component: 'GoodBuddy', keyId, registry, missingKeyIdMessage: - `${signingKeyIdEnvironment} is not configured; provision the production Agent signing key ID before building a release` + `${signingKeyIdEnvironment} is not configured; provision the production GoodBuddy signing key ID before building a release` }) } @@ -1051,7 +1051,7 @@ function main(argv = process.argv.slice(2)) { if (options.command === 'preflight') { const record = preflightProductionSigningKey() console.log( - `Production Agent signing preflight passed for bundled Runtime: ${record.keyId}` + `Production GoodBuddy signing preflight passed for bundled Runtime: ${record.keyId}` ) return } diff --git a/docs/architecture/remote-host-and-goodbuddy-agent-design.md b/docs/architecture/remote-host-and-goodbuddy-agent-design.md index e99a9f8c..51cbe238 100644 --- a/docs/architecture/remote-host-and-goodbuddy-agent-design.md +++ b/docs/architecture/remote-host-and-goodbuddy-agent-design.md @@ -216,7 +216,7 @@ Renderer 暴露固定方法名、数字 RPC code 和有界 service code,不转 `.remote-runtime-resources` 或任何可安装 Linux 远端 payload。 - `build/agent-package.cjs` 在对应原生 Linux 架构组装确定性 `.gbagent`,外层签名覆盖 描述符和每个内部文件身份;内部 Agent 与 Runtime 仍分别使用既有签名 manifest 和 lock - 校验,但所有 production 层统一使用一组 GoodBuddy Agent 发布身份并通过签名域区分用途, + 校验,但所有 production 层统一使用一组 GoodBuddy 通用发布身份并通过签名域区分用途, 不要求内部 Runtime 单独配置密钥。`build/agent-catalog.cjs` 为双架构包生成签名累计目录, 拒绝同一版本/架构改变字节。 - `.github/workflows/agents.yml` 只使用进程内临时测试 key 做分支/PR 原生验证,不发布。 diff --git a/resources/agent-release-keys.json b/resources/agent-release-keys.json index 60fdfae5..f0255474 100644 --- a/resources/agent-release-keys.json +++ b/resources/agent-release-keys.json @@ -2,23 +2,8 @@ "formatVersion": 1, "keys": [ { - "keyId": "e2e-20260823-020914-ce95d6f6", - "publicKeySpkiBase64": "MCowBQYDK2VwAyEA8w12MNcxOrU18MArvcuJmVkGKzJsa/r93q14HGX7DUU=", - "environment": "production" - }, - { - "keyId": "goodbuddy-e2e-agent-8b0b39d2e63d7cf163c23bb7", - "publicKeySpkiBase64": "MCowBQYDK2VwAyEAx5lS8/stctKe/ri3kSNLR3nmkTV1eTpS0c1TYtJuHoM=", - "environment": "production" - }, - { - "keyId": "goodbuddy-e2e-v8-de8355f998071143411bc075", - "publicKeySpkiBase64": "MCowBQYDK2VwAyEAY7rOjqPT3FAw2+F5roTqyssz4Zzzx0iJhk6tGhCVivc=", - "environment": "production" - }, - { - "keyId": "goodbuddy-e2e-v12-b5adef071b79b4fdec57ea98", - "publicKeySpkiBase64": "MCowBQYDK2VwAyEATKD9wXjCeC35SMDrczzpou5PusRkxqSgNb5Se/jYOFU=", + "keyId": "goodbuddy-signing-2026-08", + "publicKeySpkiBase64": "MCowBQYDK2VwAyEAfUkXqa3WYwb1QevpHW/XWNhGo59dETJYjrJONx6SKEQ=", "environment": "production" } ], diff --git a/tests/agent-bundle.test.ts b/tests/agent-bundle.test.ts index c7902aa3..5c80c947 100644 --- a/tests/agent-bundle.test.ts +++ b/tests/agent-bundle.test.ts @@ -512,7 +512,7 @@ describe('Agent production input handling', () => { it('requires CI-only production key variables and redacts them', () => { expect(() => agentBundle.productionSigningKey({}) - ).toThrow('required for production Agent signing') + ).toThrow('required for production GoodBuddy signing') const secret = [ '-----BEGIN PRIVATE KEY-----', @@ -521,8 +521,8 @@ describe('Agent production input handling', () => { ].join('\n') const error = `signing_key=${secret} key id prod-secret-id` const redacted = agentBundle.redactSecrets(error, { - GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY: secret, - GOODBUDDY_AGENT_SIGNING_KEY_ID: 'prod-secret-id' + GOODBUDDY_SIGNING_PRIVATE_KEY: secret, + GOODBUDDY_SIGNING_KEY_ID: 'prod-secret-id' }) expect(redacted).not.toContain('fixture-secret-material') expect(redacted).not.toContain('prod-secret-id') @@ -536,7 +536,7 @@ describe('Agent production input handling', () => { registry }) ).toThrow( - 'Production Agent public signing key ID "production-release-key" is absent from resources/agent-release-keys.json' + 'Production GoodBuddy public signing key ID "production-release-key" is absent from resources/agent-release-keys.json' ) }) diff --git a/tests/agent-release-workflow.test.ts b/tests/agent-release-workflow.test.ts index 4eaffa17..5d429d38 100644 --- a/tests/agent-release-workflow.test.ts +++ b/tests/agent-release-workflow.test.ts @@ -97,7 +97,7 @@ describe('independent Agent release workflow', () => { ) expect(buildScripts).toContain('--ignore-scripts') expect(build).toContain( - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' ) expect(workflow).not.toContain( 'GOODBUDDY_REMOTE_RUNTIME_SIGNING_' @@ -169,7 +169,7 @@ describe('independent Agent release workflow', () => { it('keeps private keys out of authorization and public publication jobs', () => { expect(serializedJob('authorize')).not.toContain('secrets.') expect(serializedJob('publish')).not.toContain( - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' ) }) }) diff --git a/tests/agents-workflow.test.ts b/tests/agents-workflow.test.ts index e815e57a..3e6dfe48 100644 --- a/tests/agents-workflow.test.ts +++ b/tests/agents-workflow.test.ts @@ -162,7 +162,7 @@ describe('GoodBuddy Agents workflow', () => { ) expect(workflow).not.toContain('secrets.') expect(workflow).not.toContain( - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' ) expect(ciBundleSource).toContain( "generateKeyPairSync('ed25519')" diff --git a/tests/packages-workflow.test.ts b/tests/packages-workflow.test.ts index 35ba2f73..8c504aec 100644 --- a/tests/packages-workflow.test.ts +++ b/tests/packages-workflow.test.ts @@ -76,7 +76,7 @@ describe('desktop packages workflow', () => { expect(workflow).not.toContain('agent-signing:') expect(workflow).not.toContain('remote-runtime-signing:') expect(workflow).not.toContain( - 'GOODBUDDY_AGENT_SIGNING_PRIVATE_KEY' + 'GOODBUDDY_SIGNING_PRIVATE_KEY' ) }) diff --git a/tests/remote-runtime-bundle.test.ts b/tests/remote-runtime-bundle.test.ts index 2ed62e5e..32a3b77a 100644 --- a/tests/remote-runtime-bundle.test.ts +++ b/tests/remote-runtime-bundle.test.ts @@ -315,7 +315,7 @@ describe('Remote Runtime bundle tooling', () => { runtimeBundle.preflightProductionSigningKey({ projectRoot: process.cwd(), environment: { - GOODBUDDY_AGENT_SIGNING_KEY_ID: + GOODBUDDY_SIGNING_KEY_ID: 'runtime-fixture' }, registry: productionRegistry @@ -326,7 +326,7 @@ describe('Remote Runtime bundle tooling', () => { }) }) - it('signs the bundled Runtime with the shared Agent identity', () => { + it('signs the bundled Runtime with the shared GoodBuddy identity', () => { const productionRegistry: AgentReleaseKeyRegistry = { ...registry, keys: registry.keys.map((key) => ({