# Feature Specification: Backup Scheduling MVP (032) **Feature**: Automatisierte Backups per Zeitplan (pro Tenant) **Created**: 2026-01-05 **Status**: Ready for implementation (MVP) **Risk**: Medium (Backup-only, no restore scheduling) **Dependencies**: Tenant Portfolio + Tenant Context Switch ✅ ## Context TenantPilot unterstützt manuelle Backups. Kunden/MSPs benötigen regelmäßige, zuverlässige Backups pro Tenant (z. B. nightly), inkl. nachvollziehbarer Runs, Fehlercodes und Retention. ## Goals - Pro Tenant können 1..n Backup Schedules angelegt werden. - Schedules laufen automatisch via Queue/Worker. - Jeder Lauf wird als Run auditierbar gespeichert (Status, Counts, Fehler). - Retention löscht alte Backups nach Policy. - Filament UI: Schedules verwalten, Run-History ansehen, “Run now”, “Retry”. ## Non-Goals (MVP) - Kein Kalender-UI als Pflicht (kann später ergänzt werden). - Kein Cross-Tenant Bulk Scheduling (MSP-Templates später). - Kein “drift-triggered scheduling” (kommt nach Drift-MVP). - Kein Restore via Scheduling (nur Backup). ## Definitions - **Schedule**: Wiederkehrender Plan (daily/weekly, timezone). - **Run**: Konkrete Ausführung eines Schedules (scheduled_for + status). - **BackupSet**: Ergebniscontainer eines Runs. **MVP Semantik**: **1 Run = 1 neues BackupSet** (kein Rolling-Reuse im MVP). ## Requirements ### Functional Requirements - **FR-001**: Schedules sind tenant-scoped via `tenant_id` (FK auf `tenants.id`). - **FR-002**: Dispatcher erkennt “due” schedules und erstellt genau einen Run pro Zeit-Slot (idempotent). - **FR-003**: Run nutzt bestehende Services: - Sync Policies (nur selektierte policy types) - Create BackupSet aus lokalen Policy-IDs (inkl. Foundations optional) - **FR-004**: Run schreibt `backup_schedule_runs` mit Status + Summary + Error-Codes. - **FR-005**: “Run now” erzeugt sofort einen Run (scheduled_for=now) und dispatcht Job. - **FR-006**: “Retry” erzeugt einen neuen Run für denselben Schedule. - **FR-007**: Retention hält nur die letzten N Runs/BackupSets pro Schedule (soft delete BackupSets). - **FR-008**: Concurrency: Pro Schedule darf nur ein Run gleichzeitig laufen. ### UX Requirements (Filament) - **UX-001**: Schedule-Liste zeigt Enabled, Frequency, Time+Timezone, Policy Types Summary, Retention, Last Run, Next Run. - **UX-002**: Run-History pro Schedule zeigt scheduled_for, status, duration, counts, error_code/message, Link zum BackupSet. - **UX-003**: “Run now” und “Retry” sind nur mit passenden Rechten verfügbar. ### Security / Authorization - **SEC-001**: Tenant Isolation: User sieht/managt nur Schedules des aktuellen Tenants. - **SEC-002**: Permissions (RBAC): - `backup_schedules.view` - `backup_schedules.manage` - `backup_schedules.run_now` - `backup_schedules.runs.view` - **SEC-003**: Runs schreiben tenant-scoped Audit Logs (keine Secrets/Tokens). ### Reliability / Non-Functional Requirements - **NFR-001**: Idempotency durch Unique Slot-Constraint (`backup_schedule_id` + `scheduled_for`). - **NFR-002**: Klare Fehlercodes (z. B. TOKEN_EXPIRED, PERMISSION_MISSING, GRAPH_THROTTLE, UNKNOWN). - **NFR-003**: Retries: Throttling → Backoff; 401/403 → kein blind retry. - **NFR-004**: Missed runs policy (MVP): **No catch-up** — wenn offline, wird nicht nachgeholt, nur nächster Slot. ## Data Model ### backup_schedules - `id` bigint - `tenant_id` FK tenants.id - `name` string - `is_enabled` bool default true - `timezone` string default 'UTC' - `frequency` string enum: daily|weekly - `time_of_day` time - `days_of_week` json nullable (array, weekly only; 1=Mon..7=Sun) - `policy_types` jsonb (array) - `include_foundations` bool default true - `retention_keep_last` int default 30 - `last_run_at` datetime nullable - `last_run_status` string nullable - `next_run_at` datetime nullable - timestamps Indexes: - (tenant_id, is_enabled) - (next_run_at) optional ### backup_schedule_runs - `id` bigint - `backup_schedule_id` FK - `tenant_id` FK (denormalisiert) - `scheduled_for` datetime - `started_at` datetime nullable - `finished_at` datetime nullable - `status` string enum: running|success|partial|failed|canceled|skipped - `summary` jsonb (policies_total, policies_backed_up, errors_count, type_breakdown, warnings) - `error_code` string nullable - `error_message` text nullable - `backup_set_id` FK nullable - timestamps Indexes: - (backup_schedule_id, scheduled_for) - (tenant_id, created_at) - **Unique**: (backup_schedule_id, scheduled_for) ## Acceptance Criteria - User kann pro Tenant einen Schedule anlegen (daily/weekly, time, timezone, policy types, retention). - Dispatcher erstellt Runs zur geplanten Zeit (Queue Worker vorausgesetzt). - UI zeigt Last Run + Next Run + Run-History. - Run now startet sofort. - Fehlerfälle (Token/Permission/Throttle) werden als failed/partial markiert mit error_code. - Retention hält nur die letzten N BackupSets pro Schedule.