Commit Graph

35 Commits

Author SHA1 Message Date
pulse-triage[bot] 34b924429b Incorporate batch-start upstream alert configuration repair
Preserve exact upstream 23bf99e1ba and all reviewed local history. Resolve the sole agent-lifecycle contract conflict by retaining both independent accounts; no frontend source resolution is required. Bound this transaction to the upstream frontier recorded at batch start.

Change-source: pulse-maintainer
2026-10-07 22:36:57 +01:00
rcourtman f7ac26b573 Keep alert settings a config save leaves out
The Thresholds page builds its PUT /api/alerts/config body from the
settings it edits. It has no control for flapping detection, alert TTL
cleanup or custom rules, so it never sends flappingEnabled,
maxAlertAgeDays, maxAcknowledgedAgeDays, autoAcknowledgeAfterHours or
customRules. The handler decoded the body into a zero AlertConfig, so
every thresholds save turned flapping detection and TTL cleanup off,
reset the flapping tuning, dropped custom rules, and persisted that.
The page's flapping, aggregation and ioNormalization objects are not
AlertConfig fields and never enabled anything.

The handler now hands the body to Manager.ApplyConfigUpdate, which
reads the stored config, merges and applies it under the manager lock,
so two concurrent partial saves cannot revert each other. The body is
decoded on its own, so the keys it carries mean exactly what
encoding/json makes of them; every top-level field it left out is
copied from a JSON round trip of the stored config, sharing no maps
with it. ApplyConfigUpdate returns a cloned snapshot, which the handler
persists, because SaveAlertConfig normalizes the config it is handed in
place and used to write into the live config's maps. Saves run one at
a time so alerts.json ends with the last applied config. A body that
is not a JSON object, null included, is a 400. Stored configs are not
rewritten, so a value an earlier save zeroed stays as it is.

On a private stack, a node CPU change saved from the Thresholds page
zeroed all four fields in memory and in alerts.json on main; with this
change the same save kept them and stored the new threshold.
2026-10-07 16:31:52 +01:00
pulse-triage[bot] c2d59d2747 Honour origin trust clears and unify webhook placeholder resolution
Treat explicit empty origins as a durable same-origin policy, preserve omitted fields and deployment overrides, and expose the effective value. Resolve webhook masks consistently across Create, Update and Test; reject unresolved or ambiguous identities before mutation or delivery while retaining masked responses, per-key edits, clears and encrypted persistence. Include real middleware/reload and HTTP Test/edit/delivery controls with all affected and shared dependent contracts.

Change-source: pulse-maintainer
2026-10-06 10:40:18 +01:00
pulse-triage[bot] a385ee7250 Align retained webhook clear control with upstream normalization
The combined-tree race check exposed the retained clear-header fixture still expecting an empty Content-Type. Upstream correctly removes cleared headers so the JSON default survives; align only that fixture expectation, and fix the strict helper comment. Preserve source-rs9sv1rz failure and do not change production normalization or rejection behavior.

Contract-Neutral: Test expectation and helper-comment reconciliation only; cleared header normalization is already supplied by reviewed upstream.
Change-source: pulse-maintainer
2026-10-06 09:55:26 +01:00
pulse-triage[bot] 4a764d7380 Complete strict webhook merge helper separation
Rename the strict update helper definition to match its resolved callers and compare the masked API response with its decoded custom-field projection. Upstream create/test restoration and retained update rejection checks stay separate.

Contract-Neutral: Integration-only helper binding and test expectation corrections.
Change-source: pulse-maintainer
2026-10-06 09:52:23 +01:00
pulse-triage[bot] a3a83b1e42 Correct webhook merge helper and test boundaries
Give the retained strict update helper a distinct name from upstream create/test restoration and close the retained connected test before the upstream tests. This additive correction repairs the integration only; both reviewed behaviors remain represented.

Contract-Neutral: Integration syntax and helper naming only; strict update-mask validation and upstream response masking remain unchanged.
Change-source: pulse-maintainer
2026-10-06 09:51:58 +01:00
pulse-triage[bot] b992e97ce2 Integrate launch-time upstream with combined table browser proof
Preserve exact 1896f1f2ba history. Resolve the webhook overlap by retaining the reviewed strict update restoration for unknown or ambiguous masks and upstream masked responses plus create/test restoration. Preserve both test groups; align response assertions with masked output and test clearing separately from the retained unknown-mask rejection cases.

Contract-Neutral: Conflict resolution preserves per-key webhook edits, persistence-before-publication, strict uncertain-mask rejection and response confidentiality; no route or feature is added.
Change-source: pulse-maintainer
2026-10-06 09:51:34 +01:00
rcourtman f279dc0b07 Resolve masked webhook values per key and keep responses masked
The webhook editor sends back the ***REDACTED*** values the list returns
for every header, custom field and signing secret the user did not
retype. Three paths mishandled them:

