Introduce scripts/source/paths.sh as the canonical path resolver for three
independently-relocatable roots:
LP_SYSTEM_DIR manager-owned control plane (configs/logs/install/db/ssl/ssh/migrate)
LP_CONTAINERS_DIR container-user-owned live app data
LP_BACKUPS_DIR container-user-owned backup repos (own mount-able)
Roots come from the environment when set (install bakes them; CLI/app inherit
from init.sh), else default to /libreportal-*. A transitional compat default
keeps EXISTING installs (legacy single /docker tree, by config marker) on /docker
until a deliberate reinstall, so deploying this never strands a running box.
- init.sh derives the same vars inline (self-contained for the bare /root/init.sh
reinstall case); paths.sh mirrors it for the standalone task/check processors,
which now self-locate their scripts dir and source it.
- Replace functional /docker literals with the derived vars across runtime,
install, backup, crontab, crowdsec/restic, headscale, and reinstall paths;
clean the inert '== /docker/containers/*' guard fallbacks to the variable form.
- backend: CONTAINERS_DIR now from LP_CONTAINERS_DIR (compose env, filled at
generation via a new CONTAINERS_DIR_TAG), legacy-safe default for un-recreated
containers.
- backup default path falls back to the backups root; exclude paths.sh from the
sourced-file arrays (bootstrap file, sourced explicitly).
The CLI-wrapper heredoc + root helpers still reference /docker; those get baked
in phase 3. No layout/ownership change yet (phase 2).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Two pre-existing bugs a genuinely-clean rootless install exposes:
copyFolder picked the copy user by destination only: a manager-owned
source (e.g. the install dir) copied into the dockerinstall-owned
containers/ ran the cp AS dockerinstall, which can't read the source ->
"cp: Permission denied". The `local result=$(...)` then masked the
failure (local returns 0) so checkSuccess printed success. This broke
installLibrePortalImageWebUI: the WebUI dir wasn't populated, so
initializeAppVariables couldn't read libreportal.config ("No app name
provided"), compose tags were never substituted, and the WebUI container
couldn't start (user: "USER_DATA"). Fix: when source and destination
owners differ (manager -> container), bridge with a tar pipe — the
manager reads, dockerinstall writes — with pipefail so a read-side
failure is no longer masked.
start.sh created the per-run install log with `sudo touch` (root:root
644) but tee's to it as the manager -> "tee: Permission denied" -> every
install-*.log was empty. Fix: chown the log to the user running the
install so the tee can append.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The old copy/move helpers ran 'sudo cp/mv X Y; sudo chown $user_name Y' (root +
arbitrary chown). Rework them to write AS the destination's owner — no root, no
chown — classifying by dest path like createTouch: /docker/containers/<app> ->
runFileOp (docker install user), manager-owned control plane -> runInstallOp.
The $user_name arg is now advisory (the path decides). Covers copyFile/copyFiles/
copyFolder/copyFolders/moveFile; copyResource is always containers -> runFileOp;
createFolders' non-container branch -> runInstallOp; updateFileOwnership (an
arbitrary user1:user2 chown) -> runSystem. Confirmed by callers (containers vs
$docker_dir/backup_install_dir/configs dests). Removes a class of root data ops
+ arbitrary-chown from the runtime.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The backup + ssh generators created their frontend/data dirs via plain/sudo
mkdir and wrote files via sudo tee/mv (root-owned), then called createTouch
(dockerinstall) which can't re-own a root file — so every write hit
'touch: Permission denied' in rootless and left root-owned data the
dockerinstall container/generators can't rewrite. Convert dir creation to
runFileOp mkdir and file writes to runFileWrite (both run as the container
owner: dockerinstall in rootless, manager in rooted), dropping the
temp/mv/createTouch dance. Also make the createFolders chokepoint mode-aware
(containers/ paths created via runFileOp) so it mirrors createTouch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys,
Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun
VPN routing, and a web dashboard to manage it all.
Free & open forever to self-host; optional paid hosted services fund it.
See PROMISE.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>