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
| Field | Values | Description |
|---|---|---|
v | 1 | Policy version (currently always 1) |
limits.hwid.mode | unlimited, sticky, limit | Hardware binding mode |
limits.hwid.maxDistinct | integer | Max distinct HWIDs (only when mode is limit) |
limits.ip.mode | unlimited, sticky, limit | IP restriction mode |
limits.ip.maxDistinct | integer | Max distinct IPs (only when mode is limit) |
limits.ip.windowDays | integer | Time window for counting distinct IPs |
limits.ip.limitType | ip, region | Whether to limit by IP address or geographic region |
limits.concurrency.mode | unlimited, limit | Session concurrency mode |
limits.concurrency.maxActive | integer | Max concurrent sessions (only when mode is limit) |
limits.resetBudget.hwid.max | integer | Total HWID resets allowed (0 = no resets) |
limits.resetBudget.hwid.cooldownHours | integer | Hours between HWID resets |
limits.resetBudget.ip.max | integer | Total IP resets allowed |
limits.resetBudget.ip.cooldownHours | integer | Hours 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:
- Start with the product default
- Override any fields specified in the license override
- 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
- Start with product-level defaults — set sensible policies for all licenses
- Use policy overrides sparingly — only for special cases (VIP customers, trials)
- Use
afterActivationfor trials — countdown starts only when the user first validates - Use
bothmode for subscriptions — combine a hard deadline with activation-based expiry - Set up blacklists — proactively block known bad actors
- Use region-based IP limiting — for products used by mobile or dynamic-IP users
- Test with
dryRun— validate policy behavior before deploying