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: 22 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,15 @@ jobs:
echo "## Desktop app (macOS / Windows)"
echo
echo "⚠️ **These builds are not code-signed, so your computer will warn you.**"
echo "Apple's certificate costs \$99/yr and keel cannot currently afford it. There"
echo "is no cheaper tier and no free option, and a certificate we made ourselves"
echo "would do nothing — macOS trusts only certificates Apple issued."
echo "Apple's certificate is \$99/yr and Azure Trusted Signing is ~\$120/yr, and"
echo "keel cannot currently afford either. There is no cheaper tier and no free"
echo "open-source option on either platform. (Windows is worse value than it looks:"
echo "since 2024 even an EV certificate no longer grants an instant SmartScreen"
echo "pass — reputation is earned from download volume over time.)"
echo
echo "**Prefer no warning at all?** Install from the wheels above instead — nothing"
echo "is downloaded as an application, so nothing objects. Needs a terminal and"
echo "Python 3.11+."
echo
echo "Full explanation, including how to check what you downloaded:"
echo "<https://github.com/${{ github.repository }}/blob/main/docs/desktop-install.md>"
Expand Down Expand Up @@ -261,10 +267,20 @@ jobs:
#
# THESE ARTIFACTS ARE NOT CODE-SIGNED, AND THAT IS A DECISION RATHER THAN AN OVERSIGHT.
#
# Apple notarisation requires a Developer ID certificate, which requires the $99/yr Developer
# Program. There is no free path: a free Apple account signs only for local development, and a
# NEITHER PLATFORM IS SIGNED, and both are decisions with the same cause.
#
# macOS: notarisation requires a Developer ID certificate, which requires the $99/yr Developer
# Program. There is no free path -- a free Apple account signs only for local development, and a
# self-signed certificate buys nothing because Gatekeeper trusts Apple-issued Developer IDs and
# nothing else. keel is open source on a small budget and has chosen not to pay it.
# nothing else.
#
# Windows: Azure Trusted Signing is ~$120/yr, MORE than Apple's, and it does not even buy the
# same thing. Since 2024 an EV certificate no longer grants an instant SmartScreen pass;
# reputation accrues from download volume over time, so a new certificate on a young project
# leaves the warning in place for a while regardless. Paying more to still be warned about is
# the worst of the three options.
#
# keel is open source on a small budget and has chosen not to pay either.
#
# What that costs the user is real and is documented rather than hidden: a `.dmg` downloaded
# from the internet carries a quarantine flag, so macOS refuses the first open until they go to
Expand Down
101 changes: 67 additions & 34 deletions docs/desktop-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,80 @@
If you downloaded keel for macOS or Windows and your computer refused to open it, nothing is
broken. This page explains exactly what happened, why, and what to do about it.

## Would you rather not deal with this at all?

There is a path with no warning on any platform, because nothing is downloaded as an application:
the install-from-source route in the README's **"Try it in five minutes"**. `pip` and `uv` fetch
the published wheels directly, and no operating system objects to that.

```
pip install --find-links . ./keel_trader-<version>-py3-none-any.whl
keel versions
```

It needs a terminal and Python 3.11 or later — which is exactly the friction the desktop app
exists to remove, so this is not the recommendation for everyone. But if you already have both, it
is the shorter road and the rest of this page does not apply to you.

## The short version

**keel's desktop builds are not code-signed.** Code signing is a paid certificate from Apple or
Microsoft that tells your operating system who built a program.
**keel's desktop builds are not code-signed, on either platform.** Code signing is a paid
certificate from Apple or Microsoft that tells your operating system who built a program.

**We cannot currently afford either one.**

| | cost | what it would buy |
|---|---|---|
| Apple Developer Program | **$99/yr** | removes the macOS warning |
| Azure Trusted Signing | **~$120/yr** | *does not* remove the Windows warning by itself |

There is no cheaper tier and no free option for open-source projects on either platform, and a
certificate we made ourselves would do nothing at all — macOS trusts only certificates Apple
issued.

**We cannot currently afford it.** Apple's certificate costs **$99 per year**, every year, and it
is the only way to remove the warning — there is no cheaper tier, no free option for open-source
projects, and a certificate we made ourselves would do nothing at all, because macOS only trusts
certificates Apple issued. keel is an open-source project with essentially no budget, and that
$99/yr is not something it can commit to today.
Windows is worth a sentence of its own: since 2024, even an expensive EV certificate no longer
buys an instant SmartScreen pass. Reputation is earned from download volume over time, so a new
certificate on a young project would leave the warning in place for a while anyway — for more
money than Apple's.

