20 KiB
Issue Backlog
Last reviewed against code: 2026-06-04.
This is the single source of truth for product work, hardening, release tasks, and production blockers. The previous docs/fixes-todo.md list has been merged into this file.
Current Status
Implemented and verified in code:
- Management persists accepted agent jobs in
job_history. - Management polls agent jobs and updates final status, error, finish time, duration, current step, and parsed backup snapshot ID.
- Operations page shows job history and audit log.
- Node-agent health checks cover required commands, ZFS pool state,
/dev/zvol, and Restic repository access. - Backup jobs parse the Restic snapshot ID, verify stored file size, and remove failed snapshots on verification errors.
- VM restore no longer writes directly to the production ZVOL. It writes to a staged ZVOL first, then swaps volumes with
zfs rename. - Agent jobs are persisted in SQLite. Active jobs are marked
failedafter an agent restart because subprocesses cannot survive restart. - Auth hardening landed: no default admin password, required agent token, CORS allowlist,
SameSite=Strict, login brute-force protection, no frontend localStorage bearer token. - Settings secrets are write-only/masked in the UI and API.
- Agent bearer-token comparison is timing-safe.
- Agent directory was renamed from
backend/toagent/.
Known caveat: active agent jobs are persisted, but deeper cleanup recovery for partially changed host resources is still future work.
Pre-Production Priorities
- Harden backup verification and add tests for stream/pipeline failure cases.
- Validate the staged restore workflow on a disposable Incus VM, including rollback scenarios.
- Evaluate true incremental ZFS-send based backups for large VM disks.
- Add cleanup and visibility for pre-restore/failed-restore ZVOLs.
- Improve agent crash cleanup for partially changed ZFS/Incus resources.
- Surface detailed node health diagnostics in the UI.
- Keep the root-running agent tightly network-restricted.
- Add automated tests and CI.
P0 - Production Blockers
1. Harden Backup Verification
Status: partially done.
Goal: ensure a backup is marked success only when the expected source data was fully stored and verified.
Tasks:
- Parse the created Restic snapshot ID after backup.
- Verify stored Restic file size against streamed source bytes or expected ZFS size.
- Remove failed Restic snapshots with
forgetandpruneon verification errors. - Make stream/pipeline failure handling explicit for VM and container sources.
- Avoid marking success if the source stream closes early but Restic exits successfully.
- Add a post-backup Restic integrity-check strategy that is safe for large repositories.
- Define an optional restore-probe workflow for critical VMs.
- Add tests with mocked command failures, pipe failures, and short reads.
Acceptance criteria:
- Successful backup jobs include a verifiable Restic snapshot ID in management history.
- Size mismatches fail the job.
- Simulated source stream errors cannot produce a successful job.
- A backup can be independently verified without trusting only the successful process exit.
- Verification behavior is covered by automated tests.
2. Validate Staged Restore Workflow
Status: implemented, not proven on real Incus/ZFS hardware in this repo session.
Goal: prove restore safety before production use.
Tasks:
- Require explicit typed confirmation including VM name.
- Validate snapshot ownership before restore.
- Replace direct
ddto production ZVOL with a staged restore workflow. - Log restore intent in audit log before dispatch.
- Add restore preflight endpoint.
- Validate node health before restore.
- Show selected snapshot metadata before restore.
- Show target VM status and disk size before restore.
- Optionally offer “create backup before restore” when the VM is accessible.
- Test staged restore on a disposable VM: backup, restore, boot, and confirm data.
- Test failed staged restore paths: Restic dump failure before swap, rename failure after old volume rename, and VM start failure.
Acceptance criteria:
- Restore stream failures happen on the staged volume, not the production disk.
- UI displays a restore plan before final confirmation.
- Restore is blocked when required preflight checks fail.
- Staged restore behavior is validated on a real Incus/ZFS test node.
3. Add Pre-Restore Volume Cleanup
Status: open.
Current behavior: every successful VM restore keeps the old volume as *.pre-restore-*. This is good for rollback, but without retention or cleanup it can fill the ZFS pool.
Tasks:
- Track pre-restore and failed-restore volumes created by the agent.
- Add a retention policy, for example keep the last N rollback volumes per VM.
- Surface retained rollback volumes in Operations or VM detail.
- Add explicit cleanup action with confirmation.
- Add health warning when retained restore volumes consume significant pool space.
Acceptance criteria:
- Repeated restores cannot silently consume unbounded pool space.
- Operators can see and clean retained restore volumes.
4. Harden Root-Running Agent Exposure
Status: partially done.
The agent runs with root-level host access because it needs Incus, ZFS, /dev/zvol, Restic, and device operations. A compromised agent is a host-level incident.
Tasks:
- Require a non-empty
API_TOKENof at least 32 characters. - Support
ALLOWED_MANAGEMENT_IPS. - Support HTTPS directly on the agent.
- Support private CA validation from management via
AGENT_CA_FILE. - Require private management-to-agent connectivity in production docs, such as NetBird or a dedicated management network.
- Prefer HTTPS agents with a private CA; document local HTTP only as development mode.
- Add installer warning when
HTTPS_ENABLED=falseand noALLOWED_MANAGEMENT_IPSis configured. - Add systemd hardening where compatible with Incus/ZFS access.
- Replace
npm startin systemd with directnode src/index.js. - Remove CORS from the agent; browsers should never call it directly.
Acceptance criteria:
- Production install docs do not expose the agent publicly by default.
- Installer warns on insecure network exposure.
- systemd unit has a documented minimum hardening baseline.
5. Encrypt or Protect Agent Tokens in Management
Status: open.
Current behavior: nodes.token is stored in plaintext in the management SQLite database. DB theft means all agent tokens are exposed.
Tasks:
- Decide between at-rest encryption with a dedicated management key or strict documented file/disk protection.
- If encrypting, add key configuration and migration for existing tokens.
- Ensure public node API responses never include tokens.
- Document database file permissions and disk-encryption expectations.
Acceptance criteria:
- Stealing the management database alone is not enough to read every agent token, or the residual risk is explicitly documented and operationally mitigated.
P1 - High Priority
6. Evaluate True Incremental VM Backups
Status: open.
Current behavior: VM backups stream the full ZVOL as vm.raw through restic backup --stdin. Restic deduplicates storage, but the agent still has to read, chunk, and hash the complete virtual disk every run. A VM with no changed files can therefore still take a long time.
Goal: reduce backup duration for large mostly-unchanged VMs by reading only changed ZFS blocks after the first full backup.
Tasks:
- Evaluate ZFS snapshot-chain based incremental backups with
zfs send -i previous current. - Design snapshot naming and retention so required incremental bases are not deleted too early.
- Define restore behavior for full plus incremental send chains.
- Decide whether incremental streams should be stored in Restic, object storage directly, or another repository layout.
- Define compatibility behavior for existing
.rawRestic snapshots. - Compare operational tradeoffs: faster backups versus more complex restore and retention.
- Add UI wording that distinguishes
Processedbytes from data actually uploaded/stored.
Acceptance criteria:
- A second backup of an unchanged large VM does not need to read the full ZVOL.
- Restore can reconstruct a VM from the chosen full/incremental chain.
- Retention cannot delete an incremental base required for restore.
7. Add Automated Tests and CI
Status: open.
Tasks:
- Add a test runner for agent and management code.
- Test validators for instance names, snapshot IDs, and ambiguous snapshot prefixes.
- Test job locking, persistence, trimming, and restart behavior.
- Test backup pipeline failure handling with mocked command/process failures.
- Test restore staged-volume command sequencing with mocked ZFS/Incus/Restic commands.
- Test management job polling updates
job_historyfor success, failed, timeout, and missing-agent cases. - Add CI for install, tests, frontend build, and syntax checks.
Acceptance criteria:
- Pull requests run tests automatically.
- Simulated backup stream failures fail the job.
- Simulated restore failures leave the original ZVOL name restored in the command sequence.
- Agent restart behavior is covered by tests.
8. Improve Agent Crash Cleanup and Resource Recovery
Status: open.
Agent jobs are persisted and active jobs are marked failed on restart, but a process crash can still leave host resources behind.
Tasks:
- Persist cleanup metadata for backup jobs: temporary Incus snapshot name, ZVOL, snapdev state, and backup type.
- Persist cleanup metadata for restore jobs: staged ZVOL, backup ZVOL, failed ZVOL, and swap state.
- On startup, scan failed active jobs and run safe cleanup actions.
- Log cleanup results into the persisted job logs.
- Avoid destructive cleanup when state is ambiguous; surface manual action instead.
Acceptance criteria:
- Crashing during backup streaming does not leave
snapdev=visibleor temporary snapshots unnoticed. - Crashing before restore swap removes staged restore volumes when safe.
- Crashing after restore swap does not automatically destroy rollback copies.
- Management can show cleanup-required states.
9. Improve Health Diagnostics in the UI
Status: partially done on API, open in UI.
Tasks:
- Agent health checks commands, ZFS pool,
/dev/zvol, and Restic repository. - Management records compact node health state.
- Add a detailed health drawer or modal on the Nodes page.
- Show command, ZFS pool,
/dev/zvol, Restic repository, and credential check results. - Show last health timestamp and the management-side error if the agent is unreachable.
- Distinguish unreachable, unauthorized, TLS failure, degraded health, and healthy states.
- Avoid global UI timeout banners when only one enabled node is slow or unreachable.
Acceptance criteria:
- A missing command is visible by name in the UI.
- Wrong Restic credentials are visible as a Restic health failure.
- TLS or connectivity failures are distinguishable from degraded agent health.
10. Fix Snapshot Device Visibility Race
Status: open.
Current behavior: backup waits a fixed 2 seconds after setting snapdev=visible.
Tasks:
- Replace fixed sleep with polling
fs.access(snapshotDevice)and timeout. - Run
udevadm triggerandudevadm settle. - Surface timeout as a clear backup failure.
Acceptance criteria:
- Slow device-node creation does not fail randomly.
11. Make Snapshot-ID Prefix Matching Unambiguous
Status: open.
Current behavior: prefix matching uses startsWith; collisions choose the first match.
Tasks:
- Return conflict when a snapshot prefix matches more than one snapshot.
- Prefer 12+ hex characters in the UI.
- Add validator tests.
Acceptance criteria:
- Ambiguous snapshot prefixes cannot restore the wrong snapshot.
12. Settings and Environment Hardening
Status: partially done.
Tasks:
- Mask secret values in
GET /settings. - Treat empty secret field as “keep existing value”.
- Improve
.envescaping for$, backticks, and newlines. - Add tests for
readEnvSettingsandwriteEnvSettings. - Document
.envfile permissions and enforcechmod 600in installer/deployment. - Keep or remove
SESSION_SECRETdeliberately: either use it for signing/encryption or remove dead config. - Set and document
SESSION_COOKIE_SECURE=truefor production.
Acceptance criteria:
- Secret settings cannot be exfiltrated through the UI/API.
- Writing
.envcannot create shell-expansion surprises.
13. Add RBAC
Status: open.
Current behavior: one user model, effectively all permissions.
Tasks:
- Add roles:
admin,operator,viewer. - Restrict restore and node settings to
admin. - Allow read-only dashboard access for
viewer. - Audit role changes.
Acceptance criteria:
- Restore is admin-only.
- Viewer cannot trigger backup, restore, node edits, or settings writes.
14. Session and Auth Cleanup
Status: partially done.
Tasks:
- Login brute-force protection.
SameSite=Strict.- Server-side cleanup of expired sessions.
- Session rotation after successful login.
- Replace
Math.random()in management user ID creation withcrypto.randomBytes. - Consider argon2id or explicit scrypt parameters and migration path.
Acceptance criteria:
- Expired sessions do not accumulate unbounded in SQLite.
- Session IDs are rotated after login.
15. Container Restore Decision
Status: open.
Current behavior: container backups can be created, but restore returns 501.
Tasks:
- Implement safe container restore with
zfs receive, or disable container backups in UI/API until restore exists. - Document the limitation prominently.
- Add tests for unsupported restore behavior.
Acceptance criteria:
- Users cannot mistake an unrestorable container backup for complete protection.
P2 - Product and Operations
16. Add Per-VM Backup Policy
Status: partially done.
Current state: schedules can be enabled per VM with interval and time of day. Retention is still global per agent.
Tasks:
- Add management database table for VM backup policies.
- Support per-VM retention values: hourly, daily, weekly, monthly.
- Support per-VM schedule enablement, interval, and time.
- Add optional policy flag: backup only when VM is running.
- Update Scheduler UI to edit policies per node and VM.
- Send policy retention to the agent backup request or apply it in management scheduling.
Acceptance criteria:
- Two VMs on the same node can have different schedules.
- Two VMs on the same node can have different retention policies.
- Disabled policies do not trigger backups.
17. Add Failure Notifications
Status: open.
Tasks:
- Add notification settings in management.
- Support webhook notifications first.
- Include node, VM, job type, error, and timestamp.
- Trigger notifications for failed scheduled backups.
- Trigger notifications for failed manual backups/restores.
- Add a “send test notification” action.
Acceptance criteria:
- A failed scheduled backup sends one notification.
- A test notification can be triggered from the UI.
- Notification failures are visible in Operations or audit logs.
18. Implement Real Snapshot File Browsing
Status: open.
Current snapshot browsing shows Restic contents, which for block-level backups is usually only /vm.raw.
Tasks:
- Design a safe read-only raw image inspection workflow.
- Restore or mount raw image read-only in a temporary workspace.
- Detect partitions and filesystems.
- Browse directories through management UI.
- Allow downloading a single file.
- Ensure cleanup of mounts and temporary files.
Acceptance criteria:
- User can browse a Linux VM filesystem from a snapshot without restoring the VM.
- Mounted/temporary resources are cleaned up after use.
- Unsupported or unsafe disk images fail with a clear error.
19. Add Version Reporting
Status: open.
Tasks:
- Add version field to management API.
- Add version field to agent health response.
- Show agent version in Nodes page.
- Flag unsupported or outdated agents.
- Document compatibility expectations.
Acceptance criteria:
- Nodes page displays agent version.
- Management can identify incompatible agents.
- Health output includes version information.
20. Containerized Management and UI Deployment
Status: open.
The node-agent remains a host-level systemd service. Management and frontend are good candidates for containers.
Tasks:
- Add a
managementcontainer image. - Add a frontend image that serves the Vite build through a small static server or Nginx.
- Provide
compose.yamlwith persistent SQLite volume for management. - Mount the agent CA certificate into the management container read-only.
- Document
CORS_ORIGINS,SESSION_COOKIE_SECURE,DATABASE_PATH, and reverse-proxy assumptions. - Decide whether frontend calls management through same-origin
/apior separate API origin. - Add health checks for both containers.
Acceptance criteria:
- Management API and frontend can be started with Compose without installing Node.js on the management host.
- Management database survives container recreation.
- Cookie login works behind HTTPS.
- Management can connect to HTTPS node-agents using the configured CA file.
21. Backup Scheduling and Quotas
Status: open.
Tasks:
- Add scheduler jitter in management and agent schedulers.
- Add per-VM and global backup concurrency limits.
- Add disk-space/quota checks before backup and restore.
- Add pre-backup VM state checks for live migration, existing snapshots, and permission problems.
Acceptance criteria:
- Many nodes do not all start scheduled backups at exactly the same time.
- A full pool or too many concurrent streams blocks new jobs with a clear error.
P3 - Cleanup and Refactoring
22. Systemd and Deployment Cleanup
Tasks:
- Add systemd hardening compatible with Incus/ZFS access.
- Use direct
node src/index.jsin service files instead ofnpm start. - Make
SCHEDULES_PATHexplicitly configurable. - Installer should warn on insecure agent exposure.
23. Data Integrity and Schema Cleanup
Tasks:
- Add audit-log integrity, such as append-only behavior and optional hash chain.
- Add
audit_events.detailssize limit or JSON column handling. - Replace ad-hoc
addColumnIfMissingwith schema versioning and migrations. - Stream Restic
lsinstead of loading all output in RAM.
24. Code Cleanup
Tasks:
- Generalize
incus snapshot deleteretry instead of matching English stderr. - Deduplicate
formatBytes. - Review
incus-backup-ui-plan.mdfor stale paths, secrets, and obsolete implementation notes. - Remove tracked
.DS_Storefiles. - Ensure
frontend/dist/remains ignored and untracked.
Done
- Default admin
admin/adminfallback removed. - Agent
API_TOKENrequired and minimum length enforced. - HTTPS required for agent URLs in management, with dev-only insecure HTTP escape hatch.
- Management can trust internal agent CA through
AGENT_CA_FILE. - Agent supports direct HTTPS.
- CORS allowlist added for management.
- Auth cookie set to
SameSite=Strict. - Login brute-force protection added.
- Bearer token removed from frontend localStorage.
- Settings secrets masked/write-only.
- Agent token comparison made timing-safe.
- Agent job persistence added.
- Staged VM restore implemented.
- Node-agent renamed from
backend/toagent/. - Agent installer script added.
- Agent update/diagnostic CLI added as
incubator. - Backup UI ETA/rate/bytes display added.
OSS Release Requirements
- Add tests.
- Add CI pipeline.
- Add
SECURITY.md. - Document threat model and trusted network assumptions.
- Add
LICENSE. - Add
CONTRIBUTING.md. - Add example
compose.yamlor Ansible role for reproducible deployment.