License Activation API¶
API endpoints for license activation, used by SentriKat instances to validate and activate licenses.
Online Activation¶
Exchange an activation code for a hardware-locked signed license key.
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
activation_code | string | Yes | Activation code (format: SK-XXXX-XXXX-XXXX-XXXX) |
installation_id | string | Yes | Installation ID from SentriKat (format: SK-INST-<64-char-hex>) |
app_version | string | No | SentriKat application version (e.g. 1.3.0) |
Example Request¶
curl -X POST https://portal.sentrikat.com/api/v1/license/activate \
-H "Content-Type: application/json" \
-d '{
"activation_code": "SK-A1B2-C3D4-E5F6-G7H8",
"installation_id": "SK-INST-abc123def456...",
"app_version": "1.3.0"
}'
Response (200 OK)¶
The license_key is a signed license string in the format base64(JSON_payload).base64(RSA_signature). The SentriKat instance verifies this signature using its embedded RSA public key.
Error Responses¶
| Status | Description |
|---|---|
400 | Invalid activation code format or installation ID format |
404 | Activation code not found |
409 | Activation code has already been used |
410 | Activation code has expired |
429 | Rate limit exceeded (10 attempts/hour/IP) |
500 | License signing unavailable |
Rate Limiting¶
- 10 activation attempts per hour per IP address
- Applies to both successful and failed attempts
- Returns
429 Too Many Requestswhen exceeded
Security Notes¶
- Activation codes are validated against the pattern
^[A-Za-z0-9\-]+$(8-128 characters) - Installation IDs must start with
SK-INST- - All activation attempts are logged (code, installation_id, IP, success/failure)
- One activation code = one installation (single use)
- Codes expire 90 days after purchase (configurable)
Heartbeat¶
Periodic check-in from active SentriKat instances.
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
license_key | string | Yes | The license key |
installation_id | string | Yes | Installation ID |
Response (200 OK)¶
Update Check¶
Check for available SentriKat updates. Called periodically by SentriKat installations.
No authentication required — just the installation ID header (same one used for heartbeats).
Request Headers¶
| Header | Required | Description |
|---|---|---|
X-Installation-ID | Yes | Installation ID (e.g. SK-INST-abc123...) |
X-App-Version | No | Current SentriKat version (e.g. 1.0.0-beta.4) |
Example Request¶
curl https://portal.sentrikat.com/api/v1/releases/latest \
-H "X-Installation-ID: SK-INST-abc123def456..." \
-H "X-App-Version: 1.0.0-beta.4"
Response (200 OK)¶
{
"version": "1.0.0-beta.5",
"download_url": "https://portal.sentrikat.com/api/v1/releases/1.0.0-beta.5/download",
"image": "ghcr.io/sbr0nch/sentrikat:1.0.0-beta.5",
"released_at": "2026-02-16T12:00:00+00:00",
"release_notes": "Bug fixes and performance improvements",
"update_available": true
}
Response (204 No Content)¶
Returned when no releases exist yet.
Notes¶
update_availableistruewhen the latest version is newer thanX-App-Version- If
X-App-Versionis not sent,update_availabledefaults tofalse - The portal logs each update check for analytics (installation, version, IP)
imageis the Docker image URL — use this for Docker-based deploymentsdownload_urlis the tar.gz package URL — for offline/air-gapped deployments
Important: Private Repository Downloads¶
If the SentriKat GitHub repository is private:
- Docker images (
imagefield): Work if GHCR package visibility is set to public (independent of repo visibility) - Release assets (
download_url): GitHub release download URLs require authentication for private repos. Either:- Host the tar.gz on the portal server (
/opt/sentrikat/downloads/) - Or set the download URL to the portal's own download endpoint
- Host the tar.gz on the portal server (
Verify Signed License¶
Verify a signed license string from the portal (used by SentriKat instances).
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
signed_license | string | Yes | The base64(payload).base64(signature) string |
installation_id | string | Yes | The requesting installation's ID |
Response (200 OK)¶
{
"valid": true,
"message": "License is valid",
"edition": "pro",
"customer": "Acme Corp",
"limits": {
"max_users": null,
"max_organizations": null,
"max_products": null,
"max_agents": 10
},
"features": ["all", "ldap", "sso", "webhooks", "api"],
"expires_at": "2027-02-08T00:00:00",
"issued_at": "2026-02-08T00:00:00"
}
Validation Rules¶
- Signature must be valid RSA-4096 (PKCS1v15 + SHA256)
- License must exist in the database (revoked licenses are rejected)
- License must be ACTIVE status
- License must not be expired
- Installation ID in the payload must match the requesting installation
- Max activation limit is enforced
License Payload Format¶
The signed license payload contains:
{
"license_id": "uuid",
"license_key": "SK-PRO-XXXX-XXXX",
"customer": "Company Name",
"email": "[email protected]",
"edition": "pro",
"installation_id": "SK-INST-...",
"issued_at": "2026-02-08T12:00:00",
"expires_at": "2027-02-08T12:00:00",
"limits": {
"max_users": null,
"max_organizations": null,
"max_products": null,
"max_agents": 10,
"max_agent_api_keys": 10
},
"features": ["all", "ldap", "sso", "webhooks", "api"]
}
The payload is JSON-encoded, base64url-encoded, then RSA-signed. The full license string is: