6.3 KiB
Worker Host Inventory
Purpose And Boundary
Inventory connects checks to the host, service, Compose project, domain, and deployment that they observe. The worker collects facts; the RSMon control plane validates identity, applies account scope, reconciles lifecycle, and persists the inventory projection.
The control-plane inventory entities remain Server, ServerIp, Site,
Deployment, Domain, Repo, and site_repos. Their API and rstuff stream
projection are control-plane concerns. This repository owns only local
discovery and worker-originated reports.
Existing Collectors
Current state:
internal/webapp/inventory.godiscovers Linux processes, command lines, working directories, TCP listeners, and basic resource facts.- snapshots are stored in the local web-console SQLite database;
internal/distworker/server_metrics.goreports bounded process/network host snapshots, but not normalized application inventory;- external
deploymentdremains the implemented authoritative collector for nginx, Docker Compose, and host inventory on the control plane.
The worker does not currently send an inventory report to the control plane. It must not claim deploymentd parity until the protocol, ingestion, and reconciliation tests below are complete.
Target Package Layout
Move collection ownership out of internal/webapp:
internal/inventory/
collector.go orchestration and partial-success envelope
process_linux.go procfs process and listener discovery
docker.go Docker and Compose discovery behind capability
nginx.go read-only nginx virtual-host discovery
systemd.go allowlisted unit discovery
normalize.go stable IDs and control-plane report conversion
store.go optional local snapshot interface
The web console consumes a read-only snapshot interface. The runner consumes a bounded report interface. Collectors return per-section errors; one failed section does not discard successful sections.
Report Contract
Add a typed WorkerInventoryReport branch to internal/wire:
{
"kind": "result",
"inventory": {
"schema": "rsmon.worker.inventory.v1",
"report_id": "uuid",
"server_id": 42,
"collected_at": "2026-07-13T15:00:00Z",
"full_snapshot": true,
"host": {},
"addresses": [],
"processes": [],
"compose_projects": [],
"nginx_sites": [],
"systemd_units": [],
"errors": [{"section": "docker", "code": "unavailable"}]
}
}
Constraints:
report_idis stable across transport retries and unique across collection runs.server_idcomes from authenticatedWorkerInit.ServerID, never local env.- A full snapshot permits missing-item reconciliation; partial reports do not.
- Section and total item counts, strings, labels, and serialized bytes are bounded before enqueue.
- Environment variable values, file contents, process environments, container
secrets, and Compose
.envvalues are never included. - The control plane rejects a report when the worker is not linked to the server or the private worker's account does not own it.
Normalization
Stable local identity keys:
- process app: executable plus canonical working directory;
- Compose site: Docker Compose project name plus canonical project directory;
- Compose deployment: project plus service name;
- nginx deployment: canonical config path;
- domain: normalized lowercase hostname without trailing dot;
- systemd service: unit name.
Container IDs and PIDs are observations, not stable entity IDs. Never use them
as the sole upsert key. Every deployment includes last_seen_at; only a full
successful section can mark previously observed entities missing.
Collector Requirements
Processes
- Group related processes deterministically by executable, working directory, and parent relationship.
- Parse TCP and UDP listeners for IPv4 and IPv6.
- Do not read another process's environment.
- Exclude kernel threads and the worker itself from application suggestions.
Docker And Compose
- Disabled without an explicit Docker capability.
- Prefer Docker/Compose JSON output and labels; do not parse human tables.
- Collect project, service, image, state, ports, mount paths, health, and Traefik host rules. Exclude environment values and registry credentials.
- Treat Docker socket access as root-equivalent and report the capability to the operator.
Nginx
- Read only configured allowlisted roots.
- Collect config path, listen values, server names, auth presence, root path, and proxy presence. Do not send certificate private-key paths or contents.
Host And Server IPs
- Collect hostname, OS/kernel summary, and valid non-loopback addresses.
- Do not guess a primary address; report interface and route metadata so the control plane can apply policy.
Relationship To deploymentd And rstuff
Worker reports and deploymentd ingest are complementary producers of the same control-plane projection. During migration:
- deploymentd remains authoritative for sections it currently reports;
- worker inventory is feature-gated per server;
- every row records source and source report ID;
- reconciliation occurs per source and section to prevent one producer from deleting another producer's observations;
- the rstuff
rsmon.inventory.v1stream remains a control-plane output/input, not a worker protocol.
Delivery Phases
- Start and stop the existing process collector; move it behind an interface.
- Add typed report and control-plane ingestion for process/host/address data.
- Add read-only Docker and Compose discovery.
- Add nginx and systemd discovery.
- Add source-aware reconciliation and deploymentd comparison mode.
- Enable monitor suggestions only after operators confirm discovered targets.
Acceptance Tests
- A worker restart preserves no false stable identity based on PID/container ID.
- A failed Docker section does not erase successful nginx or process data.
- Partial reports never mark absent deployments missing.
- Full reports reconcile only rows owned by the same source and section.
- Cross-account and worker/server mismatch reports are rejected.
- Fixture snapshots prove no environment values or credential-like fields are serialized.
- Worker and deploymentd fixtures normalize equivalent Compose/nginx entities to the same control-plane identity without deleting each other's rows.