Files
vantage/shared/license/license.go
T

77 lines
2.5 KiB
Go

// Package license defines the Vantage licence payload and its offline
// verification.
//
// A licence is a signed blob (ECDSA P-384 with SHA-256). The server checks a
// signature, an expiry, a deployment mode and an instance ID, and asks nobody's
// permission. That buys air-gapped self-hosting and means no instance depends
// on the licensing service being reachable.
//
// It costs revocation: once issued, a licence is valid until it expires
// whatever the billing system later says. Self Hosted is sold annually only so
// that window is bounded.
package license
import "time"
const (
TierFree = "free"
TierProfessional = "professional"
TierSelfHosted = "self_hosted"
DeploymentCloud = "cloud"
DeploymentSelfHosted = "self_hosted"
FeatureConsole = "console" // browser SSH/RDP/VNC
FeatureOIDC = "oidc" // per-instance single sign-on
)
// Unlimited is the sentinel for "no cap" in every Limits field.
const Unlimited = -1
// Limits are the countable caps a licence grants.
type Limits struct {
MaxServers int `json:"max_servers"`
MaxSecretGroups int `json:"max_secret_groups"`
MaxChannels int `json:"max_channels"`
}
// License is the signed payload.
//
// InstanceID is always populated: the self-hosted purchase flow links the
// instance UUID before the licence is signed, so there is no unbound licence
// and no claim protocol.
type License struct {
ID string `json:"id"` // uuid, for support and audit
InstanceID string `json:"instance_id"` // the instance this licence is bound to
AccountID string `json:"account_id"` // admin-side customer, informational
InstanceName string `json:"instance_name"` // display only
Tier string `json:"tier"`
Deployment string `json:"deployment"`
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at"`
Limits Limits `json:"limits"`
Features []string `json:"features"`
}
// HasFeature reports whether the licence grants a named feature.
//
// Callers must use this rather than switching on Tier. Adding a tier, or
// changing what a tier includes, must never require a server release.
func (l License) HasFeature(name string) bool {
for _, f := range l.Features {
if f == name {
return true
}
}
return false
}
// WithinLimit reports whether one more of something is allowed.
// A max of Unlimited always allows.
func WithinLimit(current, max int) bool {
if max == Unlimited {
return true
}
return current < max
}