license_runtime.h
Sonorie licensing RUNTIME -- the platform half of license.h.
#include <sonore/license_runtime.h>- 2types
- 13other declarations
- Guides: Getting started, Parameters and state, Presets
On this page
Design notes
license.h is pure verify crypto: it takes a machine id as a PARAMETER and has no idea what a machine, a file or a user is. That separation is deliberate -- the crypto is provable in isolation and the same header runs in a unit test with no OS at all. This header is the other half, and every line of it is POLICY rather than mathematics: where the device id comes from, where the .lic lives, what an unlicensed build sounds like, and what the user sees when they have to activate.
THE CONTRACT, which the rest of the design follows from: after ONE network moment (the server must witness a device once to count seats) every later check is pure local computation for the token's whole year -- an embedded public key and a file. NOTHING here may ever gate audio or UI on a network call. That is why activation is a PASTE: the user takes their machine code to sonorie.com/activate on any device and brings a token back. It works on an air-gapped machine, it works on Linux where there is no HTTP client to speak of, and it needs no TLS stack inside the plugin.
Accepted losses, both industry-standard: an offline machine re-activates once a year, and moving the clock backwards defeats expiry.
This header costs nothing unless a build opts in. SONORE_LICENSE_PRODUCT and SONORE_LICENSE_PUBKEY are what the marketplace generator defines; a personal export defines neither and compiles byte-identically to before.
Types 2
The .lic payload, v2: token, then email, then the purchase key.
The email and key ride along so the plugin can RENEW silently before the token expires, without asking the user for anything again. An activation done through the web page has no key to store, and simply cannot auto-renew -- which is a smaller loss than it sounds, because that user already knows where the activation page is.
One plugin instance's view of the licence.
licensed() is read on the AUDIO thread every block, so the flag is atomic and nothing else here is touched from there. Everything that reads a file or verifies a signature happens on the main thread: at construction, when an editor opens, and when the user pastes a code.
Functions 12
- fn
inline std::string persistedDeviceId()A random id generated once and kept beside the licence, for machines whose platform id cannot be read.license::persistedDeviceId · license_runtime.h:81More
The last resort is deliberately NOT the hostname, which is what the first version of this used. A hostname is not an identity: renaming the computer changes it, and on a laptop DHCP can change it by itself -- both of which would silently invalidate a licence the user already activated. A random id written once is stable for as long as the user's profile exists, which is the same lifetime the
.licsitting next to it has anyway.It is not a secret and does not need to be unpredictable: the server treats a machine id as opaque and anyone can send it any string. It only has to be unique and to stay put.
- fn
inline std::string rawDeviceId()The raw, PLATFORM identifier for this machine. Never leaves the process: it is salted and hashed bylicense::rawDeviceId · license_runtime.h:126machineId()below, because a raw hardware id is exactly the kind of thing that should not travel to a server. - fn
inline std::string machineId(const std::string& productCode)This device's id for THIS product: 32 hex characters.license::machineId · license_runtime.h:225More
Salted with the product code so the same machine reads differently to two different plugins -- one seller's activation records cannot be correlated with another's. Hashed so the raw hardware id never leaves the machine; what reaches the server identifies a device to us and nothing to anyone else. The server treats it as opaque (any string up to 128 chars) and lowercases it for the signed message, which
verifyTokenmirrors. - fn
inline std::string licDir()The per-user directory holding activations, created if missing.license::licDir · license_runtime.h:246More
Sonore/-- not "Sonorie" -- on purpose: it is one of the frozen technical tokens, and renaming it would orphan every activation already on disk. Under %APPDATA% / ~/Library/Application Support / ~/.config, i.e. always a place a plugin can write without being installed as an administrator. - fn
inline std::string licPath(const std::string& productCode)Where THIS product's activation lives. Empty when there is no home to write to, which the caller must treat as "cannot save", never as "no licence" -- those are different answers to the user.license::licPath · license_runtime.h:274 - fn
inline bool readLic(const std::string& productCode, LicFile* out)license::readLic · license_runtime.h:297 - fn
inline bool writeLic(const std::string& productCode, const LicFile& in)license::writeLic · license_runtime.h:326 - fn
template <typename Sample> inline void applyDemo(Sample* const* channels, int numChannels, int frames, double sampleRate, double* phase, uint32_t* rng)What an unlicensed build sounds like: a short mute every ~12 s plus a faint hiss. Auditionable, deliberately not usable on a record.license::applyDemo · license_runtime.h:348More
Realtime-safe by construction -- no allocation, no locks, no branches on anything but arithmetic. The phase advances with the block so the mute lands at the same wall-clock interval whatever the buffer size, and the per-channel RNG offset keeps the hiss decorrelated (identical noise in both channels reads as a centred artefact rather than as noise).
- fn
inline std::string overlayScript(const std::string& machineId)JS that puts the activation overlay on the page (idempotent: re-running it is how "still not activated" is re-asserted after a failed attempt). The machine id is 32 hex characters by construction, so there is nothing here that needs escaping -- and nothing user-supplied is ever interpolated.license::overlayScript · license_runtime.h:533 - fn
inline std::string overlayResultScript(bool ok, const std::string& message)JS that answers one activation attempt.license::overlayResultScript · license_runtime.h:586okremoves the overlay for good; anything else leaves it up with a reason the user can act on. - fn
inline const char* activateMessage(ActivateResult r)What a user should read for each outcome. Written for someone who just bought a plugin and wants to use it, not for us.license::activateMessage · license_runtime.h:600 - fn
inline std::string handleActivate(Gate& gate, const std::string& pasted)One activation attempt, start to finish: the JS to send back to the page. Shared by every wrapper so the five formats cannot answer differently.license::handleActivate · license_runtime.h:623
Enums 1
- enum
enum class ActivateResultWhy an activation attempt failed, in the user's terms rather than ours.license::ActivateResult · license_runtime.h:374