Skip to content

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.
DashboardAbdeckungsrichtlinie der Editionoder Bedingungen pro Lizenzbei AusstellungLizenzbedingungenMomentaufnahme · append-onlySequenznummer wächstsigniertAbdeckungsblockModus · seq · Bedingungenan Lizenz + Produkt gebundenactivate · validate · refreshonline, mit dem LeaseExport → ImportOffline-DateiIhr BuildVersion · Anspruchsdatum · Kanaleingebettet über die Keyright-MSBuild-PropsVergleichEntscheidung (lokal, offline)Abgedeckt · Nicht abgedeckt (Fenster abgelaufen / Bereich)Nicht konfiguriert · Offline nicht verfügbarEine Meldung, eine Aktion: Renew · Upgrade · Import fileKeine erneute Schlüsseleingabe. Keine Reaktivierung.Keine Download-Sperre in Keyright.
Richtlinie → Bedingungen auf der Lizenz → signierter Block → die Anwendung vergleicht ihren eigenen Build damit und zeigt eine einzige Meldung mit einer einzigen Aktion.

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.

VoreinstellungWas der Kunde erhältTypischer 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 UpdatesVersionVeröffentlichungsdatumjede Version, für immerUpdates für einen ZeitraumKauf+ N MonateFensterspätere ReleasesEine Hauptversionalle 2.x, wann immer veröffentlicht3.x1.xHauptversion + Upgrade-Schutzalle 2.x3.0, falls im Fenster erschienenKauf+ N MonateAbonnementbezahlte LaufzeitAblaufdanach nichtsAbonnement mit Fallbackbezahlte LaufzeitAblaufbehält, was vorherveröffentlicht wurdeSchattiert = abgedeckte Versionen. Ein Release ist abgedeckt, wenn Version und Anspruchsdatum in einer schattierten Bedingung liegen.Verlängerungen und Upgrades fügen Schattierung hinzu; bereits Schattiertes wird nie entfernt.Hotfix-RegelEin 2.3.1-Hotfix, datiert wie sein Basis-Release 2.3.0, bleibt in jedem Fenster, das 2.3.0 abdeckte.
Die sechs Voreinstellungen als Regionen auf einer Ebene aus Version und Datum. Versionslinien sind horizontale Bänder; Zeitfenster sind vertikale Bänder; „Eine Hauptversion + Upgrade-Schutz“ ist die Vereinigung beider.

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.

Releases & coverage → Releases: eine Tabelle registrierter Versionen mit Veröffentlichungsdatum, dem Datum, an dem sie als veröffentlicht gelten (ein 2.3.1-Hotfix, datiert wie sein Basis-Release), und einem Abdeckungs-Badge pro Release wie „abgedeckt für 4 von 5 · 1 nicht abgedeckt“; eine Seitenkarte zeigt den CLI-Befehl keyright release --register für CI.
Das Release-Register — Versionen, Daten, Kanal und für wie viele Ihrer aktuellen Lizenzen jedes Release abgedeckt ist.

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.

Der Dialog „Register release“: Version 2.5.0, Datumsauswahl für „released on“ und „counts as released on“ mit dem Hinweis, für einen Hotfix das Datum des Basis-Releases zu verwenden, eine Kanalauswahl und eine Live-Vorschau „who this release is covered for“ mit der Anzahl abgedeckter, nicht abgedeckter und gesamter Lizenzen.
Beim Registrieren eines Releases sehen Sie in der Vorschau, noch vor der Veröffentlichung, für wie viele bestehende Kunden es abgedeckt ist.

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.

Releases & coverage → Coverage policy für die Edition „pro“: sechs Voreinstellungskarten, „One major + upgrade protection“ ausgewählt und ein Badge „Policy in effect“; die Fragen „Which major version? 2“ und „Updates for 12 months“; Schalter für sinnvolle Standardwerte; eine Live-Vorschau, die erklärt, was ein Käufer erhält, was er lesen wird und welche Meldung erscheint, wenn er später 4.0.0 öffnet; sowie die Advanced-Abschnitte für das Verhalten bei nicht abgedeckten Versionen und das Anwenden der Richtlinie auf bestehende Kunden.
Die Abdeckungsrichtlinie einer Edition: eine Voreinstellung, ihre Fragen und eine Live-Vorschau dessen, was der Kunde erhält und liest.

Was die einzelnen Steuerelemente bedeuten:

SteuerelementBedeutung
EditionRichtlinien 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.

Der Drawer „Issue license“ über der Lizenzliste: Produkt Acme Studio, Tier-Auswahl mit der Edition und ihren Plätzen, eine Zeile „This edition includes“, Lizenznehmer, Kontakt-E-Mail, Plätze, Laufzeit, ein Testversions-Schalter und ein eingeklappter Advanced-Abschnitt für Feature-Überschreibungen, Notizen und einen benutzerdefinierten Schlüssel.
Das Ausstellen einer Lizenz ist unverändert — die Abdeckungsrichtlinie der Edition wird automatisch auf die neue Lizenz angewendet.

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.

Der Coverage-Tab einer Lizenz: „What the customer sees — Your license covers all Acme Studio 2 versions, plus any new version released through Oct 9, 2027“, Richtlinie „One major + upgrade protection“, neueste nutzbare Version 2.4.0, Durchsetzung „Enforced“, ein eingebetteter Abdeckungsprüfer, die Abdeckungshistorie, eine Schaltfläche „Change coverage…“ sowie Seitenkarten mit den registrierten Releases und einer Erklärung, wie Abdeckung funktioniert.
Der Coverage-Tab einer Lizenz: der Satz, den der Kunde liest, die Fakten dahinter, ein Prüfer und die Historie.

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.

Der Dialog „Change coverage“ für eine Lizenz: die sechs Voreinstellungskarten mit „Subscription with fallback“ ausgewählt, ein Feld „Term (months)“ mit dem Wert 12, die Schalter für sinnvolle Standardwerte und eine Live-Vorschau dessen, was der Käufer erhält.
Change coverage… auf einer einzelnen Lizenz — dieselben Voreinstellungen, nur auf diesen Kunden angewendet. Nichts, was er heute nutzen kann, wird weggenommen.
# 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).

Der geöffnete Abschnitt „Advanced: coverage history (technical)“ einer Lizenz: ein Badge „Enforced“ mit einer Schaltfläche „Pause enforcement“, eine Tabelle der Bedingungen (Versionen 2.0.0 → < 3.0.0 mit beliebigen Daten, und beliebige Version mit den Daten 9. Okt. 2026 – 9. Okt. 2027, beide stable, Purchase, Active, jeweils mit einer Schaltfläche „Remove“), die Schaltflächen „Add a custom term“ und „Download signed coverage file“ sowie „Coverage version 2“.
Das technische Protokoll: eine Zeile pro Bedingung. Das Entfernen einer Bedingung verkleinert die Abdeckung und wird festgehalten; „Coverage version“ ist die Sequenznummer, mit der die Anwendung erneut eingespielte ältere Blöcke abweist.
# 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.

Releases & coverage → Coverage checker: ein Lizenzschlüssel, Version 3.0.0 und ein optionales Veröffentlichungsdatum, mit dem Ergebnis „Not covered. That version is outside the version line this license includes.“ und einem Open-Link zur Lizenz.
Der Prüfer gibt dieselbe Antwort wie die Anwendung und die Berechtigungs-API.
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 NotConfigured und 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.

Releases & coverage → Enforcement readiness: Kacheln für 5 Lizenzen, 4 mit Abdeckungsbedingungen, 1 ohne Bedingungen; eine Checkliste (Richtlinien entworfen, Anwendung liefert RequireCoverage aus, Lizenzen haben Bedingungen); eine Warnung, dass 1 Lizenz zuerst Bedingungen braucht; die Backfill-Schaltflächen „Treat them as lifetime“, „Use each license's expiry date“, „Import coverage dates (CSV)“; eine Schaltfläche „Export offline license files“; und eine Seitenkarte mit den im Build zu setzenden MSBuild-Eigenschaften.
Durchsetzungsbereitschaft vor dem Backfill: Eine Altlizenz hat noch keine Bedingungen, also sagt die Seite das und bietet die drei Wege an, es zu beheben.

Wählen Sie einen von drei Backfills — jeder gibt der Lizenz eine Bedingung, die als Bestandskunde markiert ist (reason: legacy):

BackfillErgebnisWann
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.

Enforcement readiness nach dem Backfill: 5 Lizenzen, 5 mit Abdeckungsbedingungen, 0 ohne; die Checkliste zeigt, dass jede Lizenz Abdeckungsbedingungen hat, und ein grüner „Ready“-Hinweis sagt, dass ein Build mit RequireCoverage nie einen Kunden aussperren wird.
Nach dem Backfill: Jede Lizenz trägt Bedingungen, und das Produkt ist bereit für einen durchsetzenden Build.

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.

Das Kundenportal auf einem Smartphone: Die Lizenzkarte für acme-studio zeigt „pro · 2 of 5 devices in use · never expires“ und den Abdeckungssatz „Your license covers all acme-studio 2 versions, plus any version released through 9 Oct 2027“, gefolgt vom Lizenzschlüssel-Feld, der Geräteliste mit „Free seat“-Schaltflächen und einem eingeklappten Abschnitt „More options“.
Das Portal formuliert die Abdeckung in einem Satz. Derselbe Satz steht Ihrem eigenen Portal oder Ihrer Anwendung zur Verfügung.

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.

ZustandStandardtext
LebenslangIhre 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-SchutzIhre 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 ZeitfensterIhre Lizenz deckt jede bisher veröffentlichte Version ab. (Your license covers every version released so far.)
Nicht abgedeckt — Zeitfenster abgelaufenDiese 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 VersionsbereichsDiese 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ügbarAcme 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=true muss KeyrightCoverageEntitlementDate setzen; 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.1 mit KeyrightCoverageEntitlementDate=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:

MemberBedeutung
StatusCovered, NotCovered, NotConfigured oder UnavailableOffline.
IsCoveredtrue für Covered und NotConfigured — die Anwendung darf laufen.
ReasonFür NotCovered: WindowLapsed (die Updates des Kunden endeten vor diesem Release → verlängern) oder OutsideVersionRange (diese Versionslinie gehört ihm nicht → Upgrade).
NewestCoveredDie neueste abgedeckte Version, wenn Block oder Register sie bekannt machen (SemVer?), für „Sie können 2.4.0 weiter nutzen“.
RefreshedOb 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

SituationWas passiert
Kunde verlängert / führt ein Upgrade durchSie 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ückWeiterhin abgedeckt: Eine ältere Version liegt innerhalb jeder Versionslinie und vor jedem Zeitfenster.
Zwei Versionen nebeneinander oder unterschiedliche Versionen pro MaschineJeder Build wertet sich selbst gegen dieselbe Lizenz aus; Plätze gelten wie bisher pro Maschine.
Uhr zurückgestelltDie 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 bearbeitetSignatur oder Bindung schlägt fehl → das SDK aktualisiert; offline ohne etwas Gültiges → UnavailableOffline. Niemals „abgedeckt“.
Älterer Block erneut eingespieltNiedrigere Sequenznummer → abgewiesen; die Abdeckung bleibt beim neuesten akzeptierten Block.
Erstattung / Widerruf / ÜbertragungUnverä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 mehrNach 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. $BASE ist https://keyright.delta1labs.com in der Cloud oder bei Self-Hosting die URL Ihres eigenen Issuing-Service; $TOKEN ist Ihr Dashboard-Sitzungstoken (Cloud) oder das Mandanten-/Umgebungs-Admin-Token (selbst gehostet). ServiceUrl und PublicKeyBase64 im 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

  1. Geben Sie einer Edition eine Richtlinie (Dashboard oder coveragePolicy beim 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.
  2. 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 (WindowLapsed vs. OutsideVersionRange).
  3. Bauen Sie Ihre Anwendung mit KeyrightCoverageVersion + KeyrightCoverageEntitlementDate + KeyrightRequireCoverage=true, aktivieren Sie mit einer abgedeckten Lizenz und bestätigen Sie, dass CheckCoverageAsync Covered liefert; erhöhen Sie die Version des Builds über die Bedingungen hinaus und bestätigen Sie, dass NotCovered mit der richtigen einen Aktion zurückkommt.
  4. Für Ihre Download-Seite: Senden Sie POST /v1/coverage mit einem Kundenschlüssel + Version und bestätigen Sie, dass eligible dem entspricht, was die Anwendung sieht.
  5. Netzwerkisoliert: Laden Sie den signierten Block herunter, importieren Sie ihn mit ImportCoverageBlock auf einer nicht verbundenen Maschine und bestätigen Sie dieselbe Entscheidung ganz ohne Netzwerk.

Siehe auch