Add check_pobsync_install so native deployments can run the same runtime
diagnostics from the terminal that are available in the Django Self Check view.
The command prints every check with status, returns a failing exit code when
install-critical checks fail, supports fail-on-warning for stricter automation,
and is documented in the installer output and README update flow.
Delete the old scripts/deploy path that installed pobsync into /opt/pobsync/lib
with a standalone /opt/pobsync/bin wrapper.
Production deployment is now owned by the native systemd installer and updater,
so keeping the legacy deploy script would make the supported install story less
clear and easier to misuse.
Add scripts/update-systemd as a safer routine deploy entrypoint for native
systemd installations. The wrapper keeps updates non-interactive, preserves the
existing environment file, skips OS package installation, and avoids superuser
creation prompts while still reusing the installer refresh flow.
Document the update path in the README and capture the maintenance expectation
in the development notes.
Add a pobsync-manage wrapper that loads the native environment file before
running Django management commands, so production commands use the same
database and runtime settings as the systemd services.
Install the wrapper from the systemd installer, use it for migrations,
static collection, SSH key setup, and superuser creation, and document it
in the README for operational commands.
Create the 1.0 milestone and roadmap issues in Gitea, making Gitea the
leading tracker for release work.
Remove the repository roadmap document and README reference now that the
roadmap is tracked as milestone issues.
Document the remaining 1.0 work as ordered, reviewable chunks with
checkboxes for install/update flow, logging, preflight validation,
retention UX, dashboard polish, restore docs, config cleanup, and release
hardening.
Link the roadmap from the README and note how it can later be mirrored into
Gitea milestones and issues.
Drop the Global Configs metric from the dashboard because pobsync only uses
one default global configuration.
Keep global config management available through the existing dashboard
action and setup prompt.
Record the final rsync log path for successful real backup runs, matching
the existing dry-run and failure result payloads.
Add a staff-only run log endpoint and surface the link on run detail pages,
including fallback log discovery for older runs based on snapshot_path.
Cover direct log links and inferred scheduled backup logs with view tests.
Split each dashboard host card into a timeline area and a compact stats area
so snapshot, run, and schedule details have more room to scan.
Keep the same operational information visible while reducing cramped
column-like wrapping on desktop and mobile layouts.
Restructure the dashboard Hosts section into a card per host so snapshot,
run, schedule, retention, and data metrics have room to breathe.
Add reusable host-card styling and keep the same links and operational data
available without forcing everything into a wide table.
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.
Make SQL retention delete the snapshot root when records point at the
snapshot data directory, matching how backup metadata is stored on disk.
Before removing a snapshot tree, temporarily add user write permission to
directories inside that snapshot so rsync-preserved source permissions do
not block cleanup.
Add a regression test for pruning snapshots whose data directory mirrors a
read-only remote root.
Add a warning status for BackupRun records so successful snapshots are not
reported as failed when post-run SQL retention fails.
Keep the prune error in the run result, link the successful snapshot, and
let the management command complete with a warning instead of raising a
backup failure.
Include warning runs in backup trend summaries and add a regression test
for successful backups with failed retention cleanup.
Only apply default schedule initial values when creating a new schedule.
Avoid passing default initial data while editing an existing ScheduleConfig,
so the form renders the active cron-style expression, user, and retention
settings from the database.
Add a regression test that reopens an existing schedule and verifies the
stored values are shown instead of defaults.
Prompt for the scheduler timezone during interactive installs and support
a --time-zone flag for scripted installs.
Detect a sensible default from POBSYNC_TIME_ZONE, timedatectl, or
/etc/timezone before falling back to UTC, and validate the value with
Python zoneinfo before writing it to pobsync.env.
Document that schedules are evaluated in POBSYNC_TIME_ZONE so production
installs can avoid UTC/local-time ambiguity.
Separate operational latest-run display from trend-stat collection so
successful backups without parsed stats still appear in dashboard host rows.
Keep trend summaries limited to runs with stats, but use all successful
real runs for the host latest-run indicator.
Render next scheduled run times with an explicit timezone label to avoid
ambiguity between UTC and local scheduler time.
Add a scheduler helper that calculates the next due time for a cron-style
schedule expression and surface that value on the dashboard and host detail
pages.
Show the latest run type in host summaries and backup trend tables so
manual and scheduled backups are distinguishable in the Django UI.
Keep the calculation derived from existing ScheduleConfig data without
adding a migration.
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.
Extend derived backup statistics with average daily new data and an
estimated days-until-full forecast based on recent successful real runs.
Show the new forecast metrics on the dashboard and add compact per-run
trend bars on the host detail page so new data and matched link-dest data
are easier to compare at a glance.
Keep the implementation migration-free by deriving everything from the
existing BackupRun result stats payload.
Add a stats summary layer that aggregates recent successful real backup runs
into dashboard and host-level trend metrics.
Show backup-root usage, available space, average new data, average duration,
estimated runs until full, and link-dest savings on the dashboard. Add a host
trend table with recent run duration, file count, new data, matched data, and
snapshot links.
Keep the implementation based on existing run and snapshot stats JSON so the
UI gains useful trend visibility without introducing a schema migration yet.
Parse rsync --stats output into structured run metrics for file counts,
transferred bytes, literal data, matched data, speedup, and estimated
link-dest savings.
Store collected stats on backup run results and successful snapshot metadata,
including snapshot data usage and backup-root capacity details for future
dashboard graphs and disk-full projections.
Render the collected metrics on run and snapshot detail pages, with tests
covering parsing, metadata persistence, and UI output.
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.
Classify rsync failures in run results so transport issues such as exit
255 and broken pipes show clearer diagnostic hints.
Teach the worker to reconcile running dry-runs when their log already
contains a terminal rsync error, and to fail stale dry-runs after their
timeout window. This prevents failed rsync processes from leaving runs
stuck in the running state indefinitely.
Add default dry-run rsync output flags so long-running dry-runs expose
file-list, progress, stats, and itemized change information in their
run-specific log files.
Avoid duplicating user-supplied itemize or --info arguments so operators
can still tune rsync output from global or host configuration.
Add a cancel action for queued and running backup runs. Queued runs are
cancelled immediately, while running runs are marked for cancellation and
the worker terminates the active rsync process group.
Make dry-run log paths run-specific and add a defensive default dry-run
timeout so stuck dry-runs do not remain running indefinitely.
Remove rsync exit codes from run overview tables while keeping detailed
diagnostics available on the run detail payload.
Introduce reusable configuration checks for global settings and effective
host runtime configuration. The checks now surface risky backup settings
such as missing recursive rsync args, missing critical root excludes,
invalid SSH settings, missing credentials, and retention gaps.
Show these checks on the global config form, host edit form, and host
detail page so operators can validate the compounded host/global config
before starting real backup runs.
Clear the reused dry-run rsync log before each dry-run so run details
only show output from the current execution.
Populate new Django global configs with the existing safe rsync and
exclude defaults, including archive mode and standard pseudo-filesystem
exclusions.
Add a host check that fails when effective rsync args do not include
archive or recursive transfer, preventing real backups that only report
"skipping directory .".
When a selected SSH credential has no pinned known_hosts entries, create
and use a pobsync service-level known_hosts file under POBSYNC_HOME/state.
Pass UserKnownHostsFile and StrictHostKeyChecking=accept-new to SSH so
unattended backups no longer depend on root's known_hosts or an
interactive shell session.
Keep pinned credential known_hosts behavior unchanged when entries are
configured explicitly.
Add a host detail action that scans the target SSH host key with
ssh-keyscan and stores it on the selected SSH credential.
Merge scanned known_hosts entries without duplicates and let the
existing runtime config pass them through as UserKnownHostsFile for
unattended rsync over SSH.
Extend host checks to warn when the selected credential has no known_hosts
entries, making host key verification failures actionable from Django.
Include the selected SSH credential metadata and rsync log tail in
dry-run and failed backup results so Django shows the actual SSH or
rsync failure instead of only the exit code.
Warn in host checks when a host still uses database-stored private key
material, making it easier to spot old credentials after switching to
generated filesystem keys.
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.
Update the native installer so the pobsync service user gets journal
read access when the host exposes systemd-journal or adm groups.
Apply ownership and private directory modes to the configured backup
root, and reuse the existing environment backup root on reinstall so
production updates do not fall back to /backups.
Add a self-check for journal access and a host detail action that can
prepare missing backup directories for existing host configurations.
Add host-level checks for address, enabled state, SSH credential
selection, and backup directory readiness, and show them on the host
detail page.
Create host backup directories during host creation and prefill new
hosts from the default global config.
Add a staff-only logs view backed by journalctl with filtering by
pobsync unit, priority, and message text.
Improve runtime checks for gunicorn in virtualenv installs and ensure
the native installer grants the service user access to the backup root.
Change the native installer to enable services and then explicitly
restart web, worker, and scheduler so updated Django code is loaded
after each install or update.
Document that the installer refreshes app files and restarts systemd
services as part of the normal update flow.
Add step-based installer logging that reports pobsync actions as OK,
FAILED, or SKIPPED while suppressing noisy apt, pip, Django, and
systemd output during successful runs.
Show the captured output for the failed step when something breaks,
and add a --verbose flag to restore full command output for debugging.
Document the quieter installer behavior and verbose mode in the README
and development notes.
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.
Add an interactive setup flow to the systemd installer with defaults
for install paths, service identity, backup storage, bind address,
allowed hosts, CSRF origins, OS package installation, MariaDB support,
nginx setup, and first superuser creation.
Keep scripted installs supported through non-interactive mode, existing
overrides, environment variables, and explicit superuser flags.
Print a user-facing completion summary with the control panel URL,
Self Check reminder, first setup steps, and useful service log commands.
Update the README to describe pobsync as a Django-first, SQL-backed
service with the control panel as the primary operational interface.
Move CLI examples out of the normal workflow and document them as
maintainer tooling for debugging, services, and migration tasks.
Teach the systemd installer to install required Debian/Ubuntu
packages by default, with an opt-out for users who manage system
dependencies themselves.
Add explicit MariaDB install-extra handling so native installs can
pull in both the Python extra and required client build packages.
Slim down the README to production setup and operations, and move
development, Docker, migration helper, and architecture notes into
docs/development.md.
Expand the systemd installer so it can perform a complete native
installation with sensible defaults: copy the checkout into the target
app directory, create runtime directories, write the environment file,
install dependencies, configure systemd units, and optionally configure
nginx.
Add a staff-only Django self-check page that verifies runtime settings,
required binaries, writable paths, database connectivity, global config
state, and systemd service status when available.
Document installer overrides and expose the self-check from the main
navigation.
Make native systemd services the recommended production path for
pobsync while keeping Docker Compose available for development and
optional test installs.
Add web, worker, and scheduler systemd unit templates, a native
environment example, an optional nginx reverse proxy template, and an
installer that creates the venv, service user, env file, units, and
runs migrations/static collection.
Allow native deployments to configure POBSYNC_BACKUP_ROOT directly and
document the new production layout and update flow.
Run the Django control panel with Gunicorn instead of the development
runserver and serve static files through WhiteNoise.
Add restart policies, healthchecks, .env-driven production settings, and
a sample .env file for single-server deployments. Update the Docker
entrypoint to collect static assets and document the remote server
deployment and update flow in the README.
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.
Add latest snapshot context to each dashboard host row so imported legacy
snapshots are visible without opening every host page.
Link the latest snapshot directly to its detail page and show its kind
and status beside the host snapshot count.
Cover the dashboard latest-snapshot selection with a view test.