So your computer sees a program from a developer it cannot identify, and it does the right thing:
it stops and asks you.
keel is an open-source project with essentially no budget. So your computer sees a program from a
developer it cannot identify, and it does the right thing: it stops and asks you.

## What to do
## macOS — step by step

**macOS**
1. Open the downloaded `.dmg`. A window appears with **keel.app** and a **READ ME FIRST** file.
2. Drag **keel.app** into your **Applications** folder.
3. Eject the disk image (drag it to the Trash, or press ⌘E).
4. Open **Applications** and double-click **keel**. macOS refuses, saying it cannot be opened
because the developer cannot be verified. Click **Done**.
5. Open **System Settings** → **Privacy & Security**.
6. Scroll down to the **Security** section. There is a line saying *"keel was blocked to protect
your Mac"*, with an **Open Anyway** button beside it. Click it.
7. Authenticate with Touch ID or your password, then click **Open Anyway** once more in the
dialog that follows.

1. Double-click keel. macOS refuses, saying it cannot verify the developer.
2. Open **System Settings → Privacy & Security**.
3. Scroll down. There is a message about keel being blocked, with an **Open Anyway** button.
4. Click it, and confirm.
keel opens, and your browser opens with it. **You only do this once** — every later launch is a
normal double-click.

You only do this once. keel opens normally afterwards.
> On macOS Sequoia (15) and later, right-clicking the app and choosing *Open* no longer works as a
> shortcut for this. Apple removed that path deliberately. System Settings is the only way.

**Windows**
## Windows — step by step

1. Run the installer. SmartScreen says "Windows protected your PC".
2. Click **More info**, then **Run anyway**.
The download is a `.zip`, and Windows marks files that came from the internet.

1. **Before extracting**, right-click the downloaded `.zip` → **Properties**.
2. At the bottom of the **General** tab, if there is an **Unblock** checkbox, tick it and click
**OK**. This saves you a warning on every file inside.
3. Right-click the `.zip` → **Extract All…**, and choose a folder you own — for example
`C:\Users\<you>\keel`. Do not extract into `C:\Program Files`; keel does not need
administrator rights and should not be given them.
4. Open the extracted folder and double-click **keel.exe**.
5. If SmartScreen appears — *"Windows protected your PC"* — click **More info**, then
**Run anyway**.

> If you skipped step 2, you may see the SmartScreen prompt again on a later launch. Doing the
> Unblock on the `.zip` first is what avoids that.

## Please check what you downloaded first

Expand Down Expand Up @@ -62,21 +107,9 @@ built, and no amount of clicking "Open Anyway" makes that safe.
- It does **not** mean the app behaves differently once open. A signed and an unsigned build of
the same release are the same program.

## Would you rather avoid this entirely?

Install keel the way developers do, from the release wheels, and no warning appears at all:

```
pip install --find-links . ./keel_trader-<version>-py3-none-any.whl
keel versions
```

That path needs a terminal and a working Python. The desktop app exists precisely so that it does
not have to be the only option.

## If this changes

If keel ever has the budget, signing is a small change on our side — the release pipeline is
already built to accept it — and this page will be replaced by a sentence saying the builds are
signed. Until then, we would rather tell you the truth about what you are downloading than say
nothing and let your computer deliver the news.
already built to accept it, on both platforms — and this page will be replaced by a sentence
saying the builds are signed. Until then, we would rather tell you the truth about what you are
downloading than say nothing and let your computer deliver the news.
33 changes: 23 additions & 10 deletions packaging/macos_app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
# `keel.app/Contents/Resources/keel/keel <command>` and get the full CLI. One artifact, both
# audiences, no second build.
#
# THIS OUTPUT IS UNSIGNED, AND STAYS UNSIGNED. Apple notarisation requires a Developer ID
# certificate, which requires the $99/yr Developer Program; a free Apple account signs only for
# local development, and a self-signed certificate buys nothing because Gatekeeper trusts
# Apple-issued Developer IDs and nothing else. keel is open source on a small budget and has
# chosen not to pay it.
# THIS OUTPUT IS UNSIGNED, AND STAYS UNSIGNED -- as does the Windows artifact. Apple notarisation
# requires a Developer ID certificate ($99/yr); Azure Trusted Signing is ~$120/yr and, since 2024,
# does not even buy an instant SmartScreen pass. A free Apple account signs only for local
# development, and a self-signed certificate buys nothing because Gatekeeper trusts Apple-issued
# Developer IDs and nothing else. keel is open source on a small budget and has chosen not to pay
# either.
#
# So Gatekeeper WILL refuse the first open of a downloaded copy, and the release notes say so and
# say what to do about it (System Settings -> Privacy & Security -> Open Anyway). What replaces
Expand Down Expand Up @@ -97,7 +98,8 @@ IF MACOS REFUSES TO OPEN THIS APP, NOTHING IS BROKEN.
keel is not code-signed. Code signing is a paid certificate from Apple that tells macOS
who built a program. It costs \$99 per year, there is no cheaper tier and no free option
for open-source projects, and a certificate we made ourselves would do nothing at all --
macOS trusts only certificates Apple issued.
macOS trusts only certificates Apple issued. (Windows is not signed either, for the same
reason: that certificate costs even more.)

