Files
pulse/docs
rcourtman 7606b72a8b Let an operator split override a Proxmox node's agent link (#2741)
## Problem

The resource drawer's **Split merged resource** (`POST
/api/resources/{id}/report-merge`), and `POST
/api/resources/{id}/unlink`, on a Proxmox node merged with its
pulse-agent recorded exclusions and answered "Resource split applied",
but the pair stayed merged on every rebuild, in the monitor and in the
resources API.

`ResourceRegistry.ingestRecord` joined the pair through the node↔agent
link in `resolveLinkedResource` (both sides declared, or a node-side
link the agent's hostname corroborates) before `findMatch`, and that
path never read `rr.exclusions`. The linked node had also already taken
the agent's identity in `resourceFromProxmoxNode`, so it minted the
agent's machine-derived canonical ID before the agent was ingested.

## Decision

The operator's split wins. The node↔agent link is Pulse's own inference
from endpoint addresses and hostnames, the evidence cloned machines and
reused short hostnames defeat, and report-merge exists precisely for
wrong merges. A later `POST /api/resources/{id}/link` between the split
rows joins them again.

## Change

- `nodeAgentSplit` (`proxmox_node_links.go`) decides the pair from the
operator's decisions read across the IDs each side can hold: the store
records them against the merged ID and the `SourceTargets` candidates,
while a split node holds its own ID. The latest decision across those
sets wins (registries now keep each exclusion's `CreatedAt`). An
exclusion naming only the agent's candidate against the agent's
machine-derived ID does not count, because report-merge records that
same pair when it splits another source (a Docker host) off the agent.
- Every path that joined the pair applies it: the snapshot ingest no
longer lends a split node the agent's identity (`splitProxmoxNodeLink`);
identity-pin completion refuses the agent's machine key from a
joined-era pin; `resolveLinkedResource` refuses the declared and the
one-sided link; `applyManualLinks` skips an older link between split
rows; and the presentation filter keeps split rows apart when no single
exclusion names their IDs.
- A split node names the agent only where the monitor's node record
does, so guest discovery, deploy and service discovery are not handed
the rejected agent.
- A relink of the split rows is recorded as a folded ID, so pin
succession keeps it instead of re-keying it onto the joined ID.

Builds on #2725 (now merged). This PR's own changes are 4a97d2225f, plus
two repairs after `main` moved: 5b5026fafd records a relinked row as a
`ManualLinkFold` (#2721 replaced `linkMergedIDs`), and e4519ec5bc passes
thresholds to `CoalesceForPresentation` (#2745). The rest of the branch
is merges of `main`.

## Verification

- `TestOperatorSplitOverridesProxmoxNodeAgentLink`
(`registry_merge_policy_test.go`): seven link shapes (declared both
ways, node-side only, agent-side only, unclustered and clustered, an
agent hostname in another form, an agent reporting no machine key) × six
requests (report-merge, unlink naming the node's candidate, unlink
naming the split rows, an exclusion between the two candidates, an
exclusion naming only the agent's candidate, an unrelated exclusion).
Each step runs three rebuilds of a SQLite-backed `MonitorAdapter`,
checked in the monitor listing, the broadcast coalesce, and the
resources API's registry seeded from the listing and from the snapshot.
The test also covers agent-first ingests with and without the host, a
relink (plus an agent-first rebuild that persists its pins), and the
same request again. The unrelated exclusion and the agent-only exclusion
under a machine-derived ID are the negative controls: those pairs stay
merged.
- `TestOperatorSplitFromOneAgentKeepsAnotherAgentsPin`: a node split
from a second agent still completes its own linked agent's boot-window
pin. `TestOperatorSplitHoldsWhenTheAgentStopsReportingItsMachineID`: a
split holds when the agent comes back without its machine ID.
- 15 mutation controls, one per rule. Each makes these tests fail.
- Full `./internal/unifiedresources/...`,
`./internal/api/resourceapi/...` and `./internal/monitoring/...` pass,
as does `./internal/api/` filtered to resource, link, unlink, merge and
broadcast tests. The targeted tests also pass on the pushed head, which
includes current `main` (#2719).

## Not covered (contract "What the split does not reach")

- The browser's realtime coalesce (`mergeCanonicalResourceSnapshot`)
receives no exclusions and still joins a Proxmox-only row and an
agent-only row that share a hostname. A split pair, from this change or
from an identity-match split, therefore still renders as one row in the
UI.
- The monitor's own node record keeps its link. The SMART fallback,
disk-exclude patterns, guest discovery, deploy and service discovery
still follow the agent.
- `resolveLinkedPhysicalDisk` still joins disks inside one host without
reading exclusions.
- The agents API's node link does not replace a resource split.
- Exclusions keyed by cluster- or hostname-derived IDs carry no source.
2026-10-08 10:59:13 +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.