Keep historical upgrade recovery non-destructive

Route current upgrades to current guidance; preserve existing login and monitoring state when diagnosing missing PVE backups. Require endpoint-specific access evidence rather than blanket ACL changes or setup-token rotation.

Contract-Neutral: Documentation and regression checks only; no collector, permission, authentication or upgrade contract changes.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-07 02:25:19 +01:00
parent a249ac9d06
commit 2476a4e449
3 changed files with 171 additions and 42 deletions
+45 -21
View File
@@ -1,6 +1,9 @@
# Upgrade to Pulse v5
This is a practical guide for upgrading an existing Pulse install to v5.
This historical guide covers the v4-to-v5 transition. For an upgrade to a
current release, follow [Updating Pulse](AUTO_UPDATE.md) and the
[current installation and recovery guidance](INSTALL.md); do not downgrade
to v5 to follow this page.
## Before You Upgrade
@@ -50,11 +53,21 @@ helm upgrade pulse pulse/pulse -n pulse
### Bootstrap token on fresh auth setup
If you reset auth (for example by deleting `.env`), Pulse may require a bootstrap token before you can complete setup.
An upgrade is not a reason to reset authentication. **Do not delete `.env` or
repeat first-time setup to recover an existing login.** Follow
[password recovery](TROUBLESHOOTING.md#i-forgot-my-password), preserving the
existing configuration and data.
A bootstrap token is for an installation that genuinely needs initial setup,
not a replacement for an existing password. Retrieve it locally only when the
setup screen asks for it:
- Docker: `docker exec pulse /app/pulse bootstrap-token`
- systemd/LXC: `sudo pulse bootstrap-token`
Keep the token private and enter it in the setup screen; do not post it in a
report or put it in a URL.
### Sensor proxy removal
The `pulse-sensor-proxy` from v4 is no longer needed — temperature monitoring is now handled by the unified agent. If you had the sensor proxy installed on your Proxmox hosts, remove it **on each host** after upgrading:
@@ -89,25 +102,36 @@ Alternative option:
### Backups not showing (PVE)
If local PVE backups aren't appearing in Pulse, your API token may be missing the `PVEDatastoreAdmin` permission required for backup visibility.
An empty backup table does not establish a permission failure. Pulse's server
reads PVE backup inventory through the saved Proxmox API connection; a working
host agent does not prove that this separate collection is current.
This can happen if:
- You upgraded from v4 (older setup scripts didn't include this permission)
- You set up nodes via the unified agent before v5.1.x (the agent wasn't granting this permission)
- You created the API token manually without the storage permission
1. Compare an **existing** archive in native PVE with **Proxmox → Backups →
By date**, retaining its node, storage, guest type/ID and time. Check the
selected connection and filters. Direct PBS and PVE passthrough are distinct
sources, and Coverage posture is not the same as archive presence. A failed
or partial read is not an empty inventory.
2. Retain the existing collection status, original redacted error and time.
Connection liveness and a successful connection test do not establish backup
freshness. Do not run another backup, restore, guest-agent probe or restart
just to obtain evidence. An OK backup task does not prove guest thaw; follow
the [backup safety precaution](VM_DISK_MONITORING.md#backup-safety).
3. If a request was rejected, have the Proxmox administrator check the **actual
rejected endpoint**, installed PVE version and configured service user/token.
With privilege separation, both user and token ACLs must permit that request
at the relevant scope and inheritance. Authentication failure is not proof
of a missing storage role, and audit-only access is not proof of access to
every storage-content endpoint. Do not grant blanket storage-administrator
access, disable privilege separation or substitute an administrator token
merely because backups are missing.
**Quick fix** (run on each Proxmox host):
```bash
pveum aclmod /storage -user pulse-monitor@pve -role PVEDatastoreAdmin
pveum aclmod /storage -token 'pulse-monitor@pve!<token-name>' -role PVEDatastoreAdmin
```
Replace `pulse-monitor@pve!<token-name>` with the full token ID shown in Pulse,
for example `pulse-monitor@pve!pulse-example`. Privilege-separated PVE tokens
need the storage ACL on the token as well as the service user.
**Do not delete the monitored node, remove registration state or rerun setup as
a diagnostic.** Setup can rotate an existing API token and change permissions.
Preserve the connection, credentials, registration state and backup/history data.
Make any evidenced repair through normal maintenance outside backups, then
observe ordinary polling rather than repeating **Test Connection**.
**Alternative** (re-run setup):
1. Delete the node from Pulse Settings
2. Re-run the setup (either the UI-generated script or agent with `--enable-proxmox`)
3. The new token will have correct permissions
Note: The "re-run setup" option only works on v5.1.x or later, which includes the fix for agent-based setups.
Use the [complete missing-PVE-backup checks](UNIFIED_AGENT.md#pve-backups-not-showing-recovery)
and [Proxmox permission guidance](TROUBLESHOOTING.md#check-permissions-proxmox).
Keep token secrets, full ACL listings and private infrastructure details out of
public reports; follow [Getting Help](TROUBLESHOOTING.md#-getting-help).
+45 -21
View File
@@ -1,6 +1,9 @@
# Upgrade to Pulse v5
This is a practical guide for upgrading an existing Pulse install to v5.
This historical guide covers the v4-to-v5 transition. For an upgrade to a
current release, follow [Updating Pulse](AUTO_UPDATE.md) and the
[current installation and recovery guidance](INSTALL.md); do not downgrade
to v5 to follow this page.
## Before You Upgrade
@@ -50,11 +53,21 @@ helm upgrade pulse pulse/pulse -n pulse
### Bootstrap token on fresh auth setup
If you reset auth (for example by deleting `.env`), Pulse may require a bootstrap token before you can complete setup.
An upgrade is not a reason to reset authentication. **Do not delete `.env` or
repeat first-time setup to recover an existing login.** Follow
[password recovery](TROUBLESHOOTING.md#i-forgot-my-password), preserving the
existing configuration and data.
A bootstrap token is for an installation that genuinely needs initial setup,
not a replacement for an existing password. Retrieve it locally only when the
setup screen asks for it:
- Docker: `docker exec pulse /app/pulse bootstrap-token`
- systemd/LXC: `sudo pulse bootstrap-token`
Keep the token private and enter it in the setup screen; do not post it in a
report or put it in a URL.
### Sensor proxy removal
The `pulse-sensor-proxy` from v4 is no longer needed — temperature monitoring is now handled by the unified agent. If you had the sensor proxy installed on your Proxmox hosts, remove it **on each host** after upgrading:
@@ -89,25 +102,36 @@ Alternative option:
### Backups not showing (PVE)
If local PVE backups aren't appearing in Pulse, your API token may be missing the `PVEDatastoreAdmin` permission required for backup visibility.
An empty backup table does not establish a permission failure. Pulse's server
reads PVE backup inventory through the saved Proxmox API connection; a working
host agent does not prove that this separate collection is current.
This can happen if:
- You upgraded from v4 (older setup scripts didn't include this permission)
- You set up nodes via the unified agent before v5.1.x (the agent wasn't granting this permission)
- You created the API token manually without the storage permission
1. Compare an **existing** archive in native PVE with **Proxmox → Backups →
By date**, retaining its node, storage, guest type/ID and time. Check the
selected connection and filters. Direct PBS and PVE passthrough are distinct
sources, and Coverage posture is not the same as archive presence. A failed
or partial read is not an empty inventory.
2. Retain the existing collection status, original redacted error and time.
Connection liveness and a successful connection test do not establish backup
freshness. Do not run another backup, restore, guest-agent probe or restart
just to obtain evidence. An OK backup task does not prove guest thaw; follow
the [backup safety precaution](VM_DISK_MONITORING.md#backup-safety).
3. If a request was rejected, have the Proxmox administrator check the **actual
rejected endpoint**, installed PVE version and configured service user/token.
With privilege separation, both user and token ACLs must permit that request
at the relevant scope and inheritance. Authentication failure is not proof
of a missing storage role, and audit-only access is not proof of access to
every storage-content endpoint. Do not grant blanket storage-administrator
access, disable privilege separation or substitute an administrator token
merely because backups are missing.
**Quick fix** (run on each Proxmox host):
```bash
pveum aclmod /storage -user pulse-monitor@pve -role PVEDatastoreAdmin
pveum aclmod /storage -token 'pulse-monitor@pve!<token-name>' -role PVEDatastoreAdmin
```
Replace `pulse-monitor@pve!<token-name>` with the full token ID shown in Pulse,
for example `pulse-monitor@pve!pulse-example`. Privilege-separated PVE tokens
need the storage ACL on the token as well as the service user.
**Do not delete the monitored node, remove registration state or rerun setup as
a diagnostic.** Setup can rotate an existing API token and change permissions.
Preserve the connection, credentials, registration state and backup/history data.
Make any evidenced repair through normal maintenance outside backups, then
observe ordinary polling rather than repeating **Test Connection**.
**Alternative** (re-run setup):
1. Delete the node from Pulse Settings
2. Re-run the setup (either the UI-generated script or agent with `--enable-proxmox`)
3. The new token will have correct permissions
Note: The "re-run setup" option only works on v5.1.x or later, which includes the fix for agent-based setups.
Use the [complete missing-PVE-backup checks](UNIFIED_AGENT.md#pve-backups-not-showing-recovery)
and [Proxmox permission guidance](TROUBLESHOOTING.md#check-permissions-proxmox).
Keep token secrets, full ACL listings and private infrastructure details out of
public reports; follow [Getting Help](TROUBLESHOOTING.md#-getting-help).
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Keep the historical upgrade entry point from recommending destructive recovery."""
from pathlib import Path
import re
import unittest
ROOT = Path(__file__).resolve().parents[2]
GUIDE = "UPGRADE_v5.md"
def upgrade_guide():
return (ROOT / "docs" / GUIDE).read_text(encoding="utf-8")
def section(heading):
return upgrade_guide().split(heading, 1)[1].split("\n### ", 1)[0]
class UpgradeV5DocsTest(unittest.TestCase):
def test_shipped_help_matches_source(self):
self.assertEqual((ROOT / "docs" / GUIDE).read_bytes(),
(ROOT / "frontend-modern/public/docs" / GUIDE).read_bytes())
def test_historical_page_routes_current_upgrades_without_downgrading(self):
intro = " ".join(upgrade_guide().split("## Before You Upgrade", 1)[0].split())
for phrase in ("historical guide", "v4-to-v5 transition", "AUTO_UPDATE.md",
"INSTALL.md", "do not downgrade to v5"):
self.assertIn(phrase, intro)
def test_existing_login_recovery_does_not_reset_auth_or_expose_bootstrap_token(self):
auth = " ".join(section("### Bootstrap token on fresh auth setup").split())
for phrase in ("Do not delete `.env`", "repeat first-time setup",
"TROUBLESHOOTING.md#i-forgot-my-password",
"preserving the existing configuration and data",
"genuinely needs initial setup", "not a replacement for an existing password",
"enter it in the setup screen", "do not post it in a report",
"put it in a URL"):
self.assertIn(phrase, auth)
self.assertNotIn("for example by deleting", auth)
def test_missing_backups_do_not_trigger_acl_mutation_or_reenrolment(self):
backup = section("### Backups not showing (PVE)")
self.assertNotRegex(backup, r"aclmod|Quick fix|Delete the node|--enable-proxmox")
shell = "\n".join(re.findall(r"```(?:bash|sh)\n(.*?)```", backup, re.DOTALL))
self.assertEqual(shell, "")
backup = " ".join(backup.split())
for phrase in ("empty backup table does not establish a permission failure",
"actual rejected endpoint", "installed PVE version",
"configured service user/token", "both user and token ACLs",
"scope and inheritance", "Authentication failure is not proof",
"audit-only access is not proof", "Do not grant blanket storage-administrator",
"disable privilege separation", "substitute an administrator token",
"Do not delete the monitored node, remove registration state or rerun setup",
"Setup can rotate an existing API token", "connection, credentials",
"registration state and backup/history data", "outside backups"):
self.assertIn(phrase, backup)
def test_backup_source_freshness_and_safety_route_survive_upgrade(self):
backup = " ".join(section("### Backups not showing (PVE)").split())
for phrase in ("server reads PVE backup inventory", "saved Proxmox API connection",
"host agent does not prove", "node, storage, guest type/ID and time",
"Proxmox → Backups → By date", "connection and filters",
"Direct PBS and PVE passthrough are distinct sources",
"Coverage posture is not the same as archive presence",
"failed or partial read is not an empty inventory",
"do not establish backup freshness", "original redacted error and time",
"Do not run another backup, restore, guest-agent probe or restart",
"OK backup task does not prove guest thaw", "observe ordinary polling",
"Keep token secrets, full ACL listings and private infrastructure details"):
self.assertIn(phrase, backup)
for target in ("VM_DISK_MONITORING.md#backup-safety",
"UNIFIED_AGENT.md#pve-backups-not-showing-recovery",
"TROUBLESHOOTING.md#check-permissions-proxmox",
"TROUBLESHOOTING.md#-getting-help"):
self.assertIn(target, backup)
if __name__ == "__main__":
unittest.main()