apiscp-borg is a sibling of apiscp-kopia built on BorgBackup. This first milestone lands the project scaffold and the Layer 1 engine: - borg-apiscp-backup: per-site named archives (siteN-shadow/info/db), fresh per-site DB dumps via ApisCP site-context export, system + custom archives, borg prune retention per prefix, Prometheus metrics, email notifications. - apiscp-borg-common.sh: logging, mail, site/owner helpers. - config example, systemd service+timer, install.sh, README, DESIGN. Borg preserves POSIX ACLs and xattrs natively, so (unlike the kopia engine) no metadata sidecar is required; DESIGN.md records how Borg reshapes the design. Repository/restore tools, Layer 2 panel integration, hooks, uninstall, and the full reference are the next milestones.
5.6 KiB
Design
apiscp-borg is a sibling of apiscp-kopia: the same ApisCP integration architecture, but built on BorgBackup instead of kopia. This document records how Borg changes the design.
What is the same as apiscp-kopia
- Two upgrade-safe layers. Layer 1 is a standalone POSIX-sh engine plus
tools, driven by a systemd timer, with zero coupling to ApisCP's PHP. Layer 2
is an ApisCP module, GUI apps, and account hooks under
/usr/local/apnscp/config/custom(git-ignored, so they surviveupcp). - Runs
backup_dbs.phpfirst so per-site logical database dumps are current before they are archived. A plain cron job that runs before ApisCP's own10backup_dbscaptures day-stale dumps; running it in the engine fixes that. - Per-site granularity. Each site is archived as its own named set so a single site can be restored and retention can be reasoned about per site.
- Per-site database handling through ApisCP's site-context API. Host
mysqldumpcannot see per-site databases, so the engine dumps each site's databases withcpcmd -d <domain> mysql:export(and pgsql), which resolves the target path INSIDE the site filesystem namespace, then archives the dump from its real host location under the site'sfsttree and removes it. Import is the guarded nativemysql:import/pgsql:import. - Security boundary. The panel runs the module as the unprivileged
apnscpuser; a scoped/etc/sudoers.d/apiscp-borglets it run only the plugin binaries as root, and the binaries validate their own input. Site-owner verbs derive the site from the ApisCP auth context and never trust a site parameter. - Editions. An appliance-admin edition and a site-owner ("My Backups") edition scoped to a single account.
- Retention, schedule, maintenance/verify, email notifications, one-time key backup, and lifecycle hooks, mirroring apiscp-kopia.
What Borg changes (and why the rationale differs)
-
ACLs and extended attributes are preserved natively. This is the single biggest difference from kopia.
borg createstores POSIX ACLs and xattrs by default on Linux, andborg extractrestores them (use--numeric-idsfor servers so ownership maps by id, matching ApisCP's account model). apiscp-kopia exists largely because kopia drops that metadata and needs ACL/xattr sidecars; Borg needs no sidecars. The plugin's value here is instead: correct ordering (fresh DB dumps), per-site archives, retention (borg prune), encryption and passphrase safety, and native ApisCP GUI/CLI integration. (An optional sidecar capture remains available as belt-and-braces but is off by default because Borg already preserves the metadata.) -
Archives, not tags. Borg has named archives in a repository rather than kopia's tagged snapshots. The engine names archives with a structured prefix so per-site operations are glob-addressable:
siteN-shadow-{now},siteN-info-{now},siteN-db-{now},_system-{now},_custom-<slug>-{now}(timestamps in Borg's{now}placeholder). Restore picks an archive by name;borg listenumerates them; per-site retention isborg prune --glob-archives 'siteN-*' --keep-*. -
Retention is
borg prune. Retention runs as an explicitborg pruneafter each backup (and can be run on demand), with keep-within/keep-daily/ weekly/monthly/annual rules from the config, applied per site prefix so one site's churn cannot expire another's history. -
Maintenance is
borg compact; integrity isborg check(repository and archive consistency, optionally--verify-datato read everything back). -
Transport and backends. Borg speaks its own protocol over SSH. Backends are a local path (
/mnt/...) or a remotessh://user@host/path(with a dedicated SSH key and, ideally, anappend-onlyborg serverestriction on the server). There is no kopia-style repository server or sftp backend; ssh is the remote story. -
Encryption and the key. Repositories are created with
--encryption=repokey-blake2by default, so the encryption key lives inside the repository and only the passphrase is needed to use it. That makes recovery simpler than keyfile mode (which stores the key under~/.config/borgand must be backed up separately). The one-time key backup exports the passphrase plusborg key exportoutput and stores it off the machine; losing it makes the encrypted repository unrecoverable, exactly as with kopia. -
Single-writer locking. A Borg repository is locked to one operation at a time. The engine holds a local lock and relies on Borg's own repository lock; concurrent site-owner actions queue rather than run in parallel.
Layer 1 (this scaffold)
bin/borg-apiscp-backup: the engine (this milestone).bin/borg-apiscp-repo: repository init/connect, key backup, retention/prune, maintenance (compact), check, schedule, notifications. (Next.)bin/borg-apiscp-restore: per-site, subpath, whole-account, and system restore viaborg extract, plus site-owner-scoped variants. (Next.)lib/apiscp-borg-common.sh: shared helpers (logging, mail, site helpers, optional metadata sidecar).
Layer 2 (next)
The ApisCP module (Borg_Module_Surrogate), the admin GUI app, the site-owner
GUI app ("My Backups"), the menu templates, and the account hooks, mirroring
apiscp-kopia verb-for-verb where it makes sense.
Status
Scaffold and Layer 1 engine. The repository tools, restore tool, Layer 2 panel integration, hooks, uninstall, and the full reference are being built out next, tracking the apiscp-kopia feature set.