feat: hardware binding modes, operator management, packaged SDK
- Hardware binding: License.HwBinding (none | fixed | firstActivation).
firstActivation leaves the license unbound until the first activation,
which claims the machine; afterwards it behaves like fixed. Legacy rows
backfilled to fixed when a fingerprint was already set.
- Rebind: POST /panel/licenses/{id}/rebind moves a license to new
hardware (or releases the binding), deactivating existing activations
and re-signing a new version; 409 on revoked/expired. Operator or
company admin. Audited, emits license.rebound.
- Operator management: list, invite, promote and demote operators
(operator-only), guarding self-demotion and the last operator — until
now a second operator could only be seeded directly into the database.
- SDK packaging: CertifiEd.Client is now a proper NuGet package (id,
version, MIT license, README, XML docs, symbols) and gained the
missing public XML documentation.
This commit is contained in:
@@ -35,6 +35,9 @@ public sealed class CertifiEdLicenseClient : IAsyncDisposable
|
||||
private DateTimeOffset _lastValidatedAt;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>Creates a client for a single local license file.</summary>
|
||||
/// <param name="options">Server URL, license path and the Ed25519 public key.</param>
|
||||
/// <param name="http">Optional shared <see cref="HttpClient"/>; when omitted the client owns and disposes its own.</param>
|
||||
public CertifiEdLicenseClient(CertifiEdClientOptions options, HttpClient? http = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
@@ -189,6 +192,10 @@ public sealed class CertifiEdLicenseClient : IAsyncDisposable
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the heartbeat loop and best-effort deactivates this machine, freeing its seat.
|
||||
/// Safe to call more than once.
|
||||
/// </summary>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -431,6 +438,7 @@ public sealed class CertifiEdLicenseClient : IAsyncDisposable
|
||||
|
||||
// ── Options ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Configuration for <see cref="CertifiEdLicenseClient"/>.</summary>
|
||||
public sealed class CertifiEdClientOptions
|
||||
{
|
||||
/// <summary>Base URL of the CertifiEd server, e.g. "https://licensing.example.com".</summary>
|
||||
@@ -448,12 +456,22 @@ public sealed class CertifiEdClientOptions
|
||||
|
||||
// ── Status ────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Local license state, derived from the token and the cached heartbeat marker.</summary>
|
||||
public enum CertifiEdLicenseStatus
|
||||
{
|
||||
/// <summary>No license loaded, or its signature did not verify.</summary>
|
||||
Invalid,
|
||||
|
||||
/// <summary>Licensed — the token is valid and within its offline window.</summary>
|
||||
Active,
|
||||
|
||||
/// <summary>Offline window elapsed; still usable for a short grace period.</summary>
|
||||
GracePeriod,
|
||||
|
||||
/// <summary>The license term or the grace period has run out.</summary>
|
||||
Expired,
|
||||
|
||||
/// <summary>The license was revoked by the issuer.</summary>
|
||||
Revoked,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user