Skip to content

purfect-labs/locket

Repository files navigation

locket

License validation + tier-based feature gating for Go apps.

name badge
CI CI
Coverage Coverage
Go Go
License License
Built By Built By

Locket provides a clean interface for validating license keys against multiple backends (Lemon Squeezy, Keygen, or a static/dev mode), then mapping the validated tier to feature flags defined in config.

  • 94.7% test coverage — 47 tests across all adapters
  • Zero external runtime dependencies — only yaml.v3 for config tags
  • Pluggable backends — swap Lemon Squeezy ↔ Keygen by changing one constructor option

Installation

go get github.com/purfect-labs/locket

Quick Start

package main

import (
	"fmt"
	"github.com/purfect-labs/locket"
)

func main() {
	v := locket.New(
		locket.WithStatic(),
		locket.WithTierConfig(locket.DefaultTierConfig()),
	)

	lic, err := v.Validate("dev-t2-key")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Valid: %v\n", lic.Valid)       // true
	fmt.Printf("Tier: %s\n", lic.Tier)         // "t2"
	fmt.Printf("Features: %v\n", lic.Features) // ["chat_premium", "song_unlimited", "export_csv", "offline_mode"]

	if locket.HasFeature(lic.Tier, "export_csv", locket.DefaultTierConfig()) {
		fmt.Println("Export CSV: ENABLED")
	}
}

Backend Adapters

Static (Dev/Test)

Hardcoded keys for development:

Key Tier
dev-t0-key t0
dev-t1-key t1
dev-t2-key t2
dev-t3-key t3
dev-expired-key valid=false
v := locket.New(locket.WithStatic())

Lemon Squeezy

Validates via GET /api/license/validate?license_key=XXXXX. Maps variant names to tier strings (configurable).

v := locket.New(
	locket.WithLemonsqueezy(
		locket.WithLemonsqueezyAPIKey(os.Getenv("LS_API_KEY")),
	),
	locket.WithTierConfig(cfg),
)

Options:

Option Default
WithLemonsqueezyAPIKey(key) ""
WithLemonsqueezyBaseURL(url) https://api.lemonsqueezy.com/v1
WithLemonsqueezyTimeout(d) 10s
WithLemonsqueezyVariantOpts(m) {Free→t0, Basic→t1, Pro→t2, Enterprise→t3}

Keygen

Validates via POST /licenses/actions/validate-key. Maps policy names to tier strings (configurable). Supports expiry parsing.

v := locket.New(
	locket.WithKeygen(
		locket.WithKeygenAPIKey(os.Getenv("KEYGEN_API_KEY")),
	),
	locket.WithTierConfig(cfg),
)

Options:

Option Default
WithKeygenAPIKey(key) ""
WithKeygenBaseURL(url) https://api.keygen.sh/v1
WithKeygenTimeout(d) 10s
WithKeygenPolicyMap(m) {free→t0, basic→t1, pro→t2, enterprise→t3}

Mock (Testing)

Configurable test double — return any License or error you want.

v := locket.New(
	locket.WithMock(&locket.MockAdapter{
		ValidateFn: func(key string) (*locket.License, error) {
			return &locket.License{Valid: true, Tier: "t1"}, nil
		},
	}),
)

API Reference

LicenseValidator Interface

type LicenseValidator interface {
	Validate(key string) (*License, error)
}

Implement this interface to add custom backends.

License Struct

type License struct {
	Valid        bool
	Tier         string
	Features     []string
	Expiry       time.Time
	MachineLimit int
	MachineCount int
}

Feature Gating

ok := locket.HasFeature(tier, featureName, config)

Tier Configuration

// Default 4-tier config (t0–t3)
cfg := locket.DefaultTierConfig()

// Custom config
cfg := &locket.TierConfig{
	Tiers: map[string]locket.Tier{
		"free":  {Label: "Free",  Features: []string{"chat"}},
		"pro":   {Label: "Pro",   Features: []string{"chat", "export", "offline"}},
	}
}

Errors

var ErrInvalidKey     = fmt.Errorf("locket: invalid license key")
var ErrExpired        = fmt.Errorf("locket: license has expired")
var ErrNetwork        = fmt.Errorf("locket: network error")
var ErrMaxActivations = fmt.Errorf("locket: maximum activations reached")

Examples

See _examples/ for runnable code:

Example What it shows
basic Simple key validation
backend HTTP middleware feature gating
frontend Tier-based UI rendering
custom-config Custom tier definitions
all-options Every permutation + error handling

Architecture

┌─────────────────────────────┐
│     App Code                │
│  v.Validate(key)            │
│  HasFeature(tier, feat, c)  │
└─────────┬───────────────────┘
          │
┌─────────▼───────────────────┐
│  locket.LicenseValidator    │  ← Interface
│  (plug any adapter)         │
└─────────┬───────────────────┘
          │
┌─────────▼───────────────────┐
│  Backend Adapter            │
│  static | lemonsqueezy      │
│  keygen  | mock | custom    │
└─────────────────────────────┘

Contributing

See CONTRIBUTING.md. Conventional commits required for auto-versioning.

License

MIT — see LICENSE.

About

go module for keygen/lemonsqueezy licensing/feature flagging and validations

Resources

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
license.go

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages