Overview
Commercial MT4/MT5 Expert Advisors have no built-in copy-protection: once a buyer has the compiled .ex4/.ex5, there's nothing stopping them sharing it with someone who never paid. This project is a small, self-contained licensing engine that solves that problem — it signs a license file per customer account with a product-specific key, embeds an expiry date and grace period, and gives any EA a one-line startup check that refuses to run (with a hard ExpertRemove()) if the license is missing, tampered with, or expired.
What makes this a genuinely reusable piece of engineering rather than a one-off script is that the same CLicenseFile class and generator pattern was rebranded and redeployed across at least a dozen separately-sold products under different client names and product keys — MR LIBERIA, FX RUNPOM, PHADAAI, IDDFX, and several "Sniper"/"Nexus"/"Cop"/"Alpha" branded trading tools each get their own generator script and product key, but all of them share the identical underlying cryptographic core. Building the protection layer once and treating each product as a thin configuration of it is the same reuse discipline a SaaS vendor applies to licensing — just implemented natively in MQL.
Architecture
CLicenseFileengine (LicenseFileCheck.mqh) — the shared class: takes a product name and product key, and exposesKeyGen()(sign),FileGen()(write), andCheck()(validate) as its entire public surface.- Per-product generator scripts — one small
.mq4/.mq5script per branded product (e.g.LicenseFileGenerator.mq4,AILicenseFileGenerator.mq4,MrLiberiaLicenseFileGenerator.mq4), each instantiatingCLicenseFilewith that product's hard-codedPRODUCT_NAME/PRODUCT_KEYand a UI for entering the customer's account number and expiry window. - EA-side validation gate — every protected EA (
FX RUNPOM.mq4,MR LIBERIA,IDDFX EA, etc.) includes the same header and callsLicenseFile.Check()inOnInit(), aborting the EA withINIT_FAILEDand aMessageBoxon failure. - Remote registration variant (
AILicenseFileGenerator.mq4) — a later evolution of the generator that, instead of only writing a local.licfile, signs the license andWebRequest()s it to a hosted Python API (phadaman.pythonanywhere.com/licenses/add/...) to register it server-side.
Implementation
The core class is deliberately small. KeyGen() concatenates the license payload (account, expiry timestamp, grace-period timestamp) with the product's secret key, hashes it with CryptEncode(CRYPT_HASH_SHA256, ...), and encodes the digest as Base64 — producing a signature that can't be forged without knowing the product key, since verification recomputes the same hash from the stored payload and compares it against the stored signature. FileGen() writes signature,account,expiry,grace_expiry to a <ProductName>.lic file, and Check() reads it back, splits the signature from the payload, re-runs KeyGen() on the payload, and rejects the license outright if the two signatures don't match — meaning any edit to the account or expiry fields invalidates the file, since the signature no longer matches the tampered payload.
Every consuming EA follows the identical integration pattern: #define PRODUCT_NAME "..." / #define PRODUCT_KEY "...", #include <LicenseFileCheck.mqh>, then in OnInit(), instantiate CLicenseFile, call .Check(), and hard-fail if it returns false. Because the product name and key are the only things that differ between deployments, turning a validated EA into a differently-branded, differently-keyed product for a new client is a two-line change plus a recompile — which is exactly what happened across the MR LIBERIA, FX RUNPOM, and IDDFX product lines, each with its own generator script but the same underlying .mqh.
Licensing Backend Evolution
The later AILicenseFileGenerator.mq4 variant shows the system growing past pure local file signing: rather than (or in addition to) writing a .lic file to disk, it builds the same SHA-256/Base64 signature and pushes it to a hosted Python API via WebRequest(), passing the account and formatted expiry date as URL parameters so the license can be issued and tracked server-side. That's a meaningful architectural step up from the original design — it moves license issuance from "run a script and hand someone a file" toward a centrally auditable system, while keeping the client-side EA validation logic (and the trust model of "the signature can't be forged without the key") completely unchanged.
Reliability
Because signature verification is symmetric — the same KeyGen() function both signs and re-validates — there's no drift between what the generator produces and what the EA checks, which matters given how many independently-maintained product variants share this one file. The design also fails safe: any error opening or parsing the license file (missing file, malformed CSV, mismatched signature) returns false from Check(), and every consuming EA is wired to treat a failed check as INIT_FAILED rather than falling through to normal operation.

