Anwendungsfälle
Upgrades verkaufen & Versionen steuern
Legen Sie fest, welche Releases Ihres Produkts jede Lizenz abdeckt — lebenslange Updates, ein Support-Zeitfenster, eine Hauptversion, Upgrade-Schutz, Abonnements mit oder ohne unbefristeten Fallback — und setzen Sie es in Ihrer Anwendung oder auf Ihrer eigenen Download-Seite durch, ohne Reaktivierung, ohne erneute Schlüsseleingabe und mit voller Offline-Unterstützung.
Ziel
Verkaufen Sie Updates so, wie Ihr Geschäft tatsächlich funktioniert — „jedes 2.x-Release“, „jede Version, die im Jahr nach dem Kauf veröffentlicht wird“, „alles, was erscheint, solange das Abonnement aktiv ist“ — und lassen Sie Ihre Anwendung (und Ihre eigene Download-Seite) für jede Version eine Ja/Nein-Antwort erhalten. Das ist Release-Abdeckung: Keyright hält fest, was jede Lizenz abdeckt, signiert es, liefert es zusammen mit dem Lease auf die Maschine des Kunden und entscheidet über die Berechtigung — ohne jemals einen Kunden zu bitten, einen Schlüssel erneut einzugeben oder zu reaktivieren, und ohne eine laufende Installation außer Betrieb zu nehmen.
Keyright ist ein Abdeckungs-Orakel: Es entscheidet, ob eine Lizenz ein Release abdeckt. Es hostet niemals Downloads, Update-Feeds oder Release-Listen — Sie betreiben weiterhin Ihren eigenen Updater und Ihre eigene Download-Seite und fragen Keyright, ob ein bestimmter Kunde eine bestimmte Version haben darf.
Wann Sie es einsetzen
- Verwenden Sie dies, wenn Sie für Upgrades Geld verlangen, ein Wartungsfenster betreiben, pro Hauptversion verkaufen oder ein Desktop-Abonnement anbieten — überall dort, wo „die Software besitzen“ und „Anspruch auf diesen Build haben“ zwei verschiedene Fragen sind.
- Verwenden Sie dies, wenn Sie Ihre eigene Download-Seite oder Ihren Updater pro Kunde freigeben möchten, online oder netzwerkisoliert, aus einer einzigen signierten Quelle der Wahrheit.
- Verwenden Sie dies nicht, nur um eine abgelaufene Lizenz am Laufen zu hindern — das ist der eigene Ablauf der Lizenz (siehe Aktivieren, deaktivieren & Plätze verschieben). Die Abdeckung fügt darüber die Dimension Veröffentlichungsdatum / Version hinzu.
Release-Abdeckung ist Teil des obersten Plans des gehosteten Dienstes (Scale und Enterprise) sowie selbst gehosteter Lizenzen, die die Abdeckungsfunktion enthalten. Niedrigere Pläne sehen die Funktion im Dashboard gesperrt, mit einer Upgrade-Aufforderung. Das SDK ist über alle Pläne hinweg identisch: Eine Lizenz ohne Abdeckung wird schlicht als nicht konfiguriert gelesen und führt jede Version aus.
So funktioniert es
Jede Lizenz trägt einen Satz von Abdeckungsbedingungen. Eine Bedingung ist ein Versionsbereich (2.0.0 ≤ v < 3.0.0), ein Datumsfenster (alles, was zwischen zwei Kalenderdaten veröffentlicht wurde) oder beides — optional auf einen Release-Kanal beschränkt (stable standardmäßig; Betas brauchen eine explizite beta-Bedingung). Ein Release ist abgedeckt, wenn irgendeine Bedingung auf seine Version und sein Anspruchsdatum (normalerweise das Datum seiner Veröffentlichung) zutrifft.
Drei Eigenschaften machen die Durchsetzung sicher:
- Momentaufnahme, nicht live. Wenn Sie eine Lizenz ausstellen, wird die Abdeckungsrichtlinie der Edition in Bedingungen aufgelöst, die auf dieser Lizenz gespeichert werden. Eine spätere Änderung der Richtlinie ändert nie, was bestehende Kunden gekauft haben; neue Bedingungen wenden Sie mit einer expliziten Aktion mit Vorschau auf bestehende Lizenzen an (siehe Eine Richtlinie auf bestehende Kunden anwenden).
- Bedingungen wachsen nur. Die Bedingungen sind ein reines Anfüge-Protokoll (append-only). Verlängerungen und Upgrades fügen Bedingungen hinzu; der Support kann eine Bedingung entfernen, aber nichts, was ein Kunde heute ausführen kann, wird jemals stillschweigend durch eine Richtlinienänderung weggenommen.
- Signiert und offline. Die Abdeckung reist als separat signierter Abdeckungsblock, der an Lizenz und Produkt gebunden ist. Die Anwendung prüft ihn mit demselben öffentlichen Schlüssel, den sie bereits einbettet, speichert ihn zwischen und entscheidet über die Abdeckung lokal — online, offline oder netzwerkisoliert (air-gapped). Eine monoton steigende Sequenznummer verhindert, dass ein älterer Block erneut eingespielt wird, um erloschene Abdeckung wiederherzustellen, und ein fehlender oder manipulierter Block wird niemals als „abgedeckt“ behandelt.
Zwei weitere Konzepte, die Ihnen durchgehend begegnen werden:
- Anspruchsdatum vs. Veröffentlichungsdatum. Die Abdeckung wird anhand des Anspruchsdatums eines Releases beurteilt, das standardmäßig seinem Veröffentlichungsdatum entspricht, aber separat gesetzt werden kann. So kann ein Sicherheits-Hotfix, der nach dem Ende des Support-Zeitfensters eines Kunden erscheint, für ihn trotzdem abgedeckt sein: Datieren Sie den Hotfix wie sein Basis-Release.
- Das Release-Register ist optional. Die Durchsetzung funktioniert vollständig über die im Build selbst eingebettete Version und das eingebettete Anspruchsdatum. Wenn Sie Releases zusätzlich registrieren (aus CI oder dem Dashboard), kann Keyright Kunden die neueste Version, die sie nutzen können nennen, in einer Vorschau zeigen, für wie viele Kunden ein Release abgedeckt ist, und Hotfixes für Sie datieren.
Welches Abdeckungsmodell passt
Sechs klar benannte Voreinstellungen (Presets) decken die gängigen Geschäftsmodelle ab. Wählen Sie eine pro Edition; sie wird auf jeder Lizenz, die Sie von da an ausstellen, in Bedingungen aufgelöst.
| Voreinstellung | Was der Kunde erhält | Typischer Einsatz |
|---|---|---|
| Lifetime updates (Lebenslange Updates) | Jede Version, für immer. | Ein Einmalkauf, der kostenlose Upgrades verspricht. |
| Updates for a period (Updates für einen Zeitraum) | Jede Version, die innerhalb von N Monaten nach dem Kauf veröffentlicht wird; die Kopie funktioniert für immer weiter. | Klassische unbefristete Lizenzierung mit „einem Jahr Updates“. |
| One major version (Eine Hauptversion) | Jedes Release einer Hauptversionslinie (z. B. alle 2.x), für immer. | Upgrade-Preise pro Hauptversion. |
| One major + upgrade protection (Eine Hauptversion + Upgrade-Schutz) | Eine Hauptversion für immer, plus alles, was innerhalb von N Monaten veröffentlicht wird — einschließlich der nächsten Hauptversion, wenn sie rechtzeitig erscheint. | Preise pro Hauptversion mit Wartungsplan. |
| Subscription (Abonnement) | Alles, was veröffentlicht wird, solange das Abonnement aktiv ist; nichts nach dessen Ablauf. | Desktop-Lizenzierung nach SaaS-Art. |
| Subscription with fallback (Abonnement mit Fallback) | Wie Abonnement, aber nach dem Ablauf behält der Kunde die Versionen, die während der bezahlten Zeit veröffentlicht wurden. | Abonnements, die einen Kunden nie im Stich lassen dürfen. |
Alles andere — eine einzelne festgelegte Version, eine Minor-Linie, ein benutzerdefiniertes Zeitfenster — ist eine benutzerdefinierte Bedingung, die Sie einer Lizenz im Abschnitt Advanced (Erweitert) ihres Coverage-Tabs hinzufügen (oder über die Grants-API weiter unten).
Lebenslange Updates
Szenario. Sie verkaufen einen einmaligen Schlüssel und versprechen kostenlose Upgrades auf Lebenszeit (oder Sie beschränken Updates schlicht nicht und möchten, dass das Portal das auch so sagt). Nichts läuft jemals ab.
Bedingungen. Eine offene Bedingung: jede Version, jedes Datum. Was der Kunde liest (Portal, SDK, API): „Ihre Lizenz deckt jede Version ab.“ — ein nicht abgedeckter Zustand kann nie eintreten.
Konfigurieren. Editionsrichtlinie → Lifetime updates (keine Rückfragen), oder auf einer einzelnen Lizenz Change coverage… → Lifetime updates. API: {"preset":"lifetime"}.
Updates für einen Zeitraum
Szenario. Eine unbefristete Lizenz mit zwölf Monaten Updates. Der Kunde behält seine Kopie für immer und kann alles installieren, was innerhalb des Zeitfensters veröffentlicht wird — einschließlich einer neuen Hauptversion, wenn sie rechtzeitig erscheint. Eine nach dem Zeitfenster veröffentlichte Version ist „nicht abgedeckt — Zeitfenster abgelaufen“, also bietet die Anwendung Renew (Verlängern) an.
Bedingungen. Ein Datumsfenster vom Kaufdatum bis Kauf + N Monate, jede Version. Eine Verlängerung fügt ein weiteres Fenster an (lückenlos anschließend oder mit einer Lücke, die Sie wahlweise unabgedeckt lassen können). Der Kunde liest: „Ihre Lizenz deckt jede Version ab, die bis zum 9. Okt. 2027 veröffentlicht wird.“
Konfigurieren. Editionsrichtlinie → Updates for a period → Updates for (months after purchase) (Updates für … Monate nach dem Kauf). API: {"preset":"updatesForPeriod","periodMonths":12}.
Eine Hauptversion
Szenario. Preise pro Hauptversion: Eine 2.x-Lizenz führt jedes jemals erschienene 2.x-Release aus, auch Jahre später veröffentlichte, aber nicht 3.0. Ein 3.0-Build ist „nicht abgedeckt — außerhalb des Versionsbereichs“, also bietet die Anwendung Upgrade an. Ältere Linien (1.x) sind immer erlaubt — Downgrade-Rechte sind eingebaut.
Bedingungen. Ein Versionsbereich [major.0.0, major+1.0.0), jedes Datum. Der Kunde liest: „Ihre Lizenz deckt alle Acme Studio 2-Versionen ab.“
Konfigurieren. Editionsrichtlinie → One major version → Which major version? (Welche Hauptversion?). API: {"preset":"oneMajor","major":2}. Wenn Ihre Edition „die aktuelle Hauptversion“ verkauft, erhöhen Sie die Hauptversion der Richtlinie, sobald Sie die nächste ausliefern — bestehende Kunden behalten die Linie, die sie gekauft haben.
Eine Hauptversion + Upgrade-Schutz
Szenario. Preise pro Hauptversion plus Wartungsplan: Der Kunde besitzt 2.x für immer und erhält alles, was in den nächsten N Monaten veröffentlicht wird — erscheint 3.0 also innerhalb des Zeitfensters, bekommt er es, zusammen mit jedem 3.x-Patch, der vor Ablauf des Fensters erscheint. Ein 3.x-Release, das nach dem Fenster datiert ist, liegt außerhalb beider Bedingungen (→ Renew, da es das Zeitfenster ist, das abgelaufen ist). Dies ist das häufigste Modell für „kostenpflichtige Upgrades mit Sicherheitsnetz“.
Bedingungen. Zwei Bedingungen — die Hauptversionslinie und ein Datumsfenster vom Kauf bis Kauf + N Monate. Der Kunde liest: „Ihre Lizenz deckt alle Acme Studio 2-Versionen ab, plus jede Version, die bis zum 9. Okt. 2027 veröffentlicht wird.“
Konfigurieren. Editionsrichtlinie → One major + upgrade protection → Hauptversion + Monate. API: {"preset":"oneMajorPlusUpgradeProtection","major":2,"periodMonths":12}.
Abonnement
Szenario. Der Kunde darf jede Version nutzen, die veröffentlicht wird, solange er zahlt. Die Lizenz selbst trägt normalerweise ein Ablaufdatum, das zum Abrechnungszeitraum passt (damit die Anwendung stoppt, wenn das Abonnement endet); die Abdeckung fügt die Dimension Veröffentlichungsdatum hinzu, damit etwa ein Kunde, der auf einen günstigeren Plan wechselt oder dessen Laufzeit ohne neuen Schlüssel verlängert wird, weiterhin genau die Versionen erhält, auf die er Anspruch hat.
Bedingungen. Ein Datumsfenster von der Ausstellung bis Ausstellung + N Monate. Jede Verlängerung fügt das nächste Fenster an. Nach einem Ablauf sind Releases, die nach dem Fenster datiert sind, „Zeitfenster abgelaufen“ → Renew. Der Kunde liest: „Ihre Lizenz deckt jede Version ab, die bis zum 9. Okt. 2027 veröffentlicht wird.“
Konfigurieren. Editionsrichtlinie → Subscription → Term (months) (Laufzeit in Monaten). API: {"preset":"subscription","periodMonths":12}.
Abonnement mit Fallback
Szenario. Ein Abonnement, das einen Kunden nie im Stich lässt: Wenn er aufhört zu zahlen, behält er die Versionen, die während der bezahlten Zeit veröffentlicht wurden (ein „unbefristeter Fallback“), und nur Releases nach dem Ablauf erfordern eine Verlängerung.
Bedingungen. Bei der Ausstellung wird diese Voreinstellung genau wie Subscription aufgelöst — ein Fenster für die bezahlte Laufzeit. Der Fallback besteht schlicht darin, dass dieses Fenster beim Ablauf nicht widerrufen wird: Weil Bedingungen nur wachsen, hält ein Kunde, dessen Abonnement endet, weiterhin eine Bedingung, die alles abdeckt, was währenddessen veröffentlicht wurde. Wenn Sie einen Ablauf in Ihrem Abrechnungsprozess verarbeiten, widerrufen Sie die Abdeckungsbedingung nicht; wenn Sie zusätzlich die Lizenz selbst ablaufen lassen, folgt die Anwendung Ihrem normalen Ablaufverhalten. Um den Ablauf explizit festzuhalten, fügen Sie eine benutzerdefinierte Bedingung mit dem Grund Fallback hinzu, die am Ablaufdatum endet (siehe benutzerdefinierte Bedingungen).
Konfigurieren. Editionsrichtlinie → Subscription with fallback → Term (months). API: {"preset":"subscriptionWithFallback","periodMonths":12}.
Betas und Early Access
Eine Bedingung ohne Kanal deckt nur stabile Releases ab. Um einem Kunden Beta-Builds zu erlauben, geben Sie ihm eine Bedingung auf dem Kanal beta (schalten Sie Include beta builds (Beta-Builds einschließen) in der Richtlinie ein oder setzen Sie "channel":"beta" auf einer benutzerdefinierten Bedingung) und bauen Sie Ihre Vorschauversionen mit KeyrightCoverageChannel=beta. Keyright deckt Betas ab; es lässt Builds niemals ablaufen — wenn eine Vorschau sich selbst ablaufen lassen soll, backen Sie KeyrightCoverageBuildExpiry hinein und prüfen Sie CoverageBuildInfo.Current.BuildExpiryUtc in Ihrem eigenen Startcode.
Einrichtung im Dashboard
Alles Folgende befindet sich unter Releases & coverage (Releases & Abdeckung; in den Ansichten Mittel und Erweitert des Dashboards; es erscheint auch in der Basis-Ansicht, sobald Sie ein Release registrieren oder eine Richtlinie speichern — siehe Admin-Dashboard → Erfahrungsstufe).
1. Releases registrieren (optional, empfohlen)
Beim Registrieren eines Releases werden seine Version, sein Veröffentlichungsdatum, ein optionales separates Anspruchsdatum und sein Kanal festgehalten. Sonst nichts — keine Dateien oder URLs. Mit einem Register sehen Kunden die neueste Version, die sie nutzen können, das Dashboard zeigt in einer Vorschau, für wen jedes Release abgedeckt ist, und der Abdeckungsprüfer kann eine Version für Sie datieren.
Aus CI registrieren Sie jedes Release, sobald es ausgeliefert wird:
# KEYRIGHT_URL und KEYRIGHT_ADMIN_TOKEN können auch als --url / --admin-token übergeben werden
keyright release --register --product acme-studio --version 2.4.0 --release-date 2026-10-01
# ein Hotfix, der für alle abgedeckt bleiben muss, die 2.3.0 hatten: wie das Basis-Release datieren
keyright release --register --product acme-studio --version 2.3.1 --release-date 2026-09-30 --entitlement-date 2026-07-02
# ein Vorschau-Build auf dem Beta-Kanal
keyright release --register --product acme-studio --version 3.0.0 --release-date 2026-10-08 --channel beta
Oder über die API (idempotent pro Produkt + Version):
curl -X POST $BASE/admin/products/acme-studio/releases -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"version":"2.3.1","releaseDate":"2026-09-30","entitlementDate":"2026-07-02","channel":"stable"}'
# -> { "product":"acme-studio", "version":"2.3.1", "releaseDate":"2026-09-30", "entitlementDate":"2026-07-02", "channel":"stable" }
# wie viele Lizenzen ein Release abdecken würden, bevor Sie es ausliefern
curl "$BASE/admin/products/acme-studio/releases/preview?version=2.5.0&on=2026-10-09" -H "X-Admin-Token: $TOKEN"
# -> { "product":"acme-studio", "version":"2.5.0", "covered":4, "notCovered":1, "notConfigured":0, "total":5 }
Der Dialog Register release (Release registrieren) zeigt dieselbe Vorschau live, während Sie tippen.
2. Jeder Edition eine Abdeckungsrichtlinie geben
Öffnen Sie Releases & coverage → Coverage policy (Abdeckungsrichtlinie), wählen Sie die Edition (tier), wählen Sie eine Voreinstellung, beantworten Sie deren ein oder zwei Fragen und klicken Sie auf Save policy (Richtlinie speichern). Das Seitenpanel zeigt in einer Vorschau in Klartext, was ein Käufer erhält, den Satz, den er lesen wird, und die Meldung, die er sähe, wenn er eine Version außerhalb seiner Bedingungen öffnete.
Was die einzelnen Steuerelemente bedeuten:
| Steuerelement | Bedeutung |
|---|---|
| Edition | Richtlinien gelten pro Edition. Neue Lizenzen dieser Edition erhalten diese Bedingungen bei der Ausstellung; andere Editionen bleiben unberührt. |
| Preset (Voreinstellung) | Eines der sechs Modelle oben. |
| Which major version? (Welche Hauptversion?) | Für die beiden „One major“-Voreinstellungen: die Linie, die die Lizenz abdeckt (2 → jede 2.x). |
| Updates for / Term (months) (Updates für / Laufzeit in Monaten) | Für die datumsbasierten Voreinstellungen: die Länge des Zeitfensters, gerechnet ab dem Tag, an dem jede Lizenz ausgestellt wird. |
| Downgrade rights (Downgrade-Rechte) | Dokumentiert, dass ältere Versionen immer erlaubt sind — eine Versionslinie schließt bereits alles darunter ein, und ein Zeitfenster alles, was davor veröffentlicht wurde. |
| Hotfixes keep their base release’s date (Hotfixes behalten das Datum ihres Basis-Releases) | Dokumentiert die Anspruchsdatum-Regel: Datieren Sie einen Hotfix beim Registrieren wie sein Basis-Release, und er bleibt abgedeckt. |
| Late renewals cover the gap (Verspätete Verlängerungen decken die Lücke ab) | Dokumentiert, wie Sie verlängern: Eine Verlängerungsbedingung, die am alten Enddatum beginnt, deckt die Lücke ab; eine, die heute beginnt, lässt die Lücke unabgedeckt. |
| Include beta builds (Beta-Builds einschließen) | Legt die Bedingungen der Richtlinie auf den Kanal beta statt nur auf stable. |
| Advanced: what happens when a customer runs a version they don’t have (Erweitert: Was passiert, wenn ein Kunde eine Version ausführt, die er nicht hat) | Hält das Verhalten fest, das Sie für Ihre Anwendung gewählt haben — informieren, schreibgeschützt ausführen oder blockieren. Der Server liefert die Entscheidung; Ihre Anwendung implementiert das Verhalten (siehe In Ihrer Anwendung). |
| Advanced: apply this policy to existing customers (Erweitert: Diese Richtlinie auf bestehende Kunden anwenden) | Eine Migration mit Vorschau für Lizenzen, die vor dieser Richtlinie ausgestellt wurden — siehe unten. |
Das Speichern einer Richtlinie betrifft nur Lizenzen, die ab jetzt ausgestellt werden. Dasselbe über die API — beachten Sie, dass ein Editions-Upsert die gesamte Edition ersetzt, senden Sie also auch deren Plätze und Features mit:
curl -X POST $BASE/admin/products/acme-studio/tiers -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"name":"pro","seats":5,"entitlements":{"export":"true"},
"coveragePolicy":{"preset":"oneMajorPlusUpgradeProtection","major":2,"periodMonths":12}}'
# eine Richtlinie löschen: ein leeres Preset senden (wird coveragePolicy weggelassen, bleibt die aktuelle unverändert)
curl -X POST $BASE/admin/products/acme-studio/tiers -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"name":"pro","seats":5,"entitlements":{"export":"true"},"coveragePolicy":{"preset":""}}'
preset ist eines von lifetime, updatesForPeriod, oneMajor, oneMajorPlusUpgradeProtection, subscription, subscriptionWithFallback; major wird von den beiden „One major“-Voreinstellungen benötigt und periodMonths von den vier datumsbasierten; channel ist optional (beta).
3. Lizenzen wie gewohnt ausstellen
An der Ausstellung ändert sich nichts: Licenses → Issue license (Lizenzen → Lizenz ausstellen) (oder POST /admin/licenses oder der Fulfillment-Webhook Ihres Shops). Bei der Ausstellung wird die Richtlinie der Edition in Bedingungen auf der neuen Lizenz aufgelöst. Der Drawer zeigt die Features der Edition; die Abdeckung, die die Lizenz trägt, ist anschließend auf der Lizenzseite sichtbar.
4. Die Abdeckung einer Lizenz einsehen und ändern
Eine Lizenzseite erhält einen Tab Coverage (Abdeckung) (er erscheint automatisch, sobald die Lizenz Abdeckung hat, unabhängig von Ihrer Dashboard-Ansicht). Er zeigt den Satz für den Kunden, die geltende Richtlinie, die neueste registrierte Version, die er nutzen kann, ob die Durchsetzung aktiv ist, einen Abdeckungsprüfer pro Lizenz und eine klartextliche Historie jeder Bedingung, die die Lizenz je hatte.
Change coverage… (Abdeckung ändern…) wendet eine Voreinstellung auf diese Lizenz an — so handhaben Sie eine Verlängerung (erneut „Updates for a period“), ein Upgrade („One major version“ 3) oder eine Umstellung auf ein Abonnement. Abdeckung kann hier nur wachsen: Die neuen Bedingungen werden neben die bestehenden gestellt.
# eine Voreinstellung auf eine Lizenz anwenden (Verlängerung / Upgrade / Umstellung)
curl -X POST $BASE/admin/licenses/LIC-…/coverage/preset -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"preset":"updatesForPeriod","periodMonths":12}'
# die Abdeckung einer Lizenz lesen: was gilt, die Sequenznummer und das vollständige Protokoll
curl $BASE/admin/licenses/LIC-…/coverage -H "X-Admin-Token: $TOKEN"
# -> { "configured":true, "mode":"enforced", "seq":3, "supportUntilUtc":"2027-10-09T00:00:00.0000000Z",
# "activeGrants":[ { "id":"…", "min":"2.0.0", "maxExcl":"3.0.0", "dates":[], "channel":null,
# "reason":"purchase", "sourceRef":null, "createdUtc":"…", "active":true }, … ],
# "ledger":[ … jede jemals hinzugefügte Bedingung, mit "active":false für entfernte … ] }
Benutzerdefinierte Bedingungen und das technische Protokoll
Der eingeklappte Abschnitt Advanced: coverage history (technical) (Erweitert: Abdeckungshistorie (technisch)) ist für Support-Mitarbeiter gedacht. Jede Zeile ist eine Bedingung mit Versionsbereich, Daten, Kanal, Grund und Status; von hier aus können Sie eine benutzerdefinierte Bedingung hinzufügen (Add a custom term: eine festgelegte Version, eine Minor-Linie, ein Fenster mit expliziten Daten, einen beta-Kanal), eine aktive Bedingung entfernen (Remove), die Durchsetzung für diese Lizenz pausieren / fortsetzen (Pause enforcement) und die signierte Abdeckungsdatei herunterladen (Download signed coverage file).
# eine benutzerdefinierte Bedingung hinzufügen: alles, was in einem Zeitfenster veröffentlicht wird, auf dem Beta-Kanal, festgehalten als Promo
curl -X POST $BASE/admin/licenses/LIC-…/coverage/grants -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"minVersion":null,"maxExclVersion":null,"dates":[{"from":"2026-10-01","to":"2026-12-31"}],
"channel":"beta","reason":"promo","sourceRef":"launch-week"}'
# eine festgelegte Version: {"minVersion":"2.4.1","maxExclVersion":"2.4.2"} — eine Minor-Linie: {"minVersion":"2.4.0","maxExclVersion":"2.5.0"}
# eine aktive Bedingung entfernen (wird festgehalten, nie gelöscht)
curl -X POST $BASE/admin/licenses/LIC-…/coverage/revoke -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"grantId":"<id from the ledger>","sourceRef":"refund #4821"}'
# die Durchsetzung für eine Lizenz pausieren (der nächste Block wird als nicht durchgesetzt gestempelt → jede Version läuft); "enforced" setzt sie fort
curl -X POST $BASE/admin/licenses/LIC-…/coverage/mode -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"mode":"unenforced"}'
reason ist eines von purchase, renewal, upgrade, promo, fallback, migration, legacy; sourceRef ist Freitext für Ihren Audit-Trail (eine Bestellnummer, ein Ticket). Versionsbereiche vergleichen nur major.minor.patch — Pre-Release-Suffixe werden ignoriert, sodass 2.5.0-beta innerhalb von 2.x liegt und 3.0.0-rc1 nicht. Daten sind Kalenderdaten, die inklusiv und ohne Zeitzonenumrechnung verglichen werden.
5. Beliebige Version für beliebige Lizenz prüfen
Der Abdeckungsprüfer — auf dem Tab Coverage checker des Hubs und auf dem Coverage-Tab jeder Lizenz — beantwortet „kann diese Lizenz Version X nutzen?“ genau so, wie es die Anwendung und Ihre Download-Seite täten, mit einer einzeiligen Begründung und, wenn das Register gefüllt ist, der neuesten Version, die der Kunde nutzen kann.
curl "$BASE/admin/licenses/LIC-…/coverage/check?version=3.0.0" -H "X-Admin-Token: $TOKEN"
# -> { "status":"NotCovered", "reason":"OutsideVersionRange", "newestCovered":"2.4.0" }
# &on=2026-11-01 anhängen, um an einem expliziten Anspruchsdatum auszuwerten (Standard: das registrierte Datum der Version, sonst heute)
# &channel=beta anhängen, um einen Vorschau-Build zu prüfen
Durchgesetzt, nicht durchgesetzt und „nicht konfiguriert“
Jeder Abdeckungsblock trägt einen Modus:
- Durchgesetzt — der normale Zustand einer Lizenz mit Abdeckungsbedingungen: Die Anwendung vergleicht den laufenden Build mit den Bedingungen.
- Nicht durchgesetzt — der Block sagt „behandle jede Version als abgedeckt“. Keyright stellt nicht durchgesetzte Blöcke aus, wenn Sie die Durchsetzung für eine Lizenz pausieren (Pause enforcement), und — automatisch, nach einer Kulanzfrist — wenn der Plan eines Arbeitsbereichs oder eine selbst gehostete Lizenz die Abdeckung nicht mehr enthält. Ihre Kunden werden nie durch Ihren Abrechnungsstatus ausgesperrt; sobald der Anspruch zurückkehrt, werden wieder durchgesetzte Blöcke mit unveränderten Bedingungen ausgestellt.
- Nicht konfiguriert — eine Lizenz, die nie Abdeckungsbedingungen hatte (eine ältere Lizenz oder ein Arbeitsbereich, der keine Abdeckung nutzt), erhält einen signierten „nicht konfiguriert“-Block. Das SDK meldet ihn als
NotConfiguredund behandelt die Lizenz als uneingeschränkt. Dieser Zustand kommt ausschließlich aus einem signierten Block: Ein fehlender Block wird nie als „uneingeschränkt“ gelesen, das Entfernen des Blocks von einer Maschine ist also kein Weg, die Durchsetzung zu umgehen.
Die Durchsetzung in Ihrer Anwendung ist eine Entscheidung zur Build-Zeit (KeyrightRequireCoverage, unten). Builds ohne sie ignorieren die Abdeckung vollständig; Builds mit ihr fragen Keyright, aktualisieren einmal, bevor sie ablehnen, und fallen offline ohne Cache geschlossen (fail-closed) in den Zustand „importieren Sie eine aktualisierte Lizenzdatei“.
Eine Richtlinie auf bestehende Kunden anwenden
Das Speichern einer Richtlinie berührt bereits ausgestellte Lizenzen nie. Wenn Sie bestehenden Kunden doch etwas zusätzlich geben möchten — Sie haben etwa Upgrade-Schutz eingeführt und möchten ihn auf alle 2.x-Kunden ausweiten — verwenden Sie Advanced: apply this policy to existing customers auf dem Richtlinien-Tab. Geben Sie eine Referenzversion ein, klicken Sie auf Preview who is affected… (Vorschau der Betroffenen…), um zu sehen, wie viele Lizenzen dafür Abdeckung gewinnen würden (und wie viele sie bereits haben), und dann auf Apply (Anwenden). Abdeckung kann nur wachsen: Niemand verliert eine Version, die er heute nutzen kann.
# Vorschau: wie viele acme-studio-Lizenzen 3.0.0 neu abdecken würden, wenn alle eine Bedingung „2.x plus Zeitfenster“ erhielten
curl -X POST "$BASE/admin/products/acme-studio/coverage/migrate?dryRun=true&version=3.0.0" \
-H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"minVersion":null,"maxExclVersion":null,"dates":[{"from":"2026-10-09","to":"2027-10-09"}],"reason":"migration","sourceRef":"upgrade-protection-rollout"}'
# -> { "total":5, "coveredBefore":1, "coveredAfter":5, "gain":4, "applied":false }
# anwenden (dryRun=false): dieselbe Bedingung wird an jede nicht widerrufene Lizenz des Produkts angefügt
Altlizenzen: nachträglich ergänzen (Backfill), bevor Sie durchsetzen
Eine Lizenz, die ausgestellt wurde, bevor Sie Abdeckung eingeführt haben, hat keine Bedingungen. Würden Sie einem solchen Kunden einen RequireCoverage-Build ausliefern, ohne etwas zu unternehmen, würde er aufgefordert, eine Lizenzdatei zu importieren. Der Tab Enforcement readiness (Durchsetzungsbereitschaft) existiert, damit das nie passiert: Er zählt die Lizenzen, denen noch Bedingungen fehlen, und blockiert die Checkliste, bis sie nachträglich ergänzt sind.
Wählen Sie einen von drei Backfills — jeder gibt der Lizenz eine Bedingung, die als Bestandskunde markiert ist (reason: legacy):
| Backfill | Ergebnis | Wann |
|---|---|---|
| Treat them as lifetime (Als lebenslang behandeln) | Jede Altlizenz deckt jede Version ab, für immer. | Sie haben für Updates bisher nie Geld verlangt. |
| Use each license’s expiry date (Ablaufdatum jeder Lizenz verwenden) | Ein Fenster, das am eigenen Ablaufdatum der Lizenz endet (unbefristete Lizenzen erhalten lebenslang). | Befristete Lizenzen, deren Support mit der Lizenz endete. |
| Import coverage dates (CSV) (Abdeckungsdaten importieren (CSV)) | Support-Enddaten pro Lizenz aus einer licenseId,supportUntil-Datei; ein leeres Datum bedeutet lebenslang. | Sie haben Support-Zeitfenster anderswo verwaltet. |
curl $BASE/admin/products/acme-studio/coverage/readiness -H "X-Admin-Token: $TOKEN"
# -> { "total":5, "configured":4, "missing":1 }
# jede Lizenz nachträglich ergänzen, die noch keine Bedingungen hat (defaultRule: "lifetime" oder "supportEnd")
curl -X POST $BASE/admin/products/acme-studio/coverage/backfill -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"defaultRule":"lifetime"}'
# -> { "backfilled":1 }
# oder pro Lizenz (der CSV-Upload sendet dieselbe Struktur)
curl -X POST $BASE/admin/products/acme-studio/coverage/backfill -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"rows":[{"licenseId":"LIC-AAAA","supportUntil":"2027-03-12"},{"licenseId":"LIC-BBBB","supportUntil":null}]}'
Bringen Sie die Daten in Ordnung, bevor Sie einen durchsetzenden Build ausliefern: Sobald der signierte Block eines Kunden ausgeliefert wurde, kann er erweitert, aber nicht sinnvoll zurückgenommen werden. Online-Kunden übernehmen ihre nachträglich ergänzten Bedingungen bei der nächsten Aktualisierung; netzwerkisolierte Kunden brauchen den Export unten.
Netzwerkisolierte Kunden (air-gapped)
Eine Maschine, die Ihren Dienst nie erreicht, kann den Block bei der Aktivierung nicht empfangen. Liefern Sie ihn stattdessen als Datei:
- Eine Lizenz: die Schaltfläche Download signed coverage file (Signierte Abdeckungsdatei herunterladen) auf ihrem Coverage-Tab (oder
GET /admin/licenses/{id}/coverage/block→{ "block": "…" }). - Jede Lizenz eines Produkts: Export offline license files (Offline-Lizenzdateien exportieren) auf dem Tab Enforcement readiness (oder
GET /admin/products/{slug}/coverage/export→{ "product", "count", "items": [ { "licenseId", "coverageBlock" } ] }).
Die Datei ist der signierte Block-Umschlag — JSON mit einem payload und einer sig. Übergeben Sie sie dem Kunden zusammen mit seiner Offline-Lizenzdatei oder seinem Lease; seine Anwendung importiert sie mit ImportCoverageBlock (unten). Eine Verlängerung für einen netzwerkisolierten Kunden ist schlicht eine neuere Datei: Sie trägt eine höhere Sequenznummer und ersetzt daher die zwischengespeicherte, während eine ältere Datei abgewiesen wird. Der Export respektiert Ihren Anspruch genau wie die Aktivierung — nicht durchgesetzte Blöcke, wenn der Arbeitsbereich die Abdeckung nicht mehr enthält —, er kann also niemanden aussperren.
Was der Kunde sieht
Im Kundenportal
Die Lizenzseite des Portals zeigt den Abdeckungssatz — als Besitz formuliert, ohne interne Begriffe — direkt unter der Lizenzübersicht und darunter die Liste Devices (Geräte). Kunden erfahren, was sie besitzen, ohne ein Support-Ticket.
Wenn Sie ein eigenes Kundenportal betreiben, liefert die Portal-API (mit Anmeldung) denselben Satz und das abgeleitete Support-Enddatum: GET /portal/license/{id}/coverage → { "status": "Your license covers …", "supportUntilUtc": "2027-10-09T00:00:00.0000000Z" } (null, wenn die Abdeckung nicht durch ein Datum begrenzt ist).
Die Standardsätze und -meldungen
Dies sind die Zeichenketten, die das SDK und die API erzeugen (das Dashboard zeigt sie in der Vorschau mit derselben Bedeutung); das englische Original, das standardmäßig ausgegeben wird, steht jeweils in Klammern. Alle lassen sich pro Zustand überschreiben — siehe Meldungen unten.
| Zustand | Standardtext |
|---|---|
| Lebenslang | Ihre Lizenz deckt jede Version ab. (Your license covers every version.) |
Eine Hauptversion ({line} = „Acme Studio 2“) | Ihre Lizenz deckt alle Acme Studio 2-Versionen ab. (Your license covers all Acme Studio 2 versions.) |
| Zeitfenster (Zeitraum / Abonnement) | Ihre Lizenz deckt jede Version ab, die bis zum 9. Okt. 2027 veröffentlicht wird. (Your license covers any version released through 9 Oct 2027.) |
| Hauptversion + Upgrade-Schutz | Ihre Lizenz deckt alle Acme Studio 2-Versionen ab, plus jede Version, die bis zum 9. Okt. 2027 veröffentlicht wird. (Your license covers all Acme Studio 2 versions, plus any version released through 9 Oct 2027.) |
| Offenes Zeitfenster | Ihre Lizenz deckt jede bisher veröffentlichte Version ab. (Your license covers every version released so far.) |
| Nicht abgedeckt — Zeitfenster abgelaufen | Diese Version (2.5.0) wurde veröffentlicht, nachdem Ihre Updates am 9. Okt. 2027 endeten. Sie können 2.4.0 weiter nutzen. Verlängern Sie, um 2.5.0 zu nutzen. (This version (2.5.0) was released after your updates ended on 9 Oct 2027. You can keep using 2.4.0. Renew to use 2.5.0.) (ohne Register entfällt der Satz „weiter nutzen“) → Aktion Renew |
| Nicht abgedeckt — außerhalb des Versionsbereichs | Diese Version (3.0.0) ist in Ihrer Acme Studio 2-Lizenz nicht enthalten. Sie können 2.4.0 weiter nutzen. Führen Sie ein Upgrade durch, um 3.0.0 zu nutzen. (This version (3.0.0) isn’t included in your Acme Studio 2 license. You can keep using 2.4.0. Upgrade to use 3.0.0.) → Aktion Upgrade |
| Offline nicht verfügbar | Acme Studio konnte offline nicht bestätigen, dass Ihre Lizenz diese Version enthält. Stellen Sie eine Internetverbindung her oder importieren Sie eine aktualisierte Lizenzdatei von Acme Software. (Acme Studio couldn’t confirm that your license includes this version while offline. Connect to the internet, or import an updated license file from Acme Software.) → Aktion Import license file |
In Ihrer Anwendung (.NET SDK)
Die Abdeckung braucht zwei Dinge von Ihrem Build: welches Release er ist (damit er mit den Bedingungen verglichen werden kann) und ob er durchsetzen soll. Beides sind Eigenschaften zur Kompilierzeit, sodass einem ausgelieferten Binary zur Laufzeit nichts davon ausgeredet werden kann.
1. Die Release-Identität des Builds einbetten (MSBuild)
Das Paket Keyright.NET enthält MSBuild-Targets, die diese Eigenschaften lesen und als Metadaten in die Assembly kompilieren, die das SDK beim Start zurückliest:
<!-- in der .csproj der Anwendung oder in Directory.Build.props — normalerweise aus Ihrer Release-Pipeline gesetzt -->
<PropertyGroup>
<KeyrightCoverageVersion>2.4.0</KeyrightCoverageVersion> <!-- die Produktversion dieses Builds (major.minor.patch) -->
<KeyrightCoverageEntitlementDate>2026-10-01</KeyrightCoverageEntitlementDate> <!-- das Datum, anhand dessen die Abdeckung beurteilt wird (yyyy-MM-dd) -->
<KeyrightCoverageChannel>stable</KeyrightCoverageChannel> <!-- stable (Standard) oder beta -->
<KeyrightRequireCoverage>true</KeyrightRequireCoverage> <!-- Durchsetzung in diesen Build einbacken -->
<!-- optional: ein hartes Selbstablaufdatum für Vorschau-Builds, zurückgelesen als CoverageBuildInfo.BuildExpiryUtc -->
<!-- <KeyrightCoverageBuildExpiry>2027-01-01T00:00:00Z</KeyrightCoverageBuildExpiry> -->
</PropertyGroup>
Aus CI übergeben Sie sie auf der Kommandozeile, damit die Pipeline die einzige Quelle der Wahrheit ist:
dotnet publish -c Release \
-p:KeyrightCoverageVersion=2.4.0 \
-p:KeyrightCoverageEntitlementDate=2026-10-01 \
-p:KeyrightRequireCoverage=true
Regeln, die die Targets durchsetzen und auf die sich das SDK verlässt:
- Ein Build mit
KeyrightRequireCoverage=truemussKeyrightCoverageEntitlementDatesetzen; andernfalls schlägt der Build mit dem Fehler KR0001 fehl (es gäbe nichts, wogegen ausgewertet werden könnte). Nicht gesetzte Eigenschaften erzeugen überhaupt keine Metadaten. - Ein Hotfix erbt das Anspruchsdatum seines Basis-Releases. Bauen Sie
2.3.1mitKeyrightCoverageEntitlementDate=2026-07-02(dem Datum von 2.3.0), und er bleibt für alle abgedeckt, die 2.3.0 hatten — und registrieren Sie ihn mit demselben--entitlement-date, damit Prüfer und Register übereinstimmen. - Pre-Release-Suffixe (
2.5.0-beta.1) werden beim Bereichsabgleich ignoriert; verwenden Sie für Betas den Kanal.
Zur Laufzeit sind die eingebetteten Werte als CoverageBuildInfo.Current verfügbar — Version, EntitlementDate, Channel, BuildExpiryUtc, RequireCoverage und HasMetadata.
2. Abdeckung beim Start prüfen
Das SDK erledigt bereits die halbe Arbeit: Jedes ActivateAsync (und jede Lease-Aktualisierung) sendet Version und Anspruchsdatum des Builds, empfängt den signierten Abdeckungsblock zusammen mit dem Lease, prüft ihn gegen Ihren eingebetteten öffentlichen Schlüssel und speichert ihn neben der Lizenz zwischen. Sie fügen einen Aufruf hinzu:
using Keyright.Client;
using Keyright.Coverage;
static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
Product = "acme-studio",
PublicKeyBase64 = "MIIBIjANBgkq...", // derselbe Schlüssel prüft Lizenzen, Leases UND Abdeckungsblöcke
ServiceUrl = "https://licensing.acme.example",
});
static async Task<bool> GateOnCoverageAsync(CancellationToken ct)
{
var build = CoverageBuildInfo.Current;
if (!build.RequireCoverage) return true; // ein nicht durchsetzender Build ignoriert die Abdeckung vollständig
// Entscheidet anhand des zwischengespeicherten Blocks; falls nicht abgedeckt (oder nichts zwischengespeichert), wird die Lizenz
// einmal stillschweigend aktualisiert (kurzer Timeout, offline ist in Ordnung) und erneut entschieden. Wirft bei Netzwerkproblemen nie.
CoverageCheckResult check = await License.CheckCoverageAsync(ct: ct);
if (check.IsCovered) // Covered oder NotConfigured (die Abdeckung schränkt nicht ein)
return true;
// Nicht abgedeckt oder offline nicht verfügbar: Meldung + die eine Aktion erzeugen und die Anwendung entscheiden lassen.
var message = BuildMessage(check);
switch (check.Status)
{
case CoverageCheckStatus.UnavailableOffline: // kein verwendbarer Block und die Aktualisierung ist fehlgeschlagen (netzwerkisoliert)
ShowImportLicenseFile(message); // "…import an updated license file from Acme Software."
return false;
case CoverageCheckStatus.NotCovered:
return ShowNotCovered(message); // blockieren, schreibgeschützt ausführen oder nur informieren — Ihre Entscheidung
default:
return true;
}
}
CheckCoverageAsync(version?, entitlementDate?) setzt beide Argumente standardmäßig auf die eingebetteten Metadaten des Builds, sodass der parameterlose Aufruf die Frage „ist dieser Build abgedeckt?“ beantwortet. Er wirft nur dann eine Exception, wenn es überhaupt keine Version gibt (keine eingebettet und keine übergeben). Das Ergebnis:
| Member | Bedeutung |
|---|---|
Status | Covered, NotCovered, NotConfigured oder UnavailableOffline. |
IsCovered | true für Covered und NotConfigured — die Anwendung darf laufen. |
Reason | Für NotCovered: WindowLapsed (die Updates des Kunden endeten vor diesem Release → verlängern) oder OutsideVersionRange (diese Versionslinie gehört ihm nicht → Upgrade). |
NewestCovered | Die neueste abgedeckte Version, wenn Block oder Register sie bekannt machen (SemVer?), für „Sie können 2.4.0 weiter nutzen“. |
Refreshed | Ob während dieser Prüfung eine stillschweigende Aktualisierung versucht wurde. |
UnavailableOffline ist bewusst von NotCovered getrennt: Es bedeutet nichts ist zwischengespeichert und der Dienst war nicht erreichbar, was bei einem durchsetzenden Build eine Situation „bitte importieren Sie die Datei, die wir Ihnen geschickt haben“ ist — niemals „Ihre Lizenz ist ungültig“ und niemals eine Aufforderung zur Schlüsseleingabe.
3. Lokal entscheiden, jederzeit
Für Ihre eigenen In-App-Meldungen oder Ihren Updater — „soll ich das 3.0-Banner zeigen?“, „was ist die neueste Version, die dieser Kunde installieren kann?“ — lesen Sie den zwischengespeicherten Block ganz ohne Netzwerk:
CoverageInfo cov = License.GetCoverage(); // aus dem zwischengespeicherten, geprüften Block; kontaktiert nie einen Distributions-Endpunkt
if (cov.Available)
{
bool notConfigured = cov.IsNotConfigured; // signierter „keine Einschränkung“-Block
CoverageMode mode = cov.Mode; // Enforced / Unenforced
DateTime? supportTo = cov.SupportUntilUtc; // spätestes Fensterende oder null, wenn nicht datumsbegrenzt
long seq = cov.Seq; // die Sequenznummer des Blocks (Anti-Rollback)
foreach (Grant g in cov.Grants) // die Bedingungen: Versionsbereich, Datumsintervalle, Kanal, Grund
Console.WriteLine($"{g.Versions?.MinInclusive}..{g.Versions?.MaxExclusive} {g.Reason}");
CoverageDecision d = cov.Decide(SemVer.Parse("3.0.0"), CalendarDate.Parse("2026-11-01"), channel: null);
}
// Einzeiler: Ist diese Version für diese Lizenz abgedeckt? Anspruchsdatum und Kanal entsprechen standardmäßig den Metadaten des Builds.
bool can30 = License.IsCovered("3.0.0");
bool canOn = License.IsCovered("3.0.0", entitlementDate: "2026-11-01", channel: "beta");
IsCovered liefert true für eine nicht konfigurierte Lizenz und false, wenn kein gültiger Block verfügbar ist — ein fehlender Block ist nie „abgedeckt“. GetCoverage() liefert CoverageInfo.Unavailable (Available == false), wenn die Lizenz selbst nicht validiert oder nichts zwischengespeichert ist.
4. Meldungen und die eine Aktion
CoverageMessaging verwandelt ein Ergebnis in den Text für den Kunden und genau eine Handlungsaufforderung, mit Ihren URLs und Ihrer Formulierung:
static readonly CoverageMessageOptions Wording = new()
{
Product = "Acme Studio",
Vendor = "Acme Software",
UpdatesTerm = "maintenance", // Standard "updates"; auch "support"
LineTemplate = "{product} {major}", // wie eine Versionslinie gelesen wird, z. B. "Acme Studio 2"; oder "{major}.x"
RenewUrl = "https://acme.example/renew?key={key}",
UpgradeUrl = "https://acme.example/upgrade",
ImportLicenseFileUrl = "https://acme.example/support/offline-license",
RenewLabel = "Renew maintenance",
FormatDate = d => d.ToString("d MMMM yyyy", new CultureInfo("en-GB")), // {date} muss eindeutig lesbar sein
};
// Lokalisieren oder umformulieren Sie jeden Zustand: Schlüssel sind die CoverageMessaging.Key*-Konstanten, Vorlagen dürfen
// {product} {version} {newestCovered} {line} {date} {vendor} {updatesTerm} verwenden
// Wording.TemplateOverrides[CoverageMessaging.KeyWindowLapsed] = "…";
static CoverageMessage BuildMessage(CoverageCheckResult check)
{
var cov = License.GetCoverage();
int? lineMajor = cov.Grants.Where(g => g.Versions?.MinInclusive is not null)
.Select(g => (int?)g.Versions!.MinInclusive!.Value.Major).Max();
var ctx = new CoverageMessaging.Context
{
Version = CoverageBuildInfo.Current.Version ?? "",
LineMajor = lineMajor, // für {line}
BoundaryDate = cov.SupportUntilUtc, // für {date} bei „Zeitfenster abgelaufen“
NewestCovered = check.NewestCovered?.ToString(),
};
return CoverageMessaging.Build(check.Status, check.Reason, ctx, Wording);
}
// message.Text -> "This version (3.0.0) isn't included in your Acme Studio 2 license. You can keep using 2.4.0. Upgrade to use 3.0.0."
// message.Action -> CoverageAction.Upgrade (Renew | Upgrade | ImportLicenseFile | None)
// message.ActionUrl -> "https://acme.example/upgrade"
// message.ActionLabel -> "Upgrade"
Die Zuordnung ist fest, damit die UI immer die richtige Schaltfläche rendert: Zeitfenster abgelaufen → Renew, außerhalb des Versionsbereichs → Upgrade, offline nicht verfügbar → Import license file; Covered und NotConfigured erzeugen eine leere Meldung mit CoverageAction.None. Für eine In-App-Statuszeile („was besitze ich?“) verwenden Sie CoverageMessaging.StatusSentence(cov.Grants, cov.IsNotConfigured, Wording) — denselben Satz, den das Portal zeigt.
5. Eine Abdeckungsdatei importieren (netzwerkisoliert)
string envelopeJson = File.ReadAllText("northwind.coverage.json"); // aus Ihrem Export oder der Download-Schaltfläche der Lizenz
CoverageVerifyResult r = License.ImportCoverageBlock(envelopeJson); // prüft, bindet an die Lizenz, setzt Anti-Rollback durch; kein Netzwerk
if (!r.Ok)
{
// r.Status: Malformed | UnknownKey | BadSignature | RolledBack | WrongBinding | UnsupportedVersion
ShowImportError(r.Status);
}
Ein Ergebnis RolledBack bedeutet, dass die Datei älter ist als das, was die Maschine bereits hat (niedrigere Sequenznummer), und ignoriert wurde; WrongBinding bedeutet, dass sie für eine andere Lizenz oder ein anderes Produkt ausgestellt wurde; UnknownKey bedeutet, dass sie mit einem Schlüssel signiert wurde, den Ihr Build nicht einbettet — behalten Sie während einer Schlüsselrotation den vorherigen öffentlichen Schlüssel in AdditionalPublicKeysBase64, genau wie bei Lizenzen und Leases.
6. Eigene Downloads freigeben: die Berechtigungs-API
Ihre Download-Seite oder Ihr Updater stellt Keyright dieselbe Frage Server-zu-Server, authentifiziert mit dem Schlüssel des Kunden. Keyright antwortet; Sie entscheiden, ob die Datei ausgeliefert wird.
curl -X POST $BASE/v1/coverage -H "content-type: application/json" \
-d '{"key":"LIC-…","product":"acme-studio","version":"2.5.0"}'
# -> { "eligible":true, "licenseValid":true, "status":"Covered", "reason":"None", "newestCovered":"2.4.0" }
# optional: "on":"2026-11-01" (Anspruchsdatum; Standard = das registrierte Datum, sonst heute), "channel":"beta"
eligible ist licenseValid && (status is Covered or NotConfigured); status/reason sind die rohe Entscheidung, damit Sie die Seite selbst formulieren können. Aus einem .NET-Backend:
using var http = new HttpClient { BaseAddress = new Uri("https://licensing.acme.example/") };
var resp = await http.PostAsJsonAsync("v1/coverage", new { key, product = "acme-studio", version = "2.5.0" });
var e = await resp.Content.ReadFromJsonAsync<Eligibility>();
if (e is { Eligible: true }) ServeDownload("2.5.0"); else ShowWhy(e!.Status, e.Reason, e.NewestCovered);
record Eligibility(bool Eligible, bool LicenseValid, string Status, string Reason, string? NewestCovered);
Verhaltensreferenz
| Situation | Was passiert |
|---|---|
| Kunde verlängert / führt ein Upgrade durch | Sie fügen Bedingungen hinzu (Dashboard oder API). Die Anwendung übernimmt sie beim nächsten Start oder bei der nächsten Aktualisierung — keine erneute Schlüsseleingabe, keine Reaktivierung. |
| Kunde kehrt zu einem älteren Build zurück | Weiterhin abgedeckt: Eine ältere Version liegt innerhalb jeder Versionslinie und vor jedem Zeitfenster. |
| Zwei Versionen nebeneinander oder unterschiedliche Versionen pro Maschine | Jeder Build wertet sich selbst gegen dieselbe Lizenz aus; Plätze gelten wie bisher pro Maschine. |
| Uhr zurückgestellt | Die Abdeckung vergleicht das signierte Anspruchsdatum des Builds mit den Bedingungen, nie die lokale Uhr; der bestehende Schutz gegen Uhrmanipulation gilt weiterhin für Testversionen und befristete Lizenzen. |
| Block entfernt oder bearbeitet | Signatur oder Bindung schlägt fehl → das SDK aktualisiert; offline ohne etwas Gültiges → UnavailableOffline. Niemals „abgedeckt“. |
| Älterer Block erneut eingespielt | Niedrigere Sequenznummer → abgewiesen; die Abdeckung bleibt beim neuesten akzeptierten Block. |
| Erstattung / Widerruf / Übertragung | Unverändert: Widerrufen Sie die Lizenz (die Anwendung fällt bei der Aktualisierung auf Free zurück); Übertragungen behalten die Bedingungen. |
| Ihr Plan enthält keine Abdeckung mehr | Nach einer Kulanzfrist stellt der Dienst nicht durchgesetzte Blöcke aus; durchsetzende Builds laufen weiter; Bedingungen bleiben erhalten, und durchgesetzte Blöcke werden nach erneutem Upgrade wieder ausgestellt. |
Cloud vs. Self-Hosting
Die Abdeckung verhält sich in beiden Modi identisch — die Richtlinien-Voreinstellungen, der signierte Block, die MSBuild-Props und die SDK-Aufrufe sind dieselben. Was sich unterscheidet:
- Anspruch. Auf Keyright Cloud ist die Abdeckung Teil des obersten Plans (Scale und Enterprise); niedrigere Pläne sehen sie gesperrt, mit einer Upgrade-Aufforderung. Selbst gehostet ist sie in Lizenzen enthalten, die die Abdeckungsfunktion tragen. In beiden Fällen erhält ein Arbeitsbereich, der den Anspruch verliert, nach einer Kulanzfrist nicht durchgesetzte Blöcke, statt Kunden auszusperren, und durchgesetzte Blöcke werden wieder ausgestellt, sobald der Anspruch zurückkehrt.
- Basis-URL / Token.
$BASEisthttps://keyright.delta1labs.comin der Cloud oder bei Self-Hosting die URL Ihres eigenen Issuing-Service;$TOKENist Ihr Dashboard-Sitzungstoken (Cloud) oder das Mandanten-/Umgebungs-Admin-Token (selbst gehostet).ServiceUrlundPublicKeyBase64im SDK zeigen auf die passende Instanz und deren Signaturschlüssel. - Alles andere — die Berechtigungs-API (
/v1/coverage), der Offline-Export/-Import, das Register — ist in beiden Modi dieselbe Oberfläche.
Prüfen
- Geben Sie einer Edition eine Richtlinie (Dashboard oder
coveragePolicybeim Tier-Upsert), stellen Sie eine Lizenz aus und öffnen Sie deren Tab Coverage — bestätigen Sie, dass der klartextliche Satz und die Bedingungen zur Voreinstellung passen. - Führen Sie den Abdeckungsprüfer (oder
GET /admin/licenses/{id}/coverage/check?version=…) für eine Version aus, die Sie als abgedeckt erwarten, und für eine, die Sie als nicht abgedeckt erwarten — bestätigen Sie Status und Begründung (WindowLapsedvs.OutsideVersionRange). - Bauen Sie Ihre Anwendung mit
KeyrightCoverageVersion+KeyrightCoverageEntitlementDate+KeyrightRequireCoverage=true, aktivieren Sie mit einer abgedeckten Lizenz und bestätigen Sie, dassCheckCoverageAsyncCoveredliefert; erhöhen Sie die Version des Builds über die Bedingungen hinaus und bestätigen Sie, dassNotCoveredmit der richtigen einen Aktion zurückkommt. - Für Ihre Download-Seite: Senden Sie
POST /v1/coveragemit einem Kundenschlüssel + Version und bestätigen Sie, dasseligibledem entspricht, was die Anwendung sieht. - Netzwerkisoliert: Laden Sie den signierten Block herunter, importieren Sie ihn mit
ImportCoverageBlockauf einer nicht verbundenen Maschine und bestätigen Sie dieselbe Entscheidung ganz ohne Netzwerk.
Siehe auch
- .NET-SDK-Integration — die vollständige Client-Anleitung, auf der dies aufbaut.
- Admin-Dashboard — jeder Bildschirm und die Ansichten Basis / Mittel / Erweitert.
- Aktivieren, deaktivieren & Plätze verschieben — Leases, Offline-Dateien und netzwerkisolierte Aktivierung.
- Nutzung messen & Credits verkaufen — Verbrauch zusätzlich zu einer Lizenz verkaufen.
- HTTP-API-Referenz — die Endpunktübersicht.