Skip to content

D5 — Package and sign macOS/Windows artifacts in the release workflow #438

Description

@eaitbrahim

PRD §4/§5. Do not start this before D1–D4. Packaged today, it would ship a signed app that launches a terminal into /, cannot write its database, cannot run its console on Windows, and refuses every safety-critical action.

Approach

PyInstaller --onedir — not --onefile: faster TUI start (no self-extraction), simpler per-binary signing for notarisation, lower AV/SmartScreen false-positive rate.

  • macOS — signed + notarised + stapled .dmg. If D2 lands, the app starts the local server and opens a browser; without D2 it needs a launcher that open -a Terminals the binary, because a Finder-launched app has no controlling terminal at all (the packaging research rates this the single biggest technical risk, above signing and above Python 3.14).
  • Windows — console-subsystem exe wrapped by Inno Setup into one signed setup.exe, installing per-user to %LOCALAPPDATA%\Programs\keel (no admin prompt). Windows auto-allocates a console on double-click, so no wrapper is needed there.

Workflow integration

New job after uv build --all-packages (.github/workflows/release.yml:85-88), consuming the same four wheels PRODUCTION_WHEEL_PREFIXES already encodes (keel/commands/update.py:82-88) plus config.yaml. Matrix: macos-14 (arm64), macos-15-intel (x86_64), windows-latest — the release job runs ubuntu-latest only today.

Ship two macOS DMGs, not universal2lipo-merging bundled-CPython-plus-native-extension trees is fragile in practice. Note macos-15-intel is scheduled to sunset around Aug 2027.

Signing

Apple $99/yr. Developer ID Application cert (+ Installer cert for .pkg), codesign --options runtime, notarytool submit --wait, stapler staple. App Store Connect API key for CI.
Windows OV cert ~$70–500/yr, or Azure Trusted Signing at $9.99/mo.

EV certificates no longer bypass SmartScreen — Microsoft ended that in 2024; OV and EV are now equivalent for reputation, which accrues from download volume over time. EV remains required only for kernel-mode drivers. Azure Trusted Signing may be limited to US/Canada signing identities — verify before budgeting.

Run signing behind a protected GitHub Environment requiring manual approval, never on PR builds from forks. Import certs into an ephemeral keychain per job and delete it after; prefer OIDC federation over a stored Azure client secret.

Why signing is a precondition, not polish

An unsigned binary that then asks for exchange API keys is the shape of malware distribution — research turned up a live example while surveying this space. For a project whose proposition is auditability, shipping that silhouette without OS-level verification would be worse than shipping no installer.

Also

Reconcile .github/workflows/release.yml:93-96, which claims the wheel carries Requires-Python: >=3.14.4 while every pyproject.toml declares >=3.11 (pinned by tests/test_python_floor.py:46-59). Harmless today; misleading to anyone building a packaging job on top of it.

Verify first

PyInstaller's Python 3.14 support against the pinned release, with a smoke build including cryptography/cffi — genuinely native and production-required via coinbase-advanced-py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ciCI/workflows (Docs, CI & tooling)toolingDev/release tooling (Docs, CI & tooling)

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions