⚡ ScaleGate-SaaS License Manager

One Platform to License, Protect & Monetize Your Software.

ScaleGate gives independent software vendors a complete white-labelled licensing engine. Issue keys, enforce device limits, accept payments and track revenue, all under your own brand.

0
Tests Passing
0
Pass Rate
0
Security Checks
0
API Endpoints
Tenant Dashboard
2,847
Active Licenses
KES 142K
Monthly Revenue
9,104
Registered Devices
Live -License validations active
Production-ready 436 tests passing 30 OWASP security checks Built for Kenya & East Africa Deployed on Docker + SQL Server

Three Layers. Zero Compromise.

ScaleGate operates across three purpose-built tiers, each isolated, secured, and optimised for its users.

👑
Platform Admin

God's View: Full Platform Control

  • Onboard and manage all software vendor tenants
  • Configure Starter / Professional / Enterprise subscription plans
  • Track MRR, revenue share collected, and aggregate analytics
  • Monitor backup health across all tenants
  • Manage PesaPal disbursements and platform-wide settings
  • Audit log for every platform action including IP, actor, timestamp
🏢
For Software Vendors

Your White-Labelled Command Center

  • Create and manage software applications with versioned binaries
  • Define pricing plans with device limits, duration, and feature flags
  • Issue, extend, override, and revoke license keys
  • Full white-label branding: logo, colours, fonts, custom CSS
  • Custom domain with automatic Let's Encrypt SSL provisioning
  • Role-based access control with 16 granular permissions
  • PesaPal payment integration with automated PDF invoicing
  • Automated billing reminders and grace period enforcement
  • TOTP + Email MFA for all admin accounts
  • JWT key rotation to invalidate all sessions instantly
  • 20+ audit log action types (IP, user agent, metadata)
💻
For End Users

Effortless Licensing for Every Customer

  • Browse app catalog and purchase plans via PesaPal
  • License keys issued automatically on payment confirmation
  • Activate on desktop (WPF/WinForms) via .NET SDK
  • Activate web apps via domain fingerprinting
  • Manage registered devices and free slots remotely
  • Download app binaries with license pre-configured
  • Offline grace period (configurable days) during connectivity loss
  • View payment history and download PDF invoices

Everything a Software Vendor Needs

Built to cover the complete ISV lifecycle, from first app to enterprise scale, under your own brand.

🎨

White-Label Branding

Custom logo, colours, fonts, and CSS. Your customers never see ScaleGate. They see you.

🌐

Custom Domains + SSL

Point your own domain (e.g., licenses.yourbrand.com). ULS auto-provisions Let's Encrypt TLS with zero configuration.

💳

PesaPal Payments

Accept KES payments natively. Invoices generated as PDF. Revenue share disbursed automatically to your merchant account.

🔑

License Lifecycle Management

Issue, override, extend, and revoke keys in seconds. Bulk operations supported. Full status history per license.

🖥

Desktop SDK (.NET)

Drop the Licensing.SDK NuGet package into any WPF or WinForms app. Activate, validate, auto-update, and revoke in five lines of code.

🌍

Web License API

Six REST endpoints for web and SaaS apps. Domain fingerprinting, feature flags, server-to-server validation, and heartbeat checks.

🛡

Role-Based Access Control

16 granular permissions. Custom roles. MasterAdmin bypass. Every access decision is audited.

📊

Revenue Dashboard

Real-time MRR, active licenses, device counts, and customer activity, all in your white-labelled portal.

📱

Device Management

Enforce per-plan device limits. Customers manage their own devices. Admins can override remotely.

Feature Flags by Plan

Ship one codebase. Unlock features based on the customer's plan tier: SSO, API access, analytics, and more.

🔄

JWT Key Rotation

Rotate your tenant signing key on demand. All existing sessions are immediately invalidated.

📋

Audit Logs

20+ action types logged with IP address, user agent, actor identity, and full metadata. Compliance-ready.

Two SDKs. Every Platform.

Production-ready client libraries for desktop and web. Activate, validate, enforce, and auto-update with real API calls.

The ScaleGate.Licensing.SDK (.NET Standard 2.0) targets WPF and WinForms with hardware fingerprinting, offline caching, and self-update. The @scalegate/licensing-web-sdk (TypeScript) covers browser apps, React SPAs, and Node.js backends with domain fingerprinting and server-to-server validation.

.NET 📦 dotnet add package ScaleGate.Licensing.SDK
npm 📦 npm i @scalegate/licensing-web-sdk

WPF Desktop Application

.NET Standard 2.0 NuGet

