Initial scaffold: Borg engine + docs (Layer 1 milestone)
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.
This commit is contained in:
commit
2f0d93a0ae
10 changed files with 743 additions and 0 deletions
101
docs/DESIGN.md
Normal file
101
docs/DESIGN.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# Design
|
||||
|
||||
apiscp-borg is a sibling of apiscp-kopia: the same ApisCP integration
|
||||
architecture, but built on [BorgBackup](https://www.borgbackup.org/) 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 survive `upcp`).
|
||||
- **Runs `backup_dbs.php` first** so per-site logical database dumps are current
|
||||
before they are archived. A plain cron job that runs before ApisCP's own
|
||||
`10backup_dbs` captures 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
|
||||
`mysqldump` cannot see per-site databases, so the engine dumps each site's
|
||||
databases with `cpcmd -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's `fst` tree and removes it. Import
|
||||
is the guarded native `mysql:import` / `pgsql:import`.
|
||||
- **Security boundary.** The panel runs the module as the unprivileged `apnscp`
|
||||
user; a scoped `/etc/sudoers.d/apiscp-borg` lets 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)
|
||||
|
||||
1. **ACLs and extended attributes are preserved natively.** This is the single
|
||||
biggest difference from kopia. `borg create` stores POSIX ACLs and xattrs by
|
||||
default on Linux, and `borg extract` restores them (use `--numeric-ids` for
|
||||
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.)
|
||||
|
||||
2. **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 list` enumerates them; per-site retention is
|
||||
`borg prune --glob-archives 'siteN-*' --keep-*`.
|
||||
|
||||
3. **Retention is `borg prune`.** Retention runs as an explicit `borg prune`
|
||||
after 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.
|
||||
|
||||
4. **Maintenance is `borg compact`;** integrity is `borg check` (repository and
|
||||
archive consistency, optionally `--verify-data` to read everything back).
|
||||
|
||||
5. **Transport and backends.** Borg speaks its own protocol over SSH. Backends
|
||||
are a local path (`/mnt/...`) or a remote `ssh://user@host/path` (with a
|
||||
dedicated SSH key and, ideally, an `append-only` `borg serve` restriction on
|
||||
the server). There is no kopia-style repository server or sftp backend; ssh
|
||||
is the remote story.
|
||||
|
||||
6. **Encryption and the key.** Repositories are created with
|
||||
`--encryption=repokey-blake2` by 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/borg` and must be backed up separately). The one-time key backup
|
||||
exports the passphrase plus `borg key export` output and stores it off the
|
||||
machine; losing it makes the encrypted repository unrecoverable, exactly as
|
||||
with kopia.
|
||||
|
||||
7. **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 via `borg 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue