Facts¶
Host facts are the inventory the agent keeps about the machine it runs on — what OS it is, what hardware it sits on, what it has attached. The core collects them from the loaded modules on a schedule and stores one document per host.
- Read the inventory —
/api/v2/facts, whole document or one subtree - Collect now —
/api/v2/facts/commands/refresh
Facts or tags?¶
Both describe the host, and they answer different questions — pick by what you are going to do with the answer.
Tags (/api/v2/tags) |
Facts | |
|---|---|---|
| Shape | flat key=value strings |
a document: objects, lists, numbers, strings |
| Question | which group is this host in | what is this host |
| Collected | always | only once a set is enabled |
| Uploaded to a fleet server | yes, on every state report | when enrolled: the hash on every report, the document when it changes |
A fleet selector matches a tag whole, which is why os_family and arch are
tags. A list of volumes, or a vendor and model, is not something a selector
matches on — that is what a fact set is for, and a comma-joined tag value is
where that shape goes to die.
Enabling a fact set¶
Nothing is collected until you turn a set on, because an inventory is data an operator did not necessarily agree to ship. A set is enabled in the module that produces it, beside that module’s other settings — so a set can never be enabled with nothing to answer for it:
[/settings/system/windows/facts]
os = true
hardware = true
[/settings/system/unix/facts] on Linux, with the same keys. Which sets exist,
which module produces each and what they contain is on the
Host Facts page. The collection interval is
[/settings/facts] interval (an hour by default).
Turning a set off stops the module returning it, and the core then drops it from the document — there is nothing to clean up.
Read the inventory¶
| Key | Value |
|---|---|
| Verb | GET |
| Address | /api/v2/facts |
| Privilege | facts.get |
Request¶
GET /api/v2/facts
Optionally ?path=<dotted path> to fetch one subtree: ?path=os,
?path=os.family, ?path=storage.volumes.
Response¶
{
"revision": 7,
"collected": "2026-09-23T10:00:00Z",
"path": "",
"found": true,
"enabled": ["hardware", "os"],
"errors": {},
"gathered": {
"hardware": "2026-09-23T06:12:41Z",
"os": "2026-09-23T06:12:41Z"
},
"facts": {
"hardware": {
"cpu_cores": 20,
"manufacturer": "Dell Inc.",
"memory_gb": 32,
"model": "PowerEdge R650"
},
"os": {
"arch": "x86_64",
"family": "windows",
"name": "Windows Server 2022",
"version": "10.0.20348",
"virtualization": "none"
}
}
}
| Field | Meaning |
|---|---|
revision |
Monotonic change counter. 0 is the empty document a fresh install reports, and it only moves when the stored facts actually change — so a poller can skip a body it has already seen. |
collected |
When the last round completed, ISO 8601 UTC — when the core last asked. A round with nothing enabled completes like any other, so this carries a timestamp even when facts is empty. |
gathered |
When each set’s values were read off the machine, keyed by set id. This is the one to show a reader; see below. A set whose producer did not say is absent, and the round time is then the right answer for it. |
path |
Echoed back from the request; "" for the whole document. |
found |
Whether anything produced what was asked for. |
enabled |
The sets the loaded producers are configured to collect, including one that is enabled and currently failing. |
errors |
Per-set collection errors from the last round, keyed by set id. |
facts |
The document, or the subtree at path. |
A field a producer could not determine is omitted, never written empty: an absent key means unknown, where an empty string would read as an answer.
collected is not gathered¶
A producer is free to hand back a snapshot it took earlier rather than collect
again — that is what reason in the request is for, and what a producer of an
expensive set should do. When it does, the round that delivered the values has
nothing to do with how old they are.
CheckSystem works exactly this way: what OS a host runs and what hardware it
sits on cannot change while the process runs, so it reads once at startup and
reports that reading thereafter. An hourly round then moves collected while
gathered stays where it was — and revision does not move at all, because
the document did not change.
Show gathered against the values. Showing collected claims an
inventory is as fresh as the last time anyone asked, which for a cached
producer is not true. POST /api/v2/facts/commands/refresh is what makes a
producer read for real.
A path nothing produced¶
Not an error — a client asking for a set the operator has not enabled should render “not collected”, not a failure:
{ "revision": 7, "path": "storage", "found": false, "facts": {}, "…": "…" }
A set that failed to collect¶
A set that is enabled but could not be collected keeps the value it last had
and is named under errors, so a transient failure never blanks the
inventory:
{
"enabled": ["hardware", "os"],
"errors": { "hardware": "WMI query timed out" },
"facts": { "hardware": { "…": "…" } }
}
The bundled web UI marks such a set stale and keeps showing its values.
Collect now¶
Asks every loaded producer to collect immediately instead of waiting for the next scheduled round, and answers with the document that round produced.
| Key | Value |
|---|---|
| Verb | POST |
| Address | /api/v2/facts/commands/refresh |
| Privilege | facts.refresh |
Request¶
POST /api/v2/facts/commands/refresh
Response¶
The same body as Read the inventory.
Collecting is real work on the host, so this has its own grant rather than
riding on facts.get — a read-only caller can watch the inventory without
being able to drive collection in a loop.
In the agent and the UI¶
The same document is available without HTTP:
nscp testhas afactscommand (facts,facts os.family,facts refresh) — see Test mode.- The bundled web UI has a Facts page, one card per set, with a refresh button that drives the endpoint above.