FIX: dockerCommandRun rooted path is 'sudo $command' (unquoted word-split),
so 'docker ps --format "{{.Names}}"' was passing the format with LITERAL
quotes -> docker emitted '<name>' and the downstream grep never matched
(broken in rooted too). Switch all docker invocations to runFileOp, which
preserves args via "$@" in both modes (and runs as dockerinstall against
the rootless socket). Fixed monitoring.sh, dashy, tags_processor_network_mode.
Convert: jitsimeet (rm/wget/unzip/mv/sed/tee/gen-passwords on /docker ->
runFileOp/runFileWrite), authelia (config sed/mkdir/chmod/chown/secrets tee
-> runFileOp/runFileWrite; docker exec -> runFileOp docker, preserving
--password), reset_git (cp->/root runSystem, install-dir chown runInstallOp;
kept sudo -u manager). check_update/update_git_check need no change (all
sudo -u manager git, already least-privilege).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Add -a/--append to runFileWrite so the pervasive /docker/logs log-append
idiom (`… | sudo tee -a $logs_dir/$docker_log_file`) routes through the
mode-aware helper instead of raw sudo.
Convert scripts/config/docker/docker_config_to_container.sh fully: all
ops target /docker app config + logs (data-plane), so md5sum/grep/chmod/
cmp/editor -> runFileOp and the log-appends -> runFileWrite -a.
Byte-identical in rooted.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
In rootless the container was set to user: <host-uid> (e.g. 1002:1002) with
group_add: <host-sub-gid> (e.g. 166528). Inside the daemon's user namespace
those are out of range — group_add made runc call setgroups() with an unmapped
GID ('setgroups: invalid argument'), so the WebUI container never started.
In rootless the container now runs as 0:0 (userns-root == the install user, which
owns the bind-mounts and the rootless socket) with socket gid 0. Rooted is
unchanged. Verified: libreportal-service comes up and talks to the rootless
socket.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Application backups were driven by one crontab entry per app, each offset by
id * CFG_BACKUP_CRONTAB_APP_INTERVAL minutes. That minute offset is written
straight into cron's 0-59 minute field, so past ~20 apps it overflowed into
an invalid entry that silently never fired, and the fixed spacing could not
serialize backups that ran longer than the gap.
Replace it with a single daily entry (`libreportal backup scheduled`) that
enqueues a backup task per enabled app. The existing systemd task processor
drains them serially — no minute overflow, real serialization, and backups
are now visible/cancellable in the Tasks UI. Per-app enable is read from
CFG_<APP>_BACKUP at schedule time instead of being mirrored into crontab.
Removes the stagger machinery (timing/setup/check/remove scripts), the
now-unused cron_jobs table + insert, and the CFG_BACKUP_CRONTAB_APP_INTERVAL
config knob and its WebUI field.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Propagate the ✓ Success / ✗ Error / ! Notice / ❯ Question glyphs (from markers.sh) through the rest of the pipeline: swap the inlined helpers in init.sh and generate_arrays.sh, and replace raw echo -e "${RED}ERROR:${NC}" calls with the isX helpers in config_check_missing.sh, check_success.sh, initilize_files.sh, and reset_git.sh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Replaces the slow, interactive per-variable scan with a deterministic
reconcile: each live config is rebuilt from its (freshly-cloned) template —
keeping the user's existing values, adding new template keys
(CFG_REQUIREMENT_CONFIGS_AUTO_UPDATE), and dropping keys the template no
longer defines (new CFG_REQUIREMENT_CONFIGS_AUTO_DELETE, default true).
Structure/order/comments follow the template; non-interactive; atomic with a
.bak; refuses to act on a missing/empty template so a broken clone can't wipe
a config. Applies to both general and per-app configs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
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>
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>