NSClient

NSClient++

Release 0.12.3

Protocols, Security and bug fixes

This release rolls up everything since the last stable: five pre-releases (0.11.31, 0.11.32, 0.11.33, 0.12.1, 0.12.2) plus the latest in-development changes.

The headline themes are:

  1. New monitoring scenarios — first-class Checkmk and Icinga 2 integration, plus a real check_net family.
  2. A modern Web UI and REST API — events, metadata, settings DELETE, filterable lists, dedicated widgets for PDH counters and real-time filters.
  3. Hardened by default0.12.2 is a security release that closes listener defaults that used to be silently permissive (empty allowed hosts, plaintext check_nt, query-string tokens, etc.).
  4. Many long-standing check fixescheck_service, check_process, check_files, check_drivesize, check_uptime, CheckLogFile, and the shared filter/threshold engine all behave correctly now.

> Read the Breaking changes section before upgrading — several long-standing-but-incorrect > behaviours have been corrected and a number of listener defaults are now fail-closed. If you have an existing > configuration, plan to review it.


TL;DR for end users

  • New scenario: Checkmk agent integration. Point a Checkmk site at port 6556 and you get a native-looking agent dump. See scenarios/check-mk.md.
  • New scenario: Icinga 2 passive submission. A new IcingaClient module submits passive results to Icinga 2's REST API as an alternative to NSCA / NRDP.
  • New scenario: NSCA-ng. A new hardened NSCAngClient with PSK and AEAD-first cipher selection.
  • Native cross-platform network checks: check_tcp, check_dns, check_http, check_ntp_offset, check_connections.
  • Native Windows registry checks: check_registry_key, check_registry_value.
  • HTTP proxy support for every HTTP-based client (NRDP, Elastic, Op5, Icinga, the configuration loader, ...).
  • Windows ROOT trust store auto-export — HTTPS-bound checks validate certificates against the system trust store automatically.
  • A modern Web UI with filterable lists, settings diff, dashboard, and dedicated CheckSystem widgets.
  • New REST endpoints: GET/DELETE /api/v2/events, GET /api/v2/metadata, DELETE /api/v2/settings/.... Covered in api/rest/.
  • Linux real-time metrics — the same background CPU/memory/disk/ network/load sampling that Windows has had for years.
  • Many bug fixes in check_service, check_process, check_files, CheckLogFile, the filter/threshold engine and the HTTP stack.

Major new features

Checkmk agent integration

NSClient++ can now serve a Checkmk-compatible agent dump on TCP port 6556. A real Checkmk site can register the host with tag_agent = cmk-agent, discover services, and run checks — no proxy, no NSCA gateway.

Enable it:

[/modules]
CheckMKServer = enabled
LUAScript = enabled
CheckSystem = enabled
CheckDisk = enabled
CheckHelpers = enabled

[/settings/check_mk/server]
port = 6556
allowed hosts = 127.0.0.1, 
submission ttl = 60          ; seconds, default 60
mrpe channel = check_mk-mrpe
local channel = check_mk-local

Out-of-the-box sections (no extra config):

Section Contents
<<>> Version, OS, hostname
<<>> Unix epoch (Windows clock-skew check)
<<>> Seconds since boot (read from internal metrics store)
<<>> MemTotal:/MemFree:/SwapTotal:/SwapFree: (from metrics store)
<<>> Per-volume size/used/free/mountpoint (Windows)
<<>> name state/start_type display_name per Windows service
<<>> (user,vsz_kb,rss_kb,cputime,pid) cmdline per process

Expose any nscp check as a Checkmk service under <<>>:

[/settings/check_mk/server/local]
CPU Load = command=check_cpu warn=load>80 crit=load>95
Disk C = command=check_drivesize drive=C: "warn=free<20%" "crit=free<10%"

MRPE relay under <<>>:

[/settings/check_mk/server/mrpe]
Uptime = command=check_uptime warn=uptime<2d
Memory = command=check_memory type=committed warn=used>80% crit=used>90%

Documentation: https://nsclient.org/docs/scenarios/check-mk.md`.

IcingaClient — Icinga 2 REST API submission

A new client module submits passive check results directly to an Icinga 2 master/satellite via the /v1/actions/process-check-result REST endpoint, as an alternative to NSCA or NRDP.

[/modules]
IcingaClient = enabled

[/settings/IcingaClient/targets/default]
address = https://icinga2.example.com:5665
username = nscp
password = secret
hostname = ${hostname}
nscp client --module IcingaClient \
            --command submit_icinga \
            --address https://icinga2.example.com:5665 \
            --username nscp --password secret \
            --command heartbeat \
            --result 0 \
            --message "Hello from NSClient++" \
            --ensure-objects

NSCA-ng client

A new NSCAngClient module ships a hardened NSCA-ng submission client with PSK support, AEAD-first cipher selection, and connection retry logic.

Native support for Windows CA-store

On startup NSClient++ now exports the machine's ROOT certificate store as a single PEM bundle, so any check that does TLS (check_http, IcingaClient, NRDP, ...) can validate certificates against the trust store the rest of Windows already uses.

check_http url=https://www.ibm.com
OK: https://www.ibm.com -> 303 ok (0B in 33ms)

check_http url=https://self-signed.badssl.com/
CRITICAL: https://self-signed.badssl.com/ -> 0 error: Failed to connect ... certificate verify failed

CheckNet — five new (cross-platform) checks

CheckNet graduated from a placeholder into a full network-check module. All five commands work over NRPE as well as locally:

  • check_tcp — open a TCP socket to one or more host/port pairs, optionally send a payload and require an expected substring.
  • check_dns — resolve a hostname and optionally assert which addresses come back.
  • check_http — fetch one or more URLs, check status code, response time and body content; supports custom headers and user-agent.
  • check_ntp_offset — query one or more NTP servers and alert on offset / stratum.
  • check_connections — Windows TCP/UDP connection table inspection (counts per protocol/family/state).
check_tcp host=smtp.gmail.com port=25 send="EHLO nsclient.org" expect="250"
check_dns host=google.com expected-address=172.217.20.174
check_http url=https://nsclient.org/ expected-body="NSClient" \
    "warn=time > 500 or code >= 400" \
    "crit=time > 2000 or code >= 500 or result != 'ok'"
check_ntp_offset "servers=0.pool.ntp.org,1.pool.ntp.org" timeout=2000
check_connections "filter=protocol = 'tcp' and state = 'TIME_WAIT'" \
    "warn=count > 200" "crit=count > 1000"

CheckSystem (Windows) — registry checks

