minmaxkey

Integration recipes

Integration recipes

Everything here lives in the repo under integrations/ — copy the files for your stack, paste ~10 lines, and licensing works: offline-first startup, a persisted key, and a ready-made "enter your key" screen. The flow is the same everywhere:

activate(key) once  →  signed token cached  →  every launch checks the cache
                       first — zero network until the token goes stale

Electron

Copy integrations/electron/license.ts + preload.js (and sdk/typescript/mmk.ts) into your app:

// main process
export const licensing = setupLicensing({
  server: "https://license.yourdomain.com",
  productId: 1,
  apiKey: "<product api key>",
  publicKeyPem: readFileSync("product.pub.pem", "utf8"),
  dataDir: app.getPath("userData"),
});
registerLicenseIpc(ipcMain, licensing);

app.whenReady().then(async () => {
  if (await licensing.isLicensed()) createMainWindow();
  else openActivationWindow();   // loads the ready-made dialog
});

The key-entry screen is integrations/activation-dialog/index.html — one self-contained file that talks to preload.js automatically. Also shipped: startLicenseRecheck(licensing, { onLicenseLost }) — periodic re-check that fires only on the licensed → lost transition — and licenseMenuItems(...) for a ready-made License menu. Full recipe.

VS Code extension

Copy integrations/vscode/licensing.ts; the glue is ~20 lines: context.secrets as the key store, an InputBox command, a status-bar item.

const licensing = createExtensionLicensing({
  server: "...", productId: 1, apiKey: "...",
  publicKeyPem: readFileSync(join(context.extensionPath, "product.pub.pem"), "utf8"),
  keyStore: {
    get: () => context.secrets.get("mmk.key"),
    set: (k) => context.secrets.store("mmk.key", k),
    clear: () => context.secrets.delete("mmk.key"),
  },
});
if (!(await licensing.isLicensed())) vscode.commands.executeCommand("myext.activateLicense");

Full recipe.

Tauri

Now ships real code, both paths:

  • Webview (recommended): integrations/tauri/licensing.ts — the TS SDK wrapper with the key persisted in webview storage; createTauriLicensing(...) then await licensing.isLicensed(). Set MMK_ALLOWED_ORIGINS on your server to your webview's origin — forget it and every request is a CORS error. Tested in CI like the Electron/VS Code modules.
  • Rust sidecar: integrations/tauri/src-tauri/license.rs — complete reference around the Go binary, reachable from Tauri commands.

Same keys and offline tokens either way — switch later without re-activating customers. Full recipe.

Python desktop (PySide6 / Tkinter / PyInstaller)

We ship a Python SDK, so this one's thin: copy integrations/python-desktop/licensing.py + sdk/python/mmk.py:

licensing = create_desktop_licensing(
    server="https://license.yourdomain.com", product_id=1,
    api_key="<product api key>",
    public_key_pem=open(resource_path("product.pub.pem")).read(),
    app_name="PixelForge",
)
if not licensing.is_licensed():       # offline-first, never hangs on your server
    show_activation_dialog()

Tested in CI against a real server (activate → restart → offline validation → deactivate). Full recipe.

Unity / Godot

Both use the Go mmk binary as a sidecar — one static file per platform, no runtime, no crypto packages:

cd sdk/go && GOOS=windows GOARCH=amd64 go build -o mmk.exe ./cmd/mmk
  • Unity: drop the binary + product.pub.pem into StreamingAssets/mmk/, add MmkLicense.cs, gate with MmkLicense.Check() / MmkLicense.Activate(key). Recipe
  • Godot: register license.gd as an autoload singleton, gate with License.check() / License.activate(key) (includes a key-format pre-check so typos fail fast). Recipe

The key-entry screen (any stack)

Don't build it: activation-dialog/ ships a zero-dependency React component and a standalone HTML page with paste auto-formatting (9tmc5ts90mkq… → 9TMC-5TS9-…), typo validation, and busy/error/success states. The HTML version auto-detects your bridge (window.mmkActivate, Tauri events, or a ?callback= URL). Both take your branding: app name, title, accent color — the HTML version also follows (or forces) dark mode.

What to show when it goes wrong

Seat limit hit, refund-revoked, expired, offline — the copy for all five moments is written for you: License UX patterns.

Verified, not just documented

The Electron, VS Code, and Tauri modules run in CI against a mock server on every push (integrations/integrations.test.ts), and the Python desktop module runs in pytest against a real server: activate → persisted key → app restart → offline validation → deactivate. If a recipe breaks, CI breaks.