KubeMQ
Licensing

Usage Reporting

What an online KubeMQ server sends to the licensing service every 15 minutes — each field and why, what is never sent, retention, and how to verify it.

An online server (one licensed with a key) sends a small usage report every 15 minutes with ±10% jitter, plus one at boot and one when it drains. The report is what usage-based plans are billed on, and what lets KubeMQ tell an active installation from a dead one. An offline server (licensed with a file) sends nothing — ever.

There is no switch to turn reporting off on an online license. If your network cannot allow it, the answer is a pre-activated file.

The report

POST https://license.kubemq.io/v1/usage/report, authenticated with the current lease as a bearer token. The body:

{
  "license_id":  "6f1c2a3e-9b4d-4c5e-8a7f-1d2e3f4a5b6c",
  "fingerprint": "3f2a9c1e-7b6d-4e5f-9a8b-0c1d2e3f4a5b",
  "boot_id":     "0b3d5f7a-9c1e-4a2b-8d6f-1e3c5a7b9d0f",
  "source": {
    "type": "kubernetes",
    "host_id": "node-a",
    "cluster_id": "3f2a9c1e-7b6d-4e5f-9a8b-0c1d2e3f4a5b",
    "namespace": "kubemq",
    "pod": "kubemq-next-0",
    "version": "next-1.0.0",
    "image_digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
  },
  "cursor": { "from": "2026-09-19T12:00:00Z", "to": "2026-09-19T12:15:00Z" },
  "totals": { "messages": 123456789, "volume_bytes": 987654321 },
  "activity": null
}

The host_id and image_digest values above are illustrative. On an operator-managed Kubernetes cluster host_id is the node name (operator 1.1.1 and later inject NODE_NAME on every pod) and image_digest is filled only when the cluster's image reference is pinned by digest (…@sha256:…); with a tag-only reference it stays empty.

Field by field:

FieldWhat it isWhy it is sent
license_idYour license's public id (a uuid)Attributes the report to a license. It must match the lease used as bearer
fingerprintThis installation's identity — the kube-system namespace UID on Kubernetes, a persisted random id elsewhereCounts installations against your cap, and must match the lease
boot_idA random uuid generated when the process started; constant until it exitsLets the service tell a restart from a continuing run, so counters can be cumulative (below)
source.typestandalone, docker or kubernetesSupport context; how the server detected its environment
source.host_idOS or container hostname; on Kubernetes the node name from NODE_NAME, which operator 1.1.1 and later inject on every podSupport context — which machine
source.cluster_idThe kube-system UID (Kubernetes only; empty if unreadable)Groups pods of one cluster
source.namespace, source.podPod namespace and name (Kubernetes only): POD_NAMESPACE (else the service-account namespace file, else default) and POD_NAME (else the hostname)Support context — which pod
source.versionThe server versionWhich release is running
source.image_digestThe digest of the running image — sent only when the deployment injects it as KUBEMQ_IMAGE_DIGEST (a well-formed sha256: digest). The server cannot read its own digest; without the variable the field is empty (omitted on a standalone binary). On Kubernetes, operator 1.1.1 and later inject it when the cluster's image reference is pinned by digest (…@sha256:…); a tag-only reference leaves it emptyConfirms the server is the signed official image, once injected
cursor.from, cursor.toThe window this report covers (UTC)Ordering and de-duplication; windows never overlap and always advance
totals.messagesMessages handled since this boot_id startedThe billable count
totals.volume_bytesBytes handled since this boot_id startedThe billable volume
activity"start" on the boot report, "stop" on the drain report, null otherwiseMarks the edges of a run

Cumulative counters

totals are cumulative since boot, never deltas. The service subtracts the previous accepted report for the same (license, fingerprint, boot_id) to get the 15-minute increment; a new boot_id starts from zero. This is why nothing is lost if a report is missed — the next one carries the full count — and why the server cannot shrink a bill by choosing windows. A counter that goes backwards without a new boot_id is clamped to zero and flagged, never silently accepted.

Delivery

  • At-least-once. The server keeps a cursor in metrics.db under the store path and advances it only after the service acknowledges (200). A report sent twice — for example after a crash between the acknowledgement and the cursor write — is answered duplicate and not counted again. A 401 (the lease the report carried is no longer accepted) keeps the row for the next attempt with the next lease; a 413 or 422 is terminal — the row is dropped and the cursor advances, so one bad report cannot block the ones after it.
  • Rate limited. The service allows a burst of 5 and one report per minute per license; the server's 15-minute cadence sits far inside that. A Retry-After on a 429 (or any other unexpected answer) puts the reporter on hold: ticks that fall inside the hold are skipped.
  • Never blocking. Reporting runs in the background and never affects message traffic. There is no retry backoff: a failed report is simply attempted again at the next 15-minute tick, with the full cumulative counters.

What is never sent

The report contains no payload data and no identifying traffic data:

  • No message content, headers, metadata or tags.
  • No channel names, queue names, client ids or subscription details.
  • No client IP addresses, and no server IP addresses — the service sees only the connection it receives.
  • No configuration values, credentials, or the license key.
  • No per-channel or per-client breakdown — only two totals.

Everything it does contain is listed in the table above. The whole body is limited to 16 KiB and source to 2 KiB.

What if reports cannot get through?

Nothing stops. An online license whose refreshes succeed but whose usage reports do not arrive for 72 hours is marked silent: the server logs a warning every minute (#silent-usage), leases are shortened to 24 hours, and KubeMQ sends you an email. After 30 days of continuous silence the service stops issuing new leases, and the server runs out its lease and grace. That is the only automatic consequence, and it is suspended fleet-wide whenever the silence looks like an outage on KubeMQ's side rather than yours.

Retention

DataKept for
Raw 15-minute reports90 days, then pruned
Daily totals per license and installationIndefinitely — this is the billing record

Raw rows are pruned only after every day they cover has been rolled up into the daily totals.

Verify what your server is sending

GET /api/v1/license reports the state of the reporter:

Terminal
curl -s http://localhost:8080/api/v1/license | jq .usage
{
  "enabled": true,
  "last_report_at": "2026-09-19T12:10:00Z",
  "last_outcome": "accepted",
  "last_status": 200,
  "cursor_to": "2026-09-19T12:10:00Z",
  "boot_id": "0b3d5f7a-9c1e-4a2b-8d6f-1e3c5a7b9d0f"
}
FieldHealthy value
enabledtrue for an online license; false for a file
last_report_atWithin the last 15–17 minutes
last_outcomeaccepted (or duplicate right after a restart). rejected means the service acknowledged but did not count the row; error means it never got through; never means no report has been sent since boot
last_status200
cursor_toEqual to the end of the last accepted window

The silent flag at the top level of the same response tells you whether the service agrees that it is receiving your reports. To see the traffic itself, watch outbound HTTPS to license.kubemq.io from the container; the server sends one request per report and nothing else on that connection.

Was this page helpful?

On this page