Sessions & Bindings
GeckoGuard tracks two types of associations between licenses and devices: bindings (persistent device records) and sessions (active usage tracking).
Bindings
Bindings are persistent records of devices (HWIDs) and IPs that have been associated with a license. They are created automatically during license authorization.
Binding Types
| Type | Description |
|---|---|
HWID | Hardware ID binding — tracks which devices have used the license |
IP | IP address binding — tracks which IPs have used the license |
Binding Behavior by Policy Mode
| Mode | Behavior |
|---|---|
unlimited | Bindings are recorded but not enforced |
sticky | First binding is permanent — subsequent different values are denied |
limit | Up to maxDistinct unique bindings allowed |
Binding Fields
Each binding tracks:
valueHash— SHA-256 hash of the HWID/IPregion— Country code (for IP bindings with region-based limiting)firstSeenAt— When this binding was first createdlastSeenAt— Most recent authorization using this bindingrevokedAt— If the binding has been reset/revoked
Resetting Bindings
HWID and IP bindings can be reset via the dashboard API, subject to the license's reset budget:
POST /v1/dashboard/licenses/:id/reset-hwid
POST /v1/dashboard/licenses/:id/reset-ip
Reset budgets control:
- max — total number of resets allowed (0 = no resets)
- cooldownHours — minimum time between resets
Sessions
Application lifecycle
The first-party SDKs generate a session ID during authorization and reuse it for heartbeats. Start protected work only after authorization succeeds. The homepage and SDK quick starts demonstrate that initial request; a long-running application must also maintain its authorization.
- Configure both the API key and its one-time signing secret from the dashboard. The hosted API requires request signing.
- Authorize once and handle denial, signature, and transport errors by stopping protected work.
- Start the SDK heartbeat loop. On explicit revocation or an exhausted offline budget, stop protected work immediately. Updating the SDK's authorization state alone does not stop your application.
- Keep a finite missed-heartbeat budget. Do not set it to zero in production.
- Stop the heartbeat loop on application shutdown; preserve the same SDK instance/session throughout the run.
| SDK | Start loop | Stop loop | Required application callbacks |
|---|---|---|---|
| JavaScript | startHeartbeat(options) | stopHeartbeat() | onRevoked, onConnectionLost |
| Python | start_heartbeat(...) | stop_heartbeat() | on_revoked, on_connection_lost |
| C# | StartHeartbeat(interval, ...) | StopHeartbeat() | onRevoked, onConnectionLost |
| Go | StartHeartbeatWithOptions(options) | StopHeartbeat() | OnRevoked, OnConnectionLost |
| C++ | start_heartbeat(interval, ...) | stop_heartbeat() | revocation and connection-loss callbacks |
Callbacks may run on a background thread. Marshal UI changes onto your application's UI thread, and make stopping protected work safe to call more than once.
Sessions track active concurrent usage of a license. They're created when a sessionId is passed during license authorization and are used to enforce concurrency limits.
How Sessions Work
- Your application generates a unique
sessionId(e.g., a UUID per app launch) - On each authorization request, pass the
sessionId - GeckoGuard creates or updates the session record
- If concurrency limits are enabled, the number of active sessions is checked
- Sessions that haven't been seen recently can be considered stale
Managing Sessions
List Sessions
const response = await fetch('/v1/dashboard/licenses/LICENSE_ID/sessions', {
headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const { data } = await response.json();
// data.sessions = [{ id, sessionIdHash, ipHash, hwidHash, createdAt, lastSeenAt, revokedAt }]
// data.activeCount = 2
// data.revokedCount = 1
// data.total = 3
// data.page = 1
// data.pageSize = 20
// data.hasNext = false
// data.canRevoke = true
Use the optional page and pageSize query parameters to browse session history. Page size defaults to 20 and is capped at 100.
canRevoke reports whether the current dashboard user has the effective SUPPORT role or higher for the product.
Kill a Specific Session
POST /v1/dashboard/licenses/:id/sessions/:sessionId/revoke
Useful when a user reports a lost/stolen device or wants to log out from a specific location.
Requires the effective SUPPORT product role or higher.
Kill All Sessions
POST /v1/dashboard/licenses/:id/sessions/revoke-all
Immediately revokes all active sessions. The user will need to re-authorize on their next launch.
Requires the effective SUPPORT product role or higher.
Session Fields
| Field | Description |
|---|---|
sessionIdHash | Hashed session identifier |
ipHash | Hashed IP of the session |
hwidHash | Hashed HWID of the session |
createdAt | When the session started |
lastSeenAt | Last authorization with this session |
revokedAt | When the session was killed (null if active) |
Concurrency Limiting
Set limits.concurrency.mode: 'limit' in your policy with a maxActive count:
{
"limits": {
"concurrency": {
"mode": "limit",
"maxActive": 2
}
}
}
When the limit is reached, new sessions are denied with CONCURRENCY_LIMIT_EXCEEDED. The authorization response includes remaining counts:
{
"limits": {
"remaining": {
"concurrency": 0
}
}
}
Best Practices
- Generate unique session IDs — use a UUID per application launch, not per request
- Reuse the same session ID — send the same ID on periodic re-validations
- Kill sessions on app exit — if your app has a clean exit path, revoke the session
- Monitor session counts — check the dashboard for users with unusually high session counts
- Set reasonable limits — most software works well with 1-3 concurrent sessions