Matrix was uninstalled and the Updates tab kept listing it as up to
date. Not an instance problem — updates.json and cves.json are
scan-time snapshots on a 30-minute cadence, and nothing rewrote them at
uninstall, so any removed app haunted every updater surface until the
next scan happened to run. The backend was never wrong: the DB, the
apps data and the app's own page all said uninstalled within seconds.
Fixed at both ends. Uninstall now deletes the app's rows from both
generated files, surgically — a full rescan re-runs CVE checks against
every image and has no place inside an uninstall. And the updater's
merge drops any row whose app window.apps does not list as installed,
which covers every other way the snapshot can go stale (a crashed
uninstall, a hand-edited file, the next bug). The filter only applies
when the installed list has actually loaded, preserving the page's
degrade-gracefully contract when it has not.
The stale Matrix rows on this install were purged the same surgical
way; the tab now shows 14 rows with the merge still intact.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
'local result=$(cmd)' resets $? to 0 (the local builtin's own exit), so the
following checkSuccess always saw success regardless of cmd's real exit — the
mechanism that masked the de-sudo write failures. Split declaration from
assignment ('local result; result=$(cmd)') across all 235 active-code sites
(84 files) so the command's exit reaches checkSuccess. No behaviour change
beyond $? now being accurate (no set -e in runtime code; multi-line
assignments transform safely).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Two small uninstall-output tweaks.
1. dockerComposeDownRemove now ALWAYS calls dockerRemoveApp (the
`docker ps -aqf name=…` → stop + rm sweep) as a fallback, even when
the compose-down step is skipped because the app dir is missing.
Before, a partial prior uninstall (compose file gone but containers
still running) produced "App directory not found. Skipping container
shutdown." and then proceeded as if the uninstall were complete —
leaving the actual containers running. The name-based sweep also
runs after a successful compose-down to catch anything compose
wouldn't pick up (renamed services, orphans from earlier failures).
While here: the OS_TYPE gate (only Ubuntu/Debian) is gone too —
`docker compose down` works on any OS with docker, and gating it
meant Arch/etc. users got NO compose teardown at all.
2. The step-2 header "Keeping Docker images (pass --delete-images to
remove)" trimmed to just "Keeping Docker images". The `isNotice`
line below already explains the reuse-on-reinstall behaviour; the
CLI-flag hint reads as noise in the WebUI task log where users
can't act on it anyway. CLI users can still pass --delete-images
(cli_app_commands.sh wires it as before) or tick the WebUI's
"Also delete docker image" checkbox.
Signed-off-by: librelad <librelad@digitalangels.vip>
dockerDeleteData (uninstall) and the wipe-before-restore step in
restoreAppStart both did `runFileOp rm -rf $containers_dir$app_name`,
which runs as $CFG_DOCKER_INSTALL_USER (dockerinstall, uid 1002 on
rootless). That user owns app-template files but CANNOT remove
container sub-UID dirs created by the daemon's userns mapping —
postgres data at uid 232070, nextcloud html at uid 33, etc. The rm
therefore silently failed with
rm: cannot remove '/libreportal-containers/invidious/postgresdata':
Permission denied
while still reporting "<app> successfully uninstalled" — leaving the
sub-UID directory tree on disk to confuse the next install and leak
storage.
Fix: route the wipe through a new `app-data-remove` action in the
root-owned libreportal-ownership helper. Root can rm sub-UID files
unconditionally. The helper validates the app name (alphanumeric +
. _ -, no traversal), refuses the WebUI's own slot (libreportal), and
is idempotent when the dir is already gone.
Two callers updated:
- scripts/docker/app/uninstall/delete_data.sh
- scripts/restore/restore_app_start.sh
The helper itself ships root-owned at /usr/local/lib/libreportal/, so a
fresh install or release upgrade is needed to pick up the new action.
Bumped init.sh footprint_version 2 → 3 so the runtime updater
prompts a root re-install on the next release.
Signed-off-by: librelad <librelad@digitalangels.vip>
If it's gluetun code, it lives with gluetun. Both functions in
scripts/config/tags/processors/tags_processor_network_mode.sh manipulate gluetun
markers / gluetun's compose, so move them into containers/gluetun/scripts/
gluetun_network.sh and rename to the per-app-hook convention:
tagsProcessorNetworkMode -> appNetworkApplyMode_gluetun
tagsProcessorGluetunForwardedPorts -> appNetworkRegisterPorts_gluetun
Central call sites are now provider-agnostic — no "gluetun" literal anywhere:
- docker_config_setup_data.sh: an app routing via CFG_<APP>_NETWORK=<provider>
triggers `appNetworkApplyMode_<provider>` + `appNetworkRegisterPorts_<provider>`
via declare -F, so any future gateway provider plugs in with no engine edits.
- uninstall_app.sh: loops every `appNetworkRegisterPorts_*` hook (each self-skips
when its provider isn't installed), so removing a routed app refreshes the
right provider with no provider name in central code.
Delete tags_processor_network_mode.sh; regenerate arrays. Verified with stubs:
default mode no-ops, gluetun-routed app fires both hooks, gluetun itself is
skipped, unknown provider is silently no-op, uninstall loop calls registerPorts.
Drive-by cleanup: 9 stale "${X_scripts[@]}" array references in app_files.sh /
cli_files.sh (gluetun + headscale from this session's moves, plus 7 pre-existing:
command/ssl/swapfile/ufw/ufwd/user — all from older refactors that left them
behind). Each expanded to nothing at runtime (harmless), but they're dead
misleading refs. Cleaned both files; every remaining array ref now points to a
real files_*.sh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Container-plane docker now routes through the mode-aware helpers instead of
sudo: simple calls (exec/ps/run/build/images/inspect/port/logs across ~15
app/check scripts) -> runFileOp docker (rootless socket as the install user;
rooted via the docker group). The cd && docker compose paths drop the sudo on
the rooted branch (the rootless branch already used dockerCommandRunInstallUser
-- byte-identical now, manager-ready later); gluetun, which had no rootless
branch, now uses dockerCommandRun so force-recreate works in both modes.
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>