minmaxkey

Add it to your app

Add it to your app

Your app talks to MinMaxKey directly — not through a webhook. On the customer's machine it calls /activate once, gets a signed offline token back, and verifies that token locally so your app keeps working offline. The SDK is the piece you embed: machine fingerprint, activation, offline verification, auto-revalidating token cache.

Three flavors of the same thing — Python, TypeScript (Electron / Tauri / VS Code extensions), or the pure-Go single binary — pick the one that fits your stack, or shell out to the Go mmk binary from anything. Building a specific kind of app? The integration recipes have copy-paste modules + a ready-made activation screen for Electron, VS Code, Tauri, Unity, and Godot.

Python — sdk/python/mmk.py

One file, zero HTTP dependencies (stdlib urllib), cryptography for offline verification only.

Install

pip install cryptography        # the only dependency

Quickstart

import sys
import mmk

client = mmk.MMK(
    base_url="http://localhost:8080",
    product_id=1,
    api_key="<product api key>",               # from POST /v1/products
    public_key_pem=open("product.pub.pem").read(),  # from the same response
)

On purchase (or first run)

result = client.activate("9TMC-5TS9-0MKQ-VS8A-87Q0", name="My MacBook")
if result.get("status") != "active":
    sys.exit(f"activation failed: {result}")
# the offline token is cached automatically

On every launch

claims = client.ensure_valid()
if claims is None:
    sys.exit("license invalid — please check your key")

ensure_valid() is the whole story:

  1. cached token exists and its signature / expiry / fingerprint check out → instant approval, no network,
  2. otherwise it calls validate once; a fresh signed token is cached,
  3. revoked or expired → None → you deny.

Deactivation (optional — frees a seat)

client.deactivate()

TypeScript — sdk/typescript/mmk.ts

One file, zero dependencies — Node built-ins only (node:crypto for offline Ed25519 verification, global fetch for HTTP). This is the SDK for Electron, Tauri, VS Code extensions, and any Node runtime. Written in erasable TypeScript, so modern Node runs it directly and any bundler (esbuild/Vite/ webpack) ingests it with no config — no build step, no node_modules.

Quickstart

import { readFileSync } from "node:fs";
import { MMK } from "./sdk/mmk.ts";

const client = new MMK({
  baseUrl: "http://localhost:8080",
  productId: 1,
  apiKey: "<product api key>",                       // from POST /v1/products
  publicKeyPem: readFileSync("product.pub.pem", "utf8"),
});

// on purchase / first run — binds the key to this machine, caches the token
await client.activate("9TMC-5TS9-0MKQ-VS8A-87Q0", { name: "My MacBook" });

// on every launch — offline-first
const claims = await client.ensureValid();
if (!claims) {
  // not licensed — show your "enter key" screen
}

Same ensureValid() story as Python: a cached token that verifies (signature, expiry, fingerprint) is approved with no network; otherwise it revalidates once and refreshes the cache; revoked/expired → null → you deny. Server errors surface as MMKError carrying the server's detail.

Requires Node ≥ 18 (global fetch); Node ≥ 23.6 runs the .ts file directly.

Go — sdk/go (library + mmk binary)

Pure Go standard library — zero external modules, cross-compiles everywhere, and the CLI is a single static binary with no runtime. If your stack isn't Python (Tauri, Go apps, C++, Unity), this is your path.

Build

cd sdk/go
go build ./cmd/mmk              # mmk.exe on Windows, mmk on Linux/macOS
GOOS=linux GOARCH=amd64 go build ./cmd/mmk   # cross-compile for a server/CI

CLI quickstart

./mmk fingerprint
# 6805c498fd6654ede932a2d67949da69a96d44115ec8e7ba7cec94d5c8f4220b

./mmk activate --url http://localhost:8080 --product 1 \
  --api-key <product api key> --key 9TMC-5TS9-0MKQ-VS8A-87Q0 \
  --name "My MacBook" --platform macos
# {"status": "active", "license": {...}, "offline_token": "eyJ..."}

./mmk check --url http://localhost:8080 --product 1 \
  --api-key <product api key> --pubkey product.pub.pem
# VALID  license 9TMC-5TS9-0MKQ-VS8A-87Q0 (license type: lifetime)

./mmk verify --pubkey product.pub.pem --token "eyJ..." --fingerprint 6805c498...

check is the offline-first path: a cached token that verifies (signature, expiry, fingerprint) is accepted with zero network; otherwise it revalidates and refreshes the cache. Config can also come from env vars: MMK_URL, MMK_PRODUCT_ID, MMK_API_KEY, MMK_LICENSE_KEY.

As a library

import "minmaxkey.com/mmk"

c := mmk.NewClient("http://localhost:8080", 1, apiKey, publicKeyPEM, licenseKey)
claims, err := c.EnsureValid()          // offline-first, caches token
if err != nil {
    log.Fatal("not licensed")
}
fmt.Println("licensed for", claims.Sub)

mmk.VerifyToken(token, publicKeyPEM, fingerprint) does the offline check in any context — embed it in your Go app, or call the binary from CI.

Machine fingerprinting

All three SDKs hash the most stable hardware identifier they can find:

  • Windows — registry MachineGuid
  • Linux — /etc/machine-id (or DMI product UUID)
  • macOS — IOPlatformUUID (ioreg)
  • fallback — hostname + machine type

The machine architecture is normalized onto one vocabulary in every SDK, so the fingerprint is identical across Python, TypeScript, and Go on the same machine — an activation made by one SDK validates in the others.

It's deterministic on a given install, survives reboots, and changes on OS reinstall — that's by design, and it's why the dashboard has a reset seats button. It's a friendly binding, not a security boundary.

Offline verification

from mmk import verify_token
claims = verify_token(token, client.public_key_pem, expected_fingerprint=client.fingerprint)
# raises ValueError on bad signature / expiry / wrong machine
import { verifyToken } from "./sdk/mmk.ts";
const claims = verifyToken(token, publicKeyPem, fingerprint);
// throws MMKError on bad signature / expiry / wrong machine
claims, err := mmk.VerifyToken(token, publicKeyPEM, fingerprint)

The API key question

Your app ships with the product api_key — that's fine. It only gates operations the license key + fingerprint already authorize, and it's how the server distinguishes your product. Treat the license key itself as the credential.

Roadmap notes

  • A C# port (native .NET / Unity) will land as demand warrants it — the wire format is language-agnostic.