Lizenzschluessel einrichten
Feature-Checks anwenden
Der License-Service wird an drei Stellen gequeried:
1. Middleware — Server-side API-Guard
// src/routes/api/rdp.js
router.use(requireFeature('remote_desktop'));
requireFeature antwortet bei fehlendem Feature mit 403 {ok:false, feature, upgrade_url}. Für quantitative Limits gibt es requireLimit(key, countFn):
// src/routes/api/peers.js (schematisch)
router.post('/', requireLimit('vpn_peers', () => peers.count()), ...);
requireLimit unterscheidet drei Zustände: -1 → unbegrenzt, 0 → Feature nicht verfügbar (403), sonst Vergleich count >= limit.
2. Templates — UI-Gate
injectLicense stellt license.features in jedem View bereit:
{% if license.features.remote_desktop %}
<a href="/rdp">RDP</a>
{% else %}
<span class="lock-icon" title="Pro feature">…</span>
{% endif %}
Das UI zeigt gelockte Features normalerweise trotzdem an (mit Lock-Icon), um Upgrade-Pfade sichtbar zu machen.
3. Services — opportunistisch
Im Service-Layer werden Features abgefragt, um Nebenpfade zu überspringen:
// src/routes/api/gateway.js (Heartbeat-Handler)
if (body.hostname && hasFeature('internal_dns')) {
peers.setHostname(peerId, body.hostname, 'agent');
}
Der Heartbeat selbst schlägt nicht fehl, wenn internal_dns aus ist — das Feld wird einfach ignoriert.
Enforcement bei Downgrade
Wenn ein Lizenzrefresh einen Plan-Wechsel erkennt (previousPlan !== cachedPlan), läuft enforceLimitsInternal():
- Für jedes Limit-Feature (
vpn_peers,http_routes,l4_routes) wird die Ist-Menge gezählt. - Ist
count > limit, werdencount - limitälteste Einträge (ORDER BYcreated_atASC) aufenabled=0gesetzt. - WireGuard-Config bzw. Caddy-Config werden synchronisiert — deaktivierte Peers/Routes verschwinden vom Wire.
- Activity-Log-Eintrag
peer_license_disabled/route_license_disabled. - Optional Email-Alert wenn
email_alerts_enabled=true.
Datensätze werden nie gelöscht. Ein späterer Upgrade reaktiviert sie nicht automatisch, aber der Admin sieht sie im UI als deaktiviert und kann manuell enable.
Pattern für neue Features
Bei jedem neuen kostenpflichtigen Feature müssen vier Dinge passieren (siehe feedback_new_feature_license.md):
- Flag in COMMUNITY_FALLBACK — meist
false, als Limit meist0. - API-Guard —
router.use(requireFeature('X'))oder pro-Route. - UI-Guard — Template-Wrap mit
{% if license.features.X %}und Lock-Fallback. - Service-Fallback — bei Downgrade darf kein Service crashen, sondern nur den Feature-Code-Pfad überspringen.
Key-Rotation und Removal
removeLicense() löscht das JWT, stoppt den Refresh-Timer, setzt Community-Mode, entfernt den Key aus der DB und startet Enforcement. _overrideForTest(features) patcht cachedFeatures direkt — nur für Test-Setup, nicht für Production.
Testen des Licensing-Systems
Der Service exportiert _overrideForTest und _getHardwareFingerprint für Testharnesse. In Test-Setup wird der echte Refresh-Timer nicht gestartet (keine startLicenseRefresh()-Call), damit Tests determiniert bleiben.
Siehe auch
- routing.md — HTTP/L4-Routes werden über
route_auth,rate_limitingetc. gated. - home-gateway.md —
gateway_peers,gateway_tcp_routing,gateway_wol. - rdp-routes.md —
remote_desktop,rdp_via_gateway. - internal-dns.md —
internal_dns. - ../API.md — HTTP-Antwortformate für 403-Feature-Errors.
Quelldateien
src/services/license.js— Kern-Service,COMMUNITY_FALLBACK,validateLicense,enforceLimitsInternal.src/middleware/license.js—injectLicense,requireFeature,requireLimit,requireFeatureField.src/services/settings.js— DB-gestützte Key-Speicherung für UI-Aktivierung.config/default.js—license.server,license.tokenPath,license.key,license.signingKey.