The task processor is a systemd-service daemon, not a cron job — move it out
of the misleadingly-named scripts/crontab/task/ to scripts/task/.
To stop the systemd unit from baking the processor's in-tree path (the footprint
coupling that forces a reinstall on every reorg), the unit now ExecStarts the
stable wrapper: /usr/local/bin/libreportal __task-processor. start.sh intercepts
that early (after paths.sh, before the heavy load), exports install_scripts_dir,
and exec's the processor with start_script. Future moves/renames need only the
one hand-off updated + a regen — no footprint bump.
- git mv scripts/crontab/task -> scripts/task (filenames kept; cron-watchdog grep
+ function names unchanged)
- libreportal-svc: ExecStart -> stable wrapper launcher
- start.sh: __task-processor internal launcher (export install_scripts_dir; exec)
- crontab_task_processor.sh: fix self-location ../.. -> .. for the new 1-level
depth (latent bug the move would otherwise have introduced)
- regen files_*/function_manifest; add task_scripts to the app/cli aggregates
- footprint_version 3 -> 4 (root-owned svc unit changed -> needs a root reinstall)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Verified-dead assets from the feng-shui audit, zero consumers:
- core/icons/categories/utils.svg — no 'utils' app category exists (the only
'utils' refs are unrelated system health-check names); category icons are
requested as /core/icons/categories/<id>.svg and no id is 'utils'.
- core/icons/apps/portainer.svg — Portainer was retired to
scripts/unused/OLD_CONTAINERS/; no live containers/portainer/, and apps.json
is generated only from live containers, so the icon is never requested.
Both git-recoverable if a portainer app / utils category is ever (re)added.
Signed-off-by: librelad <librelad@digitalangels.vip>
The shipped frontend carried ~600 muted '// console.…' debug statements (and
their multi-line commented continuation lines) left over from development —
clutter across 30 files. Removed them with a guarded pass that ONLY ever deletes
lines starting with // (so it can never alter behaviour), consuming each
commented console opener plus its continuation comment lines until the
string-stripped parens balance.
665 lines removed, 30 files; 0 insertions. Verified every deleted line is a //
comment (no code touched), real prose comments preserved, full node --check
sweep clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
From the feng-shui audit naming findings:
- admin/overview/js/admin-overview.js -> overview-page.js (class AdminOverview ->
OverviewPage, window globals + the 'admin-overview' task-refresh id ->
overview-page, lazy-load path + typeof/new in config-manager.js).
- admin/system/js/admin-system.js -> system-page.js (class AdminSystem ->
SystemPage; now sits beside its -page sub-views system-metric-page.js /
system-storage-page.js).
- tasks/js/tasks-logs-modal.js -> tasks-log-modal.js (singular 'log' to match its
sibling tasks-log-stream.js; single path ref in system-loader.js).
These were the only page controllers breaking the dominant <thing>-page.js /
<Thing>Page convention (ssh-page/peers-page/backup-page/updater-page/
system-metric-page/system-storage-page). Pure renames; node --check clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
- docs: remove the docs/README.md index and docs/CONTRIBUTING.md pointer
(duplicate filenames); the canonical contributing guide stays at
docs/contributing/contributing.md. Clean tree, no name collisions.
- scripts/system/*: 6 helper headers + host_access.sh said the helpers
install to /usr/local/sbin, but init.sh installs all of them to
/usr/local/lib/libreportal/ (verified via initRootHelpers + the sudoers
Cmnd_Alias). Corrected. The only remaining /usr/local/sbin is the legit
PATH export in the task processor.
- frontend kernel: drop migration-era comments that are now false post-
modularization (feature-registry 'passive/phase 0/unused', lifecycle
'ctx.services lands with Phase 2', manifest 'scan generator lands') —
describe current behaviour instead.
Comment-only edits to scripts/system/* — no footprint_version bump (no
behavioural change; bumping would force needless reinstalls).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Audited every doc against the code. Three fixes:
- system-footprint.md: add the libreportal-crowdsec root helper row
(init.sh installs 8 helpers; the table listed 7). appcfg row clarified
to 'CrowdSec-bouncer' config since the new helper does the host install.
- .gitattributes: add 'site export-ignore' — development.md documents the
website as never-shipping, but the rule was missing, so site/ was landing
in release tarballs. No runtime refs to site/; hosting lives in the Infra repo.
- promise.md: fix LICENSE link (../../LICENSE) after the docs/ reshuffle.
Everything else (install-and-use, development, contributing) verified current:
all install/uninstall/update flags, release scripts, fetch fns, footprint_version,
service name, and config keys check out against the code.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Sort docs/ into guide/ contributing/ architecture/ roadmap/ and rename
to consistent kebab-case (USER->guide/install-and-use, FOOTPRINT->
architecture/system-footprint, frontend-modularization->architecture/
webui-architecture, etc.). Add a docs/README.md index and a docs/
CONTRIBUTING.md pointer so the forge still surfaces the contributing
guide. Fix every reference (README, init.sh comments, frontend code
comments, and the USER<->DEVELOPMENT cross-links). History preserved
via git mv. Root stays README.md + CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Comment-only tidy from the feng-shui audit — no code behavior changes. The
features/ directory was renamed to components/ during modularization, but
several header banners and inline comments still named the old path:
- 6 component module headers (admin/tasks/backup/dashboard/updater/index.js +
updater/js/updater-page.js) now name their real components/<id>/… path
- core/kernel/js/spa.js + core/tasks/js/task-router.js comments
- backend/routes/features.js doc-banner (drop a components/<id>/ folder …)
- core/update-notifier/css/update-notifier.css header (js/update-notifier.js)
Guarded the rewrite so the LIVE /api/features/list endpoint name (feature-
registry.js sources + backend route) is untouched — only stale 'features/<path>'
directory references were updated.
Signed-off-by: librelad <librelad@digitalangels.vip>
The modularization shipped (2026-05-30), so the design doc was stale and
internally contradictory: it described a features/ tree (real tree is
components/), a shell-generator/Node route that were never built, and a
'partially implemented' status. Replace the 59KB design exploration with
a short, accurate description of the component-module system as it exists
(components/<id> pages, core/ subsystems, the kernel: feature-registry/
services/lifecycle/spa, static manifest discovery, mount/unmount contract,
eager global CSS). Fix one stale features/ path in a spa.js comment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
From the feng-shui audit (all adversarially verified):
- BUG (high): apps-grid.js category tiles used onerror fallback
/core/icons/categories/default.svg, which doesn't exist (the dir has
misc.svg as its generic icon, which data-loader.js already uses). Any
category missing its named SVG showed a broken-image glyph. Repointed to
/core/icons/categories/misc.svg.
- TIDY: core/forms was the lone depth-3 nesting — JS at forms/controls/js/
while its CSS sat at forms/css/ and every other core subsystem uses
<name>/js/. 'controls/' grouped nothing (just the 2 custom-* widgets), so
flattened to core/forms/js/ (+ updated index.html). forms is now symmetric.
- CONSISTENCY: components/manifest.dev.json entries carried nav.order but not
the top-level 'order' that each feature.json has; added it so the API-down
fallback matches the live /api/features/list scan.
Signed-off-by: librelad <librelad@digitalangels.vip>
Captures the brainstorm on hotfixes, the updater reframe to
'Updates & Improvements', and registry-not-marketplace distribution:
one signed/declarative/reversible primitive behind hotfixes, app
installs, and themes. Vision/TODO doc with open forks, not a spec.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The config-category icons sat at admin/config/icons/CONFIG/ — the inner config/
duplicates the subsystem name; they belong in the icons root. Moved all 6
(backup, features, general, network, security, webui) up to
components/admin/config/icons/ and updated the two consumers (config-manager.js
header icon, config-sidebar.js category icons).
Also fixed the backup-engine logos: scripts/backup/engines/{restic,kopia,borg}
.json pointed 'logo' at /icons/config/backup.svg — a path that 404'd on two
counts (missing the components/admin/config prefix AND the now-removed config/
nesting), so the engine-details modal logo silently hid. Repointed to the real
served path /components/admin/config/icons/backup.svg.
(Left the meaningful icon groupings alone — admin/system/icons/{cpu,os} and
apps/core/icons/vpn are vendor/OS/provider logo sets, not redundant nesting.
The backup engines borrowing an admin-config icon is a minor smell; a dedicated
backup-engine icon could replace it later if wanted.)
Signed-off-by: librelad <librelad@digitalangels.vip>
Brings core/ in line with components/ — each subsystem now sorts its files into
js/ css/ html/ subfolders (and the nested auth/ + controls/ groups keep theirs):
core/topbar/{js/{topbar,mobile-menu}.js, css/{topbar,sidebar}.css, html/topbar.html}
core/theme/{js/theme-registry.js, css/{tokens,themes,base,aurora-background}.css}
core/forms/{css/{forms,config}.css, controls/js/{custom-number,custom-select}.js}
core/boot/{js/{system-loader,system-orchestrator}.js, auth/{js/auth-manager.js,css/login.css}}
core/{config,tasks,kernel}/js/… core/overlays/{js,css}/… core/setup/{js,css}/…
core/{app-meta,backup-card,data-loader,dom,live,notifications,ui-mode,ui-state}/js/…
core/{loading,update-notifier}/{js,css}/…
50 files relocated (pure git mv). All path literals rewritten from a generated
old→new map across index.html, system-loader.js bundles, topbar.js's internal
fetch (/core/topbar/html/topbar.html), and the three backup-app-card loaders. No
OLD path contained a js/css/html segment, so no double-prefixing was possible.
core/icons/ left as-is (shared asset tree). All 50 /core asset refs verified to
resolve; full node --check sweep clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
The frontend modularization moved icons to frontend/core/icons/ and updated the
frontend JS, but the host-side generators were never updated — they wrote the
apps.json/categories 'icon' field as /icons/apps/<app>.svg and /icons/categories/
<cat>.svg, and webui_app_icons.sh / webui_config.sh synced icon files into the
non-existent frontend/icons/apps. Those served paths 404 (text/html catch-all),
so every app card fell back to default.svg (the generic box) instead of its real
logo.
Repointed to /core/icons/... (where the SVGs actually live and serve as
image/svg+xml):
- webui_config.sh: icon dir + emitted apps.json icon path
- webui_app_icons.sh: icon sync dir + comment
- webui_container_setup.sh: comment
- webui_create_app_categories.sh: 11 category icon paths
Source fix only — the live apps.json refreshes on the next host-side regen
(lpRegen). NOT touched: scripts/backup/engines/*.json '/icons/config/backup.svg'
(that SVG lives at the oddly-nested components/admin/config/icons/config/ and
serves at neither path — needs a placement decision, flagged separately).
Signed-off-by: librelad <librelad@digitalangels.vip>
The generic core/css/{base,components,screens} buckets are gone; every shared
stylesheet now lives beside the subsystem that owns it:
base/tokens.css, base/themes.css, components/aurora-background.css -> core/theme/
base/style.css -> core/theme/base.css (carve deferred)
components/modal.css -> core/overlays/
components/topbar.css, components/sidebar.css -> core/topbar/
components/forms.css, components/config.css -> core/forms/ (config.css under forms)
components/update-notifier.css -> core/update-notifier/
screens/login.css -> core/boot/auth/
screens/loading-screen.css -> core/loading/
screens/setup-wizard.css -> core/setup/
href-only rewrites in index.html; the <link> line ORDER is unchanged, so the
cascade is preserved (no @import anywhere). All 13 /core css hrefs verified to
resolve. (The JS for overlays/topbar/forms/update-notifier/loading/setup/auth
co-locates in the next phase.)
Signed-off-by: librelad <librelad@digitalangels.vip>
The Phase-2 rename put DataLoader in core/data/, but update.sh's deploy rsync
uses --exclude 'data/' (to protect the runtime frontend/data/ dir the backend
serves auth-gated under /data). rsync's pattern matches a 'data' dir ANYWHERE in
the tree, so core/data/ was silently excluded from the served copy — the file
404'd and the dashboard showed 0 apps / Loading… while every sibling subsystem
deployed fine. Renamed the folder to core/data-loader/ (segment 'data-loader' ≠
'data') so it ships. No code/class change.
Signed-off-by: librelad <librelad@digitalangels.vip>
The generic core/lib/ wrapper (and its task/config/util sub-buckets) is gone.
Each child is now a named core subsystem describing what it IS:
core/lib/task/ -> core/tasks/ (task kernel: bus, refresh,
manager, router, actions,
commands, parameter-preserve)
core/lib/config/ -> core/config/ (config-shared.js→field-factory.js,
config-options.js→options.js;
options-before-factory order kept)
core/lib/util/system-live -> core/live/live-system.js
core/lib/util/lp-ui -> core/ui-mode/lp-ui.js (stays FIRST eager — no FOUC)
core/lib/util/data-loader -> core/data/data-loader.js
core/lib/util/dom-helpers -> core/dom/dom-helpers.js
core/lib/util/ui-helpers -> core/app-meta/app-helpers.js (getAppIcon survivor)
core/lib/util/dismissible -> core/ui-state/dismissible.js (generic+eager, stays
core — NOT a backup widget)
core/boot/theme-registry -> core/theme/theme-registry.js (theming, not bootstrap)
Path-only moves (git mv) + literal rewrites in index.html, system-loader.js
(config/task/apps bundles) and apps-manager ensureTaskScripts. Class/global
names unchanged (ConfigShared/ConfigOptions/LiveSystem/getAppIcon) so consumers
are untouched. All 16 referenced paths verified to resolve; full node --check
sweep clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
Verified-dead removals (zero consumers, confirmed by adversarial dependency
audit):
- core/lib/util/router.js — legacy class Router superseded by kernel/spa.js;
self-instantiated, never exposed, and added a SECOND competing popstate
listener. Dropped the file + its eager index.html tag.
- core/lib/task/task-global-functions.js — wired window.installApp/uninstallApp/
etc. that nothing calls (real calls go through class methods / the task
router). Dropped the file + its task-system scripts[] entry + the
setupTaskGlobalFunctions() block in system-loader.js.
- TopbarComponent.createNavigationHighlighting + clearAllNavigationHighlighting —
dead statics; window.topbarNavigationHighlighting was never set.
- ui-helpers.js: getAppStatus/formatAppName/getAppShortName (dead), the stale
setupMobileMenu/closeMobileMenu (superseded by core/ui/mobile-menu.js's
#mobile-drawer impl), setupActiveNavigation + the safe* helpers (verbatim dups
of dom-helpers). Only getAppIcon remains. dom-helpers loses dead
setupActiveNavigation + waitForElement; it is now the sole safe* source.
Bug fixes surfaced during the audit:
- system-orchestrator.js called this._wireLogout() which is defined nowhere —
threw on the 'Continue Anyway' boot path. Removed the dangling call (logout is
wired in topbar.setupLogout()).
- active-nav highlighting never updated on SPA navigation (it depended on the
never-set global). spa.js now calls the live window.topbar?.setActiveNav?.()
after each route handler.
No structural moves yet; full node --check sweep clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
core/css held 13 flat stylesheets. Grouped by role, matching the JS sub-system
split:
core/css/base/ foundation (3): tokens, style, themes
core/css/components/ chrome + widgets (7): topbar, sidebar, modal, forms,
config, update-notifier, aurora-background
core/css/screens/ full-page screens (3): login, loading-screen,
setup-wizard
Path-only move (git mv) + href rewrite in index.html. No @import anywhere and
the <link> line order is unchanged, so the cascade is preserved; all 13 paths
verified to resolve.
Signed-off-by: librelad <librelad@digitalangels.vip>
backup/ held 15 loose .js plus css/html at the component root. Split into
per-tab sub-systems mirroring the admin/apps layout:
backup/core/{js,css,html}/ schema, base BackupPage, fetch-client,
cron-schedule, backup.css, backup-content.html
backup/dashboard/js/ backup-dashboard
backup/snapshots/js/ backup-snapshots, backup-snapshot-actions
backup/locations/js/ backup-locations, backup-location-fields,
backup-location-modal, backup-ssh-key
backup/migrate/js/ backup-migrate
backup/configuration/js/ backup-configuration, backup-retention-presets,
backup-engine-details
Updated the scripts[] array + loadFragment in index.js, the backup.css href in
index.html, and the handleBackup() fallback paths in spa.js. No behaviour
change — files moved verbatim (prototype-augment clusters), all 17 referenced
paths verified to resolve, node --check clean.
Signed-off-by: librelad <librelad@digitalangels.vip>
admin had sub-system folders (config/overview/system/ssh/peers) AND a bare
root css/ (admin.css) — inconsistent. A component's shared base belongs in a
core/ sub-folder once it has sub-systems (matching apps/core/), so admin.css
moves to admin/core/css/admin.css. Audit confirms all components are now
consistent: sub-system components (apps, admin) use core/ + sub-systems;
single-page components (backup/dashboard/tasks/updater) stay flat js/css/html
(no sub-systems, so no core/ needed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
config-redirect/, peers/, ssh/ were redirect-only shim components (just a
feature.json each) whose handlers re-entered navigate() and were silently
no-op'd by the isLoading guard — i.e. the legacy /config /peers /ssh URLs were
broken, and nothing in the UI links to them. Replace all three with a single
_legacyRedirect() at the top of navigate() (before the guard, so it actually
works) that rewrites them to the canonical /admin/* path. Removed the 3
folders, their manifest entries, the 3 dead spa.js handlers, and their
setupRoutes() lines. components/ is now exactly the 6 real pages; the real
SSH/Peers pages live (as before) under admin/ssh and admin/peers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
app-detail was a separate sibling component but is really the apps feature's
detail view (shares the apps controllers + apps-unified-layout). Merge it in:
the apps feature now owns /apps*, /app, /app* and its mount() dispatches grid
vs detail by path (checks /apps first for wildcard precedence). Removed the
components/app-detail/ folder + its manifest entry. (The 1-level feature scan
means a feature must be a direct components/<id>/ child — folding the routes
into apps is the correct way to express the relationship.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>