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 universal2 — lipo-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.
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..dmg. If D2 lands, the app starts the local server and opens a browser; without D2 it needs a launcher thatopen -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).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 wheelsPRODUCTION_WHEEL_PREFIXESalready encodes (keel/commands/update.py:82-88) plusconfig.yaml. Matrix:macos-14(arm64),macos-15-intel(x86_64),windows-latest— the release job runsubuntu-latestonly today.Ship two macOS DMGs, not universal2 —
lipo-merging bundled-CPython-plus-native-extension trees is fragile in practice. Notemacos-15-intelis scheduled to sunset around Aug 2027.Signing
.pkg),codesign --options runtime,notarytool submit --wait,stapler staple. App Store Connect API key for CI.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 carriesRequires-Python: >=3.14.4while everypyproject.tomldeclares>=3.11(pinned bytests/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 viacoinbase-advanced-py.