Skip to content

luxfi/license

Repository files navigation

luxfi/license

Runtime, offline-verifiable license tokens for gating commercial features in Lux binaries.

The API is open source (BSD-3-Clause). The tokens it verifies are signed by Lux Industries' private Ed25519 key and embed the entitlements (customer, expiry, scopes, features) for a particular deployment. Customers who hold a valid token can run commercial-only Lux features; everyone else gets the same code, but the gated features refuse to start.

Modelled after HashiCorp Vault Enterprise's licensing package: detect a token from a well-known set of locations, verify its signature against an embedded public key, check expiry, and expose a single HasFeature(scope) predicate that the rest of the binary calls.

import "github.com/luxfi/license"

func main() {
    license.HasFeatureOrDie("gpu")  // aborts with a clear stderr message if the
                                     // user has no token, an expired token, or
                                     // a token without the "gpu" scope.
    runGPUPipeline()
}

Why this exists

Some Lux components — CUDA / Metal-accelerated EVM, the order-book DEX engine, FHE / homomorphic primitives, FPGA SHA-3 — are commercial features. The OSS build is fully functional for the non-commercial paths; the commercial paths check for a valid license at startup and refuse to run without one.

This is the "honor-system + tamper detection" middle ground:

  • Honor-system: no phone-home, no DRM, no kernel modules. A motivated attacker with the source can patch out the check.
  • Tamper detection: tokens are signed; expired or tampered tokens fail loudly. Removing the check requires modifying our open source code, which is a license violation customers don't want to commit to.

This mirrors HashiCorp's Vault Enterprise model, which has been adequate deterrence in practice.


Token shape

Tokens are framed binary (not JWT, not CBOR — stdlib only):

LUXL                         magic [4]
1                            version uint8
claims_len                   uint32 big-endian
claims_bytes [claims_len]    deterministic encoding of Claims
signature [64]               Ed25519(SHA-512(claims_bytes))

The Claims payload is itself a deterministic, length-prefixed binary layout (see claims.go). Encoding sorts scopes and feature keys ascending so that a given Claims struct always serialises identically and round-trips byte-for- byte through Encode / DecodeClaims.

Tokens are exchanged with customers as standard base64 (so they fit in env vars and .env files).

Claims

Field Type Meaning
CustomerID string Human-readable customer identifier ("Acme Inc")
LicenseID string Unique ID for this token; used for revocation logs
IssuedAt time.Time When the token was signed
Expiry time.Time (or zero) After this time, VerifyToken returns ErrExpired
Scope []string Authorized feature flags (see scopes below)
Features map[string]string Free-form attributes (tier, max_nodes, etc.)
Notes string Free-form note from the issuer

Scopes (initial set; extensible)

Scope Gates
gpu CUDA / Metal acceleration in luxfi/evm, luxfi/cevm
cuda CUDA-specific kernels (subset of gpu)
fhe-mlx OpenFHE Metal NTT acceleration
dex Commercial order-book engine in luxfi/dex
fpga FPGA SHA-3 / signature kernels in luxfi/fpga

Adding a new scope is a string change. There is no enum on the Lux side.


Go API

Runtime (every binary)

// Discover and verify a license from env / disk. Cached for the lifetime
// of the process.
claims, err := license.LoadFromEnv()

// Non-fatal check: false on missing/invalid license or missing scope.
ok := license.HasFeature("gpu")

// Fatal check: prints a clear error to stderr and os.Exit(1)s if the
// license is missing or does not entitle the named scope.
license.HasFeatureOrDie("gpu")

Issuance (Lux operators only)

priv := loadIssuerPrivateKey()  // from KMS / Vault / hardware token
tok, err := license.Sign(&license.Claims{
    CustomerID: "Acme Inc",
    LicenseID:  "lic_abc123",
    IssuedAt:   time.Now().UTC(),
    Expiry:     time.Now().Add(365 * 24 * time.Hour),
    Scope:      []string{"gpu", "dex"},
    Features:   map[string]string{"tier": "enterprise", "max_nodes": "10"},
}, priv)
fmt.Println(license.EncodeBase64(tok))

Errors

Error Meaning
license.ErrNoLicense No token in env / file / default path
license.ErrBadMagic Input is not a Lux license token
license.ErrBadVersion Unknown token version (future tokens, old binary)
license.ErrTruncated Token bytes incomplete
license.ErrBadSig Signature does not match IssuerPublicKey
license.ErrExpired Token signature is valid but Expiry has passed

C / C++ ABI

The capi/ subpackage exposes a stable C ABI:

#include "lux_license.h"

LuxLicenseClaims* c = lux_license_load_from_env();
if (c == NULL) {
    fprintf(stderr, "%s\n", lux_license_last_error());
    return 1;
}
if (lux_license_has_feature(c, "gpu") != 1) {
    fprintf(stderr, "no gpu entitlement\n");
    return 1;
}
fprintf(stdout, "licensed to %s\n", lux_license_customer_id(c));
lux_license_free(c);

C++17+ RAII wrapper:

#include "lux_license.hpp"

try {
    auto lic = lux::License::load_from_env();
    if (!lic.has_feature("gpu")) {
        throw std::runtime_error("gpu not licensed");
    }
} catch (const lux::license_error& e) {
    std::cerr << "license: " << e.what() << "\n";
    return 1;
}

Or the one-liner mirroring Go's HasFeatureOrDie:

lux::License::require_feature_or_die("gpu");

Building the C library

cd capi
go build -buildmode=c-archive -o liblux_license.a .
# or
go build -buildmode=c-shared  -o liblux_license.so .

The build emits liblux_license.a (or .so) plus a generated liblux_license.h (a superset of the stable header in include/; consumers should include include/lux_license.h for forward compatibility).


CLI

cmd/lux-license is a thin issuer / inspector:

# Generate an Ed25519 issuer keypair (paste the pub key into pubkey.go).
lux-license keygen --out ./issuer.key

# Issue a token signed by the new key.
lux-license issue \
    --customer "Acme Inc" \
    --expiry 2027-12-31 \
    --scope gpu,dex,fpga \
    --features tier=enterprise,max_nodes=10 \
    --notes "Primary commercial license" \
    --private-key ./issuer.key \
    > acme-license.txt

# Inspect (no signature check) — useful for support tickets.
lux-license inspect acme-license.txt

# Verify against the embedded public key.
lux-license verify acme-license.txt

--dev is accepted in place of --private-key to sign with the embedded development key (testing only; tokens will refuse to verify on any build that has swapped in a production key).


License discovery order

LoadFromEnv consults these locations in order, returning the first one that contains a valid token:

  1. $LUX_LICENSE — base64-encoded token, in-band.
  2. $LUX_LICENSE_FILE — path to a binary or base64 token file.
  3. $HOME/.lux/license.jwt — default path for installer-managed tokens.
  4. $HOME/.lux/license — alternate default.

Why the LUX_ prefix on these specific env vars

The standing convention in our codebases is to drop the LUX_ prefix on env vars (so we don't double-namespace inside lux/* binaries). These two are an explicit exception: LICENSE and LICENSE_FILE are catastrophically overloaded across the Unix ecosystem (every Java app, Adobe / IBM / Oracle installer, etc.), and customer ops staff need a name they can grep for in their own shell rc files without false positives. The LUX_ prefix exists purely to occupy a clean namespace in the customer's environment.


Key management

The Lux production signing key is never stored in this repository. Suggested custodianship:

  1. Generate on an air-gapped machine, or inside HashiCorp Vault's transit engine, or inside the Lux KMS (~/work/lux/kms).
  2. Distribute the public key to all build pipelines that produce Lux binaries (paste into pubkey.go:IssuerPublicKey).
  3. Treat the private key like a code-signing key: split-knowledge / dual control, hardware token, audit log of every issuance.

The current IssuerPublicKey is a development key derived deterministically from a fixed seed. The library emits a one-time warning to stderr the first time VerifyToken is called on any build that hasn't replaced it. CI should fail if license.IsUsingDevKey() returns true in a release build.


Wire-up plan (downstream)

This repository ships only the library + CLI. Each commercial-gated binary adds its own one-line opt-in:

luxfi/cevm (CUDA / Metal EVM)

// in cmd/cevm/main.go, before any GPU work
license.HasFeatureOrDie("gpu")

luxfi/dex (commercial order-book)

// in cmd/dexd/main.go
license.HasFeatureOrDie("dex")

luxfi/fpga

// in cmd/fpgad/main.go
license.HasFeatureOrDie("fpga")

luxcpp/cevm (C++ kernels)

// in src/main.cpp
lux::License::require_feature_or_die("gpu");

Those one-liners are the only edits to the downstream codebases — no imports of issuer logic, no key material, no business of license discovery duplicated elsewhere.

The wire-up is intentionally not in this PR; it's tracked in separate follow-up changes (one per downstream repository) so each commercial gate ships with its own CHANGELOG entry and customer-visible message.


Testing

go test ./...                 # 22 Go tests
CGO_ENABLED=1 go test ./...   # 22 Go + 1 cgo round-trip

The cgo round-trip test builds liblux_license.a, links a small C program against it (capi/testdata/test_capi.c), issues a dev-signed token, and verifies the C side accepts the valid token and rejects a tampered one.


Layout

.
├── LICENSE                              BSD-3
├── README.md                            this file
├── go.mod                               module github.com/luxfi/license
├── doc.go                               package doc
├── claims.go                            Claims struct + deterministic encoding
├── token.go                             Sign / VerifyToken / framing
├── pubkey.go                            embedded Ed25519 public key + dev key
├── runtime.go                           LoadFromEnv / HasFeature / HasFeatureOrDie
├── license_test.go                      22 Go tests
├── cmd/lux-license/main.go              issuer + inspector + verifier CLI
└── capi/
    ├── lux_license.go                   cgo bridge (package main)
    ├── include/lux_license.h            stable C ABI header
    ├── include/lux_license.hpp          C++17 RAII wrapper
    ├── testdata/test_capi.c             round-trip test driver
    └── internal/captest/captest_test.go cgo round-trip Go test

License

BSD 3-Clause. See LICENSE.

The library is BSD. The tokens it verifies are issued by Lux Industries Inc. under a separate commercial license; possessing this library does not grant any right to commercial Lux features.

About

Runtime license token verification API for Lux commercial features (BSD-3)

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors