Files
pulse/docs
rcourtman 81013369e9 Stop monitoring linking a node to an agent the operator split off (#2786)
## Problem

#2741 made an operator's split of a Proxmox node and its pulse-agent
(report-merge / unlink in the resources API) split the registry's rows.
The monitor's own link (`Node.LinkedAgentID`, `Host.LinkedNodeID`)
stayed in place, so everything that reads it kept treating the two
machines as one:

- the physical disk poll used the agent's SMART inventory as the node's
disk fallback, and applied the agent's `--disk-exclude` patterns to the
node's disks
- `linkedHostForNode` fed the node's disk sources from the agent
- guests inherited the agent for discovery and commands
- deployment reported the node as already running an agent
- service discovery routed through the agent
- the agent's container filesystem readings were applied to the node's
containers
- the node's temperatures came from the agent, through the link or the
exact-hostname fallback
- `sharedSystemAlertCorrelationForHost` tied the node and the agent
together

## Decision

An operator split should stop the monitor treating the pair as linked.
Two stores were recording one fact: the resource store's exclusions and
the monitor's link. The resource store's exclusion is now the one
record, and the monitor follows it:

- `MonitorAdapter.ProxmoxNodeAgentSplit` implements a new
`models.NodeAgentSplitDecider`. It applies the registry's own rule for a
node's link (`splitProxmoxNodeLink`), also under the ID the registry
holds the agent under, and confirms a split it finds against the store's
latest decisions, so a relink takes effect on the next poll.
- `models.State` holds both sides of a split link back on every link
write: the node poll (for the polled nodes and for other connections'
retained nodes, before their links can steer where views fold), the
reconcile after manual intent, `UpsertHost` and `LinkNodeToHostAgent`.
Report ingest drops a split link before it applies it, uses the link the
state kept, and clears the agent's cached container readings. Those
readings are also not shown for a node that no longer names the agent.
The temperature read skips a split agent in its exact-hostname fallback.
- Reverse case: the agents API's node link (`/api/agents/agent/link`) is
the operator's newer "same machine" decision. It now removes that pair's
split from the store (`TakeExclusion`, which returns the rows it
actually removed) before it records manual intent, and puts them back
with `RestoreExclusion` if the intent fails to persist. A restore never
replaces a decision the operator recorded in between.
- A split recorded after a manual link ends that intent on the agent's
next report that finds the split confirmed in the store, so host
continuity stops reserving the node for that agent. An unreadable store
confirms nothing and keeps the intent.
- The agents API's unlink needs no store write: it clears monitoring's
link, and the registry only joins a node and an agent through a link one
of them declares or one inferred from another declared link.

Limits (recorded in the contracts): a rebuild that cannot read the
store's decisions loads none, in the registry as in monitoring, which
needs a change in `registry.go` and `monitor_adapter.go` and is left for
a follow-up; a carried temperature reading from an agent that was tied
to its node by address stays until the ordinary carry window ends; mock
mode's fixture graph keeps its own links.

## Tests

- `TestMonitorStateHoldsBackAnOperatorSplitNodeAgentLink` runs #2741's
six split requests over four link shapes, through a `models.State`
holding the adapter's decisions. Each step runs three poll-and-rebuild
cycles and checks every view (monitor listing, broadcast coalesce,
resources API from listing and from snapshot), the state's links, that a
report alone keeps a split pair apart, a relink, an agent-first rebuild
that persists its pins, and the request again.
- `TestMonitorStateHoldsBackSplitLinksItStartsWith`,
`TestMonitorStateKeepsASplitWhenTheAgentStopsReportingItsMachineID` and
the two `internal/models` tests (a split agent can no longer fold two
connections' node views, for the polled and for the retained node).
- `TestOperatorSplitStopsMonitoringTreatingNodeAndAgentAsLinked` follows
report-merge through the real readers listed above, with a negative
control (an unrelated exclusion keeps every one of them linked).
`TestSplitAgentSensorsDoNotReachTheNodeThroughItsName`,
`TestAgentContainerReadingsAreNotShownForANodeThatNoLongerNamesTheAgent`
and `TestReportUsesTheLinkTheStateKeptWhenASplitLandsMidReport` cover
the temperature fallback, the cached readings and a split landing
mid-report.
- `TestManualNodeLinkReplacesAnOperatorSplit`,
`TestManualNodeLinkKeepsTheSplitWhenItsIntentCannotPersist`,
`TestManualNodeLinkSurvivesAnUnreadableSplitStore` and
`TestConfirmedSplitOfAManualLinkClearsTheAgentsContainerReadings` cover
the agents API link before and after a split, a failing intent, an
unreadable store and the intent ending.
- Store tests for `TakeExclusion` / `RestoreExclusion` in SQLite and
memory, including a removal that fails part way, an operator replacing
the pair between listing and removal, rows stored in the reversed order,
and a stale older link beside the exclusion.
- Mutation controls: I disabled each of the 26 rules above in turn
(state and proposal hold-back, retained-node hold-back, the `UpsertHost`
checks, the report-ingest checks, ending and keeping manual intent, the
lift, the restore, the store confirm, the held agent ID, the temperature
filter, the cached-reading checks, and so on). Each made at least one of
these tests fail.
- The full `models`, `unifiedresources`, `monitoring`, `config`,
`alerts`, `servicediscovery` and `resourceapi` suites pass on pulse-dev;
`TestGuestMetadataWriterFailuresAreOwnedWithoutSpinRetry`,
`TestDeliveryEpisodeCoalescingSurvivesRestart` and
`TestTrueNASPollerUnresponsiveRPCDoesNotFreezeOtherConnections` failed
once under load average ~80 in packages this change does not touch, and
each passes alone.
2026-10-10 10:27:33 +01:00
..
…
2026-10-05 21:53:41 +01:00
…
…
2026-10-05 21:53:41 +01:00
2026-10-05 21:53:41 +01:00

Pulse documentation

Start here for installation, platform setup, security, operations, and Pulse Intelligence. Commands, configuration keys, image names, API fields, and product identifiers remain untranslated in localized guides.

Start here

  • Install Pulse — signed Proxmox/Linux installation, Docker, Docker Compose, Kubernetes, and first-run setup.
  • Production deployment and security — least-privilege Proxmox setup, root-agent boundaries, discovery, supply-chain verification, plan limits, scale evidence, and a rollout checklist.
  • Upgrade from Pulse v5 — migration prerequisites, rollback, agent continuity, and post-upgrade checks.
  • Releases and update channels: choosing Stable or Preview, beta and RC expectations, and how releases are tested and promoted.
  • Configure Pulse — authentication, notifications, discovery, retention, and system settings.
  • Deployment models — data locations, lifecycle, and differences between supported deployment paths.
  • Troubleshooting and FAQ — common failures, diagnostics, and operator questions.

Localized getting started guides: Deutsch · Español

Platforms and agents

VMware vSphere support is early access. Current builds expose dedicated vSphere inventory and recovery context, but operators should validate the integration against their own vCenter before production use.

Monitoring and operations

Pulse Intelligence

Patrol watch-only analysis is available on Community with a local model or the operator's own provider. Investigation and governed fixes require the relevant Pulse Pro capabilities.

Security, privacy, and access

Plans and managed access

Pulse Cloud is not generally available. Ordinary self-hosted Pulse remains the primary installation path; MSP and Enterprise access are explicit commercial paths rather than defaults in self-hosted setup.

Development and reference

Detailed design notes and dated migration specifications may remain in this directory for maintainers, but they are not operator setup guides unless they are linked from the sections above.

Previous versions and migrations

Found a bug? Use the issue forms. For setup questions, use GitHub Discussions.