8 Commits

Author SHA1 Message Date
librelad
d424473b2e feat(backup): auto-discover container-side capture uid:gid (drop the literal)
The hardcoded uid:gid in libreportal.backup.files labels was brittle: matched the
default PUID in the compose, but a PUID change (or new image version) would drift
silently and the next restore would chown to a stale owner. Make it impossible to
drift by letting the engine learn the uid at capture time.

backup_files.sh:
- After a successful tar capture, run `stat -c '%u:%g'` inside the container and
  write the result to a <host_subdir>.lp-owner sidecar in staging. The sidecar
  rides in the snapshot alongside the captured tree.
- Restore reads it back when the descriptor doesn't pin uid:gid; falls back to
  0:0 with a clear notice if missing.
- The 5-field form (with explicit uid:gid) is still supported as an override; it
  wins and skips the sidecar write entirely.

Update all 4 current labels to the new 3-field form
"<container>:<container_path>:<host_subdir>" (nextcloud, bookstack, gitea,
owncloud). Engine handles both formats during the transition.

Verified with stubs: 3-field capture writes the sidecar with the discovered
33:33; restore reads it back; 5-field override correctly skips the sidecar
write. backup_files.sh parses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-26 15:04:38 +01:00
librelad
af23488df1 tidy: docs + Nextcloud APCu + container-side file-capture rollout
Three closeouts in one pass:

1. DEVELOPMENT.md — consolidated hook-conventions table covering all 8 per-app
   hook types (tools / update-specifics / compose-tags / webui-refresh / the two
   traefik markers / the two network-provider hooks). One place to look instead
   of inferring from the codebase.

2. Nextcloud APCu wired alongside Redis: appUpdateSpecifics_nextcloud now sets
   memcache.local=\OC\Memcache\APCu too (was deferred from the fpm switch). APCu
   = cheap in-process cache; the fpm-alpine image ships the extension. CLI mode
   may emit a harmless "no memory cache" notice on `occ` runs — Nextcloud is
   graceful, the FPM worker still uses APCu fine.

3. Container-side file-capture rollout to 3 confident cases:
   - bookstack: lscr.io/linuxserver/bookstack with PUID=1000 → /config (1000:1000)
   - gitea:     gitea/gitea with USER_UID=1000     → /data    (1000:1000)
   - owncloud:  owncloud/server (Apache/PHP)        → /mnt/data (33:33, www-data)
   Snapshots are now complete for these (the dir's excluded from the raw restic
   pass and captured live through the container as a tar → libreportal-owned
   staging, same proven pattern as Nextcloud). Less-evidenced candidates left
   for live verification: linkding, mastodon, jellyfin, trilium, focalboard,
   invidious, vaultwarden, headscale-service — each needs its in-container uid
   confirmed before labeling (wrong uid won't break backup, but restore would
   chown to the wrong owner).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-26 14:43:28 +01:00
librelad
898068a390 refactor(apps): make app tools + helpers fully self-contained per app
Each app now carries everything under containers/<app>/: Tools-tab actions in
tools/ (declaration <app>.tools.json + function <app>_<tool_id>.sh) and logic
helpers in scripts/ (e.g. <app>_auth.sh). The container scan live-sources every
.sh under the app (maxdepth 3, prunes only resources/) and webui_tools.sh
auto-merges the .tools.json, so an app is a true drop-in — no central edit, no
array regen.

- Empty the central webui_tools.sh heredoc; all 34 tools across 11 apps now
  come from per-app declarations (verified byte-identical to the old output).
- Retire the orphaned mattermost tool scripts to scripts/unused (there is no
  containers/mattermost; its install fn already lived in unused).
- Update the dispatch comment/error path, the auth-adapter doc, and
  DEVELOPMENT.md to the new convention.
- Regenerate static arrays (files_app.sh no longer lists app/containers/*).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:45:33 +01:00
librelad
27ad517626 feat(backup): per-app strategy override (advanced, context-aware)
Adds CFG_<APP>_BACKUP_STRATEGY (default auto) so an app's backup strategy can
be overridden from its Advanced config tab, taking precedence over the global
default. Added to the 10 live-capable apps, so the dropdown's 'live' option only
appears where it actually works.

- backupResolveStrategy now checks the per-app override before the global value.
- backupAppLiveCapable / backupAppStrategyOptions expose capability + the valid
  option set; predicate helpers hardened with explicit returns so they behave
  identically with or without shell errexit.
- BACKUP_STRATEGY field mapping (select, advanced) renders the dropdown.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 15:34:17 +01:00
librelad
69f7289b4a feat(backup): declare server databases + fail safe to stop on dump failure
- Add libreportal.backup.db labels to the MariaDB/Postgres apps (nextcloud,
  owncloud, bookstack, mastodon, invidious) so they back up live + consistent.
- If a declared dump cannot be taken (DB down, wrong path), the backup falls
  back to stop-snapshot-start for that run instead of snapshotting torn data —
  a misconfiguration degrades to 'safe with downtime', never to 'unsafe'.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-23 15:12:55 +01:00
librelad
2e4f4202e1 refactor(routing): retire HOST_NAME — derive primary host from per-port subdomains
The static per-app CFG_<APP>_HOST_NAME is gone. host_setup (the app's
canonical FQDN, feeding the legacy single DOMAINSUBNAME_DATA used by app env
vars, the app URL and trusted-domains) is now derived from the app's primary
Traefik port's subdomain: first recommended port, else first Traefik port;
@/root -> apex, set -> sub.domain, empty -> app-name. Removes HOST_NAME from
all app configs, the config-form field mapping (Hostname), the dead
headscale stub, and wireguard.sh (now uses host_setup). Completes the move to
dynamic per-port subdomain routing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 11:25:00 +01:00
librelad
dec3055b63 feat(routing): dynamic per-port subdomains + router-block toggle
Replace the static one-host-per-app model with per-port routers: each
Traefik-managed port carries a subdomain (12-col PORT format) and gets a
DOMAINSUBNAME_TAG_<n> host, so one container can serve unlimited hosts.
tagsProcessorPortSubdomains stamps per-port hosts (subdomain @/empty = apex,
multi-level allowed); tagsProcessorPortRouterBlocks comments out
# TRAEFIK_PORT_<n>_BEGIN/END blocks for non-Traefik ports so unfilled
placeholders never ship (mirrors GLUETUN_OFF). Convert all 27 router apps
(subdomains seeded from HOST_NAME; headscale admin. prefix -> subdomain).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 00:45:01 +01:00
librelad
875a60f90f LibrePortal v0.1.0 — initial release
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>
2026-05-21 20:37:54 +01:00