Changes: - Add FAL_KEY and GEMINI_API_KEY to .env.example - Update picture-it to use ~/.config/opencode/.env (unified creds) - Remove shodh-memory skill (no longer used) - Remove alphaear-* skills (deprecated) - Remove thai-frontend-dev skill (replaced by website-creator) - Remove theme-factory skill - Add mql-developer skill (MQL5 trading) - Add ecommerce-astro skill (Astro e-commerce) - Add website-creator skill (Next.js + Payload CMS) - Update install script for new skills
7.0 KiB
7.0 KiB
Security & Licensing
Table of Contents
- Account-Based Licensing
- Server-Side License Validation
- Expiration Date Checks
- Anti-Decompilation Techniques
- Distribution Best Practices
- Node.js License Server Example
- MQL4 Specific Considerations
- Complete License System Architecture
Account-Based Licensing
Simple Multi-Account Check
bool CheckAccountLicense() {
long currentAccount = AccountInfoInteger(ACCOUNT_LOGIN);
long authorizedAccounts[] = {12345678, 87654321, 11223344};
for(int i = 0; i < ArraySize(authorizedAccounts); i++) {
if(currentAccount == authorizedAccounts[i]) return true;
}
Alert("Account ", currentAccount, " is not authorized.");
return false;
}
Account + Broker Hash Check (More Secure)
Don't store plain account numbers in code. Hash account + broker + salt:
ulong HashString(string s) {
ulong hash = 5381;
for(int i = 0; i < StringLen(s); i++)
hash = ((hash << 5) + hash) + StringGetCharacter(s, i);
return hash;
}
bool ValidateLicense() {
long account = AccountInfoInteger(ACCOUNT_LOGIN);
string server = AccountInfoString(ACCOUNT_SERVER);
string raw = IntegerToString(account) + "|" + server + "|SECRET_SALT";
ulong hash = HashString(raw);
ulong validHashes[] = {0xA1B2C3D4E5F6, 0x1234567890AB};
for(int i = 0; i < ArraySize(validHashes); i++)
if(hash == validHashes[i]) return true;
return false;
}
Server-Side License Validation
Implementation Pattern
input string InpLicenseKey = "";
bool ValidateLicenseOnServer() {
long account = AccountInfoInteger(ACCOUNT_LOGIN);
string broker = AccountInfoString(ACCOUNT_SERVER);
string eaName = MQLInfoString(MQL_PROGRAM_NAME);
string json = "{";
json += "\"license_key\":\"" + InpLicenseKey + "\",";
json += "\"account\":" + IntegerToString(account) + ",";
json += "\"broker\":\"" + broker + "\",";
json += "\"ea\":\"" + eaName + "\"";
json += "}";
// Send to server, check response
// Handle network failures (fail-open vs fail-closed decision)
}
Fail-Open vs Fail-Closed
- Fail-open: Allow trading if server unreachable (better UX, weaker security)
- Fail-closed: Block trading if server unreachable (stronger security, risk of false blocks)
- Recommended: Fail-open with cached expiry and offline grace period
Caching License Locally
Use GlobalVariableSet to cache server expiry:
GlobalVariableSet("EA_LICENSE_EXPIRY", (double)StringToTime(expiry));
On next check, verify cached expiry first, re-validate with server periodically.
Expiration Date Checks
Hardcoded Expiration
datetime expiryDate = D'2025.12.31 23:59:59';
if(TimeCurrent() > expiryDate) {
Alert("EA has expired. Please renew.");
return INIT_FAILED;
}
Server-Cached Expiration
Check GlobalVariable cache first, re-validate on timer.
Demo Mode with Limited Features
After trial period, restrict to demo accounts or limited lot sizes.
Integration in OnInit
int OnInit() {
if(!CheckAccountLicense()) return INIT_FAILED;
if(!CheckExpiration()) return INIT_FAILED;
EventSetTimer(3600); // Re-check every hour
return INIT_SUCCEEDED;
}
void OnTimer() {
if(!CheckExpiration()) ExpertRemove();
}
Anti-Decompilation Techniques
Protection Layers Table
| Layer | Technique | Protection Level | Notes |
|---|---|---|---|
| 1 | Compile to .ex4/.ex5 | Basic | Strips variable/function names |
| 2 | Code obfuscation | Low | Complex expressions, dummy code |
| 3 | String encryption | Medium | Hide URLs, keys, constants |
| 4 | MQL5 Cloud Protector | High | Asymmetric encryption, native code |
| 5 | DLL offloading | High | Core logic in C++ DLL |
| 6 | Server-side logic | Highest | Core signals never leave server |
String Obfuscation Example
string DecryptString(int key) {
uchar encrypted[] = {104,116,116,112,115,58,47,47};
string result = "";
for(int i = 0; i < ArraySize(encrypted); i++)
result += CharToString((uchar)(encrypted[i] ^ (key % 256)));
return result;
}
MQL5 Cloud Protector
- Available in MetaEditor: Tools > MQL5 Cloud Protector
- Sends compiled .ex5 to MetaQuotes cloud
- Applies asymmetric encryption + unique key signing
- Source code never leaves your machine
- Same protection as MQL5 Market store
- Files NOT bound to specific computer (unlike Market)
- Free to use
Distribution Best Practices
Do's
- Distribute only .ex4/.ex5 compiled files
- Use MQL5 Market for built-in DRM (hardware + account binding)
- Combine account binding + server validation + Cloud Protector
- Include version checking in licensing server to force updates
- Use unique magic numbers or comments to identify your EA
Don'ts
- Never distribute .mq4/.mq5 source files
- Never hardcode API keys or server passwords in source
- Don't rely on a single protection layer
- Don't use simple string comparisons for license keys
Node.js License Server Example
Endpoint: POST /api/validate
app.post('/api/validate', (req, res) => {
const { license_key, account, broker, ea, version } = req.body;
// Look up license in database
const license = db.findLicense(license_key);
if(!license) return res.json({ valid: false, message: "Invalid key" });
if(license.expired) return res.json({ valid: false, message: "License expired" });
if(license.maxAccounts && license.accounts.length >= license.maxAccounts) {
if(!license.accounts.includes(account))
return res.json({ valid: false, message: "Max accounts reached" });
}
// Register account if new
if(!license.accounts.includes(account)) {
license.accounts.push(account);
db.updateLicense(license);
}
res.json({
valid: true,
expiry: license.expiryDate,
features: license.features
});
});
MQL4 Specific Considerations
Account Number Check (MQL4)
bool CheckLicense() {
int account = AccountNumber(); // MQL4 function
// ... same logic but with int instead of long
}
Hardware ID (MQL4 via DLL)
Can use Windows API via DLL to get hardware identifiers for machine-specific licensing.
Complete License System Architecture
EA (MQL5) License Server (Node.js)
| |
|-- POST /validate ----------->|
| {key, account, broker} |-- Check DB
| |-- Validate
|<-- {valid, expiry, features}-|
| |
|-- Cache expiry locally |
|-- Re-check every hour |
| |
|-- POST /heartbeat ---------->| (optional)
| {account, balance, status} |-- Track usage