- This project requires the Developer Certificate of Origin
(DCO): every commit must carry a
Signed-off-bytrailer with your own real name and email, for example:Signed-off-by: Jane Doe <jane@example.com>The easiest way is to commit withgit commit -s, which appends the trailer from your configured git identity. - The DCO bot accepts a
Signed-off-bythat matches either the commit's author or its committer, so signing off with your own identity on work you author is enough (and a cherry-picked commit that keeps the original author's sign-off still passes). If you do not already have a git identity configured (globally or for this repo), set one to your own name and email:git config user.name "Jane Doe" && git config user.email "jane@example.com"If you already have one, keep it and just make sure it is the name and email you sign off with. - If an AI coding assistant helped produce a commit, declare it with an
Assisted-by:trailer, using the format codified by the Linux kernel AI coding-assistants policy:Assisted-by: AGENT_NAME:MODEL_VERSION [tool ...], for exampleAssisted-by: Claude Code:claude-opus-4-8. Do not useCo-authored-by:for AI assistance: only a human may certify the DCO, so an AI must never carry aSigned-off-by(which co-authorship implies). KeepSigned-off-by:as the final trailer — a human always reviews, tests, and takes responsibility for the result.
- Boolean check options must be declared as
po::value<bool>(&x)->implicit_value(true)->default_value(false), notpo::bool_switch. Checks are driven over REST, which passes the flag as the tokenx=true;bool_switchrejects that with "does not take any arguments". This only fails over REST, not from the CLI, so it is easy to ship broken — cover new boolean options with an integration test. - Argument passing differs by transport: the CLI passes each
-a key=valueas two separate tokens (--keythenvalue, plus a redundant undashed pair), while REST passes a singlekey=valuetoken. Prefer the filter/option helpers over hand-rolled argument parsing.
- Modules are auto-discovered: a
modules/<Name>/module.cmakecontainingset(BUILD_MODULE 1)is enough (no top-level CMake edit). Re-runcmakeon an existing build dir to pick up a new module directory. module.jsondeclares the module and itscommands. The build generates the dispatch/export glue (module.cpp/hpp) from it:namemaps to a class<Name>in<Name>.h/.cpp, and everycommandsentry maps to a method of the same name on that class. Omit the"metrics"key unless the class implementsfetchMetrics()—"metrics":"produce"generates a call to it and will fail to link otherwise.- Cross-platform data acquisition uses the win/unix shim: platform-neutral
sources plus an
if(WIN32) … _win.cpp else() … _unix.cppsplit inCMakeLists.txt, behind a shared filter/interface header (seeCheckDisk). Keep the check logic, keyword registry and output builders platform-neutral; only the data source is#ifdef'd. - Packaging: modules self-install via
NSCP_INSTALL_MODULE()(pulled in byinclude(${BUILD_CMAKE_FOLDER}/module.cmake)in the module'sCMakeLists.txt), so Linux CPack (DEB/RPM/ZIP) packages them automatically. The Windows MSI does not — add a<File>entry toinstallers/installer-NSCP/Product.wxsunder<Component Id="Plugins">(the "Check Plugins" feature). Also add the module to the feature-hint map inservice/plugins/plugin_manager.cpp, and (optionally, commented) tofiles/NSC.dist. - A check is a
modern_filtercheck: afilter_obj+ afilter_obj_handlerregistering keywords (registry_.add_string_var/add_int_var(...,type,...)withtype_int/type_date/type_bool, chaining.add_int_perf("unit")for perfdata), driven bymodern_filter::cli_helper(add_options(warn, crit, filter, syntax, empty_state)+add_syntax(top, detail, perf, empty, ok)). SeeCheckDisk/check_single_file.cppfor a minimal template. - Unit-test binaries have no generated module glue, so they must define the
plugin singleton themselves (normally provided by
NSC_WRAP_DLL()):nscapi::helper_singleton *nscapi::plugin_singleton = new nscapi::helper_singleton();
Every new check command needs, under docs/samples/:
<Module>_<command>_samples.md— usage examples with real captured output.<Module>_<command>_desc.md— clarifying prose (suffix is_desc, not_docs).
These are merged into the reference docs by the downstream nscp-docs build.
Scenario walkthroughs live in docs/docs/scenarios/ and must be registered in
both docs/mkdocs.yml (nav) and docs/docs/scenarios/index.md.
Every new check command must have at least one test under tests/ that
actually runs it and asserts on its output — wherever it is at all possible to
exercise the check deterministically. This is in addition to the _test.cpp
unit test: the unit test covers the check's pure logic (rendering, thresholds,
parsing) in isolation, while the tests/ test covers real command dispatch,
REST-style argument parsing and live data on the target OS. The test does not
have to go over REST — pick the lighter transport when it fits (see below). Add
the case to the module's existing suite (e.g. checksystem-commands.test.ts,
checkdisk-commands.test.ts), guarding OS-specific checks with
if (!onWindows) return; / if (onWindows) return;. When the machine may lack
the underlying data (hardware sensors, empty collectors), assert the documented
no-data contract (OK/UNKNOWN + message) rather than skipping, and pin
warning=/critical= so the result is deterministic regardless of host state.
Integration tests live under tests/ (jest + ts-jest) and drive commands over
REST against a long-lived nscp test instance. Run them from tests/:
NSCP_SKIP_DOCKER=1 NSCP_BIN=<path>/nscp npx jest --runInBand <pattern>. Use the
REST path when the check reads the 1 Hz background collector (cpu, memory,
network, …) or when you are specifically verifying REST-specific argument
handling — most importantly that a valued boolean (x=true) is accepted (the
bool_switch trap above).
For a check that only needs its command exercised (and where standing up the
REST web server is undesirable or unavailable), the one-shot client-query
path works without a web server: nscp client --module <Mod> --boot --query <cmd> <k=v>… (see checkdisk-unix.test.ts, checksecurity.test.ts). It still
passes k=v as single tokens, so it exercises the same REST-style argument
parsing; note its output is the raw Nagios message|perfdata with no
status-word prefix added.
Release notes follow the style of the GitHub releases (e.g.
https://github.com/mickem/nscp/releases/tag/0.12.5). Derive entries from
git log <last-tag>..HEAD and describe user-visible behaviour, not commits.
Structure:
# <descriptive one-line title of the release theme>
<1–2 sentence intro paragraph summarising the major changes.>
## Highlights
- **<Bold lead-in>.** <One or two sentences.> Reference GitHub issues as `#NNN` when relevant.
- ... (4–8 bullets covering the headline items)
## Detailed changes
### <Area/module> — <short subject>
<Problem → solution prose.> Use tables for keyword/option/function lists and
fenced code blocks for command / INI examples. One `###` per notable change;
group minor items under a shared subsection (e.g. `### Bug fixes`).
## Upgrade notes
- **<Breaking change / migration>:** <what changed and what the user must do.>
Call out breaking changes explicitly; note when the default install is unaffected.
**Full Changelog**: https://github.com/mickem/nscp/compare/<prev-tag>...<this-tag>Conventions: title is a theme sentence, not a version number; lead each highlight bullet with a bold phrase; prefer tables for option/keyword matrices; always end with the compare link.