Two new commands let you monitor the Windows registry directly from NSClient++ instead of relying on external scripts. They support recursion, exclude lists, 32/64-bit (WoW64) views, custom filters and the usual warn=/crit= expression syntax.

  • check_registry_key — verify that a key exists, count sub-keys/values, watch its last-write time.
  • check_registry_value — read a single value assert its type, size or content.
check_registry_key "key=HKLM\Software\NSClient" \
    "warn=age > 7d" "crit=age > 30d or not exists"

check_registry_key "key=HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" \
    recursive max-depth=1 exclude=KB5005463 exclude=KB5005539

check_registry_value "key=HKLM\System\CurrentControlSet\Services\W32Time\Config" \
    value=MaxPollInterval "warn=int_value > 14" "crit=int_value > 17"

CheckSystem — check_os_updates (Windows)

A new check using the Windows Update Agent (WUA) reports pending OS updates. By default any pending update returns warning; thresholds let you alert only on security/critical:

check_os_updates "warning=important > 0" "critical=security > 0 or critical > 0"

CheckSystem (Linux) — real-time metrics

The Linux build of CheckSystem now ships with the same real-time metric collection that has been available on Windows for a long time: CPU, memory, disk, network and load are sampled in the background and exposed both to dashboards/metrics and to real-time filters (filter=... rules that fire when a threshold is crossed). Existing real-time filter configuration just works on Linux now.

Real-time filter metrics

CheckSystem's real-time filters now publish per-filter match and error counts under system.realtime..fired / system.realtime..errors. Visible via:

  • The metrics REST endpoint (/api/v2/metrics + filter)
  • Prometheus scrape
  • The new Metrics() Lua API in default_check_mk.lua

Useful for spotting filters that never fire (typo in the where-clause) or filters that always error (broken expression).

CheckDisk — check_single_file

A focused variant of check_files for inspecting a single, known path. Compared to using check_files for the same job:

  • Only one required argument (file=).
  • A clear error when the input is empty.
  • UNKNOWN: File not found: when the file is missing — instead of the empty-set / "No files found" workflow.
  • A useful default detail-syntax so a no-threshold run is informative on its own.
check_single_file file=C:/windows/WindowsUpdate.log "warn=age > 5m" "crit=age > 1h"
CRITICAL: WindowsUpdate.log (size=276, age=917)

CheckDisk — filesystem filtering for check_drivesize

check_drivesize can now filter drives by filesystem type — useful for excluding tmpfs, nfs, etc.

check_drivesize drive=* "filter=fs = 'NTFS'"

check_nscp_update

A new check command queries the GitHub releases API (with caching) and reports whether the running NSClient++ is up to date.

HTTP proxy support across every HTTP client

NSClient++ can now route HTTP and HTTPS traffic through a corporate proxy. The same surface is used by every component built on the internal http::simple_client (NRDPClient, ElasticClient, Op5Client, IcingaClient, the remote boot.ini loader, ...).

For HTTPS targets the client opens a CONNECT tunnel to the proxy, validates the proxy's response, and only then performs the TLS handshake — so a single setting covers both http:// and https:// URLs.

Two new options on every HTTP client command and target:

Option Purpose
proxy Proxy URL — scheme://[user:pass@]host[:port]/. Empty value disables the proxy.
no-proxy Comma-separated list of hosts that bypass the proxy. A leading . is a suffix match.
[/settings/NRDP/client/targets/nagios]
address = https://nagios.example.com/nrdp/
token = mytoken
proxy = http://proxy.corp.example:3128/
no proxy = localhost,127.0.0.1,.internal

Configuration loader (boot.ini):

[proxy]
url = http://proxy.corp.example:3128/
no_proxy = localhost,127.0.0.1,.internal

Notes / limits:

  • Only the http:// proxy scheme is supported. socks5:// / https:// proxies are not.
  • No automatic detection of system proxy settings (HTTP_PROXY env vars, WinINET / WPAD). The proxy must be configured explicitly.
  • On 407 Proxy Authentication Required the proxy's response body is captured in the error message.

Web UI / REST API expansion

New web routes:

Route Method Purpose
/api/v2/events GET List buffered real-time events
/api/v2/events DELETE Drain (returns + clears) the event buffer in one call
/api/v2/metadata GET Module/setting metadata index
/api/v2/metadata/counters GET List of available PDH counters
/api/v2/metadata/channels GET List of registered submission channels
/api/v2/settings/ DELETE Remove a settings key or path (staged delete; survives restart)

The settings store gained staged deletion: a DELETE is recorded so that subsequent reads of the deleted key/path return "not present" until the change is saved. Stops a deleted-but-not-yet-saved key from being re-resurrected by a concurrent read.

Web UI refresh

The bundled web interface has been heavily reworked:

  • Modern theme with active-navigation highlighting and a redesigned login page.
  • Filterable lists for Modules, Queries and Settings.
  • Settings diff dialog — the "settings changed" widget can now show exactly which keys changed.
  • CheckSystem settings UI got dedicated widgets for PDH counters and real-time filters: a counter picker that hits /api/v2/metadata/counters, "Add filter" / "Add counter" dialogs, and a live preview of metric values pulled from the metrics endpoint.

If you've been editing real-time filters in nsclient.ini by hand, the web UI is now a much faster way to do it.

SMTPClient rewrite

The SMTPClient module has been substantially rewritten with proper SMTP handling, integration tests, and a Python-based test harness.

Smaller features

  • nscp settings --sort — produce stable, sorted output, useful for diffing exported settings between hosts.
  • Performance threshold min/max bounds — perfdata threshold expressions can declare minimum and maximum bounds, propagated into emitted perfdata:
    check_pdh "counter=\\Processor(_Total)\\% Processor Time" \
            "perf-config=*(minimum:0;maximum:100)"
  • Timezone-aware check_uptime and Schduler — applies a timezone cache on both Windows and Unix, so absolute boot-time output and cron expressions agree with the host's local time.
  • WEBServer cookie attribute supportSecure, HttpOnly, SameSite, Path, Domain, Expires, Max-Age.
  • WEBServer password hashing with constant-time verification — removes the timing oracle on the previous plaintext equality check.
  • WEBServer authentication rate limiter — per-source throttling of failed authentication attempts:
    [/settings/WEB/server]
    auth rate limit max failures   = 10   ; 0 disables the limiter
    auth rate limit block seconds  = 60

Filter engine — stable summary thresholds

These changes touch the shared filter / threshold engine and therefore affect every modular check (check_files, check_service, check_process, check_eventlog, ...).

Stable count / total / *_count in warn= / crit=

warn= / crit= were evaluated during iteration. Summary variables such as count therefore exposed their running value instead of the final post-iteration value, so a mixed expression like

crit = state = 'hung' OR count < 5

mis-fired on the very first row (count == 1 < 5) regardless of how many rows ultimately matched. Per-row evaluation is now deferred: matched rows are recorded during iteration, and the warn/crit/ok engines run once the summary state is final.

Mixed warn= / crit= evaluated when no rows match

If a filter excluded every row, mixed expressions like crit = state = 'stopped' OR count = 0 were skipped entirely — leaving the check OK in the empty case. They are now evaluated with object-bound variables defaulting to false and summary variables at their final values, so the check correctly returns CRITICAL when the service is missing.

Quieter, more predictable expression evaluation

  • Operators audited so is_unsure propagates consistently; invalid-type comparisons resolve to unsure-false instead of erroring.
  • String variables on no-object cases now return an empty string with is_unsure=true and produce a warning in the log instead of an error per row — log volume on complex queries drops dramatically.
  • Removed the misleading "most likely mutating" warnings.
  • Substantial new test coverage.

check_service and check_process fixes (Windows)

  • "Failed to enumerate service: 6f7" on busy hosts — enumeration is now properly looped until the SCM signals end-of-data.
  • perf-syntax=none actually suppresses perfdatacheck_service used to emit empty perfdata aliases ( ''=4;0;1 ''=4;0;1 ...), blowing past NRPE size limits.
  • No more TODO leaking into ${desc}check_service service=Spooler used to render as OK: Spooler: TODO. Now: OK: Spooler: Print Spooler.
  • delayed only reported for SERVICE_AUTO_START — manual / boot / system / disabled services no longer randomly show up as delayed.
  • check_process sees protected / cross-user processes as NETWORK SERVICE — a PROCESS_QUERY_LIMITED_INFORMATION fallback is now attempted, so winlogon.exe, csrss.exe etc. no longer report CRITICAL: =stopped when the agent runs unprivileged.
  • Realtime check_process is now case-insensitive, matching the active path and Windows itself.

check_files fixes

  • #730max-depth=0 now scans the top directory only (was: bail out before scanning anything, returning "no files found").
  • #598 — Non-ASCII paths (accented letters, CJK, ...) are no longer silently mangled by mismatched codepage conversions.
  • #613 — Top-level paths that cannot be opened now produce UNKNOWN: Path was not found: instead of being hidden behind the configured empty-state.
  • #605 — NTFS junctions / symlinks / mount points are now skipped during recursion, preventing double-counts on self-referential trees.
  • #717 — The legacy CheckFiles shim now sets empty-state=ok when translating, restoring 0.4-era behaviour for legacy calls that find zero files.

Other check / module fixes

  • CheckDisk resilience — an error on a single unavailable volume no longer aborts the entire check_drivesize run.
  • #581CheckLogFile honours the line-split argument (previously hard-coded to \n); multi-character delimiters such as \r\n are handled correctly. Real-time seek behaviour fixed; CRLF handling harmonised.
  • #589 — Time/duration arguments such as time=3000foobar or time=3000mfoobar are no longer silently accepted; malformed inputs are rejected with a clear error.
  • #669 — The literal U (Nagios "undefined") in performance data is preserved end-to-end instead of being coerced to 0. Only an exact U, u, U% or u% token matches.
  • NSCA wire timestamps are now correctly built in UTC. Both server (IV packet) and client (data packet) used to derive seconds-since-epoch from second_clock::local_time(), which drifted by the host's TZ offset. A timezone setting on both ends allows legacy interop with agents that emit local-clock-as-Unix-time stamps.
  • Metrics collection regression fixed (some metrics were silently dropped).
  • Op5Client / ElasticClient unified on the new HTTP client; 401 path fixed; reponse → response typos corrected.
  • Gracefully handle non-numeric NSClient command codes.
  • TLS support fixes; better randomness for encryption; race condition fixes; boundary checks for various network payloads and reading certificates.
  • NRDP integration tests added; new nrdp client alias.

HTTP refactor

  • HTTP request and response are now distinct types instead of one shared bag.
  • Chunked transfer-encoding is decoded properly. check_http against servers using Transfer-Encoding: chunked ( most modern reverse proxies, Icinga 2, Kubernetes ingress, ...) now returns the full body instead of a truncated/garbled one. The IcingaClient module relies on this.
  • Header storage is normalised — case-insensitive lookup, no more duplicate-header surprises.

Security hardening

The 0.12.2 release is a security-focused pass. These do not change documented behaviour for well-formed traffic but close down attacker-controlled edge cases.

DoS / resource-exhaustion limits

  • Authorization header capped at 8 KiB to mitigate amplification.
  • Per-connection parser buffer cap to prevent memory pinning from oversized or never-completed requests.
  • Session token cap with eviction to prevent unbounded memory growth.
  • Payload lengths below the protocol minimum are rejected before allocation.
  • Path expansion now detects cycles and refuses to recurse, preventing stack overflow on pathological configurations.

NSCA hardening

  • Packet version is checked.
  • Timestamp validation tightened to mitigate replay attacks.

Log/output injection prevention

Control characters are stripped from values before they are written to external sinks, removing log/protocol-injection vectors:

  • Log file entries (file names and messages)
  • syslog messages (CR/LF/NUL stripped)
  • Graphite metric paths and values
  • HTTP response headers (header keys and values)
  • log_status is now JSON-serialised so attacker-controlled fields cannot inject extra structured fields.

Filesystem / process safety

  • PID file creation hardened against symlink attacks; exclusive access enforced.
  • Archive extraction has a zip-slip guard that validates entry paths and refuses traversal sequences.
  • Module and script names are validated to prevent path traversal at load time.
  • Argument substitution in external scripts is isolated to prevent command injection through user-controlled tokens.

Cryptography / TLS

  • HTTPS now logs explicitly when no certificate is present and warns on HTTP fallback in production.
  • SSL connections enable hostname verification by default.
  • Auto-generated passwords use OpenSSL RAND_bytes (cryptographically secure) instead of the previous predictable generator.
  • Sensitive values are no longer logged at debug level.
  • check_nt password compare is constant-time.

Breaking changes

> Read this section carefully. Some changes are listener defaults that are now fail-closed; some are corrections to > long-standing buggy behaviour; some are internal API changes for out-of-tree modules.

Listeners default to safer behaviour

  1. Empty allowed hosts now rejects all connections. Previously treated as "allow any source". To genuinely expose the agent to any source, set it explicitly:
    allowed hosts = 0.0.0.0/0,::/0
  2. check_nt (NSClientServer) defaults to ssl = true. The legacy check_nt protocol carries the password in every request. The listener will not refuse to start if TLS is off, but it will log a warning. To keep the old plaintext behaviour for legacy clients, set ssl = false explicitly in [/settings/NSClient/server].
  3. check_nt: the literal password None no longer authenticates. Empty server passwords now reject all requests. Errors are also genericised (ERROR: Bad request.) to remove the online password-guessing oracle.
  4. WEBServer: /auth/token and /auth/logout are removed (HTTP 410). They accepted the password and session token as URL query parameters, leaking credentials into browser history and proxy logs. Migrate to:
    • POST /api/v2/login with Authorization: Basic to obtain a token
    • DELETE /api/v2/login with Authorization: Bearer to log out
  5. WEBServer: ?TOKEN= / ?__TOKEN= query-string token auth removed. Send the token in a header instead: Authorization: Bearer, TOKEN:, or X-Auth-Token:.
  6. WEBServer: anonymous access is now opt-in. A role named anonymous registered in settings is silently ignored unless the new allow_anonymous flag is enabled.
  7. WEBServer: existing admin user is no longer overwritten on restart. Deployments that relied on the password being reset to the default at boot must adapt.

