Skip to content

Prototype stable macOS permission identity without Developer ID - #74

Draft
IgorArkhipov wants to merge 6 commits into
ergohaven:mainfrom
IgorArkhipov:igor/macos-stable-release-signing
Draft

Prototype stable macOS permission identity without Developer ID#74
IgorArkhipov wants to merge 6 commits into
ergohaven:mainfrom
IgorArkhipov:igor/macos-stable-release-signing

Conversation

@IgorArkhipov

@IgorArkhipov IgorArkhipov commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

EN

Summary

Entropy v0.1.5 and v0.2.0 use ad-hoc signatures.
Each changed binary gets a different cdhash-only designated requirement, so macOS can show Accessibility as enabled while AXIsProcessTrusted() rejects the updated app.

Release builds embed an explicit requirement anchored to that certificate plus com.ergohaven.entropy.
No Apple Developer account or notarization credentials are required.

Limits and security

  • This is an experimental compatibility path, not Apple-supported public distribution.
  • Apple explicitly advises against shipping self-signed apps.
  • Gatekeeper still rejects the artifact as unidentified; Privacy & Security > Open Anyway remains necessary.
  • TCC persistence is not proven until the manual two-build test below passes.
  • Release private key must remain secret and backed up. Loss or rotation forces permission re-grant; compromise lets attacker produce code matching Entropy permission identity.
  • Bundle-ID-only designated requirement is intentionally rejected because any binary could copy it and target Accessibility grants.

Validation

  • Two different Mach-O binaries produce different code hashes but identical certificate-root + bundle-ID designated requirements on macOS 26.5.2.
  • Full Entropy .app, ZIP, and DMG build succeeds with temporary self-signed identity.
  • Signed app passes strict codesign verification and fails spctl as expected.
  • Stable-signing unit/e2e tests, shellcheck, actionlint, and git diff --check pass.
  • Full Rust suite passes: 133 tests.
  • Hosted PR matrix passes deterministic stable-signing validation on Ubuntu, Windows, macOS ARM, and macOS Intel. Real two-binary signing proof ran locally on macOS 26.5.2.

Repository owner steps before Ready for review

Green PR checks do not prove that upstream signing credentials exist.
A repository owner/admin must complete steps below first:

  1. On a Mac, open Keychain Access > Certificate Assistant > Create a Certificate.

  2. Create one identity with exact settings:

    • Name: Entropy Open Source Release Signing
    • Identity Type: Self Signed Root
    • Certificate Type: Code Signing
    • Enable Let me override defaults and choose a long validity period.
  3. Export certificate and private key together as an encrypted .p12 file.

  4. Store .p12 and its password in maintainer-controlled offline backup. Never commit or upload private material as a release artifact.

  5. Copy base64-encoded P12 value:

    base64 -i entropy-release-signing.p12 | pbcopy
  6. In upstream repository Actions secrets settings, create these repository secrets with exact names:

    • MACOS_CERTIFICATE_P12_BASE64: complete base64 value copied above
    • MACOS_CERTIFICATE_PASSWORD: .p12 export password
  7. Confirm secrets are stored in upstream ergohaven/entropy. Secret values cannot be viewed again after saving.

  8. Use this same P12 for every release. Key loss or rotation changes app identity and forces users to grant permissions again.

  9. Produce two different signed builds with this identity and complete manual TCC test below on Apple Silicon and Intel.

  10. Click Ready for review only after both machines retain Accessibility/Input Monitoring without re-adding Entropy.

Manual release gate

Keep PR Draft. Before merge or release:

  1. Produce two Entropy app builds with different binaries and same identity.
  2. Grant Accessibility/Input Monitoring to first build and verify Universal Symbols.
  3. Replace it with second build at /Applications/Entropy.app.
  4. Verify Universal Symbols still work without removing or re-adding permissions.
  5. Repeat on Apple Silicon and Intel machines.

If TCC still requests permissions, do not ship this path. Fall back to ad-hoc artifacts plus explicit permission-reset UX.

RU

Кратко

https://t.me/c/1464748383/37027/131541

PR использует постоянный self-signed release certificate. Он создаёт одинаковый certificate-anchored designated requirement для разных бинарников и потенциально сохраняет TCC identity без Apple account.

Ограничения: Apple не рекомендует shipping self-signed apps, Gatekeeper продолжит отклонять artifact, приватный ключ нужно хранить как release secret, а реальное сохранение Accessibility/Input Monitoring ещё нужно проверить двумя последовательными builds на Apple Silicon и Intel.

Что должны сделать владельцы репозитория до Ready for review

Зеленые PR checks не подтверждают наличие signing credentials.

Оставшиеся необходимые шаги для создания сертификата подписи:

  1. На Mac через Keychain Access создать сертификат Entropy Open Source Release Signing: Self Signed Root, Code Signing, с большим сроком действия.
  2. Экспортировать сертификат и приватный ключ вместе в зашифрованный .p12.
  3. Сохранить .p12 и пароль в защищенную резервную копию. Не коммитить и не прикладывать эти данные к релизу.
  4. Выполнить base64 -i entropy-release-signing.p12 | pbcopy.
  5. В upstream Actions secrets settings создать repository secrets:
    • MACOS_CERTIFICATE_P12_BASE64: полный base64 из предыдущего шага
    • MACOS_CERTIFICATE_PASSWORD: пароль экспорта .p12
  6. Проверить, что secrets добавлены в ergohaven/entropy, а не только в fork.
  7. Использовать тот же P12 для всех релизов. Потеря или rotation ключа потребует повторной выдачи permissions.
  8. Собрать два разных signed builds и выполнить TCC test на Apple Silicon и Intel.
  9. Нажимать Ready for review только если второй build сохраняет Accessibility/Input Monitoring без повторного добавления Entropy.

До этого PR остается Draft. Bundle-ID-only requirement не используется: это небезопасно для Accessibility grants.

Related: #6
Related: #58

@IgorArkhipov
IgorArkhipov marked this pull request as draft July 13, 2026 01:40
@IgorArkhipov IgorArkhipov changed the title Preserve macOS permission identity across releases Prototype stable macOS permission identity without Developer ID Jul 13, 2026
@IgorArkhipov
IgorArkhipov force-pushed the igor/macos-stable-release-signing branch from 5718a9b to 0856393 Compare July 28, 2026 02:21
jidckii added a commit to jidckii/entropy that referenced this pull request Aug 4, 2026
Ветка в текущем виде не поехала бы ни в PR-гейте, ни в релизе: часть
проблем ломала сборку сразу, часть — только в CI, где их некому было бы
заметить до первого тега.

Блокеры сборки:

- macOS падал и в PR, и в релизе: build_macos_app.sh требует
  assets/entropy.icns, но файл лежал в .gitignore, а ни macos:app, ни
  workflow его не генерировали. На раннере он и не сгенерировался бы —
  там нечем растеризовать SVG. Теперь .icns закоммичен рядом с .ico, и
  clean его не трогает.
- Dockerfile не собирался с zig 0.16: с 0.15 у ziglang поменялась схема
  имён архивов (zig-<arch>-linux-, а не zig-linux-<arch>-), был 404.
- appimagetool 1.9.1 качает runtime с GitHub на каждой сборке и виснет
  без таймаута, если соединение оборвалось (проверено — 17 минут в
  CLOSE-WAIT). В CI это тихий простой job'а до его лимита, хотя
  Dockerfile обещал сборку без сети. Runtime теперь пиннут и передаётся
  через --runtime-file, у curl появились таймауты.
- task windows:* и macos:cross не работали при asdf: ZIG резолвился в
  shim, а cargo запускает build script из каталога крейта в реестре, где
  .tool-versions не находится — shim печатает подсказку вместо версии, и
  cargo-zigbuild падает на разборе semver. Резолвим реальный бинарник.

Публикация релиза:

- В GitHub Release уезжал мусор: dist/**/* тянул сгенерированный конфиг
  nfpm, а dist/macos/* — распакованный Entropy.app целиком. Пути теперь
  перечислены явно, плюс if-no-files-found: error.
- Версия артефактов бралась из Cargo.toml, поэтому предрелиз v0.3.2-rc.1
  выложил бы файлы с именами будущего стабильного v0.3.2, а deb/rpm — с
  неотличимой версией пакета. VERSION прокидывается из тега сквозь
  Taskfile, контейнер и скрипты; nfpm сам разворачивает суффикс в
  0.3.2~rc.1. MSI ProductVersion и CFBundleVersion принимают только
  числовые компоненты, поэтому для них суффикс отбрасывается отдельно.
- Всё, что можно проверить до сборки, ушло в validate: формат тега,
  наличие секции в CHANGELOG и сверка с Cargo.toml. Раньше отсутствие
  секции роняло релиз уже после десятков минут сборки. Заодно это
  страхует расширенный фильтр тегов: легаси-теги v1.13.x/v1.14.x, что
  живут в репозитории, отсекаются обеими проверками.
- Образ для docker:dist пересобирался на каждом релизе с нуля, включая
  компиляцию cargo-zigbuild и libdmg-hfsplus. Собираем его отдельным
  шагом с кэшем слоёв, а DOCKER_IMAGE_READY выключает повторную сборку.

Уклад upstream:

- build.yml вернулся к матрице upstream: снова два macOS-раннера по одной
  арке. Универсальный бандл нужен релизу, а в PR-гейте он лишь удваивал
  время; кроме того, job "Build (macos-15-intel)" мог быть прописан в
  required checks, и его пропажа подвесила бы все PR. Дифф к upstream
  сузился до concurrency и расширения артефакта.
- build_macos_app.sh снова понимает TARGET (одна арка) — так workflow не
  расходится с upstream и с открытым PR ergohaven#74.

Пакеты:

- В packaging/linux/59-vial.rules не хватало второго правила — для
  устройств по Bluetooth, где серийный номер Vial в hidraw не виден.
  Пользователи deb/rpm/arch не получили бы доступ к BLE-клавиатурам.
- В зависимости добавлен libwayland-client: eframe собран с фичей
  wayland и грузит его через dlopen.
- prepare теперь проверяет не имена пакетов, а сами инструменты, и
  сообщает, чего не хватает: набор пакетов покрывает дистрибутивы
  неравномерно (в alpine нет msitools и icnsutils), и раньше нехватка
  всплывала на середине сборки.
- zig и cargo-zigbuild выровнены между .tool-versions, Dockerfile и
  prepare_env.sh: линия cargo-zigbuild рассчитана на конкретный zig,
  поэтому они должны двигаться вместе.

Проверено сборкой с VERSION=0.3.2-rc.1: нативно linux:pkg, linux:appimage
и windows:all, затем полный docker:dist в контейнере с нуля. Метаданные
deb/rpm, ProductVersion в MSI и содержимое AppImage — как ожидалось,
права после контейнера восстановлены, закоммиченные иконки не перезаписаны.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant