Archergate wraps your Python addon with a REST validation call on startup. Keys are machine-bound. A buyer on one workstation cannot hand the key to a colleague on another.
Most Blender addon developers either ship without protection or rely on obfuscation. Python bytecode (.pyc) is trivially decompiled. String obfuscation survives maybe an hour of determined analysis. Network checks that validate "is this email registered" can be spoofed with a local DNS redirect.
Archergate's validation server checks the license key against a SHA-256 hardware fingerprint computed from the user's CPU and OS installation ID. The fingerprint cannot be spoofed by changing DNS or editing a hosts file. Each seat activates once per machine. The server enforces the seat count.
Add this block to your addon's __init__.py register() function. The Archergate Python client ships as a single-file module you include with your addon.
# bl_info = { ... } import bpy from . import archergate # bundled single-file client _VALIDATED = False def register(): global _VALIDATED key = bpy.context.preferences.addons[__name__].preferences.license_key result = archergate.validate( api_key="your-api-key", app_id="com.you.addon_name", license_key=key ) if not result.valid: raise Exception(f"License invalid: {result.error}") _VALIDATED = True # register panels, operators, etc. bpy.utils.register_class(MyPanel) def unregister(): bpy.utils.unregister_class(MyPanel)
class MyAddonPrefs(bpy.types.AddonPreferences): bl_idname = __name__ license_key: bpy.props.StringProperty( name="License Key", description="Enter your Archergate license key", default="" ) def draw(self, context): layout = self.layout layout.prop(self, "license_key") if not _VALIDATED: layout.label(text="License required", icon="ERROR")
import urllib.request, json, hashlib, platform, subprocess class _Result: def __init__(self, valid, error=None): self.valid = valid; self.error = error def _fingerprint(): cpu = platform.processor() or platform.machine() try: if platform.system() == "Windows": out = subprocess.check_output( ["wmic", "csproduct", "get", "uuid"], text=True) install_id = out.strip().split("\\n")[-1].strip() elif platform.system() == "Darwin": out = subprocess.check_output( ["ioreg", "-rd1", "-c", "IOPlatformExpertDevice"], text=True) install_id = [l for l in out.splitlines() if "IOPlatformUUID" in l][0].split('"')[-2] else: install_id = open("/etc/machine-id").read().strip() except Exception: install_id = "unknown" return hashlib.sha256((cpu + install_id).encode()).hexdigest() def validate(api_key, app_id, license_key, server="https://license.archergate.io"): payload = json.dumps({ "api_key": api_key, "app_id": app_id, "license_key": license_key, "fingerprint": _fingerprint() }).encode() try: req = urllib.request.Request( server + "/validate", data=payload, headers={"Content-Type": "application/json"} ) resp = json.loads(urllib.request.urlopen(req, timeout=8).read()) return _Result(resp.get("valid", False), resp.get("error")) except Exception as e: return _Result(False, str(e))
Single-file archergate.py. Bundle it with your addon. No pip install, no external deps.
SHA-256 fingerprint from CPU + OS install ID. Seat locked to hardware, not email.
Local signed cache valid for 30 days. Works in studios with restricted internet.
Self-host the validation server or use managed hosting at $7/month flat.