0085: Paid license through Polar
On this page
Status: accepted. Supersedes 0014.
Context
Short Leash was freeware (ADR 0014). It now needs to earn its keep: a yearly license, usable on up to 2 computers, with a 14-day trial, promo codes and free licenses, and nothing for Short Leash to run or keep up on a server. Selling software worldwide as one person also means charging EU and UK VAT from the first sale and US sales tax, which only a merchant of record takes off the seller's hands. A local development tool is used offline, on planes and behind VPNs, so a license check must never break someone's work.
Decision
- Polar sells Short Leash Pro and issues its keys. Polar is the merchant of record. A license key benefit, with an activation limit of 2, comes with each subscription: Short Leash Pro yearly ($79 list; $49 for launch buyers through a forever discount, kept while they renew) and Short Leash Pro for Teams (per seat, the same benefit, so each person gets their own key). Promo codes and free licenses are Polar discounts, a free license being a single-use 100% code.
- No server of Short Leash's own. leashd calls Polar's public license endpoints (
/v1/customer-portal/license-keys/activate,validate,deactivate), which take the key as the credential and no secret. It refuses a key whose benefit isn't Short Leash Pro's. The organization and benefit IDs live inconfig::defaults; they aren't secrets. - The license lives in
secrets/license.json(0600) (invariant 9): the key, this computer's activation, Polar's last answer, the latest time seen and when the trial started. Only leashd reads it; the app, CLI and MCP see a view without the key. - Entitlement is decided locally (
license::entitlement), from the file and the clock, so every start path can ask without the network:- A license on this computer that Polar granted gives Pro until 30 days after the last check that reached Polar, or its end date if Polar gives one and it's sooner.
- The activation stores a SHA-256 hash of the computer's ID (IOPlatformUUID, MachineGuid), sent to Polar as the activation's meta, never the ID itself. A file copied to another computer grants nothing.
- Without a license, the trial gives Pro for 14 days from the first run of a version with Short Leash Pro, once per computer, with no card or account. Polar's own trials need a card, so they aren't used.
- A clock more than 2 hours behind the latest time seen gives neither until it catches up; a check that reaches Polar resets it.
- A check that doesn't reach Polar never takes Pro away. Only Polar saying the key ended, or 30 days without reaching it, does.
- leashd checks a minute after start, then about daily with up to an hour's spread, and an hour after a failure.
- The free core is sites, runtimes, DNS and HTTPS, site processes, logs,
leash.toml, import, dumps, joining a Pair call, and backing up any database. - Short Leash Pro is services, sharing and webhooks, the Monitor, Calls, scratchpads, branch workspaces, Changes' Git actions and assist, local AI, hosting Pair calls and scheduled backups. Each one's start path calls
Engine::require_profirst, which answers HTTP 402 with what needs Pro and what to do. A test lists every such path. - When Pro ends, things stop starting, not running. A service or share already running keeps going until it's stopped or Short Leash restarts. Nothing is deleted. Back up now and
leash backupstart a locked database just long enough to export it, the one way around the check. - Polar's sandbox is reachable only from debug builds or builds made with
LEASH_LICENSE_SANDBOX_BUILD=1, withLEASH_LICENSE_SANDBOX=1at run time. Released builds ignore it, so nobody can turn test purchases into free licenses.
Alternatives we didn't take:
- A payment platform with license keys that is being folded into another company's product, which has no license keys.
- A payment platform that dropped license keys.
- One whose license API needs the seller's secret key, and so a server.
- Dedicated license servers, which need a relay between payments and licenses and a monthly fee.
- Signing our own offline licenses, which needs a server to sign them and to count activations.
Consequences
- Short Leash is no longer freeware. The website, Terms and Privacy policy say what's free and what's paid, and the Privacy policy names the license check and what it sends.
- leashd now goes online to Polar once a day when a license is entered. The trial sends nothing.
- Polar's answers aren't signed. Editing
license.json, or faking Polar's server with a root certificate the user trusts, would get around the check. We accept that at this price; signed offline licenses can come later if it matters. A local trial can be reset by wiping Short Leash's folder or using a new virtual machine. - A lost
license.jsonmeans activating again, which takes one of the 2 slots until the old one is freed in Polar's portal. - Moving to another merchant of record later means moving subscriptions and keys, and Short Leash's license client with them.