LibrePortal/docs/architecture/system-footprint.md
librelad 30612a0d87 docs: organize docs/ into purpose folders with consistent naming
Sort docs/ into guide/ contributing/ architecture/ roadmap/ and rename
to consistent kebab-case (USER->guide/install-and-use, FOOTPRINT->
architecture/system-footprint, frontend-modularization->architecture/
webui-architecture, etc.). Add a docs/README.md index and a docs/
CONTRIBUTING.md pointer so the forge still surfaces the contributing
guide. Fix every reference (README, init.sh comments, frontend code
comments, and the USER<->DEVELOPMENT cross-links). History preserved
via git mv. Root stays README.md + CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-31 00:48:38 +01:00

4.6 KiB

LibrePortal system footprint (outside the data roots)

LibrePortal's own data lives in three independently-relocatable roots, chosen at install (defaults shown) and owned by exactly one principal each:

Root (default) Owner Holds
/libreportal-system the manager user configs, logs, the install tree, the database, ssl/ssh
/libreportal-containers the container user live app data (one dir per app)
/libreportal-backups the container user backup repositories

This file catalogues the few things LibrePortal must place outside those roots to integrate with the host. The OS dictates where most of these live — sudoers, systemd units, sysctl, and $PATH entries can only be read from their fixed locations, so they can't sit inside a relocatable root. What we control, we keep together in /usr/local/lib/libreportal/; everything else is named libreportal* so the whole footprint is greppable and removable.

The three roots and the manager username are chosen at install and baked into the helpers / systemd unit / CLI wrapper below (sed placeholders, like the manager name has always been) — never read at runtime from a manager-writable config. That immutability is the privilege boundary: a root helper can't be redirected at, say, /etc by editing config. This footprint itself stays at fixed paths by design; only the data roots relocate.

Executables — /usr/local/lib/libreportal/ (root:root, our own dir)

File Owner Purpose
libreportal root the CLI wrapper (symlinked onto $PATH, see below)
uninstall.sh root the uninstaller (symlinked onto $PATH as libreportal-uninstall)
libreportal-ownership root reconcile the three-root ownership model
libreportal-dns root edit /etc/resolv.conf (nameservers)
libreportal-ssh-access root manage admin authorized_keys + sshd PasswordAuthentication
libreportal-socket root docker-socket read perms (type switcher)
libreportal-svc root generate/install the task-processor systemd unit
libreportal-bininstall root install the restic/kopia backup-engine binaries
libreportal-appcfg root rewrite AdGuard/CrowdSec/ownCloud config files

These are the scoped-sudoers trust boundary: root-owned in a root-owned dir, so the manager can sudo them but can't modify them. Each bakes the three roots + the manager name at install. Source of truth: scripts/system/ in the repo; installed by init.shinitRootHelpers (re-installed only on a reinstall, not by the quick-deploy).

OS-mandated locations (must live where the OS reads them)

Path Owner Purpose
/usr/local/bin/libreportal root symlink/usr/local/lib/libreportal/libreportal (puts the CLI on $PATH)
/usr/local/bin/libreportal-uninstall root symlink/usr/local/lib/libreportal/uninstall.sh (location-agnostic uninstall command)
/etc/sudoers.d/<manager> root scoped least-privilege grant for the manager (drop-in named after the manager user)
/etc/systemd/system/libreportal.service root the task-processor service (User=<manager>; bakes the roots as Environment=LP_*_DIR; also drives the periodic regen poll)
/etc/sysctl.d/99-libreportal-hardening.conf root kernel LPE-surface hardening
/etc/sysctl.d/99-libreportal-rootless.conf root rootless sysctl settings + "rootless configured" marker

Third-party tools we install (not ours, conventional home)

/usr/local/bin/{restic,kopia,ufw-docker,docker-compose} — installed on demand (restic/kopia via the libreportal-bininstall helper). /usr/local/bin is the correct home for these; left under their own names.

System users

The manager (default libreportal, configurable via --manager-user= / LP_MANAGER_USER) and the container user (default dockerinstall, from CFG_DOCKER_INSTALL_USER) — each with a home under /home/. The rootless daemon config lives at ~<container-user>/.config/docker/daemon.json.

Uninstall sketch

init.sh uninstall does all of this; the sketch (with default roots/manager):

sudo systemctl disable --now libreportal.service
sudo rm -f /etc/systemd/system/libreportal.service /etc/sudoers.d/libreportal
sudo rm -f /etc/sysctl.d/99-libreportal-*.conf
sudo rm -rf /usr/local/lib/libreportal /usr/local/bin/libreportal /usr/local/bin/libreportal-uninstall
sudo rm -rf /libreportal-system /libreportal-containers /libreportal-backups
# optional: the backup-engine binaries and the two users