license

package
v1.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package license — banner.go provides warning banner text for grace period states. Used by CLI header output and nself-admin UI.

Package license — cache.go implements the local license cache with Ed25519 signature verification and grace-period-aware freshness checks.

Cache location: ~/.cache/nself/license.json (0600) Fields: key_hash, tier, plugins_allowed, fetched_at, expires_at, signature

Package license — grace.go implements the license grace period state machine and degradation mode enforcement.

States: valid -> grace_soft -> grace_hard -> expired -> revoked Grace periods:

  • <24h offline: proceed silently (valid)
  • 24h-7d offline: WARNING banner (grace_soft)
  • >7d offline: read-only degraded mode (grace_hard)
  • Expired license: refuse to start (expired)
  • Revoked license: refuse to start (revoked)

Package license — multi-key support for product-based licensing.

Keys are collected from environment variables:

  • NSELF_PLUGIN_LICENSE_KEY (legacy single key)
  • NSELF_LICENSE_KEY_1 through NSELF_LICENSE_KEY_10

And from the stored key file at ~/.nself/license/key.

Package license provides CLI-level license key management operations: setting, reading, clearing, and displaying license keys.

Key storage: ~/.config/nself/license.json (chmod 0600) Legacy storage (v1): ~/.nself/license.json Env override: NSELF_PLUGIN_LICENSE_KEY

Package license — simulate.go provides offline simulation mode for testing grace period behavior without actually blocking network access.

Simulation writes an override to the license cache that makes the system believe it has been offline for N days. Guarded by LICENSE_ALLOW_SIMULATION env var (must be "true" explicitly; defaults to false in production).

Package license — validate.go implements the full license validation flow with ping.nself.org and grace state machine integration.

Flow: try remote validation -> update cache -> fall back to cached result with grace state evaluation on network failure.

Index

Constants

View Source
const DefaultCheckInterval = 6 * time.Hour

DefaultCheckInterval is the default time between license checks (6 hours).

View Source
const DefaultPingURL = "https://ping.nself.org"

DefaultPingURL is the production license validation endpoint.

View Source
const GraceHardThreshold = 7 * 24 * time.Hour

GraceHardThreshold is when hard degradation begins (7 days). Configurable via LICENSE_GRACE_DAYS env var.

View Source
const GraceSoftThreshold = 24 * time.Hour

GraceSoftThreshold is when the soft grace warning starts (24 hours).

View Source
const LicenseDirV1 = ".nself"

LicenseDirV1 is the v1 license directory name under $HOME.

View Source
const LicenseDirV2 = ".config/nself"

LicenseDirV2 is the v2 license directory name under $HOME.

View Source
const LicenseFile = "license.json"

LicenseFile is the license JSON filename used in both v1 and v2.

View Source
const SimulationGuardEnv = "LICENSE_ALLOW_SIMULATION"

SimulationGuardEnv is the env var that must be set to "true" to allow simulation mode. This prevents accidental use in production.

Variables

View Source
var ErrSimulationNotAllowed = fmt.Errorf(
	"simulation mode is disabled — set %s=true to enable (never in production)",
	SimulationGuardEnv,
)

ErrSimulationNotAllowed is returned when simulation is attempted without the guard env var being set.

Functions

func AddKey added in v1.0.4

func AddKey(key string) error

AddKey stores an additional license key. If no keys exist yet, it writes to the primary key file. If a key already exists, it stores as a numbered key file (key.2, key.3, etc.) in ~/.nself/license/.

func BannerAtExpiry added in v1.0.6

func BannerAtExpiry() string

BannerAtExpiry returns the hard-stop banner text when grace period is exhausted (day 14+).

func BannerAtWarning added in v1.0.6

func BannerAtWarning(result GraceCheckResult) string

BannerAtWarning returns the yellow-banner text shown when the license server has been unreachable for 7+ days (grace_soft state). Returns empty string if no banner is needed.

func CachePath added in v1.0.6

func CachePath() (string, error)

CachePath returns the full path to the license cache file. It respects LICENSE_CACHE_PATH if set.

func ClearKey

func ClearKey() error

ClearKey removes both the stored license key file and the validation cache file from ~/.nself/license/.

func ClearSimulation added in v1.0.6

func ClearSimulation() error

ClearSimulation restores the cache to current time (as if just validated). Also guarded by LICENSE_ALLOW_SIMULATION.

func CollectLicenseKeys added in v1.0.4

func CollectLicenseKeys() []string

CollectLicenseKeys reads all configured license keys from environment variables and the stored key file. It deduplicates and returns all non-empty keys. The order is: NSELF_PLUGIN_LICENSE_KEY, then NSELF_LICENSE_KEY_1 through _10, then the stored key file.

func DeleteCache added in v1.0.6

func DeleteCache() error

DeleteCache removes the license cache file.

func ExportCache added in v1.0.6

func ExportCache() ([]byte, error)

ExportCache reads the current cache and returns it as JSON bytes suitable for air-gap transfer.

func GetAllStoredKeys added in v1.0.4

func GetAllStoredKeys() []string

GetAllStoredKeys returns all keys stored in key files (not env vars).

func GetKey

func GetKey() (string, error)

GetKey returns the current license key. It checks the NSELF_PLUGIN_LICENSE_KEY environment variable first, then falls back to reading ~/.nself/license/key. Returns ("", nil) when no key is configured.

func HashKey added in v1.0.6

func HashKey(key string) string

HashKey returns the SHA-256 hex digest of a license key.

func ImportCache added in v1.0.6

func ImportCache(data []byte) error

ImportCache reads a previously exported cache file and writes it to the local cache location. It verifies the Ed25519 signature before accepting.

func MaskKey added in v1.0.4

func MaskKey(key string) string

MaskKey returns a masked version of the key showing the first 10 and last 4 characters. Delegates to the existing maskKey function.

func MigrateLicenseFromV1

func MigrateLicenseFromV1(home string) error

MigrateLicenseFromV1 copies ~/.nself/license.json to ~/.config/nself/license.json when upgrading from v1. The function is a no-op when:

  • v2 already exists (NEVER overwrite existing v2 license)
  • v1 does not exist (nothing to migrate)

On success the v1 file is preserved (non-destructive). File is written with mode 0600 matching the key file convention.

func PingURL added in v1.0.6

func PingURL() string

PingURL returns the configured ping API URL.

func RemoveKey added in v1.0.4

func RemoveKey(query string) (int, error)

RemoveKey removes a key by prefix match or product name. Returns the number of keys removed.

func SetKey

func SetKey(key string) error

SetKey validates the key format, creates the license directory if needed, and writes the key to ~/.nself/license/key with mode 0600.

func SetKeyReplaceAll added in v1.0.4

func SetKeyReplaceAll(key string) (int, error)

SetKeyReplaceAll replaces all stored keys with a single new key. Returns the number of keys that were replaced.

func ShowKey

func ShowKey() (masked string, tier string, err error)

ShowKey returns a masked version of the current key and its detected tier. The masked key shows the first 10 and last 4 characters with the middle replaced by asterisks. If no key is set, all return values are empty strings with a nil error.

func ValidateKeyFormat added in v1.0.4

func ValidateKeyFormat(key string) error

ValidateKeyFormat checks that a key has a recognized product prefix and meets the minimum length requirement. Returns errs.ErrInvalidLicenseKey on failure.

func WriteCache added in v1.0.6

func WriteCache(entry *CacheEntry) error

WriteCache writes the cache entry to disk with 0600 permissions.

Types

type CacheEntry added in v1.0.6

type CacheEntry struct {
	KeyHash        string   `json:"key_hash"`
	Tier           string   `json:"tier"`
	PluginsAllowed []string `json:"plugins_allowed"`
	FetchedAt      int64    `json:"fetched_at"`
	ExpiresAt      int64    `json:"expires_at"`
	Signature      string   `json:"signature"`
	SignatureKeyID int      `json:"signature_key_id"`
}

CacheEntry represents a cached license validation response with Ed25519 signature from the server.

func ReadCache added in v1.0.6

func ReadCache() (*CacheEntry, error)

ReadCache reads and parses the license cache file. Returns nil, nil if the cache file does not exist.

func (*CacheEntry) CacheAge added in v1.0.6

func (c *CacheEntry) CacheAge() time.Duration

CacheAge returns how long ago the cache was fetched.

func (*CacheEntry) VerifySignature added in v1.0.6

func (c *CacheEntry) VerifySignature() bool

VerifySignature verifies the cache entry's Ed25519 signature against the bundled public keys. It accepts the current key (keyID N) and the previous key (keyID N-1) to support rotation windows.

type GraceCheckResult added in v1.0.6

type GraceCheckResult struct {
	State        GraceState
	CacheAge     time.Duration
	ExpiresAt    time.Time
	Tier         string
	Message      string
	CanProceed   bool
	WriteAllowed bool
}

GraceCheckResult contains the outcome of a grace period check.

func DetermineGraceState added in v1.0.6

func DetermineGraceState(entry *CacheEntry) GraceCheckResult

DetermineGraceState evaluates the current grace state from a cache entry. If entry is nil, returns GraceExpired (no cache means no validation).

func SimulateOffline added in v1.0.6

func SimulateOffline(days int) (*GraceCheckResult, error)

SimulateOffline modifies the license cache to appear as if the system has been offline for the given number of days. This is used for testing grace period transitions without iptables manipulation.

The function: 1. Checks the LICENSE_ALLOW_SIMULATION guard 2. Reads the current cache 3. Backdates FetchedAt by the specified number of days 4. Writes the modified cache back

func (GraceCheckResult) IsWriteAllowed added in v1.0.6

func (r GraceCheckResult) IsWriteAllowed() bool

IsWriteAllowed checks if write operations on paid plugins are permitted given the current grace state.

func (GraceCheckResult) NeedsBanner added in v1.0.6

func (r GraceCheckResult) NeedsBanner() bool

NeedsBanner returns true if a warning banner should be displayed.

type GraceState added in v1.0.6

type GraceState string

GraceState represents the license grace period state.

const (
	// GraceValid means the license is validated and current.
	GraceValid GraceState = "valid"
	// GraceSoft means the cache is 24h-7d old; show warning banner.
	GraceSoft GraceState = "grace_soft"
	// GraceHard means the cache is >7d old; paid plugin writes are refused.
	GraceHard GraceState = "grace_hard"
	// GraceExpired means the license has expired.
	GraceExpired GraceState = "expired"
	// GraceRevoked means the license was explicitly revoked.
	GraceRevoked GraceState = "revoked"
)

type ProductPrefix added in v1.0.4

type ProductPrefix struct {
	Prefix      string
	Product     string
	DisplayName string
}

ProductPrefix maps key prefixes to product display names.

func DetectProduct added in v1.0.4

func DetectProduct(key string) *ProductPrefix

DetectProduct returns the product info for a key based on its prefix. Returns nil if the prefix is not recognized.

type PublicKeyEntry added in v1.0.6

type PublicKeyEntry struct {
	ID  int
	Key ed25519.PublicKey
}

PublicKeyEntry holds a versioned Ed25519 public key.

func GetPublicKeys added in v1.0.6

func GetPublicKeys() []PublicKeyEntry

GetPublicKeys returns the active public keys, respecting the LICENSE_PUBLIC_KEY_OVERRIDE environment variable for testing.

type ValidateResponse added in v1.0.6

type ValidateResponse struct {
	Valid     bool     `json:"valid"`
	Reason    string   `json:"reason,omitempty"`
	Tier      string   `json:"tier"`
	Plugins   []string `json:"plugins"`
	ExpiresAt string   `json:"expires_at,omitempty"`
	Signature string   `json:"signature,omitempty"`
	KeyID     int      `json:"key_id,omitempty"`
}

ValidateResponse is the JSON body returned by /license/validate on HTTP 200.

type ValidationResult added in v1.0.6

type ValidationResult struct {
	Valid        bool
	Tier         string
	Plugins      []string
	ExpiresAt    time.Time
	GraceState   GraceState
	Message      string
	FromCache    bool
	WriteAllowed bool
}

ValidationResult holds the outcome of a full license validation.

func RefreshCache added in v1.0.6

func RefreshCache(ctx context.Context, key string) (*ValidationResult, error)

RefreshCache forces a remote validation and updates the cache. Returns the validation result or an error if the remote call fails.

func ValidateFull added in v1.0.6

func ValidateFull(ctx context.Context, key string) (*ValidationResult, error)

ValidateFull performs the complete license validation flow: 1. Try remote validation against ping.nself.org 2. On success: update cache, return valid 3. On network failure: fall back to cache with grace state evaluation 4. On invalid response: mark cache as invalid, return error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL