infracensus-collector
v1.6.41
Published
InfraCensus on-premises collector: discovers your network (ICMP/ARP/SNMP/SSH, VMware, Hyper-V, SQL Server, load balancers, voice, Microsoft Entra ID, Azure) and pushes normalized inventory snapshots outbound to your InfraCensus control plane. Device crede
Maintainers
Readme
InfraCensus Collector
This is the operator's page. It is written for the person who runs the collector on their own hardware, inside their own network, holding their own device credentials — not for us.
The collector is outbound-only on your network. It polls the control plane;
the control plane never connects in, and nothing here listens on a network
interface. There is exactly one listener anywhere in the product and it is
pinned to 127.0.0.1 — the local status UI, described below, unreachable from
any other machine by construction and asserted at startup, not assumed.
How it is organized — three planes
1. The data plane
your network this host InfraCensus cloud
──────────── ───────── ─────────────────
config: /etc/netdoc-collector/collector.env (0600)
identity + the collector key + device credentials
│
▼
switches, hosts, ◄── scan ── scan modules poll (outbound) ──► control plane
hypervisors, network · vmware · hyperv ◄── scan jobs, (never connects
vCenter, SQL, sql · lb · voice · identity cancellations, into your
Entra ID, Azure azure · config-import restart signal) network)
│
│ raw facts — device credentials are used
│ HERE and go no further
▼
canonical snapshot / inventory
(normalized, redacted, stamped
with tenant + site)
│
▼
authenticated push ── Bearer collector key ──► ingest
│
▼
use log (JSONL ledger on this host —
your evidence of what was touched)
browser ON THIS HOST ──► http://127.0.0.1:9477 the local UI (status, log tail,
run-a-scan) — loopback onlyConfiguration is resolved once, locally. Scan modules read it, talk to your equipment, and hand back raw facts. Those facts are normalized into the canonical model, redacted, and pushed outbound with the collector's key. In daemon mode the same loop runs in reverse order: the collector polls for a job, the job carries the targets and credentials, and the snapshot is stamped from the job. Nothing is ever pushed that was not first normalized and redacted.
2. The security boundaries
The collector key authenticates this host to the control plane.
- At rest it lives in exactly one file (
collector.env/collector.config.json), created empty and locked to mode0600(Linux) or an explicit three-SID ACL (Windows) before the key is written into it. - It is never a process argument — there is no
--api-keyflag, and a test asserts there never will be. Enrolment takes it at a muted prompt or from a file whose path (not contents) was the argument. - It is never logged. Every log line and every ledger record passes through a redactor that knows the key's value before the first line is written.
Device credentials (SSH, SNMP, WMI, vCenter, SQL, Graph, Azure) are resolved on this host and used from this process. They are never placed in a snapshot, never logged (the redactor learns per-job credentials the moment a job is claimed, before the job is logged at all), and never sent anywhere. What leaves the building is the result of using them — and device configuration text is redacted here, before it leaves.
The local UI is loopback-only with cloud-verified login:
- It binds
127.0.0.1explicitly and the daemon checks the address the OS actually bound after listen, refusing to serve on anything else. - There are no local accounts. Signing in forwards your email and password (with the collector's own key as the Bearer) to the control plane, which accepts only an active account of this collector's own tenant. Disable the account in the portal and the UI closes with it. No password is ever stored — or logged — on this host.
- Sessions are cookies signed with a secret generated fresh at every daemon start, so restarting the daemon invalidates every session, and there is no session secret at rest to protect. Cookies are HttpOnly, SameSite=Strict, and expire after 8 hours. Five failed logins in fifteen minutes lock the login for everyone until the oldest failure ages out — one limiter across all sessions, because on loopback there is only one address.
3. The UI plane — what it serves, and what it refuses to
Serves, to a signed-in operator on this host:
- daemon status: version, when it started, when it last polled the control plane, when data last left this host;
- a per-module table of last runs (read from the use-log ledger — the same file that is your evidence, so the UI can never claim more than the ledger holds);
- the tail of the daemon log;
- a Run scan button per module (spawns the same one-shot
netdoc-collector scan <module>you could type) and a Restart button (the same restart path the portal's restart signal uses).
Refuses, by construction:
- to be reachable from any other machine (loopback bind, asserted);
- to display the collector key, or any stored credential, anywhere — the status payload is built from an allowlist that does not contain them, the page is a static string with nothing interpolated into it, and a test holds both;
- to accept credentials for storage — there are no device-credential fields in the UI; credentials are configured in the config file, on disk, under its permissions;
- to answer anything stateful without the session cookie;
- to serve at all when
COLLECTOR_UI=0— disabled means no listener exists, not a listener that says no.
Install
The collector is distributed on the npm registry as infracensus-collector.
The package installs one command, netdoc-collector (package name and command
name differ on purpose, the way typescript installs tsc). Node.js 20 or
newer is the only prerequisite.
If Node/npm is not on the host yet — Linux: install Node.js 20+ from your
distro or nodejs.org; e.g. RHEL/Fedora: sudo dnf install nodejs npm —
Debian/Ubuntu: sudo apt install nodejs npm — or nvm. Windows:
winget install OpenJS.NodeJS.LTS (or the nodejs.org installer).
Linux
sudo npm install -g --allow-scripts='ssh2,tesseract.js' infracensus-collector
sudo netdoc-collector enrol --show-input # prompts for the token; --show-input echoes it as you paste
sudo netdoc-collector service install # systemd unit, enabled and startedThe service runs as the netdoc-collector system account, keeps its identity
at /etc/netdoc-collector/collector.env (mode 0600 root:root, read by
systemd before privileges drop), and writes everything — logs, ledger, PID —
to /var/lib/netdoc-collector. An npm update -g infracensus-collector
replaces the code without touching either.
Windows
From an ADMIN PowerShell:
npm install -g --allow-scripts='ssh2,tesseract.js' infracensus-collector
'enrol_YOUR_TOKEN' | netdoc-collector enrol # piped, because the console may not paste
netdoc-collector service install # registers and starts the Windows service
Get-Service -DisplayName "InfraCensus Collector" # ask Windows, not us, whether it workedThe token is piped rather than pasted on Windows. enrol draws a readline
prompt, and a Server console frequently cannot paste into one: Ctrl+V does
nothing in a legacy conhost window, and right-click pastes only with QuickEdit
Mode enabled — off by default, and commonly off over RDP. enrol already reads
the token from stdin when stdin is not a terminal (the path built for
installers), so piping skips the prompt entirely: nothing is echoed and nothing
has to be pasted.
That form does put the token in your PowerShell history, which the prompted
path never did. Clear it with Remove-Item (Get-PSReadLineOption).HistorySavePath,
or avoid it: put the token in a file, run
Get-Content -Raw C:\temp\enrol.txt | netdoc-collector enrol, then delete the file.
The Linux prompt above still takes the token only at the prompt, never as an
argument.
This registers the InfraCensus Collector Windows service. The SCM-level id
is infracensuscollector.exe, derived by the service host and not ours to set,
which is why the check above asks by display name; netdoc-collector service
status answers the same question from the collector's side. The identity lives under
C:\ProgramData\InfraCensus Collector — locked to SYSTEM and Administrators,
and deliberately outside the npm package so an uninstall or update of the code
cannot destroy a key that was shown exactly once.
From a checkout (development / air-gap)
pnpm install in this directory, then the pnpm aliases below; pnpm build
bundles dist/. The CLI is the same code either way: npx tsx src/cli.ts
<command> in a checkout is netdoc-collector <command> on an installed host.
On an air-gapped Linux host, sudo ./scripts/install.sh registers the
from-source checkout as a service without touching a registry.
Enrolment
An enrolment is six values shown in the portal under Admin → Collectors,
wrapped in one enrol_… token beside the key. The token contains the key:
it is shown once, belongs in a password manager, and the prompt that takes it
does not echo. An all-sites collector's token carries an empty org and site on
purpose — such a collector runs as a daemon and takes tenant, site and targets
from each job.
Command reference
One installed command, netdoc-collector (npm's bin shim onto dist/cli.js).
enrol / doctor / uselog are standalone scripts underneath — they must
work before, or without, a built collector — and the CLI routes those three
subcommands to them, wiring the platform's config paths on the way.
| Command | What it does |
|---|---|
| netdoc-collector run | The daemon: poll the control plane for scan jobs, serve the local UI. This is what the service unit runs. |
| netdoc-collector scan [module] | ONE scan in the foreground, then exit. Default module: network. --dry-run builds the result and prints a summary without pushing. |
| netdoc-collector status | Local status: enrolment, daemon liveness, last run per module. |
| netdoc-collector enrol | Enrol this host. Secrets are shown masked — one dot per character — as they are typed or pasted; --show-input echoes them in full, --replace re-keys an existing install. |
| netdoc-collector doctor | Is it running, and is it talking to the cloud. Prints no credential, ever. |
| netdoc-collector uselog | What this collector did on your network, from your own ledger. |
| netdoc-collector service install | Register and start the background service (Linux: sudo, systemd — Windows: elevated, SCM). Refuses politely until the host is enrolled. |
| netdoc-collector service uninstall | Stop and deregister the service. The identity and ledgers stay on disk, and it says so. |
| netdoc-collector service status | Registered/active? Exit 0 running · 1 registered but stopped · 2 not installed. |
| netdoc-collector --version | Print the version. |
| netdoc-collector --help | Print usage. |
Anything else prints the help and exits 2.
Scan modules: network (ICMP/ARP/SNMP/SSH sweep), vmware, hyperv, sql,
lb (A10), voice (CUCM/Unity/Webex), identity (Microsoft Entra ID),
azure (CSPM), config-import (offline device configs). Each reads its own
section of the config; a module with nothing configured says what is missing
and exits non-zero.
Checkout equivalents: pnpm scan, pnpm scan:dry, pnpm scan:daemon,
pnpm vmware-scan, pnpm hyperv-scan, pnpm sql-scan, pnpm lb-scan,
pnpm voice-scan, pnpm identity-scan, pnpm azure-scan,
pnpm config-import, pnpm enrol, pnpm doctor, pnpm uselog.
The local UI
http://localhost:9477 # from a browser ON the collector hostSign in with your InfraCensus account (an active account of this collector's tenant — there is nothing local to sign in to). The page shows the daemon's status, the per-module last-run table with Run-scan buttons, a Restart button, and the daemon log tail, refreshing every five seconds. A scan started from the UI runs as a separate process and narrates itself into the same log tail the page is already showing.
Two environment variables control it — the only two the UI has:
| Variable | Effect |
|---|---|
| COLLECTOR_UI_PORT | Port for the UI (default 9477; always 127.0.0.1). An unusable value falls back to the default rather than stopping the daemon. |
| COLLECTOR_UI=0 | Disable the UI entirely. The daemon then opens no port at all. |
On an installed host, set them in /etc/netdoc-collector/collector.env (Linux)
or the service environment (Windows) and restart the service.
If the UI fails to start — a squatted port, say — the daemon logs a warning and keeps scanning. The status page is a convenience; the scans are the job.
Check it, and see what it did
netdoc-collector doctor # elevated on Windows: the config is ACL'd
netdoc-collector status
netdoc-collector uselog # your evidence ledger, rendereddoctor answers "is it running, and is it talking to the cloud" entirely
locally, printing values only from an allowlist of non-secret config keys; the
key itself appears only as a short fingerprint. uselog renders the collector's
own ledger — a rotated, redacted JSONL file (collector-use.log, ~25 MB hard
ceiling) recording what was scanned, what was contacted, and what was sent
where. It is your record, not our telemetry: nothing in it is transmitted, and
deleting it costs only your own evidence.
Uninstall
sudo netdoc-collector service uninstall && sudo npm uninstall -g infracensus-collector
# full purge of config + ledgers afterwards (the uninstall prints this too):
sudo rm -rf /etc/netdoc-collector /var/lib/netdoc-collector# elevated:
netdoc-collector service uninstall
npm uninstall -g infracensus-collectorThe identity is left on disk on purpose (the key in it was shown once); the uninstall names the directory and the purge command. Then remove the collector in the portal under Admin → Collectors — the uninstaller says this too. Until you do, its key remains valid for anyone who kept a copy.
If something is wrong
It stops with no error in the log — check EDR / antivirus. The daemon logs
uncaught exceptions and keeps running, so a code fault always leaves an ERROR
line; silence means the process was killed from outside. To a behavioral
engine, a process that sweeps thousands of addresses, opens SSH sessions with
stored credentials and spawns PowerShell for AD/WMI looks like lateral
movement. Add a tightly-scoped exclusion and check the OS event log around the
time it vanished.
Scans sit at "Waiting for a collector." Either no collector is authorized
for that site (portal, Admin → Collectors) or the collector is not polling
(doctor on its host, or the UI's "last poll" timestamp).
The UI shows the login form and rejects a good password. The control plane must be reachable from the collector host for login to verify — a 502 on the login call means the uplink, not the password. Also check the account is active and belongs to the same tenant as the collector.
It starts and immediately exits. Run it in the foreground:
netdoc-collector run (checkout: npx tsx src/cli.ts run). The usual causes
are a missing or malformed config, an unreachable control plane URL, or a
revoked key.
Files on this host
| File | Path (installed / checkout) |
|---|---|
| Config — holds the key | Linux /etc/netdoc-collector/collector.env · Windows C:\ProgramData\InfraCensus Collector\collector.config.json · checkout collector.config.json (override: COLLECTOR_CONFIG) |
| Daemon log | Linux service /var/lib/netdoc-collector/collector-daemon.log · otherwise collector-daemon.log beside the package |
| Use-log ledger | Linux service /var/lib/netdoc-collector/collector-use.log · otherwise collector-use.log beside the package (override: COLLECTOR_USE_LOG) |
| PID file | .collector-daemon.pid beside the package (best-effort — under the sandboxed systemd unit, liveness is systemd's answer, not the file's) |
| Bundled entrypoint | dist/cli.js — what npm's bin shim, the systemd unit and the Windows service all run |
