Skip to content

Results

The WEB server can act as a passive result cache: it registers a submission channel, keeps whatever is submitted to it in memory, and serves it back over REST. This inverts the usual passive-monitoring flow — instead of the agent pushing results out to a monitoring server it cannot always reach, the monitoring server polls the results out of the agent whenever it likes.

Typical producers are the Scheduler (scheduled checks submitted to a channel) and check_and_forward from CheckHelpers, but anything that submits to a channel works.

The cache is off by default. Until enabled is set the web server registers no channel, caches nothing, and answers every endpoint below with 503 Service Unavailable.

enabled and channel are read when the web server starts, not on a settings reload: the core cannot unregister a submission channel, so a reload can neither start listening on one nor move to another. Turning the cache on (or renaming its channel) therefore needs a service restart; until then the cache stays off, the endpoints keep answering 503, and the log says why. Every other setting on this page — primary index, mode, clear on poll, max entries, max age — takes effect on a reload. Turning the cache off also takes effect on a reload, and empties it.

Only one result is kept per key. When a second result arrives for a key, mode decides which of the two survives.

Configuration

The feature is configured under /settings/WEB/server/results:

[/settings/WEB/server/results]
; Off by default: no channel is registered and the endpoints answer 503.
enabled = true
; The channel to listen on.
channel = WEB
; The key a result is cached under.
primary index = ${host}/${alias-or-command}
; Which of two results for the same key survives: last or worst.
mode = last
; Whether GET /api/v2/results removes the results it returns.
clear on poll = true
; How many distinct keys to keep.
max entries = 1000
; Drop results not updated for this many seconds (0 = keep them).
max age = 0

primary index may contain literal text and any of these variables:

Variable Value
${host} The submitting host, as named in the request header
${source} The raw sender id the host was resolved from
${channel} The channel the result arrived on
${command} The command that produced the result
${alias} The alias the result was submitted under
${alias-or-command} ${alias} if set, otherwise ${command}

An unknown variable is refused at load time (with an error in the log) and the default expression is used instead, so a typo cannot silently collapse every result into a single entry.

A key travels in a URL path (/api/v2/results/{key}), so anything in it outside A-Z a-z 0-9 - _ . ~ / is percent-encoded: a key srv1/disk C: is fetched as /api/v2/results/srv1/disk%20C%3A. / is left alone on purpose — the default primary index puts one between the host and the check, and the routes that capture a key span path segments. The key field of a result is always the unescaped key; result_url is the escaped URL for it.

To submit results into the cache, point a producer at the channel:

[/settings/scheduler/schedules/disk]
command = check_drivesize
channel = WEB
interval = 5m

max entries and max age only exist to bound memory when results arrive under ever-changing keys. Results are never hidden merely for being stale — every result carries an age — so max age is about memory, not about deciding what counts as current.

Which result is kept

mode Behaviour
last (default) The newest result wins, so a recovery replaces the problem before it.
worst The most severe result wins, so a problem is still reported even if it recovered before the next poll. An equally severe result still replaces, keeping the message current.

Severity is ordered as everywhere else in NSClient++ — a plain numeric comparison of the Nagios status, so OK < WARNING < CRITICAL < UNKNOWN.

Every entry carries both timestamps this distinction needs: last_seen is when the key last submitted anything (so age still says whether the check is reporting, even while a problem is held), and result_seen is when the result actually being served arrived. In last mode the two are identical.

Polling and draining

With clear on poll = true (the default) GET /api/v2/results removes the results it returns. The next poll therefore reports what has happened since the previous one rather than repeating it, which is what makes mode = worst mean worst since the last poll — a check that went CRITICAL and recovered between two polls is reported once, and then stops being reported.

Only what a poll actually returns is dropped, so a filtered poll (?status=critical) cannot discard results its caller never saw. The response carries X-Result-Drained: true when the poll consumed its results.

Set clear on poll = false when several consumers poll the same agent, or for a dashboard that must not consume what it displays; results then stay until they are replaced, expire, or are deleted. Fetching a single result by key is a lookup rather than a poll and never drains, in either setting.

mode = worst with clear on poll = false holds a problem for ever

In worst mode a recovery does not replace the problem it recovered from, and with draining off nothing else marks where the last poll stopped. A key that once went CRITICAL therefore keeps reporting CRITICAL until it is deleted (DELETE /api/v2/results/{key}) or expires via max age. Pair worst with the default clear on poll = true, or use mode = last when several consumers share one agent.

Privileges

The endpoints are guarded by three privileges, none of which are part of any bundled role except full. Grant them explicitly to whoever polls:

nscp web add-role --role poller --grant results.list,results.get,login.get

List results

Returns every cached result, sorted by key.

Key Value
Verb GET
Address /api/v2/results
Privilege results.list

With clear on poll = true (the default) this drains what it returns — see Polling and draining.

Parameters

All filters are optional and combined with AND. channel, host, command and alias match exactly (case-insensitively) — ?host=srv1 does not also return srv10.

Parameter Description
channel Only results that arrived on this channel
host Only results from this host
command Only results for this command
alias Only results submitted under this alias
status Comma-separated list of ok, warning, critical, unknown (or 03)

An unrecognised status value is answered with 400 Bad Request rather than being ignored.

Response

[
    {
        "key": "srv1/check_drivesize",
        "index": 42,
        "channel": "WEB",
        "host": "srv1",
        "source": "srv1",
        "command": "check_drivesize",
        "alias": "",
        "status": 0,
        "result": "OK",
        "message": "OK All 2 drive(s) are ok",
        "perf": "'/ used'=12.5GB;40;45;0;50",
        "count": 17,
        "first_seen": 1757145000,
        "last_seen": 1757145900,
        "result_seen": 1757145900,
        "first_seen_date": "2026-09-06 10:30:00",
        "last_seen_date": "2026-09-06 10:45:00",
        "result_seen_date": "2026-09-06 10:45:00",
        "age": 12,
        "result_url": "https://localhost:8443/api/v2/results/srv1/check_drivesize"
    }
]
Field Description
key The cache key, built from primary index
index Bumped on every update; the lowest index is the stalest entry
status / result Nagios status as a number (0–3) and as a word
perf Performance data in Nagios plugin format, empty when there is none
count How many times this key has reported since it was first seen, including submissions worst mode suppressed
first_seen When the key was first cached, seconds since the unix epoch
last_seen When the key last submitted anything
result_seen When the result being served arrived; equals last_seen in last mode
*_date The same instants as local time, for humans
age Seconds since the key last reported

The number of results returned is sent as an X-Result-Count header, and X-Result-Drained says whether the poll consumed them.

Example

curl -s -k -u admin "https://localhost:8443/api/v2/results?status=warning,critical" | python -m json.tool

Fetch one result

Key Value
Verb GET
Address /api/v2/results/{key}
Privilege results.get

The key is used verbatim as the rest of the path, so a key containing / (as the default ${host}/${alias-or-command} produces) works unescaped. An unknown or expired key is answered with 404 Not Found. This is a lookup rather than a poll: it never drains, whatever clear on poll says.

Example

curl -s -k -u admin https://localhost:8443/api/v2/results/srv1/check_drivesize

Delete results

Drops cached results. Use this to reset the cache after a maintenance window, or to retire a key that will never report again.

Key Value
Verb DELETE
Address /api/v2/results or /api/v2/results/{key}
Privilege results.delete

Deleting the collection empties it; deleting a single key that is not cached is answered with 404 Not Found. Emptying the cache also resets what mode = worst is holding, so the next result for a key starts a fresh worst-of.

Response

{ "removed": 12 }

Example

curl -s -k -u admin -X DELETE https://localhost:8443/api/v2/results
curl -s -k -u admin -X DELETE https://localhost:8443/api/v2/results/srv1/check_drivesize