Policies and Expiration

GeckoGuard provides flexible policy controls and multiple expiration modes to fit your licensing needs.

Expiration Modes

When creating a license, set expirationMode to control how expiry works:

Never Expire

{
  "expirationMode": "never"
}

Use for: Lifetime licenses, enterprise agreements.

Fixed Date

{
  "expiresAt": "2025-12-31T23:59:59Z",
  "expirationMode": "fixed"
}

Use for: Subscription licenses, trials with fixed end dates.

Days After Activation

{
  "expiresAfterDays": 30,
  "expirationMode": "afterActivation"
}

The countdown starts on the first successful authorization. The activatedAt timestamp is recorded automatically.

Use for: Trial periods, time-limited access that starts when the user first validates.

Both (Fixed + Relative)

{
  "expiresAt": "2025-12-31T23:59:59Z",
  "expiresAfterDays": 90,
  "expirationMode": "both"
}

The license expires at whichever comes first — the fixed date or N days after activation.

Use for: Flexible licensing with both a hard deadline and a usage-based limit.

Policy Structure

Policies define how licenses behave. They are set at the product level as defaultLicensePolicy and can be overridden per-license via policyOverride.

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

Policy Fields

FieldValuesDescription
v1Policy version (currently always 1)
limits.hwid.modeunlimited, sticky, limitHardware binding mode
limits.hwid.maxDistinctintegerMax distinct HWIDs (only when mode is limit)
limits.ip.modeunlimited, sticky, limitIP restriction mode
limits.ip.maxDistinctintegerMax distinct IPs (only when mode is limit)
limits.ip.windowDaysintegerTime window for counting distinct IPs
limits.ip.limitTypeip, regionWhether to limit by IP address or geographic region
limits.concurrency.modeunlimited, limitSession concurrency mode
limits.concurrency.maxActiveintegerMax concurrent sessions (only when mode is limit)
limits.resetBudget.hwid.maxintegerTotal HWID resets allowed (0 = no resets)
limits.resetBudget.hwid.cooldownHoursintegerHours between HWID resets
limits.resetBudget.ip.maxintegerTotal IP resets allowed
limits.resetBudget.ip.cooldownHoursintegerHours between IP resets

IP Region Limiting

When limitType is set to region, GeckoGuard uses IP geolocation to determine the country code of each request. Instead of counting distinct IP addresses, it counts distinct regions:

{
  "limits": {
    "ip": {
      "mode": "limit",
      "maxDistinct": 2,
      "limitType": "region"
    }
  }
}

This allows users to connect from any IP within their country without hitting limits — useful for mobile users or users with dynamic IPs.

Policy Merging

When a license has a policyOverride, it is deep-merged with the product's defaultLicensePolicy:

  1. Start with the product default
  2. Override any fields specified in the license override
  3. Unspecified fields keep their product-level defaults

This means you only need to specify the fields you want to change in the override.

Revocation

Revoke licenses to immediately disable them:

// Revoke a single license
await fetch('/v1/dashboard/licenses/LICENSE_ID/revoke', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});

// Restore a revoked license
await fetch('/v1/dashboard/licenses/LICENSE_ID/unrevoke', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});

Revoked licenses:

  • Cannot be validated — authorization returns reasonCode: 'LICENSE_REVOKED'
  • Show as "Revoked" in the dashboard
  • Can be restored (unrevoked) at any time
  • Can be bulk revoked/unrevoked via org-level endpoints

Freezing Licenses

Freeze licenses to temporarily pause expiration:

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

Frozen licenses:

  • Can still be validated — authorization succeeds
  • Expiration countdown is paused
  • Useful for customer support scenarios (e.g., user on vacation)
  • Can be bulk frozen/unfrozen

Blacklists

Block specific hardware IDs or IP addresses at the product level. Blacklisted values are checked before policy evaluation:

// Add a blacklist entry
await fetch('/v1/dashboard/blacklists/products/PRODUCT_ID', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    type: 'HWID',           // or 'IP'
    value: 'banned-hwid',
    reason: 'Chargeback'    // Optional reason
  })
});

When a blacklisted HWID or IP attempts validation, the response is:

{
  "ok": false,
  "allow": false,
  "reasonCode": "HWID_BLACKLISTED",
  "message": "Chargeback"
}

Best Practices

  1. Start with product-level defaults — set sensible policies for all licenses
  2. Use policy overrides sparingly — only for special cases (VIP customers, trials)
  3. Use afterActivation for trials — countdown starts only when the user first validates
  4. Use both mode for subscriptions — combine a hard deadline with activation-based expiry
  5. Set up blacklists — proactively block known bad actors
  6. Use region-based IP limiting — for products used by mobile or dynamic-IP users
  7. Test with dryRun — validate policy behavior before deploying