Tighten v5-to-v6 upgrade safety, release installability, provider MSP mode handling, AI cost accounting, metrics flushing, and frontend guardrails for the v6.0.0 GA candidate.
Two follow-ups caught by independent re-review:
- #29 (8855b78c0): GetConnectionStatuses keys a name-less instance by host,
but the diagnostics lookup used 'pve-'/'pbs-'+Name only, so the
monitor-state merge was a silent no-op for unnamed instances. Fall back
to host to match.
- #22 (d9da9b18f, #1254): add a full-router test asserting an UNBOUND
agent:report token gets 404 (not 200) on config fetch — continuously
verifying the token-binding security boundary that the scope change
relies on.
#23 (a6b773a85) made Restart load the AI config once and pass it to
startWithConfig, so the start path no longer re-loads it. The test still
expected LoadAIConfig().Twice(); updated to .Once() to match the fixed
single-load behavior. (Caught by running the full internal/api suite,
which the per-fix targeted runs had missed.)
Back-port the core of v5 fix 1de1392c9 to v6 (minimal, not the full
dedup refactor). The model list API now sets ModelInfo.Provider (stamped
in prefixProviderModels and passed through HandleListModels), and the
frontend groupModelsByProvider prefers the server-supplied provider over
splitting the model id, so models with opaque ids (e.g. Ollama-hosted
llama3-8b) group under the correct provider. Adds a grouping regression
test.
Back-port v5 fix 716340dcd to v6. Extracts startWithConfig from Start so
Restart passes the config it already loaded down to the start path, instead
of Start re-reading loadAIConfig a second time. Avoids starting the AI
service with a stale/inconsistent config if it changed between Restart's
load and the start. Both Restart start paths now use startWithConfig.
Back-port v5 fix d310c257a to v6, adapted to v6's connection-status key
format. computeDiagnostics now merges a failed PVE/PBS diagnostics probe
with the monitor's live connection state: if the long-running poller still
reports the instance connected, a transient probe failure (network blip,
TLS re-check) no longer flips it to 'disconnected' in the UI. Uses v6's
'pve-<name>'/'pbs-<name>' status keys (v5 used a bare node name, which
would not match in v6). Adds a merge-logic regression test.
Back-port v5 fixes 5997fd81f and 0a7b93a84 to v6:
- GetWebhooks list response now includes the configured mention so the UI
shows it after reload instead of blanking it.
- sendResolvedWebhook now assigns data.Mention (v6 set it for grouped/
firing webhooks but dropped it on resolved), and the Discord/Slack/Teams/
Mattermost ResolvedPayloadTemplate strings gained {{if .Mention}} guards.
Without these, a configured @everyone/@channel was silently omitted from
resolved/cleared notifications. Adds list-API and per-service resolved
mention regression tests.
Back-port v5 fix 04a828264 to v6. canReadConfig now accepts
ScopeAgentReport (legacy host-agent:report) in addition to the config/
manage/settings scopes; resolveConfigAgent already restricts a
report-only token to the host it is bound to, so it can only read its
own config. Without this, a v5 agent upgraded to a v6 server while still
holding a report-only token gets HTTP 403 on config fetch and breaks.
Retargets TestHandleConfigMissingConfigScope to monitoring:read and adds
TestHandleConfigAllowsBoundAgentReportScope as a #1254 regression guard.
Dead-code sweep. Functions flagged unreachable by golang.org/x/tools/cmd/deadcode
and confirmed unused across pulse, pulse-enterprise, pulse-pro and pulse-mobile by
adversarial cross-repo verification. Cross-module reachability was checked
explicitly (only pkg/ exported symbols are importable by other modules; internal/
packages and _test.go files are not). go build, go vet and test-compile all pass.
Root fix for the recurring class behind issue #1470. The served /install.sh and
/install.ps1 endpoints existed to hand out the unified AGENT installer, but their
GitHub fallback fetched the top-level install.sh release asset, which since
49412357a is the SERVER installer. Prior commits made that fallback unreachable
in normal deployments (deploy the sidecars; serve the local script even when
unsigned), but the endpoint was still structurally capable of serving the wrong
script in the no-local-bundle case.
The agent installer is a per-build artifact bundled into every release tarball and
Docker image, not a release asset, so the endpoint has no business proxying a
release asset at all. Remove proxyInstallScriptFromGitHub and its
installScriptReleaseAssetURL wrapper. handleDownloadInstallScriptCommon now serves
the locally bundled agent installer (signed when sidecars are present, unsigned
otherwise) or fails closed with 503 when no bundled script exists. Serving the
SERVER installer at this endpoint is now structurally impossible, not merely
unreachable.
The shared version-pinning (releaseAssetTag/releaseAssetURL) and installScriptClient
remain for the agent-BINARY download proxy, which legitimately fetches published
release assets; its version-pinning stays covered by the agentBinaryReleaseAssetURL
contract tests.
Replace the obsolete install-script proxy tests with fail-closed assertions
(including a guard that the endpoint makes no outbound call), drop the four
installScriptReleaseAssetURL contract tests, and revise the four subsystem
contracts that pinned the install-script fallback transport (api-contracts items
8 and 27, agent-lifecycle item 14, storage-recovery item 14, plus the
deployment-installability note) to state that install scripts are served local or
fail closed with no GitHub fallback.
The "Install on Linux/Windows" wizard does `curl -fsSL <server>/install.sh |
bash -s -- --url ...` and never verifies the response signature headers (curl|bash
discards them). But for published releases handleDownloadInstallScriptCommon
proxied the top-level GitHub install.sh asset whenever the local agent installer
lacked its .sig/.sshsig sidecars, and since 49412357a that asset is the SERVER
installer, which rejects --url. Every install missing the sidecars served the
wrong script. The companion deploy_agent_scripts fix deploys the sidecars for new
installs, but existing boxes stay broken until they redeploy.
Serve the locally bundled agent installer when its signatures are absent instead
of proxying. An unsigned-but-correct local script beats a signed-but-wrong proxied
one when nothing verifies the headers, and this retroactively fixes already-deployed
boxes the moment they get the new binary. The proxy now runs only when no local
installer is bundled at all, so the endpoint can no longer hand the agent wizard a
server installer in any reachable deployment state. New installs still ship the
sidecars and are served signed.
Revise the install-script signature/fallback contract this changes, across the
three subsystems that pin it (api-contracts item 8, agent-lifecycle item 14,
storage-recovery item 14) plus the deployment-installability note, to state that
the served endpoint serves the agent installer with correctness outranking
signature presence. Add a handler guard asserting a published-release server with
a present-but-unsigned local installer serves it locally and does not proxy.