Soft delete & trash¶
Instead of hard-deleting, Vesana sets a deleted_at flag. Items land in trash, vanish from normal views, and can be restored within 30 days.
Which tables¶
Soft-delete-capable tables include:
- Tenants
- Hosts
- Host services (checks)
- Sites
- Aggregates
- Alert rules
- Notification channels
- Dashboards
- Collectors
- Monitoring scripts
- Wiki articles (own trash branch)
- Users (the username is released immediately on deletion; the trash entry carries the deletion date in its name)
- Device profiles, policies and icons — each with its own tab
Profile checks belong to their profile. The former "Service definitions" and "Templates" tabs no longer exist.
Deleted hosts, collectors and checks no longer count against the licence quota (from v1.9.437) — whoever deletes has room again immediately.
Columns¶
Per table:
| Field | Meaning |
|---|---|
deleted_at |
timestamp (NULL = active) |
deleted_by |
user UUID |
deletion_batch_id |
groups cascading deletes |
A central filter automatically hides deleted rows — normal endpoints and views see nothing deleted, without every single query having to account for it.
Cascade¶
When a host is deleted:
- Host gets
deleted_at - All
host_servicesof the host too — samedeletion_batch_id
When a tenant is deleted:
- Tenant + all children (hosts, services, alert rules, channels, dashboards) get the same
deletion_batch_id
On restore the whole family is reactivated.
Trash UI¶
Admin → Logs & Support → Trash shows all deleted items with:
- Tabs by type (hosts, services, alert rules, …)
- Badge with count
- Time of deletion
- Author
- Time until auto-purge (countdown)
- Restore and Purge buttons
Restore¶
Restoring is creating. The same limits apply as when creating something new:
- On top of
trash.restoreyou need the right to create that type (for a host,hosts.create). - The licensed host count and the tenant quota are checked — including the checks attached to a restored tenant. If the budget is not enough, the message names the free slots and nothing half-restored is left behind.
- A restored check starts on "waiting for data" instead of its frozen old status.
- A restored device never hangs on a collector that has since been deleted; its checks run again over the active checker right away.
Prerequisite: parent exists (or is restored alongside).
Example: a host was deleted, including its tenant. To restore only the host — doesn't work because its tenant is missing. Restore the tenant first (or both at once, which the bulk function handles).
flowchart LR
P[Tenant deleted] --> R{Restore hosts?}
R -->|Tenant missing| FAIL[Error — restore tenant first]
R -->|Tenant ok| OK[Host + all deletion_batch_id children back]
Permanent delete¶
Before 30-day auto-purge, explicit „Purge" removes the item from the DB. Hypertable data (check_results, logs) for the service isn't co-deleted, but they reference a non-existent service and are gone from normal views — auto cleanup eventually removes them via retention.
Auto purge¶
Background job runs daily at 04:00 UTC:
- Find all
deleted_at < NOW() - 30 days - Delete hypertables for the entry
- Remove item from the table
- Write audit entry
Order matters: hypertables first, since cascade delete doesn't auto-clean them.
Agent / collector / token¶
| Item | Soft delete | Token behavior |
|---|---|---|
| Host (with agent token) | Token disabled (active=false) |
Reactivated on restore |
| Collector (with API key) | Key disabled | Reactivated on restore |
So an accidental soft delete is reversible even for tokens. Hard delete (manual or after 30 d) removes the token hash forever — agent gets 401.
Permissions¶
Trash has three visibility levels instead of just yes/no, plus two action permissions:
| Permission | Effect |
|---|---|
trash.view_own |
see only items you deleted yourself |
trash.view_tenant |
see all deleted items within your own tenant |
trash.view_global |
cross-tenant view (super-admin-only) |
trash.restore |
restore items |
trash.purge |
permanently delete |
Instance-wide objects without a tenant (users, device profiles, tenants themselves) are only visible and deletable with trash.view_global — trash.view_tenant is not enough for those.
trash.purge should only be for admin roles — protection from oversight is the trash's main purpose. trash.view_global deliberately stays super-admin-only, since it crosses tenant boundaries.
Audit¶
Soft deletes and restores appear in audit log with action = <kind>.delete / restore.
Edge cases¶
Existing alerts on soft delete¶
If a service with active hard-CRIT is soft-deleted:
- Notifications stop immediately (service no longer evaluated)
- Existing alerts aren't „deleted" — they remain in history
- Restore: service back, status re-evaluated on next incoming result
Wiki articles with service link¶
If a service is deleted that has linked wiki articles: junction entries persist, restore brings links back.
Next¶
- Audit log
- Users & tenants — delete behavior