• LIC — exported live binding (ESM)
• setupLicenseMiddleware(app) — registers activation gate + Pro
feature gates (must run before any
/api/* route)
• setupLicenseRoutes(app) — /api/license-status, /api/license/
activate, /api/license/deactivate
• startLicenseRefresh() — startup + 6h periodic online check
• refreshLicenseOnline(reason) — ad-hoc refresh (e.g., during activate)
• isFreeUser() — 'no license || no core entitlement'
• tryAcquireFreeSlot() / releaseFreeSlot() — the free-tier concurrency
lock previously open-coded
in /api/process
Local 'const isFreeUser = ...' in /api/process renamed to 'isFree' to
avoid shadowing the imported helper. Open-coded freeJobInFlight reads/
writes replaced with the slot helpers.
server/index.js: 2461 → 2300 lines.
Smoke tested: server boots; /api/license-status, /api/health, /api/
process (rejects with 400 'No API key' as expected for unlicensed +
no key) all behave as before.