Install Licensing.SDK via NuGet. The builder pattern configures your API URL, offline cache, grace period, and auto-revalidation -then activates the device with a single async call. Hardware fingerprinting (CPU + motherboard + machine SID, SHA-256 hashed) is automatic.

App.xaml.cs -Startup Activation
// Install: dotnet add package Licensing.SDK using Licensing.SDK; public partial class App : Application { public static LicenseClient License { get; private set; } protected override async void OnStartup( StartupEventArgs e) { base.OnStartup(e); var key = LoadKeyFromStorage(); License = await LicenseClient .Create("my-wpf-app", key) .WithApiUrl("https://acme.scalegate.io") .WithOfflineValidation(true) .WithGracePeriod(7) .WithAutoRevalidation(true, 30) .WithTimeout(15) .BuildAndActivateAsync(); if (!License.IsValid) { MessageBox.Show( License.CurrentStatus.Message, "License Error", MessageBoxButton.OK, MessageBoxImage.Warning); Shutdown(); return; } } }
MainWindow.xaml.cs -Events & Updates
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var lic = App.License; // React to license status changes lic.LicenseStatusChanged += (s, e) => { if (e.NewStatus == LicenseStatus.Expired) ShowRenewalDialog(); if (e.NewStatus == LicenseStatus.GracePeriod) StatusBar.Text = $"Grace period: {e.ValidationResult.DaysRemaining}d left"; }; // Listen for available updates lic.UpdateAvailable += (s, e) => { var msg = $"v{e.LatestVersion} available.\n{e.ReleaseNotes}"; if (MessageBox.Show(msg, "Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes) ApplyUpdate(e.LatestVersion); }; // Start background update checks lic.StartAutoUpdateCheck("1.0.0"); // Display plan info in UI PlanLabel.Text = lic.CurrentStatus.PlanName; DeviceCount.Text = $"{lic.CurrentStatus.ActiveDevices}/{lic.CurrentStatus.MaxDevices}"; } private async void ApplyUpdate(string ver) { var result = await App.License .PrepareSelfUpdateAsync(ver); if (result.ReadyToApply) SelfUpdater.ApplyAndRestart( Environment.ProcessPath, result.DownloadedFilePath, null); } }
🔒
Offline Cache
AES-256 encrypted license stored in %LocalAppData%
🔨
Hardware Fingerprint
CPU + motherboard + Machine SID, SHA-256 hashed
🔄
Self-Update
Download, replace binary, restart with auto-rollback
🔔
Real-Time Events
StatusChanged, ValidationCompleted, UpdateAvailable, UpdateProgress
WPF .NET 8 / 9 .NET Framework 4.6.1+ .NET Standard 2.0 Offline Validation Auto-Revalidation Grace Period Self-Update Device Revocation Thread-Safe
  • Fluent builder pattern. Configure in a single chain
  • AES-256 encrypted offline license cache persisted locally
  • Hardware fingerprint: CPU ID + motherboard serial + SID
  • Configurable grace period for offline use (default 7 days)
  • Background auto-revalidation at configurable intervals
  • Self-update: check, download, replace .exe, auto-restart
  • Automatic rollback to .old backup if update fails
  • 4 event hooks: StatusChanged, ValidationCompleted, UpdateAvailable, UpdateProgress
  • Extension methods: EnsureValidAsync(), EnsureActivatedAsync()
  • LicenseValidationException with full result for try/catch patterns
  • 3 build strategies: Build(), BuildAndValidateAsync(), BuildAndActivateAsync()
  • IDisposable. Cleans up timers and HTTP client

Windows Forms Application

.NET Standard 2.0 NuGet

Identical SDK, different UI framework. The LicenseClient is framework-agnostic -events marshal back to the UI thread using Invoke(). Activation, validation, revocation, and auto-update work identically to WPF.

