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.
Extensions and history
Dashboard pages and authorization use the same effective expiration. Both configured limits still apply unless recorded expiration history identifies a fixed-date override of an older activation-based deadline. Legacy overrides require an expiration update whose date matches the current fixed date; missing or mismatched evidence never removes a limit. This resolution does not rewrite stored license dates.
License details show recorded expiration changes, their timestamps, and added days when that amount was recorded. New changes save before/after values atomically with the update. Older audit entries may not contain the original date or number of days added, so those values are not inferred.
Last Seen is the latest successful verification, authorization, or recorded session activity. Activation alone is not activity evidence; licenses with no recorded activity show Never.
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