Collectors
A collector is the reachability domain that actually opens connections to devices and runs collections. Every source is bound to one. There are two kinds: the embedded collector built into the core, and standalone collectors you deploy near networks the core can’t reach directly — which also move the work of talking to those devices off the core entirely.
Collectors are a platform-wide primitive, so they live under Settings → System → Collectors — not inside the Configuration Tracker section.
What a collector is for
Section titled “What a collector is for”Two things, and the second is easy to miss because the first is so visible.
Reach. A collector runs where the devices are, so a network the core cannot open a session to is still collectable.
Taking the work off the core. Collecting a fleet is not one expensive operation — it is hundreds of small, slow ones. Every device means opening a session, sending commands, and then waiting: on a switch’s CLI to echo back, on a slow WAN link, on a box that has decided to think about it for thirty seconds. A standalone collector does all of that at the site and hands the core only the finished result.
That is the honest shape of the win: collection is I/O-bound, not CPU-bound. What the core is relieved of is mainly the fan-out — hundreds of concurrent, long-lived, mostly-idle sessions and the retry churn around them — rather than raw processing. The CPU-bound steps the collector also performs (scrubbing, hashing, fingerprinting) are a small share of the cost, but they are done at the site too, and they are what turns a raw capture into something small enough to be worth shipping.
And it is a tool, not a place data lives. A standalone collector holds no database. Its durable state is its own identity (a private key it generates, its certificate, and the pinned fingerprint of the core it trusts) plus an outbox of results the core has not acknowledged yet. Nothing accumulates — once a result is acknowledged, it is gone from the site. There is no history there to query, back up or lose, and no device credentials at rest: a stolen collector volume yields already-scrubbed configs and nothing else.
The practical consequence: raw configuration bodies are never written down at the site. What crosses the wire is a scrubbed body and its hash — so the segment’s secrets stay in the segment, and the core stores the one copy of history, where your backups and your RBAC already are.
The one exception, and it is deliberate. A recipe can keep an unmasked copy of every capture. A standalone collector does collect it — but it holds that copy in memory only and re-attaches it at the moment it uploads the result; it is stripped out before anything reaches the outbox, because the outbox’s contract is that a device secret cannot touch disk. The memory buffer is bounded (64 captures / 64 MiB, oldest evicted first). So a result that gets queued — the collector restarted, or the core was unreachable long enough for the uploader to fall behind — arrives without its unmasked copy. The configuration snapshot itself is never lost; only that second copy is.
Embedded vs standalone
Section titled “Embedded vs standalone”| Embedded | Standalone | |
|---|---|---|
| Runs | In-process on the core (leader) node | As a separate container at a site |
| Setup | None — it’s built in | Deploy + enroll (see the deploy guide) |
| Reaches | Whatever the core can reach on the network | Devices local to its site |
| Connection to core | It is the core | Dials out to the core (outbound only) |
| Best for | Single-site / flat networks | Segmented, remote or NAT’d networks; multi-site |
| Count | Exactly one, the default, undeletable | As many as you need |
The embedded collector is seeded at first boot under the name default, and the
Collectors list renders its mode as Local (built-in). It is always the default
collector — the one a source binds to when you don’t pick one — and it can’t be deleted or
turned into a standalone. Only the built-in may hold the default flag, so a standalone
collector can never take it. For a single site it’s all you need: point sources at it and
go.
The Collectors list under Settings → System — the built-in default collector (mode Local (built-in)) alongside a standalone one at a site, both online. Cluster node answers two different questions in one column, and says which it is answering: a standalone collector is connected to the node that served its last poll, because under HA there is no client-facing address shared by the nodes and each collector talks to exactly one — that is what tells you whose outage explains a collector gone quiet. The embedded collector is active on whichever node currently holds the leader role, and relocates itself at failover.
The standalone collector’s security posture
Section titled “The standalone collector’s security posture”A standalone collector is designed so that deploying one into a sensitive segment adds as little attack surface as possible:
- Dial-out only — listens on zero ports. The core never connects to a collector. The collector makes outbound connections to the core, long-polls for work, and streams results back. There is no inbound listener to expose or firewall.
- Reveal-once Ed25519 identity. A collector enrolls with a one-time token and then proves itself on every request by signing with an Ed25519 key it generates locally and never sends. The core pins the collector’s public key on first enrollment; each subsequent request carries a signature and a timestamp, accepted only within a five-minute clock-skew window — which also bounds how long any replay of it could matter. A verbatim repeat inside that window is rejected outright.
- Credentials handed out just-in-time. A collector receives the credentials for a collection only in the poll response for that work, uses them in memory, and keeps nothing long-lived — including just-in-time passwords, which rotate the moment the collector’s work drains.
- Credential-free at rest. The collector persists its own identity key and a durable result outbox, but no device credentials — a stolen disk yields no way into your fleet.
- Durable outbox. Results are buffered locally in a small on-disk queue and delivered idempotently — the core deduplicates on the result’s key — so a restart or a link outage never loses a capture or double-writes one. It is also backpressure: once the queue is full the collector stops claiming new work and says so, and the uploader keeps draining until it recovers. The one thing the outbox will not hold is an unmasked copy (above).
Health, enrollment and compatibility
Section titled “Health, enrollment and compatibility”The Collectors list shows each collector’s name, mode, status, cluster node, when it was last seen, and what uses it. A collector’s own page adds the address it last dialled in from, its version pair and compatibility badge, and its enrolled identity.
Health / liveness
Section titled “Health / liveness”Status is worked out when you read the page, from the last time a collector reported in — it isn’t a stored flag, so a wedged collector reads correctly. A standalone collector long-polls the core every few tens of seconds, and that poll is its heartbeat:
| Status | Meaning |
|---|---|
| Online | Reported in within the staleness window. |
| Offline | Hasn’t reported within the window — 15 minutes, which is many missed polls: a deliberately coarse signal, not an SLA. |
| Unknown | Has never reported (freshly created, not yet enrolled/started). |
The embedded collector is a different question, and it is answered differently. It has no dial-in heartbeat that could go stale — it runs wherever the writable primary is, so its liveness is “is there a primary to run it”. On a single node that is always yes and it reads Online. Under HA it reads Online when a writable primary is observed, Offline during a failover window or a quorum loss when no node is sweeping, and Unknown when the cluster could not be reached at all — because failing to look is not the same as finding out that nothing is running, and the two must not render alike.
This is also why the collection cadence never moves the embedded collector’s status: the two were deliberately decoupled.
A standalone collector that goes offline raises one
ncm_collector_offline alert — one per collector, not one per
config, carrying the count of tracked configs it serves as the blast radius. The scan runs
every 5 minutes. An embedded collector never raises it, and neither does a standalone that
has never reported (Unknown, not Offline).
Enrollment
Section titled “Enrollment”A standalone collector’s identity is managed from its detail page:
- Issue enrollment token (Re-enroll, once it has an identity) issues a reveal-once enrollment token — with a lifetime you choose, default 60 minutes and at most 7 days — plus the core’s URL and a ready-to-paste join command with both already filled in. The token is shown once; copy it then. Issuing a new token revokes any token issued earlier for that collector.
- Trust is pinned, not configured. The collector accepts the certificate the core presents on first contact and nothing else afterwards, so there is no CA file to stage before deploying one. Verifying against a CA bundle is available as a stricter mode.
- Revoke identity invalidates the collector’s key immediately: its very next signed request is rejected — the check is a lookup on every request, so there is no CRL to publish and no cache to wait out. After a few consecutive rejections the collector stops polling and halts into a re-enroll required state rather than hammering the core; it keeps whatever is in its outbox, which uploads once it has an identity again. Re-enrolling issues a fresh token and identity.
The full deploy walkthrough is in Deploy a standalone collector.
Version compatibility — two numbers, only one of which can stop you
Section titled “Version compatibility — two numbers, only one of which can stop you”A standalone collector reports two versions on every poll, and they do completely different jobs. Mixing them up is the difference between a nudge and a stopped fleet.
| What it is | What it does | |
|---|---|---|
| Contract version | The snapshot-identity contract: how a config is scrubbed, hashed and fingerprinted. Bumped only when those bytes change. | Gates. Any mismatch — in either direction — fail-closes. |
| Release version | Which build the collector image is. | Cosmetic. Feeds an “update recommended” nudge and nothing else. |
The collector’s page shows both as a pair (core vX · collector vY) with one badge:
| Badge | Meaning |
|---|---|
| Compatible | Contracts match. Nothing to do. |
| Update recommended | Contracts match, but the core is a newer release. It works fine; upgrade when convenient. |
| Update required | Contracts differ. The collector is already idle — see below. |
The embedded collector has no badge at all: it is the core, so there is nothing to compare.
What “fail-closed” actually costs
Section titled “What “fail-closed” actually costs”The contract check runs before the core will hand out any work. On a mismatch the core dispatches nothing and ingests nothing from that collector — and this is not scoped to the configs affected by whatever changed. The collector idles entirely: every tracked config it serves stops being collected. A version-skewed collector writing history the core would hash differently is worse than a collector that collects nothing, so idling it is the intended outcome.
It recovers by itself. The collector keeps slow-polling while refused, so the moment its image matches the core again it resumes — nothing to re-enable, nothing to re-enrol.
How to recognise it
Section titled “How to recognise it”A refused poll never counts as a heartbeat — the contract check runs before the core gets that far. So a fail-closed collector doesn’t just stop collecting, it also stops reporting in, and within 15 minutes it reads Offline and raises the ordinary collector-offline alert. The alert names a silent collector; the version skew underneath it is what the collector’s badge and its own log tell you.
Before 1.2.7 this could read as its own opposite. The core attached its version only to answers that succeeded, so the one answer that needed it — the refusal — carried none; the collector went on reporting the version it had heard before the core was upgraded, which naturally matched the version it was still running, and its own updater answered “already in step with the core — nothing to do” while nothing at all was being collected. Since 1.2.7 the core reports its version on refusals too, so the way out is no longer locked by the failure it exists for. (Unauthenticated callers still learn nothing: the version is attached only after a signature has verified.) The command that moves a collector to the version its core runs is on the deploy page.
Common scenarios
Section titled “Common scenarios”One flat site. Do nothing — use the built-in default collector for every source.
A remote branch behind NAT. Deploy a standalone collector at the branch, enroll it, and set your branch sources’ collector to it. Nothing needs to be opened inbound to the branch or the core.
A hardened DMZ or OT segment. Put a standalone collector inside the segment. It reaches the gear locally and dials out to the core — no inbound path into the segment, no device credentials stored on the collector.
When to use what
Section titled “When to use what”| Situation | Collector |
|---|---|
| Core can reach the devices directly | Embedded |
| Devices are in a segment the core can’t route to | Standalone |
| Site is behind NAT / has no inbound path | Standalone (dial-out) |
| Multiple sites, each isolated | One standalone per site |
| You need zero new inbound listeners in a sensitive zone | Standalone |
| The core is spending its time waiting on hundreds of slow device sessions | Standalone, one per site — the waiting happens there |
| Raw configuration bodies must not be written down in a segment | Standalone (a scrubbed body and its hash are shipped; nothing unmasked ever reaches the site’s disk) |
Reference
Section titled “Reference”| Field | Notes |
|---|---|
| Name | Unique. The built-in one is seeded as default. |
| Mode | embedded (rendered Local (built-in) — exactly one, always the default, undeletable, and its mode can’t be changed) or standalone. Only standalone collectors can be created. |
| Site label | Free text, standalone only — where this collector runs. It is not a URL: the core never calls a collector. |
| Status | online · offline · unknown — derived when read, not stored. Standalone = heartbeat staleness (15 min); embedded = is there a writable primary. |
| Cluster node | Standalone: the node that served its last poll — observed, not inferred. Embedded: the node currently holding the leader role, resolved live, so it moves on failover. Either renders — when it can’t be resolved. |
| Last seen | Standalone: the last poll, plus the address the collector dialled in from — this is the liveness signal. Embedded: a “last swept” stamp only; its status doesn’t come from it. |
| Release version | The collector image’s build. Cosmetic — drives the “update recommended” nudge only. |
| Contract version | The snapshot-identity contract. Must equal the core’s, or the poll fail-closes and the collector idles for every config it serves. |
| Compatibility | compatible · update_recommended · update_required. Derived from the contract, never from the release. None for the embedded collector. |
| Identity | Standalone only: Ed25519 key generated on the collector and pinned at enrollment; status active or revoked. |
| Enrollment token | Reveal-once; operator-chosen lifetime (default 60 min, maximum 7 days); one live token at a time. |
| Used by | Sources and tracked-config addresses referencing the collector (blocks deletion). |
| Permissions | Collectors carry their own Collectors permission section under System (view · create · edit · delete), separate from the Configuration Tracker sections — running collectors is a distinct trust from curating the config inventory. |
Related
Section titled “Related”- Deploy a standalone collector — the operator guide.
- Tracked configs & transports — sources pick a collector.
- Credentials & the vault — the run-scoped credential hand-out.
- Recipes & scrub sets — the masking switches that set the contract, and the unmasked copy.
- Change alerts & safe deletion — the collector-offline alert.
- Alerts reference — the
ncm_collector_offlinefingerprint and what it carries.