Program.cs -Entry Point Guard
using Licensing.SDK; static class Program { internal static LicenseClient? License; [STAThread] static async Task Main() { ApplicationConfiguration.Initialize(); var key = Settings.Default.LicenseKey; if (string.IsNullOrEmpty(key)) { using var dlg = new ActivationForm(); if (dlg.ShowDialog() != DialogResult.OK) return; key = dlg.LicenseKey; } License = await LicenseClient .Create("my-winforms-app", key) .WithApiUrl("https://acme.scalegate.io") .WithOfflineValidation(true) .WithGracePeriod(7) .WithAutoRevalidation(true, 60) .BuildAndActivateAsync(); if (!License.IsValid) { MessageBox.Show( License.CurrentStatus.Message, "License Error"); return; } Application.Run(new MainForm()); } }
MainForm.cs -Status Bar & Deactivation
public partial class MainForm : Form { public MainForm() { InitializeComponent(); var lic = Program.License!; var s = lic.CurrentStatus!; // Show license info in status strip lblPlan.Text = $"Plan: {s.PlanName}"; lblDevices.Text = $"Devices: {s.ActiveDevices}/{s.MaxDevices}"; lblExpiry.Text = $"Expires: {s.ExpiryDate:MMM dd, yyyy}"; // Warn on grace period lic.LicenseStatusChanged += (_, e) => { Invoke(() => { if (e.NewStatus == LicenseStatus.GracePeriod) warningPanel.Visible = true; if (e.NewStatus == LicenseStatus.Expired) { MessageBox.Show("License expired."); Close(); } }); }; } // Deactivate on uninstall / sign-out private async void btnDeactivate_Click( object s, EventArgs e) { var result = await Program.License! .RevokeAsync(); if (result.IsValid) { Program.License.Dispose(); Settings.Default.LicenseKey = ""; Settings.Default.Save(); Application.Restart(); } } }
WinForms .NET 8 / 9 .NET Framework 4.6.1+ Offline Cache Device Revocation Grace Period

TypeScript / JavaScript

npm ESM + CJS

Install @scalegate/licensing-web-sdk from npm. The client auto-detects browser vs. server mode, registers the domain or IP, and starts periodic validation with configurable callbacks for invalid/valid transitions.

Initialisation & Validation
import { LicenseClient } from '@scalegate/licensing-web-sdk'; // Create and initialise the client const client = new LicenseClient({ apiUrl: 'https://acme.scalegate.io', licenseKey: 'XXXX-XXXX-XXXX-XXXX', appId: 'my-saas-app', mode: 'web', // 'web' = browser, 's2s' = server validateInterval: 300000, // 5 min periodic check onInvalid: (status) => { console.error(`License invalid: ${status.message}`); showPaywall(); }, onValid: (status) => { console.log(`License OK -${status.daysRemaining}d left`); }, }); // Register this domain and start validation loop const status = await client.initialise(); if (status.isValid) { console.log(`Plan: ${status.planName}`); console.log(`Domains: ${status.activeDomains}/${status.maxDomains}`); }
Feature Flags & Status
// Check which features the plan unlocks const features = await client.checkFeatures( ['sso', 'api_access', 'analytics', 'white_label'] ); if (features.valid) { console.log(`Plan: ${features.planName}`); if (features.features.sso) enableSSOLogin(); if (features.features.analytics) loadAnalyticsDashboard(); } // Full status with registered domains const detail = await client.getStatus(); console.log(detail.currentDomainRegistered); console.log(detail.registeredDomains); console.log(detail.featureFlags); // Lightweight heartbeat (returns boolean) const alive = await client.heartbeat(); // Cached status (no network call) const cached = client.getCachedStatus(); // Cleanup on unmount / shutdown client.destroy();
🌐
Domain Fingerprint
Automatic Origin header detection in browser mode
Feature Flags
Check plan-gated features with one API call
🔁
Periodic Validation
Configurable interval with onValid/onInvalid callbacks
🔒
Dual Mode
'web' for browsers, 's2s' for server-to-server with IP
TypeScript JavaScript ESM + CommonJS Domain Fingerprint Feature Flags Periodic Validation Heartbeat Cached Status LicenseError
  • initialise() registers domain and starts validation loop
  • validate() performs on-demand license check
  • getStatus() returns full detail: domains, feature flags, plan
  • checkFeatures() queries plan-specific feature flags
  • heartbeat() lightweight boolean alive check
  • getCachedStatus() returns last result without network call
  • onValid / onInvalid callbacks for periodic validation events
  • LicenseError class with httpStatus and licenseStatus properties
  • destroy() stops interval and cleans up resources
  • Full TypeScript definitions included (.d.ts)

React Integration

React 18/19 Hooks

Wrap the Web SDK in a React context provider and a custom hook. The provider initialises the license on mount, cleans up on unmount, and exposes status + feature flags to every component in your tree.

LicenseProvider.tsx -Context Provider
import { createContext, useContext, useEffect, useState, useCallback } from 'react'; import { LicenseClient } from '@scalegate/licensing-web-sdk'; import type { LicenseStatus, FeatureCheckResult } from '@scalegate/licensing-web-sdk'; interface LicenseCtx { status: LicenseStatus | null; features: Record<string, boolean>; isValid: boolean; loading: boolean; refresh: () => Promise<void>; } const Ctx = createContext<LicenseCtx>(null!); export const useLicense = () => useContext(Ctx); export function LicenseProvider({ licenseKey, appId, features: featureKeys, children }: { licenseKey: string; appId: string; features?: string[]; children: React.ReactNode; }) { const [status, setStatus] = useState<LicenseStatus | null>(null); const [features, setFeatures] = useState<Record<string, boolean>>({}); const [loading, setLoading] = useState(true); const [client] = useState(() => new LicenseClient({ apiUrl: 'https://acme.scalegate.io', licenseKey, appId, mode: 'web', onInvalid: setStatus, onValid: setStatus, })); const refresh = useCallback(async () => { const s = await client.validate(); setStatus(s); if (featureKeys?.length) { const f = await client.checkFeatures(featureKeys); setFeatures(f.features); } }, [client, featureKeys]); useEffect(() => { client.initialise().then(async (s) => { setStatus(s); if (featureKeys?.length) { const f = await client.checkFeatures(featureKeys); setFeatures(f.features); } setLoading(false); }); return () => client.destroy(); }, []); return ( <Ctx.Provider value={{ status, features, isValid: status?.isValid ?? false, loading, refresh }}> {children} </Ctx.Provider> ); }
App.tsx -Usage in Components
import { LicenseProvider, useLicense } from './LicenseProvider'; // Wrap your app at the root function App() { return ( <LicenseProvider licenseKey="XXXX-XXXX-XXXX-XXXX" appId="my-saas-app" features={['sso', 'analytics', 'api']} > <Dashboard /> </LicenseProvider> ); } // Any child component can read license state function Dashboard() { const { isValid, status, features, loading } = useLicense(); if (loading) return <Spinner />; if (!isValid) return <Paywall message={status?.message} daysLeft={status?.daysRemaining} />; return ( <div> <h1>Welcome -{status.planName} Plan</h1> <p>Expires: {status.expiryDate}</p> {features.analytics && <AnalyticsPanel />} {features.sso && <SSOSettings />} </div> ); } // Feature gate component function FeatureGate({ flag, children }: { flag: string; children: React.ReactNode }) { const { features } = useLicense(); return features[flag] ? <>{children}</> : null; }
React 18 / 19 Context API Custom Hooks Feature Gating Auto-Cleanup

Node.js / Express Middleware

Server-to-Server s2s Mode

Use mode: 's2s' for server-side validation. The SDK sends the server's IP address instead of the browser Origin header. Ideal for protecting API backends, microservices, and server-rendered apps.

middleware/license.ts -Express Guard
import { LicenseClient } from '@scalegate/licensing-web-sdk'; import type { Request, Response, NextFunction } from 'express'; let client: LicenseClient; export async function initLicense() { client = new LicenseClient({ apiUrl: process.env.SG_API_URL!, licenseKey: process.env.SG_LICENSE_KEY!, appId: process.env.SG_APP_ID!, mode: 's2s', // server-to-server validateInterval: 600000, // 10 min onInvalid: (s) => { console.error( `[SG] License invalid: ${s.status}` ); }, }); const status = await client.initialise(); if (!status.isValid) throw new Error(`License failed: ${status.message}`); console.log(`[SG] Licensed -${status.planName}`); } export function requireLicense() { return (req: Request, res: Response, next: NextFunction) => { const cached = client.getCachedStatus(); if (!cached || !cached.isValid) { res.status(503).json({ error: 'Service license expired' }); return; } next(); }; }
server.ts -Express App
import express from 'express'; import { initLicense, requireLicense } from './middleware/license'; const app = express(); // Validate license before accepting traffic await initLicense(); // Protect all API routes app.use('/api', requireLicense()); app.get('/api/data', (req, res) => { res.json({ status: 'licensed' }); }); app.listen(3000, () => { console.log('Server ready on :3000'); }); // ─── Feature-gated endpoint ─── import { LicenseClient } from '@scalegate/licensing-web-sdk'; app.get('/api/premium', async (req, res) => { const client = getClient(); const { features } = await client .checkFeatures(['api_access']); if (!features.api_access) { res.status(403).json({ error: 'Upgrade plan for API access' }); return; } res.json({ premium: true, data: '...' }); });
Node.js Express Fastify Server-to-Server IP Fingerprint Middleware Guard

REST API Reference

HTTP Any Language

Don't use .NET or JavaScript? Call the licensing API directly from any language. All endpoints accept and return JSON. Desktop endpoints use hardware fingerprints; web endpoints use domain or IP fingerprinting.

💻 Desktop SDK Endpoints
MethodEndpointDescription
POST/api/licenses/activateRegister a device with a license key. Sends appId, licenseKey, and hardware fingerprint. Returns full validation result.
POST/api/licenses/validateValidate a license. Returns isValid, status, expiry, plan name, device counts, and days remaining.
POST/api/licenses/renewRenew a license with a new plan ID. Updates expiry and plan details.
POST/api/licenses/revokeDeregister a device, freeing its slot for another machine.
POST/api/licenses/check-updateCheck if a newer binary version is available. Returns version, release notes.
POST/api/licenses/download-tokenGet a time-limited token for downloading an update binary from Azure Blob Storage.

🌐 Web SDK Endpoints
MethodEndpointDescription
POST/api/web-licenses/initialiseRegister a domain with a web license. Browser mode uses Origin header for fingerprinting.
POST/api/web-licenses/validateValidate a web license from the browser. Returns status, plan, expiry, domain counts.
POST/api/web-licenses/s2s/validateServer-to-server validation using the server's IP address as fingerprint.
GET/api/web-licenses/status/{key}Full status detail: registered domains, feature flags, current domain registration, plan info.
POST/api/web-licenses/featuresCheck which feature flags are enabled for a license's plan. Accepts array of feature keys.
GET/api/web-licenses/heartbeat/{key}Lightweight alive check -returns boolean. Use for fast polling without full validation overhead.

📋 License Status Values (Both SDKs)
Active Valid and online-verified
GracePeriod Expired but within offline grace window
Expired Past expiry + grace period
Inactive Deactivated by admin
NotFound Invalid license key
DeviceNotRegistered Device not yet activated
DeviceLimitReached Max devices registered
InvalidApp Key not issued for this app
ValidationRequired Must validate online
NetworkError Could not reach the API

cURL -Activate a Desktop License
curl -X POST \ https://acme.scalegate.io/api/licenses/activate \ -H "Content-Type: application/json" \ -d '{ "appId": "my-app", "licenseKey": "XXXX-XXXX-XXXX-XXXX", "deviceFingerprint": "a1b2c3d4..." }' # Response: # { # "isValid": true, # "status": "Active", # "planName": "Professional", # "expiryDate": "2026-12-31T00:00:00Z", # "daysRemaining": 276, # "maxDevices": 5, # "activeDevices": 2 # }
cURL -Check Web Feature Flags
curl -X POST \ https://acme.scalegate.io/api/web-licenses/features \ -H "Content-Type: application/json" \ -H "Origin: https://myapp.com" \ -d '{ "licenseKey": "XXXX-XXXX-XXXX-XXXX", "featureKeys": ["sso", "api_access", "analytics"] }' # Response: # { # "valid": true, # "planName": "Enterprise", # "features": { # "sso": true, # "api_access": true, # "analytics": false # } # }
cURL Python Go Java Ruby PHP Any HTTP Client

Enterprise Security. Benchmarked Performance.

Hardened for multi-tenancy. Verified by automated testing and OWASP benchmarks.

  • Physical database separation per tenant
  • AES-256-CBC encryption for connection strings
  • PBKDF2-SHA256 password hashing (100K iterations)
  • TOTP + Email MFA on all admin accounts
  • Per-user IP allowlist (CIDR) for Forest Admin
  • 30 / 30 OWASP security checks passing
  • Rate limiting: Auth 10/min · Licensing 30/min
  • SQL injection protection (9 vectors blocked)
  • JWT isolation: Forest Admin ≠ Tenant tokens
  • CSP · X-Frame-Options · HSTS headers enforced
< 1ms
TenantResolver avg latency
(target: <5ms)
~5s
1,000 concurrent validations
(target: <60s)
100%
Redis cache hit rate
after warmup
0 leaks
Cross-tenant data leaks
across 50 concurrent tenants

Transparent Plans. Built for Growth.

Subscription plans managed by the platform. Your revenue share is automatically disbursed on every customer payment.

Loading plans...

Ready to Take Control?

Access the right portal for your role.

🌳
Platform Admin

Forest Admin Portal

Platform owner access. Manage all tenants, plans, and revenue from a single God's-View dashboard.

Access Forest Admin →
Restricted access -Platform admins only
🏢
Software Vendors

Tenant Admin Portal

Already a ScaleGate tenant? Log in to your white-labelled dashboard to manage your apps, licenses, and customers.

Login to Your Portal →
🚀
New to ScaleGate?

Create Your Account

Sign up as a software vendor. Get your own white-labelled portal, custom domain, and full licensing engine in minutes.

Create Free Account →