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");
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(...)thenawait licensing.isLicensed(). SetMMK_ALLOWED_ORIGINSon 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.pemintoStreamingAssets/mmk/, addMmkLicense.cs, gate withMmkLicense.Check()/MmkLicense.Activate(key). Recipe - Godot: register
license.gdas an autoload singleton, gate withLicense.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.