Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,46 @@ jobs:
- name: Build and verify ad-hoc macOS package
run: bun run --cwd apps/desktop package:macos:adhoc

codex_signature_macos_26:
name: Codex signature normalization (macOS 26)
runs-on: macos-26
timeout-minutes: 20
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
- name: Install exact dependencies
run: bun install --frozen-lockfile
- name: Reproduce the Codex signature contract
run: bun test ./apps/desktop/runtime/test/codex-signature-normalization.macos.test.ts

required:
name: Required
if: ${{ always() }}
needs: [verify]
needs: [verify, codex_signature_macos_26]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Require every blocking job
shell: bash
env:
CODEX_SIGNATURE_MACOS_26_RESULT: ${{ needs.codex_signature_macos_26.result }}
VERIFY_RESULT: ${{ needs.verify.result }}
run: |
set -euo pipefail
if [[ "$VERIFY_RESULT" != "success" ]]; then
echo "::error::verify finished with $VERIFY_RESULT"
exit 1
fi
if [[ "$CODEX_SIGNATURE_MACOS_26_RESULT" != "success" ]]; then
echo "::error::codex_signature_macos_26 finished with $CODEX_SIGNATURE_MACOS_26_RESULT"
exit 1
fi
6 changes: 3 additions & 3 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
"build:macos": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-build -- bun run build:macos:uncoordinated",
"build:macos:uncoordinated": "bun run check:generated:macos && bun run build:frontend && bun run build:runtime && bun run runtime/run-zig.ts build -Dplatform=macos -Doptimize=ReleaseFast && bun run check:direct-boundary",
"package:macos": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-package -- bun run package:macos:uncoordinated",
"package:macos:uncoordinated": "bun run check:generated:macos && bun run build:runtime && bun run runtime/run-zig.ts build package -Dplatform=macos -Doptimize=ReleaseFast && bun run runtime/package-macos.ts && bun run runtime/verify-macos-package.ts && bun run runtime/create-dmg.ts",
"package:macos:uncoordinated": "bun run check:generated:macos && bun run build:runtime && bun run runtime/run-zig.ts build package -Dplatform=macos -Doptimize=ReleaseFast && bun run runtime/package-macos.ts && bun run runtime/verify-macos-package.ts && bun run runtime/verify-codex-signature-tamper.ts && bun run runtime/create-dmg.ts",
"package:macos:adhoc": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-ad-hoc-package -- bun run package:macos:adhoc:uncoordinated",
"package:macos:adhoc:uncoordinated": "bun run check:generated:macos && bun run build:runtime && bun run runtime/run-zig.ts build package -Dplatform=macos -Doptimize=ReleaseFast && bun run runtime/package-macos.ts && bun run runtime/verify-macos-package.ts && bun run runtime/create-dmg.ts --core-only",
"package:macos:adhoc:uncoordinated": "bun run check:generated:macos && bun run build:runtime && bun run runtime/run-zig.ts build package -Dplatform=macos -Doptimize=ReleaseFast && bun run runtime/package-macos.ts && bun run runtime/verify-macos-package.ts && bun run runtime/verify-codex-signature-tamper.ts && bun run runtime/create-dmg.ts --core-only",
"verify:package:macos": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-package-verification -- bun run verify:package:macos:uncoordinated",
"verify:package:macos:uncoordinated": "bun run runtime/verify-macos-package.ts --release-directory zig-out/release/macos/arm64",
"verify:package:macos:adhoc": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-ad-hoc-package-verification -- bun run runtime/verify-macos-package.ts --core-release-directory zig-out/release/macos/arm64",
Expand Down Expand Up @@ -82,7 +82,7 @@
"baseline:reactive": "bun run runtime/reactive-baseline.ts",
"test:property": "bun test ./frontend/src/features/chat/model.property.test.ts ./frontend/src/runtime/*.property.test.ts ./frontend/direct/*.property.test.ts ./contracts/*.property.test.ts ./runtime/test/*.property.test.ts",
"test:macos": "bun run ../../scripts/check-resource-scheduler.ts --mode=exclusive --label=HRA-desktop-macOS-tests -- bun run test:macos:uncoordinated",
"test:macos:uncoordinated": "bun run runtime/run-zig.ts build test -Dplatform=macos && bun test ./runtime/test/image-normalizer.macos.test.ts"
"test:macos:uncoordinated": "bun run runtime/run-zig.ts build test -Dplatform=macos && bun test ./runtime/test/image-normalizer.macos.test.ts ./runtime/test/codex-signature-normalization.macos.test.ts"
},
"dependencies": {
"@hraness/agent-tasks-protocol": "workspace:*",
Expand Down
7 changes: 4 additions & 3 deletions apps/desktop/runtime/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
- `shipped-javascript-licenses.ts` – fail-closed, overinclusive production dependency inventory with nested license text and reviewed package exceptions.
- `generate-codex-schema.ts` – pinned Codex schema generation and checked-source verification.
- `frontend-package-integrity.ts` and `prepare-package-output.ts` – deterministic frontend asset validation used by the native build graph.
- `package-macos.ts`, `verify-macos-package.ts`, `create-dmg.ts`, and `corresponding-sources.ts` – credential-free runtime staging, inside-out ad-hoc signing, package verification, DMG assembly, and full-commit GPL/LGPL source archives.
- `test/image-normalizer.macos.test.ts` – macOS-only container, orientation, metadata, bounds, filesystem-race, deterministic-output, and code-signing regressions for the bundled image normalizer.
- `package-macos.ts`, `verify-macos-package.ts`, `verify-codex-signature-tamper.ts`, `create-dmg.ts`, and `corresponding-sources.ts` – credential-free runtime staging, inside-out ad-hoc signing, package verification and tamper regressions, DMG assembly, and full-commit GPL/LGPL source archives.
- `codex-signature-normalization.ts`, `codex-signature-normalization.entitlements.plist`, and `CODEX-SIGNATURE-NORMALIZATION.md` – exact, reversible policy, JIT entitlement allowlist, and evidence for the two pinned Codex payloads whose upstream Developer ID signatures fail strict validation on supported macOS.
- `test/image-normalizer.macos.test.ts` and `test/codex-signature-normalization.macos.test.ts` – macOS-only image-normalizer regressions and cross-host Codex signing-page determinism evidence.
- `control-plane-maintenance.ts` – app-stopped health checks plus encrypted backup, inspection, verification, and restore.
- `installation-handoff.ts`, `installation-path-authority.ts`, and `installation-process-authority.ts` – the fail-closed OPRTE-to-HRA application handoff, resumable committed cleanup, exact filesystem authority, ordered native-root shutdown, and unchanged-state rollback.
- `release-download-contract.ts`, `release-provenance.ts`, and `release-download.json` at the repository root – strict candidate/publication evidence and hermetic canonical Git provenance for downloadable releases.
Expand All @@ -33,7 +34,7 @@
- Page immutable snapshots that exceed Native's response limit. Replace oversized recoverable events with `snapshot.invalidated`; never discard terminal or human-in-the-loop events to make them fit.
- Keep the gateway as the semantic proxy. Native owns launch, lifecycle, trusted directory selection, and transport plumbing.
- Recovery-only local-data removal startup may resume only its strict recovery state machine. It must not open normal application writers.
- Keep Developer ID signing, notarization, provider writes, and publication code outside the public source workspace. Ad-hoc packaging must remain credential-free, preserve trusted upstream signatures, sign HRA code inside-out, and keep Sparkle disabled.
- Keep Developer ID signing, notarization, provider writes, and publication code outside the public source workspace. Ad-hoc packaging must remain credential-free, preserve strict-valid trusted upstream signatures except for the exact pinned Codex normalization policy, sign HRA code inside-out, and keep Sparkle disabled. Bind each Codex exception to exact package and source identity; the exact two-key JIT entitlement allowlist; explicit digest, timestamp, hardened-runtime version, and 16 KiB page-size inputs; structural non-signature equivalence; exact packaged identity; and an exact reversible source delta. Require the unchanged native payload verifier to accept each owner-private reconstruction and the signed code-mode host to complete a framed-protocol V8 JIT smoke.
- Stage `hra-image-normalizer` as entitlement-free HRA-owned code in every macOS package shape. Bind its SHA-256 and CodeDirectory hash into the runtime manifest, verify its exact identifier and empty entitlement set in both the app and mounted DMG, and never weaken nested verification to accommodate another runtime.
- Reconcile image-normalizer residue before opening attachment state: remove only exact `.hra-image-normalizer-<32 lowercase hex>.tmp` sibling directories through no-follow descriptor-relative traversal. Never promote temp residue. Treat a missing final generation as uncommitted, and accept an existing final generation only when its exact two-file inventory, identities, sizes, and SHA-256 values match the durable receipt.
- Inventory the installed frontend and gateway production dependency closure, including nested license files. Fail packaging on missing identity, version, license metadata, text, unexpected file types, UTF-8 BOMs, or hash drift; every defective upstream tarball needs an exact version-bound reviewed exception and provenance.
Expand Down
66 changes: 66 additions & 0 deletions apps/desktop/runtime/CODEX-SIGNATURE-NORMALIZATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Codex signature normalization

HRA packages the exact official `@openai/codex@0.144.6-darwin-arm64`
payload identified by npm integrity
`sha512-6zgvh70MzBNSeT17HEhSOrmmGGZGAKzSC7x6JAq+edkJkdPYA9P0I1tG7aJ49GlBkBxuC+MKBH1qm6+2Cghcww==`.
Its `codex` and `codex-code-mode-host` Developer ID signatures validate on
some supported macOS builders but fail strict validation on macOS 26.5.2.
The same failure is reproducible from fresh official npm downloads, follows
the bytes across copies, and is not caused by quarantine or path metadata.

The package build verifies the official source package and both source files
before changing the staged copy. It requires these exact source identities:

| Payload | Source SHA-256 | Size | Identifier | Team | CDHash |
| --- | --- | ---: | --- | --- | --- |
| `bin/codex` | `80a3933d11a9d13ef806aa24f7bb8afc9169cfe4e9b09d6da6a92922cbde9cff` | 260472144 | `codex` | `2DC432GLL2` | `14fe9fce7d47a8c12e42094e5cc90ff97b2cf627` |
| `bin/codex-code-mode-host` | `de329ec247b5ebbdf796b5888a7c2a9d731e221321584c5abdcc686c70b2db81` | 46374288 | `codex-code-mode-host` | `2DC432GLL2` | `d4a7d8e1af4b06413ef43fa933d983c3db019e8f` |

Only those two exact staged files are re-signed. The command fixes every input
that otherwise varies across supported builders: ad-hoc identity, the original
identifier, hardened-runtime flags and version `15.5.0`, SHA-256 digest, no
timestamp, 16 KiB signing pages, and DER entitlement generation. In particular,
the default signing page size differs between supported macOS builders.

The canonical
`codex-signature-normalization.entitlements.plist` has SHA-256
`a2f94dda68da5a6d994132cfc3ee49f07b83bccc5c1b9d5653e2e5fdb228ff41`
and contains exactly two true keys:

- `com.apple.security.cs.allow-jit`
- `com.apple.security.cs.allow-unsigned-executable-memory`

Those permissions reproduce the two permissions on the official payload and
are required by the V8 code-mode host. No Info.plist, sealed resources,
timestamp, or custom requirement is bound. The build and verifier require
these exact packaged identities:

| Payload | Packaged SHA-256 | Size | Identifier | Team | CDHash | Page size | Runtime |
| --- | --- | ---: | --- | --- | --- | ---: | --- |
| `bin/codex` | `055f18d2a33a719a2fab08e0a8326d950fa733340c596bb3df0d8dc94f85a96e` | 258960048 | `codex` | unset | `d5a8decaaecc44cd318c818f9ad794083570a812` | 16384 | `15.5.0` |
| `bin/codex-code-mode-host` | `7f622f21007acac2780b0e9e39822ba493425366fc1cf996c24adafc9c0a6e08` | 46107184 | `codex-code-mode-host` | unset | `62c42f5ea878b3d0cf931a993216a4034cd8e91f` | 16384 | `15.5.0` |

Both normalized files must pass strict code-signature verification before the
outer app is signed. The runtime manifest records the source and packaged
identity and signing contract for each file. Structural verification proves
that every byte outside the Mach-O signature envelope is unchanged. A
deterministic, bounded source delta is also packaged for each normalized file.
Verification reconstructs the exact official source bytes in an owner-private
temporary directory and runs the unchanged native payload verifier against the
reconstructed vendor tree. The deltas are pinned to SHA-256
`b0b05a7e03adf00fc1293b3e2679464cd8ec63024ca0ab5448915b5c33a1dadd`
and `5952f9bc32083e1f62e1cc13c55b5b50145f8f7e4df56dd89c2d8d5267d9c2c2`.

All other reviewed third-party signatures remain unchanged. Any upstream
version, package integrity, source hash, size, identifier, team, CDHash, or
source delta, entitlement, signing page, or runtime-version change requires a
new reviewed policy and evidence. macOS 15 and macOS 26 CI both reconstruct
the exact normalized identities, and the signed code-mode host must complete a
framed-protocol V8 JIT execution.

Every macOS package build runs destructive-in-fixture regressions against the
exact staged app. They alter each normalized payload and source delta, alter a
normalized manifest path, and re-sign one payload without hardened-runtime
flags. Each mutation must make the full app verifier fail. The runner restores
the exact original bytes, reruns the full verifier, and finishes with deep,
strict verification of the restored outer app.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
Loading