License¶
License activation, status, info, and recheck endpoints. Some routes are public and others require auth - see the Guards column per endpoint.
All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.
Endpoint count: 7
Endpoints¶
| Method | Path | Handler | Guards |
|---|---|---|---|
GET |
/api/license |
getLicenseStatus |
AuthWithDB(cfg.JWTSecret, db) |
POST |
/api/license/activate |
activateLicense |
- |
GET |
/api/license/block-status |
getLicenseBlockStatus |
- |
POST |
/api/license/check |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
POST |
/api/license/deactivate |
deactivateLicense |
AuthWithDB(cfg.JWTSecret, db) |
GET |
/api/license/info |
getLicenseInfo |
- |
POST |
/api/license/recheck |
recheckLicense |
- |
Source¶
Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:
On error:
Reference¶
GET /api/license¶
Returns the locally stored license status, verifying the stored ECDSA signature against the recorded license data and flagging tampering.
Guards: AuthWithDB(cfg.JWTSecret, db)
Response - 200 OK
{
"data": {
"hardware_hash": "string",
"status": "active",
"expires_at": "2027-01-01",
"last_check": "2026-06-17 12:00:00",
"message": "string"
}
}
When no license_info row exists, status is "unchecked" and message is "License not yet verified".
Errors
| Status | Code | Meaning |
|---|---|---|
503 |
license_blocked |
Stored signature is invalid, or DB status/expires_at no longer match the signed values - license considered tampered. Response also includes hardware_hash. |
Side effects
- On tamper detection, writes the hardware hash to
/etc/starkone/license_blocked(mode 0644), blocking the panel.
Example
POST /api/license/activate¶
Activates a license key against the StarkOne license server, saving the returned signed license blob to disk and reloading it. This route is registered publicly (before auth), so the stub's Guards cell shows -.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
license_key |
string | yes | The license key to activate. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Request body missing or license_key not provided. |
400 |
activation_failed |
License server rejected activation; message carries the server's error. |
500 |
decode_error |
The base64 bin blob returned by the license server could not be decoded. |
500 |
write_error |
Failed to write the license file to disk. |
502 |
license_server_error |
Could not reach the license server, or it returned an unparseable response. |
Side effects
- Calls
https://api.ipify.orgto determine the public server IP. - POSTs to
https://license.starkone.io/licensing/api/v2/activate/with the key, hardware ID, server IP, panel version, and hostname. - On success, writes the decoded license blob to the license file path (mode 0600) and reloads the in-memory license.
Example
curl -X POST https://panel.example.com:2087/api/license/activate \
-H "Content-Type: application/json" \
-d '{"license_key": "STARK-XXXX-XXXX-XXXX"}'
GET /api/license/block-status¶
Reports whether the panel is currently license-blocked and returns the associated hardware hash and block message. Public, no auth.
Response - 200 OK
This handler returns a flat (non-enveloped) body.
blocked is true if either the /etc/starkone/license_blocked file exists or the in-memory license reports blocked. When not blocked, hardware_hash falls back to the computed hardware hash.
Example
POST /api/license/check¶
Performs a live license check against the license server, verifies the signed response, persists the result to the license_info table, and toggles the panel block file accordingly. Registered with middleware.RequireSuperuser(); the stub's Guards cell is shown verbatim below.
Guards: AuthWithDB(cfg.JWTSecret, db)
Response - 200 OK
Returns 200 OK even for failures, conveying the outcome in the status field.
Alternate status values: "grace" (server unreachable but within a 7-day grace window - body has status and message only), "error" (server unreachable past grace, unparseable response, or signature verification failure - body has status and message only).
Side effects
- Enumerates non-loopback IPv4 interface addresses and resolves the hostname.
- POSTs to
https://license.starkone.io/license.php(3-second timeout) with hardware hash, version, hostname, and local IPs. - Creates the
license_infotable if absent, deletes any existing row, and inserts the new verified result. - On a valid license, removes
/etc/starkone/license_blocked. On an invalid license, writes the hardware hash to that file (mode 0644). - In grace mode, updates
last_checkandmessageon the existinglicense_inforow.
Example
POST /api/license/deactivate¶
Deactivates the current license with the license server, removes the local license file, and reloads (clearing) the in-memory license.
Guards: AuthWithDB(cfg.JWTSecret, db)
Response - 200 OK
Side effects
- If a license is currently loaded, POSTs the license key and hardware ID to
https://license.starkone.io/licensing/api/v2/deactivate/(10-second timeout; errors ignored). - Removes the license file from disk and reloads the in-memory license.
Example
curl -X POST https://panel.example.com:2087/api/license/deactivate \
-H "Authorization: Bearer $TOKEN"
GET /api/license/info¶
Returns the current in-memory license details (status, tier, features, limits, and block state). Public, no auth.
Response - 200 OK
{
"data": {
"status": "active",
"tier": "string",
"features": [],
"max_websites": 0,
"blocked": false,
"block_message": "string",
"expired": false
}
}
Example
POST /api/license/recheck¶
Public endpoint that sends a heartbeat to the license server to refresh the current license (used for unblocking after a prior failure). Returns a flat (non-enveloped) body.
Response - 200 OK
On success:
{
"success": true,
"message": "License refreshed",
"features": [],
"tier": "string",
"status": "active"
}
When no license is loaded, the server is unreachable, or the response is invalid, returns { "success": false, "message": "..." } (still 200 OK).
Side effects
- Calls
https://api.ipify.orgfor the public server IP and resolves the hostname. - POSTs the license key, hardware ID, panel version, server IP, and hostname to
https://license.starkone.io/licensing/api/v2/heartbeat/(15-second timeout). - If the server returns a new base64
binblob, writes it to the license file (mode 0600) and reloads the in-memory license.
Example