411 Commits

Author SHA1 Message Date
librelad
3adc960c00 chore: untrack stale site/ build artifacts swept in by mistake
The previous commit accidentally tracked site/dist/** and the generated
site/src/_data/*.json — leftover Eleventy build output from the legacy site/
location (the active site now lives in containers/weblibreportal). They are
generator artifacts, not source. Untrack them (kept on disk) and gitignore so
they can't be swept in again.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:14:58 +01:00
librelad
4fe1dea847 Merge claude/1 2026-05-25 22:13:54 +01:00
librelad
7bed2de2d2 feat(tools): auto-discover per-app <app>.tools.json (drop-in tool registration)
webui_tools.sh now merges any containers/<app>/<app>.tools.json into apps-tools.json
(jq, sets .apps[<app>]) on top of the central heredoc. So a dropped-in app — e.g.
from LibrePortal-Infra — registers its own Tools-tab actions WITHOUT editing this
file. Combined with the container scan already sourcing containers/<app>/*.sh live,
an app can now be fully self-contained (install fn + tool fns in <app>.sh + tool
declarations in <app>.tools.json) → true copy-on-top deploy, no array regen, no
central edits. Core apps in the heredoc are unaffected; invalid tools files are
skipped with a notice. Verified the merge (drop-in registers, core preserved).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:13:54 +01:00
librelad
1efb5cf772 Merge claude/2 2026-05-25 22:12:50 +01:00
librelad
16eda07b3d fix(webui): make SSH Access page full-width like config/admin pages
The SSH Access page was boxed to max-width 860px and centered, unlike the
Overview and System admin pages (.admin-page) which span the full content
width. Drop the cap and match .admin-page padding so /admin/tools/ssh-access
looks like the rest of the Admin area.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 22:12:50 +01:00
librelad
f2acb36a35 Merge claude/1 2026-05-25 21:15:38 +01:00
librelad
ef67ab9b71 refactor(infra): move hosting apps out to LibrePortal-Infra
getlibreportal (downloads host) + weblibreportal (website) — including the website
Eleventy source and the publish tool functions — now live in the separate
LibrePortal-Infra repo (Webstar/LibrePortal-Infra). They're the project's own
outward-facing hosting, not something users install, so the base stays clean.

Removed from base: containers/{getlibreportal,weblibreportal}, the
scripts/app/containers/<app>/<app>_publish.sh tool functions, and their entries in
webui_tools.sh; regenerated the sourced-file arrays; dropped the dead .gitignore
docroot lines. scripts/release/make_release.sh stays here (it builds the base
release). docs/DEVELOPMENT.md now points publishing at LibrePortal-Infra.

LibrePortal-Infra overlays onto an install and picks up releases/catalogue from the
base tree — see its README.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 21:15:38 +01:00
librelad
f0f8416c93 Merge claude/1 2026-05-25 21:02:53 +01:00
librelad
8800f524d4 feat(tools): WebUI/CLI publish tool for getlibreportal + weblibreportal
Surface the publish step through the existing Tools system (apps-tools.json -> Tools
tab + 'libreportal app tool <app> publish'), so the docroot can be (re)built from
the WebUI instead of a manual cd + script.

- webui_tools.sh: declare a 'publish' tool (no inputs) for getlibreportal + weblibreportal.
- scripts/app/containers/getlibreportal/getlibreportal_publish.sh (appGetlibreportalPublish):
  runs the host's publish.sh into the served data dir, as the container user (owns it).
- scripts/app/containers/weblibreportal/weblibreportal_publish.sh (appWeblibreportalPublish):
  builds Eleventy as the manager (owns the install tree), then syncs the result into
  the container-user-owned docroot — handling the build-vs-write owner split.
- Both guard for the build prerequisites (repo source / npm / dist) and fail with a
  clear message; regenerated the sourced-file arrays.

Honest status: scaffolding only — wiring verified (dispatch names match, files sourced,
JSON valid) but the end-to-end tool RUN is untested, and it's build-box-only (needs the
repo checkout + npm + a built dist/). These hosting apps are dev-only and headed for a
separate repo; this just sets the automation up so it's ready to iterate on.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 21:02:53 +01:00
librelad
0e3a3d76ed Merge claude/1 2026-05-25 20:44:38 +01:00
librelad
ef100acb3c refactor(hosting): website -> containers/weblibreportal; getlibreportal = downloads-only
Move the loose root-level site/ into a proper containers/weblibreportal app
(mirrors getlibreportal): the Eleventy source + nginx serving ./data via publish.sh
(npm run build -> docroot). Fix gen-data.mjs repoRoot (now ../../.. from
containers/weblibreportal/scripts) so it still finds containers/ for the catalogue.

Decouple the two hosts:
- weblibreportal  -> the website (libreportal.org)
- getlibreportal  -> downloads only (install.sh + signed release channels); its
  publish.sh no longer builds the site, and its config text updated to match.

Both are dev-only project hosting and will move to a separate repo later; for now
they live under containers/ as normal apps. ignores updated for their built
docroots; dropped the dead 'site export-ignore'.

Verified: gen-data builds the catalogue from the new location (33 apps), and
weblibreportal/publish.sh produces a docroot with index.html.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 20:44:38 +01:00
librelad
ca01fd503f Merge claude/1 2026-05-25 20:02:47 +01:00
librelad
63b53c7751 feat(hosting): getlibreportal as a first-class LibrePortal app (phase E)
Redo the download/website host as a normal app under containers/ (dogfooded — the
project hosts its own downloads on LibrePortal), instead of the bespoke repo-root
thing. Modeled on speedtest: standard getlibreportal.{config,sh,svg} +
docker-compose.yml (tagged template) so it plugs into the app scan + install
dispatch (installGetlibreportal) like every other app. nginx serves ./data (the
app data dir) — no special /web.

- getlibreportal.config: features category, public (login=false — it's a download
  host), no backup (regenerable), healthcheck on.
- docker-compose.yml: nginx:alpine, ./data:ro docroot + ./nginx.conf, traefik tags.
- nginx.conf: install.sh + latest.json no-cache; tarball/.sha256/.minisig immutable.
- publish.sh: assembles the docroot (built site + install.sh + dist/<channel>) into
  a target data dir; run on a full repo checkout (site/ + dist/ are host-side).
- exec bits set on the run-directly scripts (make_release.sh, install.sh, publish.sh).
- .gitattributes: dropped the stray 'getlibreportal export-ignore' (the no-slash
  pattern would also have excluded containers/getlibreportal — the app must ship);
  data/ gitignored.

Verified: app discovered by the site catalog (32 apps), installGetlibreportal matches
the dispatch name, and the full release->publish flow yields a docroot with the
website + install.sh + the signed/checksummed stable channel. The actual app-install
run + DNS/TLS for get.libreportal.org are operational steps (need a real host).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 20:02:47 +01:00
librelad
b9daae36e6 Merge claude/1 2026-05-25 19:40:30 +01:00
librelad
5700f78c6b feat(release): minisign signature signing + verification
The sha256 only proves a download is intact; a compromised host could swap the
tarball + its checksum. Add minisign signatures, which prove authenticity (the host
can't forge them without the offline secret key). Ships INACTIVE behind a REPLACE_ME
placeholder, so installs work until a real key is generated; then it's REQUIRED.

- make_release.sh: signs the tarball when LP_MINISIGN_SECKEY is set -> <tarball>.minisig.
- libreportal.pub: the public key (placeholder), ships in the tarball and is installed
  to the ROOT-OWNED footprint (/usr/local/lib/libreportal/libreportal.pub) by init.sh
  -> the manager can't swap it to accept forged updates. footprint_version -> 2.
- install.sh: LP_MINISIGN_PUBKEY constant; once non-placeholder, downloads + verifies
  the .minisig (minisign -P) and REFUSES on invalid/missing (auto-installs minisign if
  needed). --no-verify-signature is a dev-only escape hatch.
- fetch.sh (update path): verifies against the footprint .pub (minisign -p), refuses on
  invalid/missing.
- docs/DEVELOPMENT.md: keygen (minisign -G), paste pubkey into libreportal.pub +
  install.sh, keep the secret key offline, sign builds via LP_MINISIGN_SECKEY, bump
  footprint_version on key rotation.

Verified end-to-end with a real throwaway key: good signature accepted; tampered,
wrong-key, and missing-signature all refused; placeholder skips (sha256 still enforced).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 19:40:30 +01:00
librelad
d0162ccda1 Merge claude/1 2026-05-25 19:07:16 +01:00
librelad
3014965b66 feat(update): FOOTPRINT_VERSION drift detection — flag when a root re-install is needed
A manager-run 'update apply' refreshes code/apps/WebUI but CANNOT rewrite the
root-owned footprint (helpers/wrapper/uninstall/unit/sudoers) — that immutability
is the de-sudo boundary. Previously a release that changed those would silently
leave them stale. Make it explicit:

- init.sh: footprint_version=1 constant, baked at install into
  /usr/local/lib/libreportal/.footprint_version (root:root 0644) by initRootHelpers.
  Bump it whenever a root component changes.
- make_release.sh: publishes footprint_version in latest.json.
- fetch.sh: lpInstalledFootprintVersion (marker) + lpReleaseLatestFootprint (manifest).
- check_update.sh: 'update apply' REFUSES when the release's footprint_version
  exceeds the installed one, directing to a root re-install (which fetches +
  re-bakes everything atomically). No half-applied updates.
- webui_system_update.sh: badge sets footprint_update_needed + clears can_update so
  the WebUI won't offer a one-click apply for a footprint-bumping release.
- docs/DEVELOPMENT.md: the bump rule + the footprint exception explained.

Verified: manifest carries footprint_version; drift decision correct both ways
(no marker/older -> needs re-install; equal -> no drift).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 19:07:16 +01:00
librelad
d95560c48c Merge claude/1 2026-05-25 18:59:38 +01:00
librelad
f2c2b0485a refactor(uninstall): drop repo uninstall.sh; init.sh generates the command
Remove the redundant repo-root uninstall.sh (it duplicated libreportal-uninstall).
init.sh now GENERATES the libreportal-uninstall launcher into the fixed footprint
(/usr/local/lib/libreportal/uninstall.sh + the /usr/local/bin symlink) — same
pattern as the CLI wrapper, so the on-box command survives without a separate repo
file. The launcher just runs the engine's uninstall ($script_dir/init.sh baked in,
/root/init.sh fallback).

This resolves the install/uninstall asymmetry: a bootstrap (install.sh) exists only
because install faces a bare box with no code yet; uninstall always runs the engine
that's already installed, so it needs no bootstrap — just a generated door into
init.sh. Repo root install/uninstall surface is now init.sh + install.sh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:59:38 +01:00
librelad
94d91ff551 Merge claude/1 2026-05-25 18:50:26 +01:00
librelad
8472fdf0ae feat(uninstall): location-agnostic 'libreportal-uninstall' command (+ de-hardcode docs)
The docs were telling users to run /libreportal-system/install/uninstall.sh — a
hardcoded data path, wrong for any custom --system-dir, contradicting the whole
relocatable design.

Fix it the way the CLI already works: install uninstall.sh to the FIXED footprint
(/usr/local/lib/libreportal/uninstall.sh) and symlink it onto $PATH as
'libreportal-uninstall' (initLibrePortalCommand). It self-resolves the real data
roots from the systemd unit, so the command is the same everywhere regardless of
where data lives. Teardown removes the new symlink; FOOTPRINT.md lists it.

Docs now say 'sudo libreportal-uninstall' — no data path. (Dev-from-clone still
uses ./uninstall.sh / ./init.sh uninstall.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:50:26 +01:00
librelad
55dd62af56 Merge claude/1 2026-05-25 18:46:05 +01:00
librelad
cb298d41e6 docs(user): point uninstall at ./uninstall.sh (handles custom roots)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:46:05 +01:00
librelad
45626d0641 Merge claude/1 2026-05-25 18:45:16 +01:00
librelad
805d557fd7 fix(uninstall): resolve the real install roots + add ./uninstall.sh
Bug: runFullUninstall used the derived $docker_dir/$containers_dir/$backup_dir,
but a bare 'init.sh uninstall' on a CUSTOM-location install has no LP_*_DIR in
scope and no /docker marker — so it defaulted to /libreportal-* and would MISS the
real data (e.g. /mnt/ssd), leaving it behind.

Fix: libreportalReadBakedRoots reads the authoritative baked record from the
systemd unit (Environment=LP_SYSTEM_DIR/CONTAINERS_DIR/BACKUPS_DIR + User=<manager>)
and runFullUninstall re-derives from it before removing anything. Legacy units
(no LP_*_DIR) fall through to the derive defaults + /docker compat shim.

Add top-level uninstall.sh: a root-only convenience that finds the installed
init.sh (via the unit's system root, then common locations) and runs it —
'sudo ./uninstall.sh [--skip-docker-images]'. Verified the unit parsing extracts
custom roots/manager and the discovery picks the right init.sh (without running the
destructive teardown).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:45:16 +01:00
librelad
800c70fefd Merge claude/1 2026-05-25 18:40:03 +01:00
librelad
40aa6d9d1a docs: move dev/reference docs into docs/ + refresh FOOTPRINT for the 3-root layout
Tidy the repo root (README + LICENSE stay there per convention; everything else
moves):
- CONTRIBUTING.md, PROMISE.md, FOOTPRINT.md -> docs/ (alongside USER.md/DEVELOPMENT.md)
- update the references: README links, the website site.json raw URLs, init.sh's
  'see FOOTPRINT.md' comments -> docs/FOOTPRINT.md; drop the now-redundant
  CONTRIBUTING.md export-ignore (docs/ is already export-ignored).

Refresh FOOTPRINT.md: it claimed 'everything lives under /docker', which is no
longer true. Now describes the three relocatable roots (system/containers/backups)
and makes explicit that the roots + manager name are baked into the helpers/unit/
wrapper at install (the privilege boundary) while this out-of-root footprint stays
fixed by design. Uninstall sketch + sudoers/unit rows updated for the configurable
manager. CONTRIBUTING/PROMISE were already current — left as-is.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:40:03 +01:00
librelad
846936617e Merge claude/1 2026-05-25 18:27:59 +01:00
librelad
a48a241fbe docs: add docs/USER.md + docs/DEVELOPMENT.md (+ README pointer)
Two guides covering what wasn't written down:
- USER.md: install (the install.sh one-liner), placing the three roots on separate
  disks/external drives, channels, updating, backups (REQUIRE_MOUNT), uninstall.
- DEVELOPMENT.md: the install-mode/roots/users model + key files; running a dev copy
  (local/git); cutting stable/edge releases (bump VERSION -> make_release.sh ->
  dist/<channel>/{tarball,.sha256,latest.json} -> publish); testing a release
  locally via LP_RELEASE_BASE_URL + python3 -m http.server (incl. checksum-refusal);
  how release updates work; conventions.

README Quick start updated to the release flow + a docs pointer. docs/ is
export-ignored so it doesn't bloat release tarballs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:27:59 +01:00
librelad
89049864a6 Merge claude/1 2026-05-25 18:16:35 +01:00
librelad
ddea6b8a4d feat(update): route reset/reinstall recovery paths through the release fetch (phase D)
The git-era recovery commands now do the right thing in release mode instead of
attempting a clone:
- gitReset (libreportal reset / update reset) and runReinstall (CLI/system reset,
  missing-files recovery): a release branch re-fetches the verified tarball via
  lpFetchRelease, then refreshes /root/init.sh + ownership.
- the CLI wrapper's clone_and_install (libreportal reset): sources fetch.sh and
  re-fetches the release; falls back to directing the user to the install.sh
  bootstrap if the helper isn't present.

git/local behaviour unchanged. Wrapper still bakes cleanly (no placeholders left).

Phases A–D (release build, bootstrap installer, fetch abstraction, release-aware
install + update + recovery) are complete and locally verified. Remaining: phase E
(host install.sh + channels + tarballs on get.libreportal.org) and a real fresh
install on a throwaway box.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:16:35 +01:00
librelad
540b0518c5 Merge claude/2 2026-05-25 18:13:55 +01:00
librelad
3064328aa8 fix(webui): populate admin sidebar on cold visit
The admin landing (overview) and the tools pages (ssh-access, system) call
populateSidebar() without first loading window.configData. On a cold admin
visit — e.g. navigating straight from the dashboard — configData is undefined,
so populateSidebar() bails early and the sidebar renders empty. Visiting
Backups happened to set window.configData, which is why returning to admin
afterward showed the sidebar.

Load (cached) config data up front in renderConfig before any branch renders so
the sidebar always has its categories. The config-category path's later
loadConfig is now a cache hit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:13:55 +01:00
librelad
6be0db57da Merge claude/1 2026-05-25 18:13:09 +01:00
librelad
acfe7d6bfa feat(update): release-aware update detection + apply (phase D)
Make the WebUI updater work off release versions, not git commits, in release mode
(git/local paths untouched):

- webui_system_update.sh: a release branch resolves latest_version from the channel
  manifest (lpReleaseLatestVersion), computes update_available via lpVersionGt vs
  the local VERSION, reuses the same throttle + the same update_status.json schema
  (source="release"); reuses last-known latest when throttled so the badge
  doesn't flicker.
- check_update.sh webuiRunUpdate: a release branch version-compares and, if newer,
  lpFetchRelease (download + checksum-verify) the new tarball + dockerInstallApp
  redeploy + regen. No config-backup dance — lpFetchRelease replaces only the
  install tree; configs/logs are in the separate system tree.

Verified against a local server: latest-version read + the no-update / update-
available decision (0.2.0==0.2.0 no; 0.3.0>0.2.0 yes). Remaining: route the
reset/reinstall recovery paths through the release fetch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:13:09 +01:00
librelad
584d330636 Merge claude/1 2026-05-25 18:10:42 +01:00
librelad
c78391753b fix(release): exclude scripts/release/ from sourced arrays
Adding scripts/release/ made generate_arrays emit files_release.sh, which would
source the build tool make_release.sh at runtime (it runs git archive at load —
wrong + harmful). Skip release/ in the folder scan (like system/ and unused/),
remove the orphaned files_release.sh, and regenerate the meta-list. fetch.sh stays
sourced; make_release is in no array.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:10:42 +01:00
librelad
d9b76db157 Merge claude/1 2026-05-25 18:08:39 +01:00
librelad
90663a077a feat(install): release fetch mode + lpFetchSource abstraction (phase C)
scripts/source/fetch.sh (sourced at runtime via files_source.sh):
- lpFetchRelease [ver]: resolve channel manifest -> download tarball -> VERIFY
  sha256 (refuse on mismatch/absence) -> replace the install tree ( is
  code-only now; configs/logs live in the separate system tree, so no backup
  dance). Host/channel from LP_RELEASE_BASE_URL/CFG_RELEASE_BASE_URL + channel.
- lpFetchSource: dispatch release|git|local.
- lpVersionGt: numeric dotted semver compare (used by the updater + badge).

init.sh initGIT is now release-aware: the bootstrap (install.sh) stages+verifies
the code and sets LP_ALREADY_FETCHED=1 (skip re-fetch); a direct release run sources
fetch.sh; a bare /root reinstall is directed to install.sh. install.sh exports
LP_ALREADY_FETCHED + LP_RELEASE_BASE_URL on hand-off. validateUnattended already
accepts release (git-url is gated on git mode).

Config: CFG_INSTALL_MODE default -> release, + CFG_RELEASE_BASE_URL / CFG_RELEASE_CHANNEL
(add-only reconcile preserves existing installs' git/local mode).

Verified: lpVersionGt across cases; lpFetchRelease downloads+verifies+extracts a
clean tree against a local server. The updater + reset/reinstall release paths are
phase D.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:08:39 +01:00
librelad
c03b54d9a5 Merge claude/1 2026-05-25 18:04:14 +01:00
librelad
9207ec384b feat(install): thin bootstrap installer install.sh (phase B)
curl -fsSL https://get.libreportal.org/install.sh | sudo bash [-s -- FLAGS]

Self-contained bootstrap (downloaded and run before any LibrePortal code exists):
resolves the channel manifest (<base>/<channel>/latest.json), downloads the
release tarball, VERIFIES the sha256 (manifest or .sha256 sidecar) and refuses on
mismatch, extracts to <system>/install, then hands off to init.sh unattended with
the relocatable flags (--system-dir/--containers-dir/--backups-dir/--manager-user/
--allow-home). curl-or-wget, sha256sum-or-shasum; honours LP_RELEASE_BASE_URL for
testing against a local/file server.

Dev modes preserved: --local=PATH and --git-url= (with --git-user/--git-token).
Generates a random manager/WebUI password if none given. --dry-run stages + verifies
without installing.

Verified against a local http server: dry-run resolves→downloads→verifies→extracts
and reports the correct init.sh handoff; a corrupted tarball is refused. The
end-to-end exec needs init.sh release-mode awareness (phase C) + a throwaway box.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:04:13 +01:00
librelad
7f6e397cc9 Merge claude/1 2026-05-25 18:00:11 +01:00
librelad
2ce0d22954 build(release): add make_release.sh + export-ignore for clean tarballs (phase A)
scripts/release/make_release.sh builds a versioned, checksum-verified release
artifact from the committed tree via 'git archive' — the basis for installing
LibrePortal without git/auth. Output lands in dist/<channel>/ laid out as the
hosting will serve it (libreportal-<ver>.tar.gz + .sha256 + latest.json), so it
can be served locally for testing via LP_RELEASE_BASE_URL.

.gitattributes marks dev-only trees export-ignore (scripts/unused, scripts/release,
site, .claude, CONTRIBUTING.md, the git meta files) so they never ship; validated
the archive includes init.sh/start.sh/scripts/configs/containers/VERSION and
excludes all of the above. dist/ gitignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:00:11 +01:00
librelad
295ca2a9b4 Merge claude/1 2026-05-25 17:47:05 +01:00
librelad
b47e20133d feat(install): make the control-plane manager user configurable
sudo_user_name (the real manager — owns the install, runs the runtime, baked as
__MANAGER__ into the root helpers) was hardcoded to 'libreportal'. Make it
configurable, consistent with the relocatable roots:

- --manager-user=NAME flag + LP_MANAGER_USER env (default libreportal); resolved
  early in init.sh and in scripts/source/paths.sh (so the standalone processors
  get it too), validated as a real Linux username in libreportalValidatePaths.
- Baked everywhere it must be stable: the helpers + CLI wrapper (CHECK_USER now
  __MANAGER__, exports LP_MANAGER_USER) via the install-time sed; the systemd unit
  exports LP_MANAGER_USER=<manager>. User creation (initUsers), the sudoers
  drop-in, and ~35 call sites already used $sudo_user_name, so they follow.
- Fix the stray manager-name literals: install_crowdsec.sh chown, the
  check_install_type fallback. (Brand/identity strings like the backup
  engine:libreportal tag are left — they're not the username.)

Verified: resolves default/env/flag; wrapper bakes a custom name (admin) with no
placeholders left; validation rejects invalid usernames. The footprint paths
(/etc, /usr/local) stay fixed by design.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 17:47:05 +01:00
librelad
f56bbda2a8 Merge claude/1 2026-05-25 17:42:20 +01:00
librelad
27b4a877f5 refactor(cleanup): retire the dead SFTP 'Docker Manager User' to unused/
CFG_DOCKER_MANAGER_USER / installDockerManagerUser was a chrooted SFTP file-access
user — unrelated to the LibrePortal control-plane manager (sudo_user_name), and
the source of the 'two managers' confusion. It was permanently-off dead code: the
gate CFG_DOCKER_MANAGER_ENABLED and the CFG_DOCKER_MANAGER_USER/_PASS keys are
defined in no config template, so it never ran. Its SSH-key-management sibling
(unused/ssh_manager.sh) was already retired; admin host SSH access is handled by
the current /ssh page + scripts/ssh/host_access.sh.

Move install_user_manager.sh / uninstall_user_manager.sh / check_manager.sh to
scripts/unused/manager/ (recoverable, matches the graveyard convention — not
deleted, in case the SFTP-user idea is rebuilt cleanly later), drop the two call
sites (start_preinstall.sh, check_requirements.sh), regenerate the arrays.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 17:42:20 +01:00
librelad
f5afb91968 Merge claude/1 2026-05-25 17:18:46 +01:00
librelad
16571134b5 refactor(paths): scrub residual /docker references in display text + comments
Audit follow-up — after a full-repo sweep, the only remaining functional /docker
refs are intentional (the legacy compat shim + the env-overridden legacy-safe
backend default). Fix the last user-visible/stale ones:
- config-options.js: backup PATH_MODE 'auto' label no longer hardcodes
  /docker/backups (the path is relocatable) — describes the behaviour instead.
- config.js / setup-detector.js / webui_install_image.sh: refresh comments that
  named /docker to the relocatable system/containers roots.

No behaviour change. Active container app scripts already use $containers_dir;
the remaining /docker hits across the tree are docker-compose.yml filenames,
/var/lib/docker, the docker binary, relative array paths, docs/site, and the
unused/ graveyard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 17:18:46 +01:00
librelad
67921eeaf2 Merge claude/1 2026-05-25 15:35:00 +01:00