diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cbbf2b1..eca1faa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 "" @@ -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 diff --git a/docs/desktop-install.md b/docs/desktop-install.md index c073e66..a645b05 100644 --- a/docs/desktop-install.md +++ b/docs/desktop-install.md @@ -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--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\\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 @@ -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--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. diff --git a/packaging/macos_app.sh b/packaging/macos_app.sh index 5bcd527..c7d6368 100755 --- a/packaging/macos_app.sh +++ b/packaging/macos_app.sh @@ -14,11 +14,12 @@ # `keel.app/Contents/Resources/keel/keel ` 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 @@ -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 @@ -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--py3-none-any.whl BEFORE YOU DO, PLEASE CHECK WHAT YOU DOWNLOADED diff --git a/tests/test_desktop_packaging.py b/tests/test_desktop_packaging.py index 218fcc8..d5edebd 100644 --- a/tests/test_desktop_packaging.py +++ b/tests/test_desktop_packaging.py @@ -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". @@ -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