Software Distribution
Gate your own software's downloads and update checks behind a license key, and give end-user apps a signed way to check "is there a newer version." This works the same way whether you're running License Server self-hosted or on the Hosted (SaaS) Tier — the endpoints themselves don't care which. See the Software Distribution API reference for the full endpoint list and request/response shapes, and Auto-Update Checking for a worked example.
What License Server does and doesn't do here
License Server never stores or proxies your software's actual bytes — only release metadata and a signed pointer. artifact_url must be a URL your own end users can already reach (your own CDN, object storage, or GitHub Releases). Registering a release doesn't verify the URL resolves or that checksum matches the real file — that's on you to get right at publish time; the signature only proves what you told License Server at registration hasn't been tampered with since.
Register a release for a product_id you own (channel/platform-scoped — e.g. stable/darwin-arm64), then have your app call POST /check-update with one of your customers' license keys to find out whether it's behind and get a signed manifest pointing at the newer version. "Latest" is the highest semver version among your published releases, not registration order — publishing a hotfix to an older line after a newer one is already out never shadows the genuinely newer release. Unpublishing a bad release stops it appearing in live check-update results immediately - the row itself is kept, not deleted. There's no dedicated "republish" call, but getting the version back is straightforward: register it again with the exact same product ID, version, channel, and platform - the uniqueness check that normally rejects a duplicate registration only applies to published releases, so re-registering an unpublished one always succeeds.
channel and platform are matched exactly and case-sensitively at check-update time - "Stable" and "stable" are different channels. A mismatch fails closed (no update offered), not an error, so a typo'd value here is silent - double-check these match what you registered if updates never seem to arrive for a customer.
Unpublishing doesn't revoke an already-cached manifest
check-update's signature proves a manifest wasn't tampered with — it doesn't prove the manifest is still current. A response your app cached earlier still verifies successfully even after you unpublish that release. If your app ever caches a check-update response instead of calling it fresh, check the response's issued_at field yourself and reject anything older than you're willing to trust — this is your responsibility as the integrator, not something License Server can enforce on your behalf.
Self-hosted: your signing key must persist
Update manifests are signed with the same RSA private key used for offline license verification (LICENSE_RSA_PRIVATE_KEY — see Generating Secure Keys), and verified by end-user apps against the matching public key served from GET /v1/public-key. If that key is ever rotated or regenerated — including via AUTO_GENERATE_SECRETS=true on a fresh deployment that didn't carry the old value forward — every manifest your app already shipped stops verifying for clients still holding the old public key. Back up LICENSE_RSA_PRIVATE_KEY (or your .secrets.json, if using auto-generation) the same way you'd back up any other production secret, and carry it forward on every redeploy.
On the Hosted (SaaS) Tier
check-update keeps working after a billing lapse
POST /register-release checks your subscription standing and refuses new releases once payment fails, the same as POST /issue-license. But POST /check-update doesn't - your existing end users keep getting served their already-registered releases and signed manifests regardless of your account's billing or revocation status, the same as every other public license-validation route. This matches this API's existing posture everywhere else (no public route consults account status), it's just worth knowing explicitly for this one, since it means "publishing" and "distributing what's already published" are not gated the same way.
