Server protocol
Set up a server
Kumo Tabs never logs into your servers or installs anything on them. Each server answers one small HTTP request with a JSON report that you write yourself, in any language.
In a hurry? Copy a ready-made endpoint from the examples: PHP, Laravel, a WordPress plugin, a PrestaShop module, Node.js, Bun, Python, Go, Rust, C#, Ruby, Java…
Steps
- Pick an address for the endpoint, for example
https://example.com/vitals. Any path works. - In Kumo Tabs, open Settings → Servers → Add a server, fill in the name and the address, then press Generate. The token stays in your browser.
- Copy the Token hash (SHA-256) shown under the token, and store it on the server, in an environment variable or a config file. The server never needs the token itself.
- Write the endpoint described below, and deploy it.
- Back in Kumo Tabs, press Save & check and allow access to that host when the browser asks. The card turns green when the report arrives.
The request
Kumo Tabs sends:
GET /vitals HTTP/1.1
Host: example.com
X-Vitals-Token: <the token>
Accept: application/json
The endpoint hashes the header with SHA-256, compares it with the stored hash, then answers:
| Situation | Answer | Kumo Tabs shows |
|---|---|---|
| No hash configured on the server | 404 |
Down |
| Missing or wrong token | 401 |
Bad token |
| Right token | 200 with the JSON report |
Up or Degraded |
| Anything else, timeout, non-JSON | - | Down |
?fresh=1 is added when the user presses ⟳ Check now: if your endpoint caches parts of its
report, rebuild them for that request.
No CORS headers are needed: the extension calls your server with the host access the user granted.
Test it from a terminal
The token’s hash, to compare with the one shown in Kumo Tabs:
printf '%s' 'the-token' | sha256sum
The request itself:
curl -i -H 'X-Vitals-Token: the-token' https://example.com/vitals
The report
The smallest valid report is:
{ "ok": true }
Every other section is optional: send the ones you can, and the card shows what it gets. A complete report looks like this:
{
"ok": true,
"app": { "name": "My app", "build": 1792288695, "branch": "main", "commit": "b80995c" },
"php": { "version": "8.5.12", "sapi": "fpm-fcgi", "opcache": true },
"db": { "ok": true, "version": "10.11.6-MariaDB", "latencyMs": 1.4 },
"server": {
"software": "Apache/2.4.58 (Ubuntu)",
"os": "Linux 6.8.0-85-generic",
"uptime": 1224600,
"load": [0.42, 0.37, 0.31],
"cpus": 4,
"memory": { "total": 8589934592, "free": 3328599654 },
"disk": { "total": 171798691840, "free": 76235669504 }
},
"crons": {
"counts": { "ok": 28, "idle": 3, "late": 1 },
"failing": [{ "name": "cleanSessions", "status": "late" }]
},
"dependencies": [
{ "name": "PHP", "group": "runtime", "version": "8.5.12" },
{ "name": "MariaDB", "group": "database", "version": "10.11.6" },
{ "name": "smarty", "group": "php", "version": "5.8.5", "package": "smarty/smarty" },
{ "name": "chart.js", "group": "npm", "version": "4.5.1", "package": "chart.js", "wanted": "^4.5.1", "dev": false }
],
"cache": { "stack": { "at": 1792288000, "ttl": 3600 } }
}
ok (required)
true when the endpoint could build its report. Anything else counts as Down; an optional error
string is shown on the card.
app
| Field | Type | Shown as |
|---|---|---|
name |
string | Application name |
build |
string or number | Build or release number |
branch, commit |
string | branch@commit |
php
| Field | Type | Shown as |
|---|---|---|
version |
string | Also shown in compact mode |
sapi |
string | e.g. fpm-fcgi |
opcache |
boolean | Adds “opcache” when true |
Other runtimes (Node.js, Python, Go…) go in dependencies with "group": "runtime".
db
| Field | Type | Shown as |
|---|---|---|
ok |
boolean | false marks the server Degraded |
version |
string | As the database reports it (SELECT VERSION()) |
latencyMs |
number | Time of a trivial query |
server
| Field | Type | Shown as |
|---|---|---|
software, os |
string | Web server and operating system |
uptime |
number | Seconds |
load |
number[3] | 1, 5 and 15-minute load averages |
cpus |
number | CPU count, compared with the 5-minute load |
memory, disk |
{ total, free } |
Bytes, drawn as usage bars |
crons
| Field | Type | Shown as |
|---|---|---|
counts |
object | Number of jobs per status, one chip each. ok, idle, running, late, failed and stuck get their own colours |
failing |
{ name, status }[] |
Jobs that need attention; any entry marks the server Degraded |
dependencies
A list of the software your server runs, grouped on the card and checked for updates.
| Field | Type | Meaning |
|---|---|---|
name |
string | Display name; also picks the release source (below) |
group |
string | runtime, database, server, system, php or npm |
version |
string | Installed version |
package |
string | Packagist or npm package name, for update checks |
wanted |
string | Constraint from package.json, e.g. ^4.5.1 |
dev |
boolean | Development dependency (can be hidden in the settings) |
cache
If your endpoint caches its slow parts, cache.stack.at (Unix seconds) tells the card how old the
versions are.
Health
| State | When |
|---|---|
| Up | 200, "ok": true and none of the issues below |
| Degraded | db.ok is false, crons.failing is not empty, disk or memory has less than 10% free, or the 5-minute load is above cpus |
| Down | Network error, timeout, non-JSON answer, any status other than 200 or 401 |
| Bad token | 401 |
The response time is measured by Kumo Tabs; you do not need to send it.
Update checks
Each dependency is compared with its official releases, picked by name or package:
| Dependency | Source |
|---|---|
name is PHP, Node.js, Bun, MySQL, MariaDB, Apache, nginx or OpenSSL (spelled exactly so) |
endoflife.date: end of life, LTS and supported branches |
group: "php" with a package |
Packagist, stable tags |
Any other package |
npm |
| No match | Listed without a badge |
Security
- Serve the endpoint over HTTPS, so the token never travels in clear text.
- Store only the SHA-256 hash, and compare it in constant time (
hash_equalsin PHP,crypto.timingSafeEqualin Node.js,hmac.compare_digestin Python,subtle.ConstantTimeComparein Go). - Answer
404while no hash is configured, so an unconfigured endpoint reveals nothing. - Report versions and states only: never paths, environment variables, keys or user data.
- Cache slow parts (version commands, cron history) for a few minutes; Kumo Tabs calls every 60 seconds by default.
- To revoke access, generate a new token and replace the stored hash.