Documentation
¶
Overview ¶
Package licverify is the product-side license verification library. It has no dependency on the license server; products embed the issuer's public keys and verify licenses fully offline.
Index ¶
- Constants
- Variables
- func ActivationCode(licID, fp string) string
- func Eval(text string, keys map[string]ed25519.PublicKey) (State, *Payload)
- func EvalAt(text string, keys map[string]ed25519.PublicKey, now time.Time) (State, *Payload)
- func Fingerprint() (string, error)
- func FingerprintFrom(identity string) string
- func ParsePublicKey(b64 string) (ed25519.PublicKey, error)
- func VerifyBound(p *Payload, localFP string) error
- type Customer
- type Guard
- type GuardConfig
- type Payload
- type Snapshot
- type State
Constants ¶
const EnvInstanceID = "OPENBKN_INSTANCE_ID"
EnvInstanceID overrides all hardware sources when set.
const GracePeriod = 30 * 24 * time.Hour
GracePeriod is how long after expiry the product keeps features enabled (and the server still accepts renewal). Shared constant so offline product behavior and server renewal policy agree.
Variables ¶
var ( ErrNoFingerprint = errors.New("licverify: no stable instance identity found; set " + EnvInstanceID) ErrFingerprintMismatch = errors.New("licverify: license is bound to a different instance") )
var ( ErrMalformed = errors.New("licverify: malformed license") ErrUnknownKey = errors.New("licverify: unknown signing key") ErrBadSig = errors.New("licverify: signature verification failed") ErrExpired = errors.New("licverify: license expired") ErrNotYet = errors.New("licverify: license not yet valid") )
Functions ¶
func ActivationCode ¶
ActivationCode builds the offline activation request code the customer pastes into the license portal (which returns the signed, bound receipt license). licID may be empty; the portal then binds the license the code is submitted against.
func Eval ¶
Eval judges a license file for gating decisions: valid / grace / fallback-to-community / invalid. Unlike Verify it never returns an error for expiry — expiry is a state, not a failure.
func Fingerprint ¶
Fingerprint computes this machine's instance fingerprint (fp_ + 16 hex). Deterministic per machine; different across machines and products with a different salt version.
func FingerprintFrom ¶
FingerprintFrom derives a fingerprint from a caller-supplied stable identity (e.g. a cluster UID a product resolved itself).
func ParsePublicKey ¶
ParsePublicKey decodes a base64 (std, raw or padded) Ed25519 public key, as served by the issuer's /api/keys endpoint.
func VerifyBound ¶
VerifyBound checks an activated license against the local fingerprint. Licenses not yet bound (empty hw_fingerprint) pass — binding happens at activation. Returns ErrFingerprintMismatch for a copied license.
Types ¶
type Guard ¶
type Guard struct {
// contains filtered or unexported fields
}
func NewGuard ¶
func NewGuard(cfg GuardConfig) (*Guard, error)
NewGuard validates the config, fills defaults, and performs the first evaluation synchronously so State() is meaningful immediately.
func (*Guard) Refresh ¶
Refresh re-evaluates the license locally WITHOUT any network renewal, and returns the fresh snapshot. Safe to call from the constructor and right after importing a new .lic — it never blocks on the license server.
func (*Guard) RenewNow ¶
RenewNow forces one renewing evaluation immediately (the same work a Run tick does), blocking on the network if a renewal is due. Products can call it to renew on demand; Run calls it on its schedule. Unlike Refresh, this may block — do not call it from a latency-sensitive path.
type GuardConfig ¶
type GuardConfig struct {
// Keys are the embedded verification public keys (kid → key). Required.
Keys map[string]ed25519.PublicKey
// Load returns the current license text (from file, DB, …). Required.
Load func() (string, error)
// Store persists a replacement license after a successful renewal.
// Required when RenewURL is set.
Store func(text string) error
// RenewURL is the license server's renew endpoint
// (…/api/licenses/renew). Empty disables auto-renew (pure offline
// deployments carry a full-contract certificate instead).
RenewURL string
// InstanceFP defaults to Fingerprint() of this machine.
InstanceFP string
// Interval between re-evaluations. Default 1h; ±10% jitter is applied.
Interval time.Duration
// OnChange fires on every state transition (not on steady state).
OnChange func(old, cur Snapshot)
// Logf receives operational messages (renew attempts/failures). Optional.
Logf func(format string, args ...any)
// HTTPClient defaults to a 30s-timeout client.
HTTPClient *http.Client
}
Guard is the in-process license watchdog for products: it re-evaluates the license periodically (Eval + VerifyBound), auto-renews online deployments ahead of expiry, and reports state transitions. It is deliberately NOT a separate OS daemon — enforcement must live in the product process anyway, so a killable side process would add operational surface without any security gain (client-side anti-tamper is a non-goal).
Usage:
guard, _ := licverify.NewGuard(licverify.GuardConfig{
Keys: keys,
Load: func() (string, error) { return os.ReadFileString(licPath) },
Store: func(t string) error { return os.WriteFile(licPath, []byte(t), 0o600) },
RenewURL: "https://license.example.com/api/licenses/renew",
OnChange: func(old, new licverify.Snapshot) { gate.Apply(new) },
})
go guard.Run(ctx)
...
if guard.State().State == licverify.StateValid { ... }
type Payload ¶
type Payload struct {
LicID string `json:"lic_id"`
Kid string `json:"kid"`
Edition string `json:"edition"`
Customer Customer `json:"customer"`
IssuedAt int64 `json:"issued_at"`
// ExpiresAt is the technical validity of this single license (the only
// time the product enforces). 0 means never expires (community).
ExpiresAt int64 `json:"expires_at"`
// ContractExpiresAt is the contract end date, shown as "licensed until".
// Renewal never extends past it. 0 means perpetual.
ContractExpiresAt int64 `json:"contract_expires_at"`
Features []string `json:"features"`
Limits map[string]int64 `json:"limits"`
// HWFingerprint is the instance fingerprint bound at activation; empty
// until the license has been activated and reissued.
HWFingerprint string `json:"hw_fingerprint,omitempty"`
}
Payload is the signed content of a license.
func Parse ¶
Parse verifies the signature only (no expiry check) and returns the payload. The signature is verified over the exact transmitted payload bytes, so no JSON canonicalization is involved.
func Verify ¶
Verify checks the license text against the given public keys and the current time. Returns the payload on success.
func VerifyAt ¶
VerifyAt is Verify with an explicit clock, for testing and for callers that use a trusted time source.
func (*Payload) HasFeature ¶
HasFeature reports whether the license enables the named feature.
type Snapshot ¶
type Snapshot struct {
State State
Payload *Payload // nil when State is invalid
// Err explains an invalid/unusable license (gate features off).
Err error
// RenewErr reports that a background auto-renew failed while the license
// itself is still fine — do NOT gate features off on this; surface it as a
// "renewal pending" warning. Kept separate from Err so `if snap.Err != nil`
// does not disable a perfectly valid license over a transient network blip.
RenewErr error
}
Snapshot is the point-in-time judgement the product gates on.
type State ¶
type State string
State is the product-side judgement of a license file.
const ( // StateValid: signature good, within validity — full feature set. StateValid State = "valid" // StateGrace: expired less than GracePeriod ago — features stay enabled, // product should prompt for renewal. StateGrace State = "grace" // StateFallback: a commercial license expired beyond grace but its // signature still verifies. That is proof the instance was once formally // licensed: the product automatically falls back to the community // feature set, keeping data readable and exportable. StateFallback State = "fallback_community" // StateInvalid: malformed, bad signature, unknown key, or not yet valid. StateInvalid State = "invalid" )