- Update restored the whole saved header or custom-field map whenever any
  value was masked, silently dropping a header or field added in the same
  edit.
- Update echoed the restored saved custom fields, so a Pushover token and
  user key came back in the response even though the list masks them, in
  breach of the api-contracts webhook response clause. Create echoed the
  request, and could store a masked value verbatim.
- The editor's Test button posts the form, so testing an edit after a
  reload sent the literal mask as the token or header.

Resolve each masked value to the saved value of the same key on update and
on a form test that carries a saved webhook's id, keep values typed in the
same request, and drop masks with no saved value. Build list, create and
update responses from one masked shape. Normalization now drops blank
headers, so clearing a header removes it instead of sending an empty
Content-Type that generic receivers cannot parse.
2026-10-06 09:21:41 +01:00
pulse-triage[bot] 2908fa8180 Keep explicit empty maps in the webhook request fixture
Construct replacement-map requests explicitly rather than applying WebhookConfig omitempty before the handler. Distinguish omitted maps from empty maps while retaining the original expected clearing semantics. The complete first connected lifecycle control passes; both initial aggregate fixture failures remain retained.

Change-source: pulse-maintainer
2026-10-06 02:55:11 +01:00
pulse-triage[bot] 2602db3690 Respect existing empty custom-field normalisation in edit controls
The initial focused run stopped at a mock oracle expecting an empty map although existing JSON and configuration normalisation retain nil. Keep its failed proof, preserve replacement-map deletion semantics, and check the canonical submitted response fields.

Change-source: pulse-maintainer
2026-10-06 02:46:51 +01:00
pulse-triage[bot] b10112cc77 Exercise webhook mask failures through the API boundary
Use the real update entry point for unrecoverable masks and let the connected control retain both response-confidentiality and durable-edit failures against the exact parent handler.

Change-source: pulse-maintainer
2026-10-06 02:41:34 +01:00
pulse-triage[bot] 9ea8a760d4 Preserve explicit webhook edits beside masked credentials
Restore only the submitted masked header or custom-field value instead of silently reverting the whole map. Reject unresolved masks before saving, retain explicit removals and case-correct header edits, and withhold restored custom-field credentials from the response. Connect encrypted form editing/reload to grouped and individual firing/recovery and keep admitted queue snapshots unchanged.

Change-source: pulse-maintainer
2026-10-06 02:40:17 +01:00
pulse-triage[bot] 2e331d594d Correct Apprise confidentiality queue and API fixtures
Handle nullable retry-row diagnostics while checking the actual attempt audit and DLQ error; verify omitted stored API keys through the established redacted response type and preserve authorised endpoint/target round-trips. Retain the original diagnostic repair and its adverse proof.

Change-source: pulse-maintainer
2026-10-02 10:17:49 +01:00
pulse-triage[bot] 1ce98f2693 Keep Apprise credentials and private payloads out of diagnostics
Suppress raw CLI targets/output, HTTP response bodies and credential-bearing endpoint paths across firing, recovery, tests, queue audits and settings logs. Preserve exact delivery inputs, typed causes and retry classification; retain structured status, counts and safe validation reasons.

Contract-Neutral: Shared agent-lifecycle and storage-recovery references are unchanged; this diagnostic repair alters no agent or storage behaviour, API schema, destination admission or delivery policy.
Change-source: pulse-maintainer
2026-10-02 03:34:36 +01:00
pulse-triage[bot] 439c1a6195 fix(notifications): refresh recovery queue owner after monitor reload
Recovery handlers retained the startup monitor after router reload, unlike normal notification handlers. Stopping that monitor cleared its queue and left Retry/Dismiss returning 503 even when the replacement notifier was available. Refresh the stable handler in both replacement paths and synchronise its owner reads. Add stopped-owner regression, concurrent replacement and scope proofs; keep queue persistence semantics unchanged.

Change-source: pulse-maintainer
2026-09-08 11:37:20 +01:00
rcourtman e2b6fe3b16 Preserve canonical incident history and Assistant handoffs
Filter canonical history before selecting occurrences, preserve source evidence
and expose bounded reads and failures. Reconcile duplicate saved shells without
splitting one alert lifecycle, and keep note identity and canonical risk intact.

Carry attributed operator notes into Assistant. Preserve mobile investigations
across layout changes, transfer composer focus on handoff and keep long event
text readable. Record scoped qualification and its unresolved wider limits.

Refs #1782
2026-09-08 02:08:15 +01:00
pulse-triage[bot] 21ed7a8927 fix(notifications): contain encoded query and resolved ntfy diagnostics
Literal-only query matching and the separate resolved ntfy transport caller leaked recognised URL credentials. Decode each query name once and project ntfy transport errors before logging or returning them, without changing destinations or error causes. Add synthetic sink-matrix and HTTP projection regressions plus the bounded notification contract in this commit.

