Licenses

Licenses are the core unit of access control in GeckoGuard. Each license grants access to a specific product for a specific end user.

License Lifecycle

1. Creation

Licenses are created through the dashboard or API:

const response = await fetch('https://api.geckoguard.net/v1/dashboard/orgs/ORG_ID/licenses', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_SESSION_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    productId: 'product-id',
    // key: 'CUSTOM-KEY-123',          // Optional — auto-generated if omitted
    // expiresAt: '2025-12-31T23:59:59Z',  // Fixed expiry
    // expiresAfterDays: 30,               // Relative expiry (days after activation)
    // expirationMode: 'never',            // 'never' | 'fixed' | 'afterActivation' | 'both'
    // policyOverride: { ... },            // Override product default policy
    // count: 1                            // Bulk create up to 500 at once
  })
});

2. Activation

When an end user first validates a license that has expiresAfterDays set, the license is automatically activated — the activatedAt timestamp is recorded and the expiry countdown begins.

const response = await fetch('https://api.geckoguard.net/v1/licenses/authorize', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    productId: 'product-id',
    licenseKey: 'LICENSE-KEY-123',
    hwid: 'device-hwid',         // Optional
    ip: '192.168.1.1',           // Optional
    deviceId: 'device-uuid',     // Optional
    sessionId: 'session-uuid',   // Optional: for concurrency limits
    dryRun: false                // Optional: test validation without persisting changes
  })
});

3. Usage

After activation, licenses are validated on each application launch or periodically. Each validation checks:

  • Blacklists — HWID and IP checked against product blacklist
  • Hardware binding — HWID checked against policy (sticky/limit mode)
  • IP restrictions — IP checked against policy, including region-based limiting
  • Concurrency limits — Active session count checked
  • Expiration — Both fixed and relative expiry validated

4. Expiration, Revocation, Freeze, or Suspension

Licenses can end or be held in several ways:

  • Expire naturally based on their expiration settings
  • Revoked manually by an admin (permanent)
  • Frozen temporarily to pause the expiration countdown — the license still authorizes
  • Suspended temporarily to both pause the countdown and block all use

License States

StatusDescription
ACTIVEValid and can be authorized
EXPIREDPast expiration date — automatically set on failed validation
REVOKEDManually disabled by admin (permanent)
SUSPENDEDTemporarily disabled by admin — authorization is denied and the expiration countdown is paused. Reversible back to ACTIVE.

Note: FROZEN is not a status — freezing sets frozenAt/frozenDaysRemaining while the status stays ACTIVE, so a frozen license keeps authorizing. Use SUSPENDED when you need to lock a user out while also pausing their clock.

License Keys

License keys are unique identifiers. They can be:

  • Auto-generated: GeckoGuard creates a secure random key
  • Custom: You provide your own key format (must be unique per product)

Dry Run Mode

Pass dryRun: true to the authorize endpoint to test validation without persisting any changes (no bindings created, no sessions tracked, no activation recorded). Useful for testing policies:

const result = await authorize({
  productId: 'product-id',
  licenseKey: 'KEY-123',
  hwid: 'test-hwid',
  dryRun: true
});
// result includes debug info about policy evaluation

Policy Overrides

Products have a default license policy, but you can override it per-license:

{
  policyOverride: {
    v: 1,
    limits: {
      hwid: { mode: 'sticky' },
      ip: { mode: 'limit', maxDistinct: 3, windowDays: 30 },
      concurrency: { mode: 'limit', maxActive: 2 },
      resetBudget: {
        hwid: { max: 3, cooldownHours: 24 },
        ip: { max: 5, cooldownHours: 12 }
      }
    }
  }
}

The override is deep-merged with the product default — you only need to specify the fields you want to change.

License Limits

Hardware Binding (HWID)

ModeBehavior
unlimitedNo restrictions on hardware IDs
stickyFirst device is permanently bound
limitAllow up to maxDistinct distinct devices

IP Restrictions

ModeBehavior
unlimitedNo restrictions
stickyFirst IP is permanently bound
limitAllow up to maxDistinct distinct IPs within windowDays

IP limits also support a limitType field:

  • 'ip' (default) — limit by distinct IP addresses
  • 'region' — limit by geographic region (country code via IP geolocation)

Concurrency

ModeBehavior
unlimitedNo session limits
limitMaximum maxActive concurrent sessions

Reset Budgets

Reset budgets control how many times HWID or IP bindings can be reset:

  • max: Total number of resets allowed (0 = no resets permitted)
  • cooldownHours: Minimum hours between resets

Reset endpoints:

  • POST /v1/dashboard/licenses/:id/reset-hwid
  • POST /v1/dashboard/licenses/:id/reset-ip

Freezing Licenses

Freeze a license to temporarily pause its expiration countdown:

// Freeze via PATCH
await fetch('/v1/dashboard/licenses/LICENSE_ID', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer YOUR_SESSION_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ frozen: true })
});

Frozen licenses:

  • Stop counting down toward expiration
  • Can still be validated (authorization succeeds)
  • Track frozenDaysRemaining for restoring time when unfrozen
  • Can be bulk frozen/unfrozen via the org-level endpoints

Suspending Licenses

Suspend a license when you need a temporary hard lock — the user is blocked from using the license, but the suspension is reversible and their remaining time is preserved (the countdown is paused exactly like a freeze):

// Suspend (blocks use + pauses the clock)
await fetch('/v1/dashboard/licenses/LICENSE_ID/suspend', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_SESSION_TOKEN' }
});

// Unsuspend (restores ACTIVE and resumes the countdown from where it left off)
await fetch('/v1/dashboard/licenses/LICENSE_ID/unsuspend', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_SESSION_TOKEN' }
});

Suspended licenses:

  • Are denied at /v1/enduser/verify and /v1/enduser/authorize-download (reason SUSPENDED)
  • Cannot be claimed via /v1/enduser/licenses/activate
  • Pause the relative-expiry countdown (reusing frozenAt/frozenDaysRemaining)
  • Return to ACTIVE on unsuspend — or EXPIRED if the resumed expiry has already passed
  • Emit license.suspended / license.unsuspended webhook events

Frozen vs. Suspended: freeze pauses the clock but still authorizes; suspend pauses the clock and locks the user out.

Bulk Operations

GeckoGuard supports bulk operations on licenses via org-scoped endpoints:

  • Freeze/UnfreezePOST /v1/dashboard/orgs/:orgId/licenses/freeze
  • Revoke/UnrevokePOST /v1/dashboard/orgs/:orgId/licenses/revoke
  • ExtendPOST /v1/dashboard/orgs/:orgId/licenses/extend
  • DeleteDELETE /v1/dashboard/orgs/:orgId/licenses
  • ImportPOST /v1/dashboard/orgs/:orgId/licenses/import
  • ExportGET /v1/dashboard/orgs/:orgId/licenses/export?format=csv|json

Best Practices

  1. Use auto-generated keys — more secure than predictable patterns
  2. Set expiration dates — even if far in the future, it's good practice
  3. Use dryRun for testing — validate your policy setup without side effects
  4. Monitor analytics — check the Analytics dashboard for unusual patterns
  5. Revoke compromised licenses immediately — act quickly if a key is leaked
  6. Use policy overrides sparingly — prefer product-level defaults for consistency
  7. Set up blacklists — proactively block known bad HWIDs or IPs