Scheduler — cron expressions evaluate in local time by default (#570)

The Scheduler module previously used UTC, so 40 15 * * * fired at 15:40 UTC regardless of host TZ. The default has changed to local time, matching standard cron semantics. Hour and minute fields will shift accordingly on non-UTC hosts.

A new timezone setting under [/settings/scheduler] controls the reference clock:

[/settings/scheduler]
timezone = local                          ; default — standard cron semantics
; timezone = utc                          ; restore the pre-0.12 behaviour
; timezone = EST-05EDT,M3.2.0,M11.1.0     ; any POSIX TZ string is honoured

IANA names such as Europe/Stockholm are not supported — use the POSIX form. Unparseable values fall back to UTC and surface as UTC? in any timezone label.

Filter / threshold engine

  1. warn= / crit= no longer fire mid-iteration on running counts. Configurations "tuned" against the buggy early-fire will produce different results.
    crit = state = 'hung' OR count < 5
    # Old: CRITICAL on the very first row (count == 1).
    # New: CRITICAL only if any row is 'hung' OR final count < 5.
  2. Mixed warn= / crit= now evaluate when no rows match.
    crit = state = 'stopped' OR count = 0
    # Old: OK when nothing matched (count = 0).
    # New: CRITICAL when nothing matched (count = 0).

    If your old config implicitly treated "empty" as OK, add a count > 0 AND ... guard or move the empty-case logic into a dedicated check.

Check-specific corrections

  1. check_service: delayed is no longer reported for non-auto services. Filters that matched start_type = 'delayed' on Manual / Boot / System / Disabled services will stop matching. To alert on "any non-running service that isn't disabled":
    filter = start_type IN ('auto','delayed','boot','system') AND state != 'running'
  2. Realtime check_process is now case-insensitive. A rule that intentionally matched only an exact casing will now match all variants (almost certainly the desired behaviour).
  3. check_service: ${desc} no longer returns the literal TODO. Use the real display name.
  4. check_service: perf-syntax=none actually suppresses perfdata. Backends that consumed the empty-aliased entries (highly unlikely) will see them disappear.

check_files — corner cases changed

  1. max-depth=0 now scans the top directory instead of returning empty (#730).
  2. Missing paths now return UNKNOWN instead of OK / empty (#613).
  3. NTFS junction loops are no longer double-counted (#605).
  4. Legacy CheckFiles calls that previously returned UNKNOWN on empty results will now return OK (#717).

Configuration / startup

  1. CheckExternalScripts: malformed alias commands are refused at startup. The fallback "split-on-space" parser has been removed. Aliases whose command line does not parse cleanly are refused with an error in the log instead of being silently registered with surprising tokenisation. Review your logs after upgrading.

Internal API (out-of-tree module authors)

  1. HTTP request/response API changed. Internal C++ types http::request / http::response are now distinct, headers are case-insensitive, and chunked decoding happens transparently. Out-of-tree modules linked against the old shared bag type need a small adjustment:

    // before
    http::packet pkt = client.send(...);
    auto body = pkt.body;
    
    // after
    http::response resp = client.send(http::request{...});
    auto body = resp.body();   // chunked decoding already applied

Documentation reorganisation

  1. The documentation tree was restructured (concepts/, checks-in-depth/, scenarios/, tutorial/, reference/ are now clearly separated). Bookmarks and external links may need updating.

Upgrade checklist

  1. Audit allowed hosts on every node — empty values now reject everything.
  2. check_nt (NSClientServer) now defaults to ssl = true. If your clients don't speak TLS, set ssl = false explicitly. Either way the listener will log a warning at startup if TLS is off or a password is configured, recommending a switch to REST or NRPE.
  3. Replace any client that calls /auth/token or /auth/logout with the /api/v2/login flow.
  4. Replace any client that passes ?TOKEN= / ?__TOKEN= in the query string with a header-based token.
  5. Scheduler cron expressions on non-UTC hosts will shift to local time. Either update them or set [/settings/scheduler] timezone = utc to restore the previous behaviour.
  6. Review check_service / check_process / check_files filters that may have relied on the corrected behaviours listed above.
  7. Restart the service and review the log for new "refused alias" or "rejected connection" warnings — these flag configurations that were previously silently accepted.

No configuration migration is required for the new HTTP proxy keys, the Checkmk server, the Icinga client, the NSCA-ng client, or the new checks — they are all opt-in.

Full Changelog: https://github.com/mickem/nscp/compare/0.12.2...0.11.30

nscp-0.12.3.tar.gz

Release tarball
application/gzip 2026-05-11 Download from Github

check_nsclient-0.12.3-rocky-10-arm64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-rocky-10-x64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-rocky-9-arm64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-rocky-9-x64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-ubuntu-24.04-arm64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-ubuntu-24.04-x64

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-Win32.exe

raw 2026-05-11 Download from Github

check_nsclient-0.12.3-x64.exe

raw 2026-05-11 Download from Github

NSCP-0.12.3-docs.zip

raw 2026-05-11 Download from Github

NSCP-0.12.3-rocky-10-aarch64.rpm

raw 2026-05-11 Download from Github

NSCP-0.12.3-rocky-10-x86_64.rpm

raw 2026-05-11 Download from Github

NSCP-0.12.3-rocky-9-aarch64.rpm

raw 2026-05-11 Download from Github

NSCP-0.12.3-rocky-9-x86_64.rpm

raw 2026-05-11 Download from Github

NSCP-0.12.3-ubuntu-24.04-amd64.deb

raw 2026-05-11 Download from Github

NSCP-0.12.3-ubuntu-24.04-arm64.deb

raw 2026-05-11 Download from Github

NSCP-0.12.3-Win32-legacy-xp.msi

raw 2026-05-11 Download from Github

NSCP-0.12.3-Win32-legacy-xp.zip

raw 2026-05-11 Download from Github

NSCP-0.12.3-Win32.msi

raw 2026-05-11 Download from Github

NSCP-0.12.3-Win32.zip

raw 2026-05-11 Download from Github

NSCP-0.12.3-x64.msi

raw 2026-05-11 Download from Github

NSCP-0.12.3-x64.zip

raw 2026-05-11 Download from Github

Release 0.12.2

What's Changed

This release follows 0.12.1 and is a security-hardening release. Many listener-side defaults are now safer, several long-standing bugs are fixed, and a handful of new modules and commands ship. Read the breaking changes section before upgrading — a few defaults changed in ways that can refuse connections or fail startup if your existing configuration relied on the old behaviour.


⚠️ Breaking changes

Empty allowed hosts now rejects all connections (fail-closed)

Previously an empty allowed hosts = setting was treated as "allow any source". It now rejects every connection and logs the reason.

If you genuinely want to expose the agent to any source, set it explicitly:

allowed hosts = 0.0.0.0/0,::/0

The change is to prevent configurations from drifting to empty by mistake silently turning into open listeners.

Scheduler cron expressions now evaluate in local time by default (fixes #570)

The Scheduler module previously used UTC as its reference clock, so a cron entry like 40 15 * * * fired at 15:40 UTC regardless of the host time zone. The default has changed to local time, which matches standard cron semantics — that entry now fires at 15:40 host-local time. Hour and minute fields in cron schedules will shift accordingly on non-UTC hosts.

A new timezone setting under the [/settings/scheduler] section controls the reference clock:

[/settings/scheduler]
timezone = local                          ; default — standard cron semantics
; timezone = utc                          ; restore the pre-0.12 behaviour
; timezone = EST-05EDT,M3.2.0,M11.1.0     ; any POSIX TZ string is honoured

Accepted values match the existing NSClient++ timezone semantics (shared with check_uptime): local (default), utc/gmt, or any POSIX TZ string parseable by Boost. IANA names such as Europe/Stockholm are not supported — use the POSIX form. Unparseable values fall back to UTC and surface as UTC? in any timezone label so the misconfiguration is visible.

check_nt (NSClientServer): TLS is on by default

The legacy check_nt protocol carries the password in every request, so the listener now defaults to ssl = true. The server does not refuse to start if TLS is off — check_nt's SSL path has always been best-effort and many third-party clients never implemented it — but it will log a startup warning when TLS is off and another when a password is configured, suggesting a switch to NRPE or NSCA-ng.

If you need to keep the old plaintext behaviour for legacy clients, set ssl = false explicitly in the [/settings/NSClient/server] section. The change is intentionally an opt-out: defaulting on makes "no transport security" a deliberate, visible choice rather than a silent default.

check_nt: the literal password None no longer authenticates

Previously, if no server password was configured, a client sending the literal word None was accepted. An empty server password now rejects all requests outright. Configure a password (or, preferably, switch to a modern protocol such as NRPE or NSCA-ng).

The listener now also returns a generic ERROR: Bad request. instead of distinct Invalid password / No command specified errors, removing the oracle that allowed online password guessing.

WEBServer: /auth/token and /auth/logout removed (HTTP 410)

These legacy endpoints accepted the password and session token as URL query parameters, which leaked credentials into browser history, proxy logs, and Referer headers. They now return 410 Gone.

Migrate to:

  • POST /api/v2/login with Authorization: Basic to obtain a token
  • DELETE /api/v2/login with Authorization: Bearer to log out

WEBServer: ?TOKEN= / ?__TOKEN= query-string token auth removed

For the same reason, supplying the session token as a URL query parameter on any other endpoint is no longer accepted. The server logs the rejection and returns 403. Send the token in one of the header-based forms instead:

  • Authorization: Bearer
  • TOKEN:
  • X-Auth-Token:

WEBServer: anonymous access is now opt-in

A role named anonymous registered in settings is silently ignored unless the new allow_anonymous flag is enabled. Two knobs are required to expose endpoints without authentication; one accidental setting is no longer enough.

CheckExternalScripts: malformed alias commands are refused at startup

The fallback "split-on-space" parser used when the principal command parser failed has been removed. Aliases whose command line does not parse cleanly are now refused with an error in the log instead of being silently registered with surprising tokenisation. Review your logs after upgrading and fix any flagged alias definitions.

WEBServer: existing admin user is no longer overwritten

If you manually configured the admin user, it will no longer be overwritten on subsequent boots. This may change behaviour for deployments that relied on the password being reset to the default on restart.


✨ New modules and features

NSCA-ng client (#1255)

A new NSCAngClient module ships, providing a hardened NSCA-ng submission client with PSK support, AEAD-first cipher selection, and connection retry logic.

SMTPClient module rewrite

The SMTPClient module has been substantially rewritten with proper SMTP handling, integration tests, and a Python-based test harness.

check_nscp_update command

A new check command that queries the GitHub releases API (with caching) and reports whether the running NSClient++ is up to date. The User-Agent includes the current version.

nscp settings --sort

The settings CLI gained a --sort flag to produce stable, sorted output — useful for diffing exported settings between hosts.

Performance threshold min/max bounds

Performance data threshold expressions can now declare minimum and maximum bounds, propagated into the emitted perfdata.

check_pdh "counter=\\Processor(_Total)\\% Processor Time"  "perf-config=*(minimum:0;maximum:100)"

CheckDisk: filesystem filtering for drive size checks

check_drivesize can now filter drives by filesystem type (for example, to exclude tmpfs or nfs mounts).

check_drivesize drive=* "filter=fs = 'NTFS'"

Timezone-aware check_uptime

check_uptime now correctly applies a timezone cache on both Windows and Unix, so absolute boot-time output and filter expressions agree with the host's local time.

WEBServer: cookie attribute support

Cookies can now declare Secure, HttpOnly, SameSite, Path, Domain, Expires, and Max-Age attributes.

WEBServer: password hashing with constant-time verification

User passwords are now hashed and verified with a constant-time comparison, removing the timing oracle on the previous plaintext equality check.

WEBServer: authentication rate limiter

A new per-source rate limiter throttles failed authentication attempts to slow online brute-force attacks. Two settings tune (or disable) it:

[/settings/WEB/server]
auth rate limit max failures   = 10   ; 0 disables the limiter
auth rate limit block seconds  = 60

🐛 Bug fixes

  • #581CheckLogFile now honours the line-split argument (previously hard-coded to \n); multi-character delimiters such as \r\n are also handled correctly.
  • #589 — Time/duration arguments such as time=3000foobar or time=3000mfoobar are no longer silently accepted; malformed inputs are now rejected with a clear error.
  • #669 — The literal U (Nagios "undefined") in performance data is preserved end-to-end instead of being coerced to 0. Only an exact U, u, U%, or u% token matches; values like Unknown or Unicorn no longer trigger the marker.
  • #570 — Scheduler cron expressions now run in local time (see breaking change above).
  • #419, #595, #370, #205 — Several long-standing settings/format issues, fixed alongside nscp settings --sort.
  • CheckLogFile real-time seek behaviour fixed; CRLF handling harmonised.
  • Metrics collection regression fixed (some metrics were silently dropped).
  • NSCA wire timestamps are now correctly built in UTC. Previously both the server (IV packet) and client (data packet) emitted seconds-since-epoch derived from second_clock::local_time(), which drifted by the host's TZ offset. The new replay-window check exposed the bug as skew=-7200s-style errors on non-UTC senders; the wire format itself was always supposed to be Unix time. A timezone setting is available on both ends for legacy interop:
    • [/settings/NSCA/server] timezone = utc (default)
    • [/settings/NSCA/client/targets/] timezone = utc (default; also --timezone on the CLI) Set both ends to local (or any POSIX TZ string) only when peering with an old agent that emits local-clock-as-Unix-time stamps. Both ends must agree.
  • Connection and process logging is more verbose at error/timeout boundaries to aid traceability.

🔒 Security hardening (defense in depth)

These do not change documented behaviour for well-formed traffic but close down attacker-controlled edge cases.

DoS / resource-exhaustion limits

  • Authorization header capped at 8 KiB to mitigate amplification.
  • Per-connection parser buffer cap to prevent memory pinning from oversized or never-completed requests.
  • Session token cap with eviction to prevent unbounded memory growth.
  • Payload lengths below the protocol minimum are rejected before allocation.
  • Path expansion now detects cycles and refuses to recurse, preventing stack overflow on pathological configurations.

NSCA hardening

  • Packet version is checked.
  • Timestamp validation tightened to mitigate replay attacks.

Log/output injection prevention

Control characters are stripped from values before they are written to external sinks, removing log/protocol-injection vectors:

  • Log file entries (file names and messages)
  • syslog messages (CR/LF/NUL stripped)
  • Graphite metric paths and values
  • HTTP response headers (header keys and values)
  • log_status is now JSON-serialised so attacker-controlled fields cannot inject extra structured fields

Filesystem / process safety

  • PID file creation hardened against symlink attacks; exclusive access enforced.
  • Archive extraction now has a zip-slip guard that validates entry paths and refuses traversal sequences.
  • Module and script names are validated to prevent path traversal at load time.
  • Argument substitution in external scripts is isolated to prevent command injection through user-controlled tokens.

Cryptography / TLS

  • HTTPS now logs explicitly when no certificate is present and warns on HTTP fallback in production.
  • SSL connections enable hostname verification by default.
  • Auto-generated passwords use OpenSSL RAND_bytes (cryptographically secure) instead of the previous predictable generator.
  • Sensitive values are no longer logged at debug level.

Other

  • simpleini.h switched from sprintf to snprintf.
  • Windows credential blob size now correctly accounts for wchar_t width.
  • check_nt password compare is constant-time (see breaking changes).

Upgrading

  1. Audit allowed hosts on every node — empty values now reject everything.
  2. check_nt (NSClientServer) now defaults to ssl = true. If your clients don't speak TLS, set ssl = false explicitly. Either way the listener will log a warning at startup if TLS is off or a password is configured, recommending a switch to REST or NRPE — informational, not a hard failure.
  3. Replace any client that calls /auth/token or /auth/logout with the /api/v2/login flow.
  4. If you use the Scheduler module with cron expressions on a non-UTC host, expect schedules to shift to local time. Either update them to the host's local time, or set [/settings/scheduler] timezone = utc to restore the previous behaviour.
  5. Restart the service and review the log for new "refused alias" or "rejected connection" warnings — these flag configurations that were previously silently accepted.

Full Changelog: https://github.com/mickem/nscp/compare/0.12.1...0.12.2

nscp-0.12.2.tar.gz

Release tarball
application/gzip 2026-05-08 Download from Github

check_nsclient-0.12.2-rocky-10-arm64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-rocky-10-x64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-rocky-9-arm64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-rocky-9-x64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-ubuntu-24.04-arm64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-ubuntu-24.04-x64

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-Win32.exe

raw 2026-05-08 Download from Github

check_nsclient-0.12.2-x64.exe

raw 2026-05-08 Download from Github

NSCP-0.12.2-docs.zip

raw 2026-05-08 Download from Github

NSCP-0.12.2-rocky-10-aarch64.rpm

raw 2026-05-08 Download from Github

NSCP-0.12.2-rocky-10-x86_64.rpm

raw 2026-05-08 Download from Github

NSCP-0.12.2-rocky-9-aarch64.rpm

raw 2026-05-08 Download from Github

NSCP-0.12.2-rocky-9-x86_64.rpm

raw 2026-05-08 Download from Github

NSCP-0.12.2-ubuntu-24.04-amd64.deb

raw 2026-05-08 Download from Github

NSCP-0.12.2-ubuntu-24.04-arm64.deb

raw 2026-05-08 Download from Github

NSCP-0.12.2-Win32-legacy-xp.msi

raw 2026-05-08 Download from Github

NSCP-0.12.2-Win32-legacy-xp.zip

raw 2026-05-08 Download from Github

NSCP-0.12.2-Win32.msi

raw 2026-05-08 Download from Github

NSCP-0.12.2-Win32.zip

raw 2026-05-08 Download from Github

NSCP-0.12.2-x64.msi

raw 2026-05-08 Download from Github

NSCP-0.12.2-x64.zip

raw 2026-05-08 Download from Github

Release 0.12.1

NSClient++ Release Notes

This release is dominated by a long-overdue cleanup of the Windows check_service / check_process paths, a substantial overhaul of how thresholds (warn= / crit= / ok=) are evaluated against summary variables such as count, a brand-new IcingaClient** module for submitting passive results to Icinga 2's REST API, an HTTP stack refactor, and finally support for Windows CA-store.

A large number of long-standing GitHub issues are closed by this release.


TL;DR

  • New module: IcingaClient (Icinga 2 REST API passive submission).
  • Windows ROOT store auto-export — service exports the system trust store to ${ca-path} at every boot, so HTTPS-bound checks (check_http, Elastic, Op5, Icinga, Graphite, NRDP, Syslog) "just work" without manually staging a CA bundle.
  • check_service is finally correct on busy machines, on mixed start-type services, and when perf-syntax=none is used.
  • check_process now sees protected / cross-user processes when running as NETWORK SERVICE, and the realtime path is finally case-insensitive like Windows itself.
  • The filter engine now evaluates warn= / crit= after iteration, so summary variables such as count are stable, and mixed expressions are evaluated correctly even when no rows match.
  • HTTP client/server code: separate request/response types, chunked transfer decoding, normalised headers.

See Breaking changes at the bottom for behavioural changes you may need to react to in existing configurations.


New features

IcingaClient — Icinga 2 REST API submission

A new client module that submits passive check results directly to an Icinga 2 master/satellite via the /v1/actions/process-check-result REST endpoint, as an alternative to NSCA or NRDP.

Real-world example — submit a check from a scheduled task / NSCP console:

[/modules]
IcingaClient = enabled

[/settings/IcingaClient/targets/default]
address = https://icinga2.example.com:5665
username = nscp
password = secret
hostname = ${hostname}
# submit a passive result
nscp client --module IcingaClient \
            --command submit_icinga \
            --address https://icinga2.example.com:5665 \
            --username nscp --password secret \
            --command heartbeat \
            --result 0 \
            --message "Hello from NSClient++" \
            --ensure-objects

Native support for Windows CA-store

On startup NSClient++ will export the machine's ROOT certificate store as a single PEM bundle, so any check that does TLS (check_http, IcingaClient, NSCA over TLS, ...) can automatically validate certificates.

Real-world example — verify an internal HTTPS endpoint signed by your enterprise CA that is already trusted by Windows:

heck_http url=https://www.ibm.com
OK: https://www.ibm.com -> 303 ok (0B in 33ms)

check_http url=https://self-signed.badssl.com/
CRITICAL: https://self-signed.badssl.com/ -> 0 error: Failed to connect to self-signed.badssl.com:443: certificate verify failed (SSL routines) (0B in 0ms)

check_service fixes

"Failed to enumerate service: 6f7" on busy hosts

Enumerating service might fail on server with many services. The enumeration is now properly looped until the SCM signals end-of-data.

perf-syntax=none actually suppresses perfdata

check_service was emitting a stream of empty perfdata aliases (''=4;0;1 ''=4;0;1 ...) even when the user set perf-syntax=none, making the output unusable over size-limited transports such as NRPE.

Real-world example — quietly checking 200 services over NRPE:

# before: blew past the 1 KB / 4 KB NRPE response limit
# after:  no perfdata at all, message stays small
check_service "filter=start_type='auto'" "warn=state!='running'" "perf-syntax=none"

no more TODO leaking into ${desc}

When using service= instead of a filter=, the display name was constructed with the literal string "TODO" and overwritten later. In some instances this was read before being populated causing TODO to end up in check results.

Real-world example:

# before
$ check_service service=Spooler "syntax-detail=${name}: ${desc}"
OK: Spooler: TODO

# after
OK: Spooler: Print Spooler

delayed only reported for SERVICE_AUTO_START

QueryServiceConfig2(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) only returns a meaningful value for auto-start services. The old code checked the delayed flag before the start type, so manual / boot / system / disabled services could randomly show up as delayed / delayed-trigger.

Real-world example:

# A service configured "Manual" used to render as start_type=delayed.
# Now:
$ check_service service=MyManualSvc "syntax-detail=${start_type}"
OK: manual

check_process fixes

see protected / cross-user processes as NETWORK SERVICE

When NSClient++ runs under a non-administrative account it cannot OpenProcess(PROCESS_QUERY_INFORMATION) on critical processes (csrss.exe, smss.exe, services.exe, winlogon.exe, ...) or on processes owned by other users — they were silently dropped from the enumeration, causing false CRITICAL: =stopped.

A third fallback using PROCESS_QUERY_LIMITED_INFORMATION + QueryFullProcessImageName is now attempted. The process is visible by name and PID; detailed metrics (handle counts, VM, command line, modules) remain unavailable for those processes because they require broader rights.

Real-world example — service is installed to run as NT AUTHORITY\NetworkService:

# before:  CRITICAL: winlogon.exe=stopped
# after:   OK: winlogon.exe=started
check_process "process=winlogon.exe" "crit=state!='started'"

case-insensitive process= in realtime

Processnames were not caompared case insensetive so process=notepad.exe failed to match a process whose on-disk image name was NOTEPAD.EXE.

Real-world example:

[/settings/system/windows/real-time/checks/notepad]
alias = notepad
filter = process = 'notepad.exe'
crit = count > 0

This now fires regardless of how Windows happens to capitalise the image name.


Filter engine — stable summary thresholds

These changes touch the shared filter / threshold engine and therefore affect every modular check (check_files, check_service, check_process, check_eventlog, ...).

stable count / total / *_count in warn= / crit=

warn= / crit= were evaluated during iteration. Summary variables such as count therefore exposed their running value instead of the final post-iteration value, so a mixed expression like

crit = state = 'hung' OR count < 5

mis-fired on the very first row (count == 1 < 5) regardless of how many rows ultimately matched.

Per-row evaluation is now deferred: matched rows are recorded during iteration, and the warn/crit/ok engines run in match_post() once the summary state is final. Realtime checks now also call match_post() so the deferred verdict is materialised before the realtime helper inspects the return code.

Real-world impact:

# "alert if any process is hung, OR if fewer than 5 are alive"
check_process "filter=name='myworker.exe'" "crit=state='hung' OR count<5"
# Pre-fix: always CRITICAL on the first row.
# Post-fix: CRITICAL only when the final count of matching rows is < 5
#           (or any matched row is hung).

mixed warn= / crit= evaluated when no rows match

If a filter excluded every row, no per-row evaluation happened and the post-row pass only re-evaluated expressions whose AST did not require an object. Pure-summary expressions like crit=count=0 worked, but mixed expressions like

crit = state = 'stopped' OR count = 0

were skipped entirely — leaving the check OK in the empty case.

A force-evaluation path is added: when no rows matched, object-bound variables resolve to their default (false) and summary variables resolve to their final values, so the example above evaluates to (false OR true) = true and correctly returns CRITICAL.

Real-world example:

# "CRITICAL if MyService is stopped, or if it doesn't exist at all"
check_service "filter=name='MyService'" "crit=state='stopped' OR count=0"
# Pre-fix: OK when MyService is missing.
# Post-fix: CRITICAL when MyService is missing.

Quieter, more predictable expression evaluation

  • Operators audited so is_unsure propagates consistently; invalid-type comparisons resolve to unsure-false instead of erroring.
  • String variables on no-object cases now return an empty string with is_unsure=true and produce a warning in the log instead of an error per row — log volume on complex queries drops dramatically.
  • Removed the misleading "most likely mutating" warnings.
  • Summary variables return sure-int during deferred evaluation so they don't get demoted to "unsure" by the new code path.
  • Substantial new test coverage for these paths.

HTTP refactor

  • HTTP request and response are now distinct types instead of one shared bag.
  • Chunked transfer-encoding is decoded properly (Icinga 2 responses use it).
  • Header storage is normalised — case-insensitive lookup, no more duplicate-header surprises.

Real-world impact: check_http against servers using Transfer-Encoding: chunked (most modern reverse proxies, Icinga 2, Kubernetes ingress, ...) now returns the full body instead of a truncated/garbled one. The IcingaClient module relies on this.

plugin_manager response formatting

Performance data is now appended to the response message only when it exists, so checks with no perfdata no longer end with a stray |. The CLI parser also gained tighter option handling and clearer logging.


Build / quality

  • a7194df5, f7614b58, 82d8e7a6: new GitHub Actions workflow that builds with -fsanitize=address,undefined and runs the test suite — sanitizers are now opt-in via the CMake config.
  • 12beda0c: documentation cleanup, link fixes; passive-monitoring scenario doc renamed to passive-monitoring-nsca.md.

Breaking changes

> Read this section if you have existing configurations or scripts on > top of NSClient++ — several long-standing-but-incorrect behaviours > have been corrected, which by definition is observable.

1. delayed is no longer reported for non-auto services

If you have any filter / threshold that matched start_type = 'delayed' on services that were actually configured as Manual, Boot, System or Disabled, that match is gone — the field will now correctly report the real start type.

Impact example:

# Was: spuriously matched manual services that the SCM happened to
#      flag as "delayed".
# Now: matches only true auto-start-with-delayed-start services.
filter = start_type = 'delayed'

If you actually wanted to alert on "any non-running service that isn't disabled", you should now write:

filter = start_type IN ('auto','delayed','boot','system') AND state != 'running'

2. warn= / crit= no longer fire mid-iteration on running counts

If a check incidentally relied on a mixed expression firing on the first matching row (e.g. crit=count<5 mixed with a per-row term), the verdict will now be computed against the final counts. This is the documented and intuitive behaviour, but configurations that were "tuned" against the buggy early-fire will produce different results.

Impact example:

crit = state = 'hung' OR count < 5
# Old: CRITICAL on the very first row (count == 1).
# New: CRITICAL only if any row is 'hung' OR final count < 5.

3. Mixed warn= / crit= now evaluate when no rows match

Mixed expressions used to be silently skipped on empty result sets, returning OK. They are now evaluated with object-bound variables defaulting to false and summary variables at their final values.

Impact example:

crit = state = 'stopped' OR count = 0
# Old: OK when nothing matched.
# New: CRITICAL when nothing matched (because count = 0 is true).

If your old config was implicitly treating "empty" as "OK", you may want to add count > 0 AND ... guards, or move the empty-case logic into a dedicated check.

4. Realtime check_process is now case-insensitive

The realtime path matched process= case-sensitively; the active path was already case-insensitive. They are now consistent.

Impact: a realtime rule that intentionally matched only the exact casing (e.g. process='Notepad.exe' to ignore notepad.exe) will now match both. This was almost certainly a bug in the original config.

5. ${desc} no longer returns the literal string TODO

If any monitoring backend was matching on the string TODO in the description field of check_service results to detect "this is the NSClient++ default", that will stop working. Use the real display name instead.

6. perf-syntax=none now actually suppresses perfdata in check_service

Previously, perf-syntax=none was silently ignored and a stream of empty-aliased perfdata entries was produced. Any monitoring backend that consumed those empty entries (highly unlikely, but possible) will see them disappear when the user requests none. Match the documented semantics, shared with filter / ok / warn / crit.

7. HTTP request/response API changed (C++ consumers / module authors)

Internal C++ types http::request / http::response are now distinct types, headers are stored case-insensitively, and chunked decoding happens transparently. Out-of-tree modules that linked against the old shared "request/response bag" type will not compile against this release without a small adjustment — typically:

// before
http::packet pkt = client.send(...);
auto body = pkt.body;

// after
http::response resp = client.send(http::request{...});
auto body = resp.body();   // chunked decoding already applied

8. Documentation reorganisation

Several old documentation pages have been merged or converted with the new scenarios so some old links might now be broken.

Full Changelog: https://github.com/mickem/nscp/compare/0.12.0...0.12.1

nscp-0.12.1.tar.gz

Release tarball
application/gzip 2026-05-03 Download from Github

check_nsclient-0.12.1-rocky-10-arm64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-rocky-10-x64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-rocky-9-arm64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-rocky-9-x64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-ubuntu-24.04-arm64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-ubuntu-24.04-x64

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-Win32.exe

raw 2026-05-03 Download from Github

check_nsclient-0.12.1-x64.exe

raw 2026-05-03 Download from Github

NSCP-0.12.1-docs.zip

raw 2026-05-03 Download from Github

NSCP-0.12.1-rocky-10-aarch64.rpm

raw 2026-05-03 Download from Github

NSCP-0.12.1-rocky-10-x86_64.rpm

raw 2026-05-03 Download from Github

NSCP-0.12.1-rocky-9-aarch64.rpm

raw 2026-05-03 Download from Github

NSCP-0.12.1-rocky-9-x86_64.rpm

raw 2026-05-03 Download from Github

NSCP-0.12.1-ubuntu-24.04-amd64.deb

raw 2026-05-03 Download from Github

NSCP-0.12.1-ubuntu-24.04-arm64.deb

raw 2026-05-03 Download from Github

NSCP-0.12.1-Win32-legacy-xp.msi

raw 2026-05-03 Download from Github

NSCP-0.12.1-Win32-legacy-xp.zip

raw 2026-05-03 Download from Github

NSCP-0.12.1-Win32.msi

raw 2026-05-03 Download from Github

NSCP-0.12.1-Win32.zip

raw 2026-05-03 Download from Github

NSCP-0.12.1-x64.msi

raw 2026-05-03 Download from Github

NSCP-0.12.1-x64.zip

raw 2026-05-03 Download from Github