sqlinsight-pro/src/SQLInsight.Licensing/License.cs
Claude 1ba274d699
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
Fase 3: offline-verifiable signed licenses and license tool
- SQLInsight.Licensing: ECDSA P-256 signed documents (SQLI1.payload.sig),
  licenses with plan, expiry, instance count and key id for rotation;
  30-day grace; invalid or missing licenses are states, never errors,
  and never stop monitoring (a license only adds hub sync and support)
- Trusted public keys are embedded; the source ships with none, so a
  published key can never mint licenses. The vendor adds its own.
- tools/SQLInsight.LicenseTool: keygen, issue, verify
- *.private.pem ignored by git

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 08:30:28 +00:00

52 lines
1.8 KiB
C#

namespace SQLInsight.Licensing;
public enum LicensePlan
{
Community,
Professional,
Enterprise,
}
/// <summary>
/// What a license grants. SQLInsight Pro monitors everything without a license; a license adds findings sync to
/// SQLInsight Hub and records the support agreement.
/// </summary>
public sealed record License
{
public required string LicenseId { get; init; }
public required string Customer { get; init; }
public required LicensePlan Plan { get; init; }
public required DateTimeOffset IssuedAt { get; init; }
public required DateTimeOffset ExpiresAt { get; init; }
/// <summary>How many instances may sync findings to the hub. 0 means unlimited.</summary>
public int MaxInstances { get; init; }
/// <summary>Id of the key that signed the license (for key rotation).</summary>
public required string KeyId { get; init; }
public bool IncludesFindingsSync => Plan is LicensePlan.Professional or LicensePlan.Enterprise;
public bool IncludesPrioritySupport => Plan is LicensePlan.Professional or LicensePlan.Enterprise;
public bool IncludesPhoneSupport => Plan is LicensePlan.Enterprise;
}
public enum LicenseState
{
/// <summary>No license configured: Community.</summary>
None,
Valid,
/// <summary>Expired, but within the grace period: everything still works; renew soon.</summary>
ExpiredInGrace,
Expired,
/// <summary>The signature does not match, the key is unknown, or the text is damaged.</summary>
Invalid,
}
public sealed record LicenseStatus(LicenseState State, License? License, string Message)
{
/// <summary>Findings may be synced while the license is valid or in grace.</summary>
public bool AllowsFindingsSync => State is LicenseState.Valid or LicenseState.ExpiredInGrace && License!.IncludesFindingsSync;
}