Change-source: pulse-maintainer
2026-09-07 19:07:52 +01:00
pulse-triage[bot] 5f99f5c13f test(notifications): prove delivery diagnostic API contract
Pin useful diagnostic context and credential masking at the HTTP boundary, and document the API, agent and retained-history boundaries omitted from a2dbb27. This supplement must be packaged with that source commit for per-commit governance; it does not clear the historical hosted failure.

Change-source: pulse-maintainer
2026-09-07 18:33:06 +01:00
pulse-triage[bot] a2dbb27a68 fix(notifications): retain context around redacted delivery URLs
PR #1959 exposed that the URL-only redactor was called with a complete delivery error, causing safe but unhelpful replacement of the whole diagnostic. Redact embedded webhook URLs separately so credentials stay masked and non-secret failure context remains available; malformed URLs continue to fail closed.

Change-source: pulse-maintainer
2026-09-07 18:07:09 +01:00
pulse-triage[bot] 0f2d7ae055 Make notification delivery failures actionable 2026-08-31 00:39:53 +01:00
Pulse Test 5ee1cdd002 Make informational alerts a first-class severity 2026-08-28 00:45:20 +01:00
Pulse Test ff6c6a7fe7 Make mock alert history lifecycle-consistent 2026-08-27 23:36:55 +01:00
rcourtman 8ade613dfd Add complete mock alert timelines 2026-08-27 22:04:26 +01:00
rcourtman 1878245cdb Make alert incident timelines complete 2026-08-27 21:42:25 +01:00
pulse-triage[bot] 5f6bfefa56 Make notification destination updates durable 2026-08-27 19:46:40 +01:00
rcourtman 8df9db7ab1 feat(alerts): add destination severity routing 2026-08-27 19:13:32 +01:00
rcourtman ff507a6e44 feat(alerts): add per-alert snooze 2026-08-27 17:20:32 +01:00
rcourtman f4e1e47e74 feat(alerts): add external dead-man monitoring 2026-08-27 16:27:46 +01:00
pulse-triage[bot] 30b4fbd95e test(notifications): isolate terminal recovery paths 2026-08-27 16:08:53 +01:00
rcourtman 9572a113ae feat(alerts): append-only alert event log with suppression reasons
Adds internal/alerts/eventlog: a SQLite-backed, additive event log that
records lifecycle transitions (resolved, acknowledged, unacknowledged,
escalated, flapping detected) and notification decisions (dispatched,
deferred by quiet hours, suppressed — with the mechanism that held
them). Appends never block alert evaluation: a full buffer drops the
event and counts the drop; a store that fails to open degrades to
recording nothing. 90-day retention, hourly prune.

The manager emits at the existing funnels only — dispatchAlert and the
safe-call resolve/ack/escalate seams — so no lifecycle behavior
changes. Lifecycle "fired" is deliberately not recorded yet: the
active-alert store funnel also runs on persisted restore, so firing
waits for the explicit activation seam in a later phase. The monitoring
bootstrap enables the log per manager; ephemeral managers and tests
record nothing unless they opt in.

GET /api/alerts/events (monitoring:read) reads the log with
alertIdentifier/type/since/limit filters, newest first.

Phase 0 of docs/ALERT_ENGINE_EVOLUTION.md (coverage gap
alert-engine-suppression-observability).
2026-08-26 21:15:37 +01:00
rcourtman 515aa319e8 feat(alerts): bulk delivery diagnosis for all active alerts
GET /api/alerts/delivery-diagnosis without alertIdentifier now returns
the diagnosis array for every active alert in one manager pass, so list
surfaces do not need a request per alert. Extracts the per-alert
diagnosis into a locked helper shared by both paths; single-alert
behavior is unchanged.

First slice of coverage gap alert-engine-suppression-observability
(docs/ALERT_ENGINE_EVOLUTION.md Phase 0).
2026-08-26 21:05:37 +01:00
rcourtman cba1e2abeb Reconcile delivery health and Proxmox Assistant controls 2026-08-26 18:33:41 +01:00
rcourtman a7f87471f4 Stop returning the stored Apprise API key from the notifications API
GET /api/notifications/apprise and the PUT echo returned the saved API key
verbatim, so any settings:read token could exfiltrate a write-capable
secret while the sibling email handler blanks the SMTP password under the
same scope. Responses now drop the key and report hasApiKey instead, the
update and test-send paths fall back to the saved key when the request
leaves it blank (matching the email password contract), and the settings
form tells the user a saved key is kept when the field stays empty.
2026-08-23 14:39:07 +01:00
rcourtman bce5564025 fix notification terminal failure recovery 2026-08-23 09:05:54 +01:00
rcourtman 58bf77c1ae Extract alert delivery API package 2026-08-21 14:56:04 +01:00