Add email and webhook notification targets with delivery tracking, and send
notifications when backup runs reach a terminal status.
Expose notification target management in the Django UI and keep delivery
failures recorded without failing the backup worker.
Replace refactor-era wording such as Source, Source root, SQL records,
database, runtime, and Django generation labels with operator-facing copy
around backup source, tracking records, changelog files, active config, and
pobsync-managed SSH keys.
Add view assertions so the old source/SQL labels do not quietly return.
Refs #24
Add a dedicated cleanup path for incomplete snapshots instead of letting
retention prune them implicitly. The retention plan now exposes a guarded
form that requires host and delete-count confirmation before removing
.incomplete snapshot directories and their SQL records.
Keep scheduled/manual retention behavior unchanged, add path safety checks,
and cover cleanup success, confirmation failures, max-delete limits, and
unexpected paths in tests.
Refs #10
Drop the obsolete pobsync_home field from GlobalConfig and remove it from
runtime config generation, form saves, and configuration commands.
The runtime state root now comes exclusively from POBSYNC_HOME/settings,
which keeps the Django model focused on backup behavior instead of install
layout.
Make manual retention application more explicit by requiring operators to
confirm both the host name and the current number of planned deletions.
This reduces the risk of applying a stale or misunderstood retention plan
when the delete set changes between review and confirmation.
Drop the legacy schedule user setting from the Django model, form, defaults,
and configure command.
Schedules are executed by the pobsync scheduler service under the configured
systemd service user, while remote SSH login users are configured separately
on global or host backup config.
Add a migration to remove the unused database column and update schedule
view tests around the simplified form.
Rename the schedule form label from "Cron expression" to "Schedule
expression" and explain that cron-style timing is evaluated by the
pobsync scheduler service rather than host cron.
Update host detail and schedule form copy so operators can see that
schedules are SQL-backed and dispatched by pobsync itself.
Expose a verbose rsync output option in the Django manual backup form and
store the selected value with the queued run request.
Propagate the option through the worker, direct management command, and
rsync command builder so real backups can emit itemized changes, file-list
progress, and stats when requested. Dry-runs continue to use verbose output
by default and report that consistently in requested options.
Cover the queue, worker, view, and rsync command behavior with focused
tests.
Add filesystem-backed SSH credentials for the native systemd deployment
path. Generated keys are stored below POBSYNC_HOME with 0600
permissions, while Django keeps the public key, fingerprint, path, and
selection metadata.
Add a Django SSH key generation view, delete action for unused generated
keys, and a management command used by the installer to ensure a default
backup key exists.
Update runtime config to use generated key paths directly as IdentityFile,
extend host checks to verify key readability, and keep legacy uploaded
keys available for compatibility.
Allow SSH credentials to be created from an uploaded private key file
as an alternative to pasting the key into a textarea.
Use multipart form handling in the credential views so server-side
keys can be imported without copy/paste wrapping or formatting damage.
Cover the upload path with a view test while keeping existing pasted
key validation behavior intact.
Canonicalize uploaded OpenSSH private keys before validation by
normalizing line endings, removing whitespace from the base64 body,
and re-wrapping it between the BEGIN and END markers.
Add SSH credential tests that generate a real ed25519 key, damage its
wrapping, and verify that validation succeeds after normalization.
Return a clearer validation error for PEM private keys, which are not
supported by the current credential flow.
Normalize pasted private keys before validation and detect common SSH
credential mistakes, including public keys pasted into the private key
field and public keys that do not match the supplied private key.
Translate OpenSSH libcrypto parse failures into a clearer user-facing
message and disable browser spellcheck/autocomplete on SSH key fields.
Document the native update flow as git pull followed by the
non-interactive installer so deployments refresh cleanly.
Validate uploaded SSH private keys with ssh-keygen before saving them so
invalid, malformed, or unsupported key material is rejected in the
control panel instead of failing later during rsync.
Auto-populate the public key when it is omitted, add an edit flow for
existing SSH credentials, and cover create, update, and invalid-key
paths with view tests.
Add SSH credentials as first-class Django data so backup keys can be
uploaded through the control panel instead of mounted into containers.
Credentials can be selected globally or overridden per host. At runtime
the selected key is materialized inside the container with restrictive
file permissions and injected into the rsync SSH command via IdentityFile.
Known hosts entries are handled the same way when configured.
Add control panel views for creating and listing SSH keys, expose the
fields in config forms and admin, document the workflow, and cover global
and host credential selection with tests.
Turn the host detail page into a more useful operator surface for
starting greenfield backups from Django.
Add quick actions for dry-run and real backup runs, keep the advanced
manual options available, and show whether a host is ready, disabled, or
blocked by missing global config. Surface queued and running counts plus
a direct link to the active run.
Expose requested backup options on the run detail page and cover the new
control flow with view tests.
Expose retention apply from the host retention plan page so planned
snapshot deletions can be executed from the Django UI.
The form requires explicit host confirmation, carries through the
selected retention kind and base-protection setting, and uses max_delete
as a deletion guard. The view delegates to the SQL retention apply
service and reports predictable pobsync errors back through Django
messages instead of surfacing a server error.
Add view coverage for confirmed deletion, invalid confirmation, and
POST-only enforcement.
Remove backup_root from the normal Django global config form and display
the fixed container path /backups instead.
Always persist /backups from the setup form so Docker deployments do not
mix host paths with container paths.
Update tests and docs to clarify that the host backup directory is chosen
through the Docker mount, while Django always uses /backups internally.
Add a staff-only manual backup form to host detail pages with safe
dry-run defaults and optional retention settings.
Queue manual BackupRun records through the existing worker-backed runner
path instead of executing backups inside the web request.
Validate disabled hosts, missing global config, and invalid methods with
view tests covering the new UI flow.
Fix the global config edit view so default initial values are only used
when creating a new config, preventing saved backup_root values from
being hidden by form defaults.
Keep pobsync_home as an internal runtime setting instead of exposing it
in the normal Django setup form.
Mount a host backup directory into Docker at /backups and document
POBSYNC_BACKUP_ROOT so backup_root behaves predictably in containers.
Add staff-only UI routes for creating/editing the default GlobalConfig
and creating the first HostConfig from the dashboard.
Improve the empty dashboard state so a fresh database guides the user
towards the next useful setup action instead of only showing empty tables.
Cover the setup flow with view tests for empty state prompts, global
config creation, and host creation.
Add a staff-only Django form for editing operational host settings while keeping
host identity stable. Support address, enablement, SSH/source overrides,
include/exclude lists, rsync extra args, and retention settings using the same
SQL-backed HostConfig model consumed by backup and scheduler flows.
Parse newline-separated list fields into JSON lists, preserve nullable
excludes_replace semantics, and cover rendering plus update behavior with view
tests.
Add a staff-only Django form for creating and updating host schedules using the
SQL-backed ScheduleConfig model. Link the form from host detail pages, validate
cron expressions with the existing scheduler parser, and preserve scheduler/CLI
behavior by writing to the same source of truth.
Cover default rendering, schedule creation, updates, and invalid cron handling
with view tests.