Licensing & downloads
Selling a desktop program means three separate truths: the INSTALLER (the same file for everyone, obtainable by any signed-in customer, even before paying), the LICENCE (a signed statement of the purchased tier the program verifies offline), and the PURCHASE (an ordinary order of a product variation). This module ties them together.
Releases
Upload the installer (exe/zip) in Files, then create a release on Backoffice → Licensing: label, version, the file, publish. Every signed-in customer sees published releases in their account area with the size and SHA-256 so a careful one can verify the download. WHY the installer is free to download: the program is the shop window — the licence, not scarcity of the file, is what is sold.
The website's download page states the newest published release's
facts to everyone — version, release date, platform (read from the
file name: .exe is Windows, .dmg macOS, .deb/.AppImage Linux),
size and SHA-256 — rendered on the server, so a search engine and a
visitor on a slow line see a real page. Only the file itself waits for
the account. The same facts are on /api/web/v1/releases/latest.
Tiers and licences
The product's price levels are ordinary VARIATIONS; ticking Licensed product on the variation type makes a paid order of one issue a licence key per purchased unit. The variation's SKU is the tier the key carries — the desktop program maps SKUs to feature sets, so a new tier is a new variation, no code. SKUs are unique per firm: a SKU names exactly one variation.
The key is a signed payload (tier, customer, issue date) verified OFFLINE with the firm's public key — shown on the Licensing screen to embed in the program. No call home: WHY — a licence check that needs the seller's server turns every seller outage into every customer's outage.
Keys are mailed automatically (the brandable Licensing: licence delivery template), listed in the customer's account area, and can be revoked — or issued manually for support cases — on the Licensing screen. Every issue and revocation lands in the activity trail.
What triggers issuing
The once-only order paid event — however the money arrived: a card gateway, Paddle, or a matched wire transfer. Replayed payment notifications issue nothing twice.
The public download and the editor's update check
The newest published release is public: the download page offers it to anyone, no account needed, and every download is counted the same way as a customer's (an anonymous row, a tally on the release). Four doors serve a build:
/api/web/v1/releases/latest/downloadis the page's button./downloadwithout a slash is the address a desktop program's update check fetches. A browser asking for a page is sent to the page; a client asking for bytes gets the installer, counted as an update./download/<file name>is the file itself at a fixed public address — every published build keeps its own (/download/ctrl32-setup-1.00.043.exe), not only the newest, so a manifest may name the file directly and an older manifest still resolves. No cookies, no sign-in; a browser gets the file too. Counted apart (direct). The licensing home shows the address under every published release. An unpublished build answers 404./update.txtis the release's update manifest — a file uploaded beside the installer (the program's signed statement of the newest version and where to fetch it), served byte for byte, never edited by the platform. A release picks up anupdate.txtfrom the installer's folder by itself; the release form has a picker for another file.
The download page also fires the analytics event file_download on the click, so the firm's Google property counts downloads as well (consent permitting).
One release, two files, two readers
A release may carry an archive beside its installer. Where it does,
the download page and the customer's account hand over the archive;
/download, /download/<file name> and the manifest keep serving the
installer. A release without an archive serves the installer
everywhere, exactly as before.
WHY the split: a browser refuses a freshly signed .exe until the
signing CERTIFICATE has earned its reputation with the operating
system's reputation service, and that reputation accrues to the
certificate, not to any one build. Packing the installer buys the
warning away at the download step, and the installer inside is
byte-identical and still signed. The PROGRAM must not get the archive:
its update check verifies a fingerprint and a signature over the
installer itself, so an archive would fail that check at every customer
at once — which reads to them as a broken program, not a broken page.
The archive is picked up from the installer's folder by itself, matched on the installer's own name, so a folder holding two builds never hands a release the wrong one; the release form has a picker for another file.
What the reader is told follows what the reader gets: the size and file name beside the button are the archive's. The SHA-256 stays the installer's — it identifies the file inside, which is the one worth publishing and the one the program checks. The page says in a line that the download is an archive and why.
The counter belongs to the release, not to whichever of its files went out, so the tally, the download log and the notice mail read the same whether a person took the archive or a program took the installer.
Download notices
A firm may hear about every download by e-mail: on the Licensing screen, Download notices takes one address. Each download then mails it through the firm's own mail door — which build, when, who (the account and the customer when signed in, "anonymous" otherwise), through which door (the customer account, the download page, the program's update check) and the tally so far. The wording is the brandable mail template Licensing: download notice; an empty address means no notices. A mail hiccup never undoes the download or its count.
Downloads
Every download of a release by a signed-in customer leaves a record: when, which build (label and version), which customer (the partner the account acts for) and which account. No addresses and no browser details — the log answers "who has it", not "where were they". A resumed download (a range request) is not counted twice.
- The Releases table on the Licensing screen shows a tally per release.
- Download log (
/backoffice/licensing/downloads/) is a shipped view: filter by version or customer, reshape or revert it like any default view; only the firm's own rows, ever. - The activity trail records release_downloaded on the account.
- Claudia:
licensing_downloads(per-release tallies, the newest rows, optionally since a date or for one release).
Installations
A download is not an installation. The same person downloads twice, an archive is unpacked on three machines, and a link checker fetches a build nobody runs. The only thing that can say how many installations exist is the program itself, and it does — on every start it asks for the update manifest with a query:
/update.txt?i=<32 hex>&v=<version>&l=<language>
i= is a random number the program draws for itself at its first
start and keeps in its own settings. It is not derived from the machine,
the person or the licence, and nothing about the work travels with it.
So:
- Counting distinct
i=counts installations. - Counting the requests counts starts, which is a different number.
The platform records one row per installation per day, with the last
version and language that installation reported that day. A day per row
is deliberate: a program that starts twenty times would otherwise bury
the number, and "how many installations on the 9th" would be a growing
distinct over every start ever made.
- Installations (
/backoffice/licensing/installations/) is a shipped view: filter by version or language, reshape or revert it like any default view; only the firm's own rows, ever. The installation id is a column you can add, not one shown by default — nobody reads it, and it identifies nothing but itself. - Claudia:
licensing_installations— how many installations, how many starts, today's count, and the spread across versions and languages.
What is NOT counted, and why that is correct
- A program older than the version that added the query sends no query. It is served the manifest and counted nowhere.
- A program whose update check is switched off sends nothing at all. That is the opt-out, and it is meant to work.
- An installation that never starts again stops appearing. The table says who is running the program, not who ever installed it.
An id that is not exactly what the program promises — truncated, invented, in the wrong alphabet — is dropped rather than repaired. A salvaged id would count as its own installation and inflate the one number this table exists to give, silently and forever.
Nothing about the count may ever refuse the manifest. A program that
cannot read /update.txt is a program that can never be updated
remotely again, so a failure to record is swallowed and logged and the
manifest is served regardless. For the same reason /update.txt answers
Cache-Control: no-cache and must reach the origin on every check: a
cache answering it would serve a correct manifest and lose the count
with nobody noticing.