diff --git a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md index 4c9051a0f3..733d6fa3e4 100644 --- a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md +++ b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md @@ -1687,6 +1687,11 @@ token-free command. No copy receipt establishes installed removal or a release. ## Shared Boundaries +Webhook DELETE acknowledges committed destination removal with `success: true` +and the retained legacy status. It does not acknowledge agent removal, revoke an +agent credential or change an already admitted agent/update job. Notification +queue destination-ID revocation remains with the notification owner. + Proxmox helper operation v2 carries the shared `ProxmoxLXCInventory` completeness and bounded omitted-VMID contract. V1 stays strict complete-only. The collector tries v1 only after an explicit unsupported-v2 response, under one unchanged operation deadline; transport, authorization and provider failures never select a broader fallback. The shared monitor constructor also composes notification bootstrap: it loads diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index d699383fa8..6ce20ef1c3 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -21,6 +21,18 @@ ## Purpose +### Webhook deletion has an explicit committed acknowledgement + +`DELETE /api/notifications/webhooks/{id}` returns `success: true` after saved and +live inventories both remove that ID. The legacy `status: "success"` field remains +for existing clients. The destination editor already requires the boolean before +accepting deletion; the acknowledgement must not leave a successfully removed +row shown as failed. Missing destinations, failed persistence and failed live +publication retain their existing error and rollback paths, not a success body. +The handler's deletion assertion pins both fields; persistence-failure controls +continue to require no live removal. This changes no route, scope, credential, +retention or admitted-delivery snapshot contract. + ### Typed MCP fleet filters survive HTTP projection GET/DELETE capability projection preserves public string and boolean query diff --git a/docs/release-control/v6/internal/subsystems/notifications.md b/docs/release-control/v6/internal/subsystems/notifications.md index a3f996d4d1..464354aa40 100644 --- a/docs/release-control/v6/internal/subsystems/notifications.md +++ b/docs/release-control/v6/internal/subsystems/notifications.md @@ -39,6 +39,14 @@ operator retry, cancellation, audit/receipt readback and new-alert delivery. This is synthetic source proof, not evidence of prior exposure, installed-provider recovery or resolution of a reported notification outage. +The DELETE acknowledgement includes `success: true` only after durable and live +removal complete, retaining the legacy `status: "success"` field. The existing +editor requires that explicit boolean before removing the visible destination; +a successful mutation must not be displayed as failed or encourage a blind retry. +Persistence/live-publication failures retain their existing refusal/rollback +behaviour. `TestNotificationHandlers/DeleteWebhook` pins both success fields; +its failure controls still prove that a rejected save never removes live state. + ### Approved addresses share a bounded connection budget Webhook and HTTP Apprise transports pass their validated numeric destinations diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md index b7a63ca5e2..f66f9c54c3 100644 --- a/docs/release-control/v6/internal/subsystems/storage-recovery.md +++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md @@ -775,6 +775,11 @@ Compact Coverage age cells use the existing metadata-size text and reclaim numer ## Shared Boundaries +Webhook DELETE's explicit success acknowledges saved/live destination removal, +not deletion of its retained queue configuration or failed-attempt history. +Queue records still follow the existing cancellation/retention policy; no +storage schema, backup, restore or data-removal acknowledgement is added. + ### Operator storage vocabulary without identity changes Storage topology names preserve Proxmox plugin vocabulary, including LVM-Thin, diff --git a/internal/api/alerting/notifications.go b/internal/api/alerting/notifications.go index e5756bb858..53721b2367 100644 --- a/internal/api/alerting/notifications.go +++ b/internal/api/alerting/notifications.go @@ -750,7 +750,9 @@ func (h *NotificationHandlers) DeleteWebhook(w http.ResponseWriter, r *http.Requ return } - if err := utils.WriteJSONResponse(w, map[string]string{"status": "success"}); err != nil { + // The destination editor accepts a mutation only after success is explicit. + // Keep status for existing clients; acknowledge only after durable/live removal. + if err := utils.WriteJSONResponse(w, map[string]interface{}{"success": true, "status": "success"}); err != nil { log.Error().Err(err).Str("webhookID", webhookID).Msg("Failed to write webhook deletion response") } } diff --git a/internal/api/alerting/notifications_test.go b/internal/api/alerting/notifications_test.go index fd2c682938..2b633cceea 100644 --- a/internal/api/alerting/notifications_test.go +++ b/internal/api/alerting/notifications_test.go @@ -718,6 +718,13 @@ func TestNotificationHandlers(t *testing.T) { h.DeleteWebhook(w, req) assert.Equal(t, 200, w.Code) + var result struct { + Success bool `json:"success"` + Status string `json:"status"` + } + assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &result)) + assert.True(t, result.Success, "the destination editor must be able to accept the persisted deletion") + assert.Equal(t, "success", result.Status, "retain the legacy acknowledgement") }) t.Run("GetWebhookTemplates", func(t *testing.T) {