Pulse/pkg/licensing/commercial_migration_load.go
rcourtman f9b10316b8 fix(licensing): surface unreadable persisted v5 licenses and retry failed startup exchanges
Two silent-downgrade paths in the v5→v6 migration are now visible and
self-healing:

License load/decrypt failure (was: one log line, no UI state): when
license.enc exists but cannot be read, getTenantComponents now persists
a terminal commercial_migration state with reason
persisted_license_unreadable, so the licence panel and global banner
tell the customer to re-enter their v5 key instead of leaving them to
discover missing Pro features.

Failed startup exchange (was: once per process, Community until manual
restart): exchange failures classified as pending — license-server
blips, DNS failures, rate limits — now schedule a background retry loop
with backoff (30s → 30m cap) that re-attempts the exchange until it
succeeds, hits a terminal classification, or the org's service activates
through another path. The loop stops cleanly on manual activation and
StopAllBackgroundLoops.

Registers the new files in the subsystem registry (cloud-paid
commercial-migration verification policy + shared ownership) and pins
both behaviors in the cloud-paid and api-contracts contracts.
2026-06-11 08:44:35 +01:00

29 lines
1.3 KiB
Go

package licensing
// Kept in a separate file from ClassifyLegacyExchangeError so the
// load-failure contract can evolve with persistence (not exchange) concerns.
const (
// CommercialMigrationReasonPersistedUnreadable marks a persisted v5
// license file that exists but cannot be read or decrypted on this
// system (e.g. license.enc sealed under key material this install can
// no longer derive).
CommercialMigrationReasonPersistedUnreadable CommercialMigrationReason = "persisted_license_unreadable"
)
// ClassifyPersistedLicenseLoadError converts a failure to read or decrypt the
// persisted v5 license into the commercial-migration contract. Without this,
// an undecryptable license.enc degraded a paid install to Community with
// nothing but a log line. Re-running the exchange cannot fix an unreadable
// file, so the state is terminal and the remedy is re-entering the v5 key.
func ClassifyPersistedLicenseLoadError(err error) *CommercialMigrationStatus {
if err == nil {
return nil
}
return &CommercialMigrationStatus{
Source: CommercialMigrationSourceV5License,
State: CommercialMigrationStateFailed,
Reason: CommercialMigrationReasonPersistedUnreadable,
RecommendedAction: CommercialMigrationActionEnterSupportedV5,
}
}