keel is an open-source project with essentially no budget, and that yearly cost is not
something it can commit to today. So macOS sees a program from a developer it cannot
Expand All @@ -106,11 +108,22 @@ identify, and does the right thing: it stops and asks you.
TO OPEN IT

1. Drag keel.app to your Applications folder.
2. Double-click it. macOS refuses.
3. Open System Settings -> Privacy & Security.
4. Scroll down to the message about keel and click "Open Anyway".
2. Eject this disk image.
3. Open Applications and double-click keel. macOS refuses; click Done.
4. Open System Settings -> Privacy & Security.
5. Scroll to the Security section. There is a line saying keel was blocked, with an
"Open Anyway" button beside it. Click it.
6. Authenticate, then click "Open Anyway" once more in the dialog that follows.

You only do this once.
You only do this once. Note that on macOS Sequoia (15) and later, right-clicking the app
and choosing Open no longer works as a shortcut -- Apple removed that path deliberately.

PREFER NO WARNING AT ALL?

Install from the release wheels instead. Nothing is downloaded as an application, so
nothing objects -- but it needs a terminal and Python 3.11 or later:

pip install --find-links . ./keel_trader-<version>-py3-none-any.whl

BEFORE YOU DO, PLEASE CHECK WHAT YOU DOWNLOADED

Expand Down
36 changes: 35 additions & 1 deletion tests/test_desktop_packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,40 @@ def test_the_script_runs_no_signing_command() -> None:
_INSTALL_DOC = _ROOT / "docs" / "desktop-install.md"


def test_the_install_note_covers_both_platforms_and_both_prices() -> None:
"""Windows was left as "a separate decision, still open" for one round. It is not: Azure
Trusted Signing is ~$120/yr, MORE than Apple's, and since 2024 an EV certificate no longer
grants an instant SmartScreen pass -- so it buys less for more. A page that named only the
Apple cost would leave a reader assuming Windows was simply forgotten."""
text = _INSTALL_DOC.read_text(encoding="utf-8")
assert "$99" in text
assert "120" in text
assert "SmartScreen" in text
assert "either" in text


def test_the_install_note_leads_with_the_path_that_has_no_warning() -> None:
"""Someone on this page is deciding whether to proceed. The first thing they should read is
that there is a route with no warning at all -- not four paragraphs about certificates."""
text = _INSTALL_DOC.read_text(encoding="utf-8")
body = text[text.index("\n## ") :]
first_heading = body.split("\n")[1]
assert "not deal with this at all" in first_heading, first_heading
assert body.index("Try it in five minutes") < body.index("The short version")


def test_the_install_note_gives_real_per_os_steps() -> None:
""" "Open Anyway" alone is not instructions. Someone who has never done this needs to be told
where the setting is, that Sequoia removed the right-click shortcut, and -- on Windows -- to
Unblock the zip BEFORE extracting, which is what stops the prompt returning."""
text = _INSTALL_DOC.read_text(encoding="utf-8")
assert "Privacy & Security" in text
assert "Sequoia" in text
assert "Unblock" in text
assert "Extract All" in text
assert "Program Files" in text # and why not to use it


def test_the_install_note_exists_and_states_the_actual_reason() -> None:
"""The reason is a budget, and saying so is better than "not signed at this time".

Expand All @@ -210,7 +244,7 @@ def test_the_install_note_exists_and_states_the_actual_reason() -> None:
they can weigh -- and it is the truth."""
text = _INSTALL_DOC.read_text(encoding="utf-8")
assert "$99" in text
assert "cannot currently afford" in text
assert "cannot currently afford" in text.replace("either one", "")
assert "no cheaper tier" in text or "no free option" in text
# A self-signed certificate is the obvious "why not just..." and must be answered.
assert "made ourselves" in text or "self-signed" in text
Expand Down