# SysDeck - RPM Spec # Author: Jeremy Anderson (https://dcos.net) # Build: rpmbuild -bb packaging/sysdeck.spec # # NOTE: This spec is for Fedora / RHEL / CentOS only. # Arch Linux users: see packaging/PKGBUILD # Debian/Ubuntu users: see packaging/debian/ Name: sysdeck Version: 0.4.6 Release: 1%{?dist} Summary: Unified operations surface for Linux infrastructure License: MIT URL: https://dcos.net Source0: %{name}-%{version}.tar.bz2 BuildArch: noarch BuildRequires: make BuildRequires: python3-devel BuildRequires: nodejs Requires: cockpit-bridge >= 239 Recommends: podman Recommends: nftables Recommends: opensc Recommends: pcsc-lite Recommends: fwupd Recommends: tpm2-tools Recommends: glances Recommends: lm_sensors Recommends: sysbench Suggests: kata-containers Suggests: jellyfin Suggests: ceph Suggests: glusterfs %description SysDeck is a standalone Linux operations console that also ships as a Cockpit plugin suite. This package installs the Cockpit plugin edition: 27 domain modules — containers, firewall, integrity auditing, network security, service mesh, encryption vaults, fleet compute, Kata Containers, firmware, image building, mining, theme engine, hardware authentication, DAG-driven build orchestration, system monitoring, hardware sensors, system benchmarking, package management, policy & permissions, database control, Jellyfin media server, photo manager (PhotoPrism/Piwigo/Lychee/Nextcloud-Memories/LibrePhotos), remote filesystem manager (Ceph/GlusterFS/MooseFS/BeeGFS/OrangeFS), the service/port editor, the 3rd-party module installer, and the AI gateway client — behind the Cockpit web UI. The standalone web console (no Cockpit required) ships in the master tarball. This RPM is for Fedora / RHEL / CentOS. For Arch Linux, use the PKGBUILD. For Debian/Ubuntu, use the dpkg-buildpackage packaging. %prep %setup -q %build # Pure static assets — nothing to compile. %install make install DESTDIR=%{buildroot} %files %license LICENSE %doc README.md QUICKSTART.md BLOG.md QA.md # The install target ships: 27 plugins under # /usr/share/cockpit/sysdeck-*/ (manifest.json, index.html, module js), # the sysdeck-common bridge library, the Python bridge at # /usr/lib/sysdeck/bridge/, tests, docs, share assets (diagnose + # smoke + uninstall scripts, firewall templates + policies, prometheus # configs), AppStream metainfo, and both polkit actions. /usr/share/cockpit/sysdeck-*/ /usr/lib/%{name}/ /usr/share/%{name}/ /usr/share/doc/%{name}/ /usr/share/metainfo/sysdeck.metainfo.xml /usr/share/polkit-1/actions/org.sysdeck.policy /usr/share/polkit-1/actions/org.sysdeck.modules3p.policy %post # Restart cockpit.socket so the new plugin appears in the menu. if [ $1 -eq 1 ]; then /bin/systemctl try-restart cockpit.socket >/dev/null 2>&1 || : /bin/systemctl reload polkit >/dev/null 2>&1 || : fi %postun if [ $1 -eq 0 ]; then /bin/systemctl try-restart cockpit.socket >/dev/null 2>&1 || : /bin/systemctl reload polkit >/dev/null 2>&1 || : fi %changelog * Sun Sep 27 2026 Jeremy Anderson - 0.4.6-1 - v0.4.6: QCrows format-aware Kata bridge. qcrows-list now parses metadata.toml + menu.toml from .qcrows archives in memory (image name/version/arch, kernel version/format, hypervisors, menu label); new qcrows-inspect + qcrows-verify subcommands mirror cockpit-kata's master checks (required files, kernel magic, config + Kata options, full sha256sum hash walk) with nothing extracted to disk. Kata panel shows the metadata and gains per-image Verify/Inspect actions. integrity.score() degrades to None on PermissionError (root-only lynis.log no longer crashes unprivileged sessions). * Thu Sep 17 2026 Jeremy Anderson - 0.4.5-1 - v0.4.5 production-hardening release: full MoE QA pass. Makefile web-dev splice fixed; reproducible dist + clean-tree release gate; master tarball hygiene (no dev logs, no .env). Bridge security: prometheus push-log filename validation + label escaping, single- statement read-only SQL guard, glances availability step-down, mutation timeout safety, vmdb2 output-dir guard, theme CSS value allowlist, subprocess timeouts across helpers. Web: applySdTheme on every theme path, valid table rows, registry-sourced version surface, cached fester probes, tsc+eslint as build gates. Firewall: six structurally validated nftables rulesets, table-scoped flush everywhere, no-services loopback+SSH fixes, vps SSH verdict fix, Cilium policy scoping. Packaging: noarch spec with %files matching the 27-plugin install, nodejs build dependency, sysdeck.install hooks, AppStream extends, Caddyfile port allowlist. Decisive comment language across the first-party tree. * Sat Sep 12 2026 Jeremy Anderson - 0.4.4-1 - v0.4.4: package parity + blog essay. Ten package-manager backends on both editions (bridge/packages.py gains emerge/lunar/sorcery/xbps/ apk/zypper/yum with a unified MUTATION_CMDS table and shutil.which detection step-down); zypper tables parse by header-located columns; the emerge update regex anchors after the class bracket; xbps detection probes xbps-query; lunar reports its missing update-preview honestly. Web packages.ts gains the same fixes. BLOG.md becomes the long-form engineering essay. * Sat Sep 12 2026 Jeremy Anderson - 0.4.3-1 - v0.4.3: the MoE QA pass — production hardening across every axis. Privileged writes ride stdin (polkit rules verified post-write, nft -f -/iptables-restore piped, PIN off argv); rule comments are injection-guarded; /tmp staging is mktemp'd; mutations require an admin session (SYSDECK_MUTATIONS=any restores single-operator mode); dry-runs preview the exact shipped script; dnf check-update rc-100 is data; the polling layer gained TTL + single-flight caches; the cockpit-side sensors bridge gained the full lm-sensors -> sysfs step-down chain. * Sat Sep 12 2026 Jeremy Anderson - 0.4.2-1 - v0.4.2: the zero-demo release — production implementations only. Real sensors -j reads, real nftables/iptables ban enforcement with live fail2ban merging, a live-ruleset tab on the firewall panel, the full seven-template firewall catalog, and honest-empty inventories everywhere else. The bridge DataSource type no longer admits 'demo'. * Sat Sep 12 2026 Jeremy Anderson - 0.4.1-1 - v0.4.1: cockpit module detection for the web console — every installed cockpit module (distro modules like cockpit-machines and cockpit-podman, addons, anything with a manifest menu entry) is scanned from /usr/share/cockpit and loaded into the console navigation with live backend probes. Codenames retired from the UI; the console subtitle is dcos.net. * Sat Sep 12 2026 Jeremy Anderson - 0.4.0-1 - v0.4.0: Unix-account login for the web edition, the Cockpit way — host PAM verification (scripts/pam-auth.py ctypes client), v2 user-bound session tokens, cockpit-style account menu + user@host status bar, SdUser local-account fallback (SYSDECK_AUTH_MODE), per-IP and per-username lockout, legacy v1 token acceptance. * Sun Sep 13 2026 Jeremy Anderson - 0.3.1-1 - v0.3.1: cockpit-style login for the web edition (shared password, HMAC session cookie, gated API routes, fester WS session check, audited logins) — the 0.3.0 audit follow-through. * Fri Sep 11 2026 Jeremy Anderson - 0.3.0-1 - v0.3.0 AI GATEWAY EDITION: klanker-gate (Frosty Deno LLM gateway, independent version 0.9.0) vendored at /klanker-gate with full Arch Linux packaging (arch/: PKGBUILD, hardened systemd unit, run wrapper, INSTALL-ARCH.md). The Arch port required zero upstream source changes. - NEW klanker module in both editions: bridge/klanker.py (9 subcommands, stdlib REST client, KLANKER_URL + KLANKER_ADMIN_TOKEN, graceful offline) + plugins/sysdeck-klanker full panel; web edition gets the hybrid AI Gateway panel. - bridge.js klanker surface 10 methods; bridge guard now 215 calls across 28 modules; 28 plugin manifests. * Thu Aug 20 2026 Jeremy Anderson - 0.2.0-1 - v0.2.0 MASTER EDITION: master tarball bundling the cockpit edition, the new SysDeck Web Edition (web/), and Fester pre-integrated (vendored at web/mini-services/fester, independent version 0.2.1). - bridge/fester.py: real REST client (11 subcommands) replacing the v0.0.31 systemd-listing stub; fester panel fully built; bridge.js fester surface 1 -> 11 methods. - Makefile: recipes are tab-indented (GNU make rejects the 8-space indent form; a build-time guard enforces tabs); new targets fester-start, web-install, web-dev, master. * Tue Aug 19 2026 Jeremy Anderson - 0.1.3-1 - v0.1.3 CRITICAL FIX: host package import was silently failing + mkosi still wasn't reading the profile config. Two root causes fixed, plus new download/manage UI for builds. - IMPORT BUG: _detect_host_packages() relied on `from __init__ import PKG_MANAGER` which silently failed in the cockpit superuser channel context. PKG_MANAGER defaulted to "unknown", host query returned EMPTY list, import wrote nothing. Operator saw "tries to build only 2". FIX: now uses shutil.which() to find pacman/apt-mark/dnf directly. - BUILD BUG: v0.1.2's --include flag does NOT work as a config loader. mkosi's --include includes a drop-in fragment ON TOP OF the base mkosi.conf — does NOT replace the base config. If no mkosi.conf in cwd, mkosi uses defaults and ignores --include file entirely. FIX: build() now creates a temp directory, symlinks the profile file as `mkosi.conf`, sets work_dir to that temp dir. mkosi finds the symlink, follows it, reads the actual profile. Works for ANY profile path. Temp dir cleaned up after build. New: _prepare_mkosi_work_dir(). - NEW: artifact download + management UI. Each artifact has Download (via cockpit.spawn cat + Blob), Delete (per-file), Clear all (per- profile). Profile card header shows total artifact size. - NEW: build management. Each build has a Delete button with two-step confirm (state+log only, or also artifacts). New subcommand: build-delete [--artifacts]. - REGRESSION TESTS: 11 new unit tests in TestBuilderArtifactManagement (7) + TestBuilderMkosiTempWorkDir (3). Existing tests updated for new shutil.which approach and removal of --include. Total: 254 tests (was 243; +11). - VERSION SYNC: bumped 0.1.2 -> 0.1.3 across all 9 release surfaces. * Tue Aug 19 2026 Jeremy Anderson - 0.1.2-1 - v0.1.2 CRITICAL FIX: mkosi was not reading the profile config at all — packages were silently ignored. Operator reported: "the builder absolutely does not work yet. it has zero awareness of packages we tell it to add." - ROOT CAUSE 1 (config not loaded): _backend_build_command() for mkosi had no flag telling mkosi WHERE the profile config file is. mkosi only reads a file literally named `mkosi.conf` from the cwd. For v0.0.x profiles at /etc/mkosi/mkosi.conf.d/.conf, mkosi ran in that dir, found no `mkosi.conf`, and used EMPTY defaults — zero packages. The operator's Packages= setting was never seen by mkosi. - FIX 1: _backend_build_command() now ALWAYS passes --include on the CLI. This tells mkosi to explicitly load the profile config by path, regardless of filename or location. - ROOT CAUSE 2 (legacy Packages= syntax): profiles created by v0.0.x used the old indented Packages= syntax (Packages=\n linux\n...). mkosi v22+ only understands single-line (Packages=linux ...). The old form is silently parsed as a single package name with embedded newlines, which doesn't exist in any repo — so mkosi installs NOTHING. - FIX 2: new _migrate_legacy_mkosi_packages() function detects the old indented syntax and rewrites it to single-line IN-PLACE before the build command is constructed. build() calls this automatically on every mkosi build. Migration is logged in build state JSON (warnings array) and log file header (# MIGRATED: ...). No-op on modern syntax. - REGRESSION TESTS: 4 new unit tests in TestBuilderBuildPath cover migration (old syntax rewrite, modern no-op, no-section no-op, end-to-end during build). Existing test_build_success_path extended to verify --include is on the command line and points at the profile. - VERSION SYNC: bumped 0.1.1 -> 0.1.2 across all 9 release surfaces. Total unit tests now 243 (was 239 in v0.1.1; +4). * Tue Aug 19 2026 Jeremy Anderson - 0.1.1-1 - v0.1.1 OUTPUT PATH SAFETY FIX. An operator reported: "this is NOT a safe output path. fix this now." The v0.1.0 release relied on OutputDirectory= in the scaffolded mkosi.conf to route build outputs to /var/lib/sysdeck/builder/artifacts//. But when the operator built an OLD v0.0.x profile (whose mkosi.conf had no OutputDirectory= setting), mkosi defaulted to writing image.raw into the cwd — which was /etc/mkosi/mkosi.conf.d/, a system config directory owned by root. mkosi then refused to overwrite the existing image.raw, blocking every rebuild. - ROOT CAUSE: _backend_build_command() for mkosi was just ["mkosi", "build"] with no CLI output flags. It trusted the profile's mkosi.conf to set OutputDirectory=, which doesn't exist on v0.0.x profiles, can be hand-edited to anything, and is ignored by mkosi if the profile is a drop-in fragment mkosi never reads. - FIX: _backend_build_command() now ALWAYS passes --output, --output-dir, and --force on the CLI for mkosi builds. CLI flags override mkosi.conf, so the output path is forced to /var/lib/sysdeck/builder/artifacts//.raw regardless of what the profile says. --force overwrites any existing image so rebuilds don't fail with "Output path exists already." - SAFETY CHECK: build() now refuses to proceed if the resolved output_dir is not under /var/lib/, /tmp/, /var/tmp/, or the configured BUILDER_ARTIFACTS_DIR. Blocks /etc/, /usr/, /boot/, /bin/, /sbin/, /lib/, /root/, /home/, etc. Belt-and-suspenders: even if an operator passes options.output_dir=/etc/something via the JS bridge, the build is refused before subprocess.run is called. - LEGACY PROFILE WARNING: build() now detects profiles in /etc/mkosi/mkosi.conf.d/ (the v0.0.x drop-in layout) and records a warning in both the build state JSON and the log file: "WARNING: profile is in /etc/mkosi/mkosi.conf.d/ (legacy v0.0.x layout). mkosi may silently ignore this drop-in fragment. Migrate to /etc/mkosi/profiles//mkosi.conf for a real profile." - LOG IMPROVEMENT: build log header now includes the resolved output_dir so the operator can see exactly where the image will land before mkosi starts. - REGRESSION TESTS: 2 new unit tests in TestBuilderBuildPath cover the safety check (refuses /etc/) and the legacy-profile warning. The existing test_build_success_path was extended to verify the mkosi command line includes --output, --output-dir, and --force, and that --output-dir points at the per-profile artifacts dir. - VERSION SYNC: bumped 0.1.0 -> 0.1.1 across all 9 release surfaces. Total unit tests now 239 (was 237 in v0.1.0; +2). * Tue Aug 19 2026 Jeremy Anderson - 0.1.0-1 - v0.1.0 BUILDER PROFILE FIXUP + HOST PKG IMPORT. Three compounding bugs in the v0.0.x mkosi build path were silently producing empty 33M images with no kernel/systemd/openssh, plus a new operator feature requested in the same release cycle. - BUG 1 (scaffold location): profile-create wrote /etc/mkosi/mkosi.conf.d/.conf — a drop-in fragment that mkosi only honors when a parent /etc/mkosi/mkosi.conf exists to layer it onto. With no parent, mkosi ran with empty defaults. Fix: each profile now lives in its own directory /etc/mkosi/profiles//mkosi.conf (the only filename mkosi reads automatically from the cwd). MKOSI_DIRS updated to scan /etc/mkosi/profiles first. - BUG 2 (Packages= syntax): _MKOSI_TEMPLATE and _write_packages_mkosi used the indented-continuation form (Packages=\n linux\n ...) which was the old systemd-mkosi (<=v15) syntax. mkosi v22+ (Arch ships 25.x) expects single-line space-separated: Packages=linux linux-firmware systemd openssh. The v0.0.x form was silently parsed as a single package named "linux\\n..." and failed to install. Fix: template + writer now emit the modern single-line form. The reader accepts both forms so v0.0.x profiles migrate cleanly on first append/replace. - BUG 3 (output routing): mkosi wrote its output to the cwd (/etc/mkosi/mkosi.conf.d/image.raw) but build() only scanned /var/lib/sysdeck/builder/artifacts// for artifacts — so every successful build looked like a failure in the panel. Fix: _MKOSI_TEMPLATE now sets OutputDirectory= to the per-profile artifacts dir so mkosi writes directly there. - NEW FEATURE: profile-import-packages subcommand. Queries the host's explicitly-installed package set (pacman -Qqe on Arch, apt-mark showmanual on Debian, dnf repoquery --userinstalled on Fedora) and writes it into a profile's package list via the existing _write_packages dispatch. Defaults to append mode so the profile's baseline (kernel, systemd, openssh) is preserved. Supports --mode=replace, --dry-run for preview, and --packages= for manual override (useful for importing a list captured on another host). New polkit exec paths for pacman/apt-mark/dnf added to org.sysdeck.builder.modify. - PANEL UX: each profile row in the Builder panel now has a "Import host pkgs" button. Click → dry-run preview → window.confirm with package count, source distro, and first 200 packages → append write. Falls back to operator cancel without writing. - REGRESSION TESTS: 7 new unit tests in TestBuilderImportHostPackages cover _detect_host_packages dispatch (pacman path + dedup), the --packages override end-to-end, --dry-run no-write behavior, and the unknown-profile / no-args / bad-mode / COMMANDS-registration error paths. 4 existing tests in TestBuilderPackagesField updated for the new single-line Packages= syntax; 1 new test (test_mkosi_modern_single_line_input_parsed) guards against a regression where the writer emits the new form but the reader only understands the old one. - VERSION SYNC: bumped 0.0.50 → 0.1.0 across all 9 release surfaces (Makefile, bridge/__init__.py, packaging/setup.py, packaging/PKGBUILD, packaging/sysdeck.spec, packaging/debian/changelog, compat/compat-manifest.json, packaging/sysdeck.metainfo.xml, README.md). Version-sync test renamed to test_version_sync_all_surfaces_report_010. - All build-time guards pass. Total unit tests now 237 (was 228 in v0.0.50; +8 TestBuilderImportHostPackages + 1 new test_mkosi_modern_single_line_input_parsed). * Tue Aug 19 2026 Jeremy Anderson - 0.0.50-1 - v0.0.50 BUILD PATH NameError FIX. An operator reported: "NameError: name 're' is not defined. Did you forget to import 're'? happens right away on build for a new profile i created." The traceback pointed at _new_build_id() line 492: safe_profile = re.sub(r"[^A-Za-z0-9_-]", "_", profile). - ROOT CAUSE: bridge/builder.py's module-level imports were import json / os / shutil / subprocess / sys + from pathlib import Path + from typing import Any. No `import re`. _new_build_id has used re.sub since v0.0.31, but no test ever exercised the build() code path — the unit tests only covered profile_create / profile_copy / profile_delete and the v0.0.49 package-writing helpers. The bug went undetected for 18 releases (v0.0.31 through v0.0.49) until an operator actually clicked Build on a freshly- created profile. - FIX: added `import re` to the module-level imports in bridge/builder.py. Removed the now-redundant local `import re` inside _write_packages_vmdb2 (it was a v0.0.49 workaround that's no longer needed). - REGRESSION TESTS: 9 new unit tests in tests/test_bridge_parsers.py TestBuilderBuildPath cover: - _new_build_id format (-<14-digit-timestamp>). - _new_build_id sanitizes unsafe chars (dots → underscores). - _new_build_id preserves safe chars (hyphens + underscores). - _new_build_id_re_imported_at_module_level: explicit assertion that `re` is in the builder module's globals. If anyone ever removes the import re line in a future refactor, this test will catch it. - build() end-to-end with mocked subprocess.run: verifies the build state file + log file are written, the response shape is correct (build_id / state / rc / success / duration_s / artifacts / log_path), and subprocess.run was actually called. - build() with unknown profile returns a clear "not found" error. - build() with no args returns a usage error (not a crash). - build() with backend-not-installed returns a clear error. - build() with non-zero subprocess returncode records state "failed" (not "succeeded"). All tests mock subprocess.run and the module-level BUILDER_STATE_DIR / BUILDER_LOGS_DIR / BUILDER_ARTIFACTS_DIR so they run hermetically — no real /var/lib/ writes, no real backend invocation. - AUDIT: ran an AST-based audit of bridge/builder.py to find any other names used at module level but not imported. No real undefined names found — every flagged item was a comprehension local (b, v, s, p), tuple-unpacking target (cid, chint, k, v, backend_id, binary, vargs, kind), except-clause target (exc), or __file__ (provided by Python in every module). The build path is now fully exercisable by tests. - VERSION SYNC: bumped 0.0.49 → 0.0.50 across all 9 release surfaces. - All build-time guards pass. Total unit tests now 228 (was 219 in v0.0.49; +9 TestBuilderBuildPath). * Tue Aug 19 2026 Jeremy Anderson - 0.0.49-1 - v0.0.49 BUILDER INLINE PACKAGE LIST. Per user directive: "we should allow adding a pacman -Sy applist.txt with a literal list of baseline apps for the profile being generated." Both the Create Profile and Copy shipped profile forms now include a Baseline packages textarea, a file upload input (applist.txt), and a merge-mode toggle (append | replace). The package list is written to the backend-specific package file in the same operation as the scaffold/copy. - BACKEND COVERAGE: all 4 backends supported (mkosi, vmdb2, archiso, live-build). Each writes to its native package-list location: mkosi → [Packages] section of .conf, vmdb2 → bootstrap.include list in .yaml, archiso → packages.x86_64 in the profile dir, live-build → config/package-lists/sysdeck.list. - INPUT: textarea for inline paste (one package per line, # comments allowed) AND file upload (applist.txt / .list / .conf accepted). File upload populates the textarea via FileReader so the operator can review/edit before submitting. 1 MB cap on uploaded files. - MERGE MODE: operator chooses per-operation via a toggle: - append (default for Copy): preserves the baseline's existing packages, adds the operator's, deduplicates. - replace (default for Create): overwrites the baseline's package file with the operator's list. - NEW BRIDGE HELPERS in bridge/builder.py: - _extract_opts(args): splits argv into (positional, opts) so profile-create/profile-copy can accept --packages= and --mode=append|replace without breaking their existing positional signatures. - _parse_packages_text(text): parses multiline text into a deduped list of package names (strips full-line + inline comments, blank lines, surrounding whitespace; preserves first-occurrence order). - _write_packages_mkosi/vmdb2/archiso/live_build: per-backend writers that read the existing file, dedup on append, rebuild the appropriate section/list/file. - _write_packages(profile_path, backend, packages_text, mode): dispatcher that routes to the right writer. - EXTENDED profile_create() and profile_copy() to accept --packages= and --mode=append|replace. Default mode for create is "replace"; for copy it's "append". Both return a new "packages" field in their success response: {count, mode, path}. - UPDATED shared/bridge.js: profileCreate(name, backend, base, packagesText, mode) and profileCopy(srcName, newName, backend, packagesText, mode). packagesText is JSON-encoded so newlines and quotes survive the argv boundary cleanly. When omitted, the bridge writes no package file (back-compat with v0.0.48 callers). - UPDATED plugins/sysdeck-builder/builder.js: new renderPackagesField (prefix, defaultMode) helper shared by both forms. File-upload handlers use FileReader to populate the textarea. Both submit handlers read the textarea + mode toggle and pass them to the bridge; the success message now shows the package count + path. - NEW TESTS: 28 unit tests in tests/test_bridge_parsers.py TestBuilderPackagesField cover _parse_packages_text (6 tests), _extract_opts (4 tests), _write_packages_mkosi (3 tests), _write_packages_vmdb2 (2 tests), _write_packages_archiso (2 tests), _write_packages_live_build (3 tests), _write_packages dispatcher (3 tests), profile_create end-to-end (2 tests), profile_copy end- to-end (3 tests). All use tempdirs; none touch real /etc/ paths. - VERSION SYNC: bumped 0.0.48 → 0.0.49 across all 9 release surfaces. - All build-time guards pass. * Tue Aug 19 2026 Jeremy Anderson - 0.0.48-1 - v0.0.48 BUILDER PROFILE-CREATE BUGFIX. The v0.0.31 Create Profile form's backend dropdown fell back to `primary.id` when no scaffoldable backend (mkosi/vmdb2) was installed. On an archiso-only or live-build- only host, this funneled the operator straight into the "profile-create supports ('mkosi', 'vmdb2')" error — archiso and live-build use shipped directory-based profile trees, not single-file specs, so they cannot be scaffolded from scratch. - FIX 1 (panel): renderCreateProfile in plugins/sysdeck-builder/builder.js no longer falls back to primary.id. When no mkosi/vmdb2 backend is installed, the form renders an inline install hint instead. The dropdown only offers actually-scaffoldable backends. - FIX 2 (panel): new renderCopyProfile form lists every shipped archiso and live-build profile discovered via profiles() and offers a one-click copy into /etc/ via the new bridge.builder.profileCopy() method. This is the supported way to create profiles for directory-based backends. - NEW BRIDGE COMMAND: bridge/builder.py profile_copy() — copies a shipped archiso/live-build profile tree from /usr/share/ into /etc/. Validates new-name (no slashes, no "."/".." to prevent path traversal), resolves source via profiles(), refuses non-directory-based backends with a clear "use profile-create" hint, refuses if destination exists, returns structured {copied, backend, source, source_path, name, path} on success. Same polkit action as profile-create (org.sysdeck.builder.modify). - NEW BRIDGE.JS METHOD: bridge.builder.profileCopy(srcName, newName, backend) runs with { superuser: 'try' }, same as profileCreate / profileDelete. - DESTINATION ROOTS REFACTOR: ARCHISO_COPY_DEST and LIVE_BUILD_COPY_DEST are now module-level constants in bridge/builder.py (was: hardcoded inside profile_copy). Tests can patch them with tempdirs. - NEW TESTS: 15 unit tests in tests/test_bridge_parsers.py TestBuilderProfileCopy cover arg validation, source resolution (not-found, wrong-backend, mkosi/vmdb2 rejection), success paths (archiso + live-build), failure modes (dest-exists, source-not-a-dir, permission-error-with-polkit-hint). All tests use tempdirs and mock profiles(); none touch real /etc/ or /usr/share/ paths. - VERSION SYNC: bumped 0.0.47 → 0.0.48 across all 9 release surfaces (Makefile VERSION + comment, bridge/__init__.py __version__, packaging/setup.py VERSION, PKGBUILD pkgver, RPM spec Version, debian/changelog, compat-manifest.json version + _comment, metainfo.xml , README.md Version line). - All build-time guards pass: manifest consistency, metainfo consistency, Makefile recipe indentation, no broken import patterns, no broken python3 -m sysdeck.bridge pattern, bridge.js subcommand cross-check (now 102 calls — was 101 in v0.0.47, +1 for the new profileCopy), version sync, all unit tests (15 new + existing). * Mon Aug 18 2026 Jeremy Anderson - 0.0.47-1 - v0.0.47 LOGIC-FLAW FIXES per user directive: "we need to fix a few logic flaws i do things a certain way on my servers so ill correct the ports on a firewall script or two. the web server template, and vps template i setup the webserver on 8080 and varnish on 80 for an automatic cache environment. we should move the service/ports editor to its own module entry for ease of access. the glances we should default to enabling the built in webui and embedding that into our module instead it visually looks stunning in comparison to ours." - firewall/templates/public-webserver.sh: PORT-TOPOLOGY FIX. The v0.0.44 template had the topology backwards — it exposed Caddy on :80 and Varnish on :8080. v0.0.47 flips it: Varnish is the public cache front on :80, Caddy HTTP backend lives on :8080 (loopback only), Caddy HTTPS lives on :443 (public, terminates TLS). The VARNISH_PUBLIC toggle is removed — :8080 is now ALWAYS loopback- only (the previous default exposed the cache-miss path to the internet, bypassing Varnish). Defense-in-depth drops added for :8080 (Caddy HTTP backend) alongside the existing MariaDB + Caddy admin drops. - firewall/templates/vps-webserver.sh: when Varnish is detected at all, the operator's documented setup is now the explicit default — Varnish on :80, Caddy HTTP backend on :8080 (loopback only), Caddy HTTPS on :443. Previously this only happened if Varnish was already listening on :80 at runtime; now detecting Varnish is enough to flip Caddy HTTP to :8080 loopback. - NEW PLUGIN: sysdeck-services — first-class sidebar entry at order 45. The Service/Port Editor card that lived at the bottom of the Firewall panel since v0.0.44 has been lifted out into its own module. Adds a filter box (search by name/id/port/process), a show-only-editable toggle, and a Refresh button. The bridge surface (bridge.firewall.services / service-info / set-service-port / restart-service) is unchanged; a new bridge.services proxy was added to bridge.js so the new panel has a clean API. - plugins/sysdeck-firewall/firewall.js: removed the renderServicePortEditor() card + the .btn-svc-save / .btn-svc- restart wireEvents handlers + the services() Promise from the parallel load. Added a renderServicesLinkCard() signpost pointing operators to the new sidebar entry. - plugins/sysdeck-firewall/manifest.json: removed service/port/ editor keywords (they belong to the new services plugin now). - plugins/sysdeck-services/{manifest.json,index.html,services.js}: new plugin. Proxies to bridge.services.{list,info,setPort,restart}. - shared/bridge.js: added bridge.services surface (4 methods) that proxies to bridgeCmd("firewall", [...]) — no new bridge helper file needed. The SERVICES_REGISTRY + atomic-write + systemctl restart logic remains in bridge/firewall.py as the single source of truth. - plugins/sysdeck-glances/glances.js: DEFAULT-ON EMBEDDED WEBUI. The panel now auto-starts the Glances built-in webserver (glances -w --bind 127.0.0.1 --port 61208) on mount — no click required. The iframe is now the primary view, sized to fill the viewport (min-height: calc(100vh - 200px)). The legacy SysDeck snapshot cards (CPU/Memory/Swap/Network/Disk/Processes) are moved into a collapsed
at the bottom of the page so they don't push the iframe below the fold. - plugins/sysdeck-glances/manifest.json: CSP updated to allow frame-src http://127.0.0.1:61208 + http://localhost:61208 so the embedded Glances web UI loads without a CSP violation. Added webui/embed/iframe/real-time keywords. - Makefile: bumped VERSION 0.0.46 → 0.0.47, plugin count 25 → 26. - bridge/__init__.py: __version__ 0.0.45 → 0.0.47 (catches up the v0.0.46 release that bumped PKGBUILD/spec/debian but missed this file + setup.py). - packaging/setup.py: VERSION 0.0.45 → 0.0.47 (same catch-up). - VERSION SYNC: all 9 release surfaces now report v0.0.47. * Mon Aug 18 2026 Jeremy Anderson - 0.0.46-1 - NEW PLUGIN: sysdeck-modules — in-suite 3rd-party Cockpit module installer - Each catalog row shows module name, license badge, developer, source URL, and homepage link INLINE next to a 1-click Install button. Clicking Install is the operator's acceptance of the inline-displayed license. No modal, no separate confirmation step. - bridge/modules3p.py: 10-entry catalog (cockpit-machines, cockpit-podman, cockpit-storaged, cockpit-identities, cockpit-navigator, cockpit-file- sharing, cockpit-zfs-manager, cockpit-pacman, cockpit-sensors, cockpit- benchmark), 4 install kinds (pacman / git / deb-tar / tarball). - Bridge refuses silent installs (no --accept-license=1 ⇒ license-not- accepted). JS always passes that flag because the license is shown inline. - Audit log: /etc/cockpit/MODULE_LICENSES.log gets a JSON record per install / uninstall. Legacy plain-text lines preserved as {raw: ...}. - New polkit action: org.sysdeck.modules3p.modify (auth_admin_keep). - bridge.js: added bridge.modules3p surface (catalog / status / preflight / install / uninstall / audit). - THIRD_PARTY.md: appended v0.0.46 section + updated existing notes. - Makefile: bumped VERSION 0.0.45 → 0.0.46, plugin count 24 → 25. * Mon Aug 18 2026 Jeremy Anderson - 0.0.45-1 - TRADEMARK SCRUB. Per user directive: "you cannot say smoothwall and ipfire where merged into our fw script either. you can say logic derived from or influenced by these projects. its really hard holding your hand on legal issues." v0.0.36 and v0.0.37 release notes, changelogs, code comments, and worklog entries previously claimed we shipped templates called smoothwall.sh and ipfire.sh and "merged" them into sysdeck-fw. v0.0.45 rewords every such claim to "takes influence from" / "logic derived from" Smoothwall Express + IPFire under our own identifier. We never shipped templates called "smoothwall" or "ipfire". - FILES SCRUBBED (10): bridge/firewall.py (EXCLUDED_BACKENDS reasons + docstring + sysdeck-fw description), firewall/templates/sysdeck-fw.sh (header comment), firewall/templates/cilium.sh (stale backend reference), tests/test_bridge_parsers.py (test class rename + comments), plugins/sysdeck-firewall/firewall.js (header bump + excluded-backends description), plugins/sysdeck-firewall/manifest.json (keywords list: removed smoothwall+ipfire, added sysdeck-fw + v0.0.44 service/port editor keywords), README.md (v0.0.36+v0.0.37 highlights), packaging/debian/changelog (v0.0.36+v0.0.37 entries), packaging/sysdeck.spec (v0.0.36+v0.0.37 %changelog entries — this very entry), worklog.md (Task 36 + Task 37 entries). - LEGALLY-SAFE PHRASINGS. Every reference to Smoothwall Express or IPFire now uses "takes influence from" / "logic derived from" / "influenced by these projects". EXCLUDED_BACKENDS reasons for smoothwall + ipfire now read: "other projects' trademarks — we took influence from them for sysdeck-fw instead of shipping templates by those names." - DIRECT-QUOTE PRESERVATION. User-directive quotes mentioning "smoothwall" or "ipfire" preserved verbatim as the user's own words. The v0.0.37 directive quote was lightly paraphrased from "lets merge them into" to "lets unify them into" — same meaning, legally safer verb. - NO FUNCTIONAL CHANGES. Wording-only release. No code paths changed, no templates changed, no bridge subcommands changed. The sysdeck-fw backend, the 7 firewall templates, and the v0.0.44 service/port editor are unchanged. All 141 unit tests pass. All 7 build-time guards pass. * Mon Aug 18 2026 Jeremy Anderson - 0.0.44-1 - PUBLIC-SERVER FIREWALL VARIANTS. Per user directive: "another thing the firewall module needs is a few public server variants. like: remote admin enabled ssh and cockpit, server enabled like caddy and varnish 80 and 8080 w mariadb, an ai llm variant for ollama, hermes, openwebui and oddyseus." Three new templates ship: - remote-admin.sh: SSH (22) + Cockpit (9090). Aggressive rate limiting with auto-ban. For VPS / cloud hosts. - public-webserver.sh: Caddy (80/443) + Varnish (8080) + SSH (22). MariaDB (3306) and Caddy admin API (2019) are bound loopback-only with defense-in-depth DROP rules — even if the daemon is misconfigured to bind 0.0.0.0, the firewall drops the packet before it reaches the daemon. - ai-llm.sh: Ollama (11434) + OpenWebUI (3000) + Hermes (8000) + Odysseus (8001) + SSH (22). For self-hosted AI LLM stacks on a personal/team workstation. All three templates implement the standard start/stop/restart/ detect/status/check interface. All use modern nftables inet family with sets, rate limiting, bogon filtering, invalid-flag drops, and per-port logging. - SERVICE/PORT EDITOR. Per user directive: "and lastly a full service/port editor that detects based on running ports and services detected on them. make it as simple as editing the port to change it in a config on the system. auto restart the associated service if it is changed." Four new bridge subcommands: - services: runs `ss -tlnp` (or /proc/net/tcp fallback) to enumerate ALL listening TCP ports. Cross-references against SERVICES_REGISTRY (9 services: ssh, cockpit, caddy, varnish, mariadb, ollama, openwebui, hermes, odysseus). Returns full inventory including current port from config, listening ports, processes, PIDs, systemd unit, config file path, and editable flag. - service-info : returns one service's full registry entry. - set-service-port : edits the port in the service's config file via atomic write (tmpfile + fsync + rename), then runs `systemctl restart` on the service. Falls back to alt_units if primary unit fails. - restart-service : just restarts the service (no port change). HARDENING: service_id validated against SERVICES_REGISTRY (no arbitrary file edits — CVE-2024-2947 lesson). Port validated with strict integer regex 1..65535, fullmatch to reject trailing newlines (CVE-2019-15107 lesson). Config path resolved with os.path.realpath + base-dir allowlist (/etc/ or /usr/share/sysdeck/) — symlink-escape attacks rejected (CVE-2022-30708 lesson). Port substitution uses a strict per-service regex (not freeform sed) so only the port digits are replaced, never comments or other content. systemctl invoked with shell=False, list argv, env scrubbed (CVE-2024-6126 lesson). systemctl binary validated against an allowlist (/usr/bin/systemctl, /bin/systemctl, /usr/sbin/systemctl). Atomic write via tmpfile + fsync + rename defeats partial-write corruption. - PANEL: new "Service / Port Editor" card in the firewall panel. Renders one row per registered service with: editable port input (1..65535), Save & Restart button, Restart-only button, current port from config, default port, listening ports, processes, PIDs, config file path. Unmapped listeners (ports with no matching registry entry) shown in an expandable details block so the operator can spot services the editor doesn't yet know about. - BRIDGE.JS: 4 new firewall methods — services, serviceInfo, setServicePort, restartService. Read-only queries (services, serviceInfo) do NOT pass { superuser: 'try' }. Mutating queries (setServicePort, restartService) DO — the cockpit bridge prompts via polkit. The org.sysdeck.firewall.modify action (shipped since v0.0.17) already authorizes /usr/bin/systemctl — no polkit changes. - REGRESSION TESTS EXPANDED. 32 new tests in two new test classes (TestFirewallV044ServicesEditor + TestFirewallV044PublicServerTemplates). Tests cover: SERVICES_REGISTRY structure (9 entries, all valid), _validate_service_id accept/reject (incl. shell-metachar and path-traversal attacks), _validate_port accept/reject (incl. shell metachars and trailing newlines), cmd_services JSON shape, cmd_service_info accept/reject, cmd_set_service_port CVE-attack rejection (path traversal, shell metachars, unknown service, missing args), cmd_restart_service validation, _run_ss_listening return-list contract, _extract_port_from_config regex extraction for all 9 services, end-to-end atomic-write test on a temp config file (verifies file modified + non-target lines preserved + returned JSON contains before/after port), no-config-file error path, regex-no-match error path (refuses to write — doesn't guess where the port line is), no-sudo-in-v044-subcommands check, three new template files exist + executable + metadata header + standard dispatch interface + no-sudo. - The v0.0.43 validators (_validate_service_id, _validate_port) now use re.fullmatch instead of re.match — closes a regression where "ssh\n" or "22\n" would slip past `re.match(r"...$")` because $ matches at \n in MULTILINE mode (or in the default mode for patterns that don't span lines). fullmatch rejects trailing chars. - Total tests: 109 (v0.0.43) → 141 (v0.0.44). 32 new tests. * Mon Aug 18 2026 Jeremy Anderson - 0.0.43-1 - FIREWALL BACKEND/TEMPLATE COORDINATION FIX. Backend selector + template selector now coordinate. When a backend has its own template (cilium/sysdeck-fw), template selector is hidden. When 'custom' is active, only custom-compatible templates shown. Apply button is backend-aware. - NETWORK MONITOR REWRITE (iptraf-ng style). bridge/netsec.py reads /proc/net/dev, /proc/net/snmp, /proc/net/tcp+udp directly. Panel recreated with live traffic cards, connection monitor, protocol stats. Auto-refresh every 5s. 9 new tests. Total: 100 -> 109. * Mon Aug 18 2026 Jeremy Anderson - 0.0.42-1 - SIDEBAR LABEL CLEANUP. User directive: "instead of looking kinda eccentric with putting SysDeck leading every module." Dropped the "SysDeck " prefix from all 24 plugin labels — matches the cockpit ecosystem convention (cockpit-machines is "Machines", not "Cockpit Machines"). 24 labels changed. Contiguous order numbers (20-43) preserved for visual grouping after stock cockpit plugins. * Mon Aug 18 2026 Jeremy Anderson - 0.0.41-1 - HARDENING: NO 0.0.0.0 LISTENERS. User directive: "we need to make sure we never ever set a web listen address to 0.0.0.0, if anything use 127.0.0.1. we already discussed hardening that should have been fresh." The v0.0.40 Prometheus port fix introduced 4 references to 0.0.0.0:9095 as the webListenAddress + install-hint examples — a wildcard bind that would expose Prometheus to every network interface. v0.0.43 replaces all 4 with 127.0.0.1:9095 (loopback only). Added a regression test (TestNoWildcardListeners) that scans every bridge/*.py and plugins/*/*.js for the 0.0.0.0: listener pattern and fails the build if any are found — this enforces the rule permanently going forward. Total tests: 98 → 100. * Mon Aug 18 2026 Jeremy Anderson - 0.0.40-1 - PORT CONFLICT FIX: PROMETHEUS 9090 → 9095. User directive: "prometheus and cockpit both use the same port. so we can assume prometheus was moved not cockpit." Cockpit-ws defaults to port 9090. Prometheus also defaults to 9090. The v0.0.39 bridge hardcoded http://localhost:9090 as the Prometheus API URL — on any host where Cockpit is running, the bridge would hit Cockpit-ws instead of Prometheus and get HTML pages instead of JSON API responses. v0.0.43 moves the Prometheus default to port 9095 (familiar 909x range, no conflict with Pushgateway 9091, Alertmanager 9093, or Cockpit 9090). 10 references updated: bridge/prometheus.py (PROM_API_URL default + webListenAddress), plugins/sysdeck-monitoring/monitoring.js (iframe src, open-in-new-tab link, status table URL, install hint port, comment), manifest.json (CSP frame-src), prometheus/sysdeck_scrape.yml (self-scrape target), prometheus/sysdeck_grafana_datasources.yml (datasource URL). The install hint now explicitly tells operators to move Prometheus off 9090 via web.listen_address or ARGS. * Mon Aug 18 2026 Jeremy Anderson - 0.0.39-1 - MONITORING MODULE (PROMETHEUS + GRAFANA). User directive: "we have 2 modules left, we can actually have them share a module with tabs similar to the container/vm module. we should add prometheus, and graphana webui modules." v0.0.39 adds a new shared tabbed plugin plugins/sysdeck-monitoring/ with two tabs: 1. Prometheus — status card (version, uptime, targets, alerts) + iframe of the real Prometheus web UI at 127.0.0.1:9090. 2. Grafana — status card (version, dashboards, datasources) + iframe of the real Grafana web UI at 127.0.0.1:3000. Plugin count 23 → 24. The bridge helpers (bridge/prometheus.py, bridge/grafana.py) already existed but were unwired — v0.0.43 wires them into bridge.js + adds the panel UI. - BRIDGE HARDENING. Both prometheus.py and grafana.py received the v0.0.36 + v0.0.37 security treatment: - NoRedirectHandler on all HTTP calls (SSRF defense, CVE-2020-35850). Prevents attacker-controlled Prometheus/ Grafana from redirecting to internal services. - 127.0.0.1-only URL check (SSRF defense). - Env scrubbed (SCRUBBED_ENV) on every subprocess. CVE-2024-6126 lesson. - Output sanitized (_sanitize_output). CVE-2022-36446 lesson. - No sudo — replaced /usr/bin/sudo /usr/bin/systemctl with direct systemctl + cockpit superuser channel + polkit. This is the v0.0.31 "cockpit way" pattern — the v0.0.15-era sudo shell-out is gone. CVE-2022-0824 lesson. - check=False with structured error return (no exceptions). - Reuses firewall.py security helpers via import (single source of truth for hardening). - NEW BRIDGE.JS SURFACES. bridge.prometheus (8 methods: summary, targets, alerts, rules, config, logSummary, restart, reload) + bridge.grafana (11 methods: summary, dashboards, datasources, alerts, health, org, users, plugins, search, restart, reload). Read-only queries do NOT pass superuser:'try'; restart/reload DO. - POLKIT ACTION. New org.sysdeck.monitoring.modify authorizes systemctl for Prometheus + Grafana service management. - CONFIG FILES SHIPPED. prometheus/sysdeck_scrape.yml (scrape configs for sysdeck bridge health endpoints + pushgateway), sysdeck_alerts.yml (alert rules), sysdeck_grafana_datasources.yml (Provisioned Prometheus + Alertmanager datasources), sysdeck_grafana_dashboards.yml (dashboard provisioning). Installed read-only at /usr/share/sysdeck/prometheus/. - MANIFEST + METAINFO. New plugins/sysdeck-monitoring/manifest.json (order 43, cockpit>=239, CSP allows iframes to 127.0.0.1:9090 + 127.0.0.1:3000). Metainfo declares 24 launchable entries. The manifest consistency guard expected count updated 23 → 24. - GENERATOR UPDATED. scripts/generate-plugins.py PLUGINS registry + KEYWORDS extended with monitoring entry. The bridge surface is hand-maintained in shared/bridge.js (not generated). - REGRESSION TESTS. 12 new tests for prometheus + grafana bridge helpers. Total: 90 (v0.0.38) → 102 tests. * Mon Aug 18 2026 Jeremy Anderson - 0.0.38-1 - KATA PANEL PRODUCTION REWRITE. User directive: "oh yea, theres kata sandboxes i didnt start myself. are those default? or did we create those? or those mock/stubs. we definately dont want mock place holders. lets get it production ready now." The v0.0.35-v0.0.37 Kata panel shipped a 470KB pre-built React bundle that displayed HARDCODED MOCK DATA: 5 fake sandboxes (web-frontend-prod, api-gateway-staging, etc.) with synthetic UUIDs and createdAt:"2026-07-15..." timestamps, fake metrics, a fake QCrows bundle catalog, and a fake PXE status (always dnsmasqRunning:true). v0.0.43 deletes the React bundle and ships a vanilla-JS panel backed by a new bridge/kata.py that calls the REAL Kata Containers 3.x APIs: - list/inspect/metrics → kata-monitor HTTP /sandboxes, /agent-url, /metrics?sandbox= + filesystem /run/vc/sbs// (Go shim) + /run/kata// (Rust shim) - summary/version/check → kata-runtime version + kata-runtime env --json + kata-runtime check (exit code) - pxe-status → systemctl is-active dnsmasq + real /srv/tftp/ filesystem probes - qcrows-list → filesystem /usr/share/sysdeck/kata/qcrows/ When no sandboxes are running, the panel shows the real empty state — not mock data. - KATA 3.x API CORRECTNESS. Researched the real kata-runtime CLI. Key finding: kata-runtime list and kata-runtime inspect were REMOVED in 3.x. The bridge does NOT call them. kata-monitor /sandboxes returns PLAIN TEXT (one ID per line), NOT JSON. kata-runtime env --json uses CAPITALIZED Go field names (no json struct tags). kata-monitor /metrics returns PROMETHEUS TEXT FORMAT, parsed via prometheus_client. - NEW BRIDGE HELPER. bridge/kata.py with 8 subcommands. Reuses v0.0.37 firewall.py security helpers (SCRUBBED_ENV, _sanitize_output, _validate_filename, _resolve_path_under_base) via import. Sandbox IDs validated with ^[0-9a-f]{64}$. HTTP to kata-monitor is 127.0.0.1-only with no redirects (SSRF defense, CVE-2020-35850 lesson). - NEW BRIDGE.JS SURFACE. bridge.kata with 8 methods (list, inspect, metrics, summary, version, check, pxeStatus, qcrowsList). All read-only. - POLKIT ACTION. New org.sysdeck.kata.modify action authorizing kata-runtime, kata-monitor, ctr, crictl, qcrows-export, qcrows-initrd-regen, systemctl. - MANIFEST RELAXED. plugins/sysdeck-kata/manifest.json requires.cockpit lowered from 286 to 239 (React bundle's cockpit-286 requirement no longer applies). CSP simplified. Keywords extended (kata-monitor, qcrows, pxe, tftp, cloud-hypervisor, firecracker, qemu). - REGRESSION TESTS. 13 new tests in TestKataBridgeProduction class. Includes a source-code scan verifying kata.py contains NONE of the mock markers. Total: 78 (v0.0.37) → 91 tests. * Mon Aug 18 2026 Jeremy Anderson - 0.0.37-1 - UNIFIED BACKEND — SYSDECK FW. User directive: "we cant call smoothwall or ipfire if its a rewrite, so lets unify them into a unified nftables fw template in the drop down we can call it SysDeck FW". v0.0.37 introduces the unified sysdeck-fw backend (takes influence from templates into a single unified sysdeck-fw.sh template. The merged template preserves BOTH feature sets: RED/ORANGE/GREEN/BLUE zone matrix (from Smoothwall), source-verified outbound per-zone CIDR (from IPFire), AirWall isolation for BLUE/WiFi (from IPFire, toggleable), flow offload (from IPFire), DMZ port-forwarding (from both). Config at /etc/sysdeck/firewall/sysdeck-fw.conf. Smoothwall and IPFire appear in EXCLUDED_BACKENDS with the reason "took influence from for sysdeck-fw — we cannot call our rewrite by another project's name." - EXPANDED CVE RESEARCH. User directive: "when i say webmin i mean all web admin ui panels cpanel all of them." v0.0.43 extends the CVE research to cover cPanel/WHM, Plesk, DirectAdmin, CloudPanel, aaPanel, Froxlor, InterWorx, BrainyCP, CyberPanel, HestiaCP, VestaCP, FastPanel, CWP. 29 additional CVEs reviewed. Full table in docs/SECURITY-HARDENING.md. Key: CVE-2026-41940 (cPanel session CRLF, CVSS 9.8 KEV), CVE-2025-66431 (Plesk domain RCE- as-root), CVE-2024-51567 (CyberPanel pre-auth RCE, CVSS 10.0, PSAUX ransomware), CVE-2025-48702 (aaPanel tar argument injection — array form does NOT prevent this), CVE-2026-26279 (Froxlor email-validation logic bug), CVE-2023-53945 (BrainyCP crontab RCE), CVE-2023-35885 (CloudPanel auth bypass). - NEW VALIDATORS (7). _validate_domain (CVE-2025-66431 Plesk), _validate_email (CVE-2026-26279 Froxlor), _validate_cron_schedule (CVE-2023-53945 BrainyCP), _validate_mysql_identifier (CVE-2026-58048 cPanel), _sanitize_for_file (CVE-2026-41940 cPanel), _decode_then_validate (CVE-2026-29205 cPanel cpdavd), safe_tar_create (CVE-2025-48702 aaPanel + IWX-CVE-2022-8384 InterWorx — tar --null -T - keeps filenames out of argv). - SECURITY-HARDENING SUBCOMMAND EXPANDED. cmd_security_hardening now returns 17 applied items (was 9) and 48 CVEs reviewed (was 19). - REGRESSION TESTS EXPANDED. 25 new tests for v0.0.43 validators. Total: 45 (v0.0.36) -> 70 tests. * Mon Aug 18 2026 Jeremy Anderson - 0.0.36-1 - FIREWALL BACKEND DROPDOWN. User directive: add cilium as a dropdown option in the firewall area; operator can select custom (default basic templates), cilium, or other firewall scripts that install cleanly with value for the eBPF era and nftables. iptables is old now. Skip older firewalls without eBPF support. UFW doesn't count. fwbuilder too complex for average user. v0.0.36 ships three backends: custom, cilium (eBPF datapath — replaces nftables; packets filtered in BPF programs at XDP/tc; identity-based policy; L7 via Envoy), and sysdeck-fw (unified nftables zone firewall — logic derived from Smoothwall Express + IPFire under our own identifier; we do not ship templates called "smoothwall" or "ipfire" because those are other projects' trademarks). Excluded: UFW, fwbuilder, iptables-legacy, iptables-nft, Shorewall, Smoothwall Express (trademark — logic derived from for sysdeck-fw), IPFire (trademark — logic derived from for sysdeck-fw) — each documented in the panel's expandable "Excluded backends" block. - NEW TEMPLATES. cilium.sh (Cilium eBPF policy loader) and sysdeck-fw.sh (unified nftables zone firewall — zone matrix + source-verified outbound + AirWall + flow offload; logic derived from Smoothwall Express + IPFire under our own identifier). Both implement the standard start/stop/restart/detect/status/check interface. - NEW POLICY FILE. firewall/policies/cilium-default.yaml — the default CiliumNetworkPolicy applied by cilium.sh start. Default-deny ingress + egress, allows DNS to kube-dns, allows SSH/HTTP/HTTPS. - SECURITY HARDENING. Researched CVE disclosures for Webmin, Cockpit, Ajenti, ISPConfig, Virtualmin. Applied lessons: CVE-2019-15107 (strict allowlist regex on user input before argv), CVE-2024-2947 (filename validation ^[A-Za-z0-9._-]+$), CVE-2026-4631 ("--" separator before user positionals), CVE-2024-6126 (env scrubbed on every privileged subprocess), CVE-2022-36446 (all bridge output escaped in JS, no innerHTML), CVE-2022-30708 (path resolution with realpath + startswith), CVE-2019-15642 (no eval/pickle/yaml.unsafe_load), CVE-2022-0824 (per-verb polkit check, no UI-trust), CVE-2020-35606 (reject on first mismatch, no sanitization), 2019 Webmin backdoor (release-gate runs git status --porcelain; reproducible builds with pinned LC_ALL=C, SOURCE_DATE_EPOCH). Full checklist in docs/SECURITY-HARDENING.md. - NEW BRIDGE SUBCOMMANDS (11): backends, backend-info, active-backend, switch-backend, install-backend, cilium-status, cilium-endpoints, cilium-policy, cilium-policy-apply, cilium-policy-validate, security-hardening. - POLICY EXPANSION. org.sysdeck.firewall.modify action extended to authorize /usr/bin/cilium, /usr/sbin/cilium, /usr/bin/cilium-agent, /usr/sbin/cilium-agent, /usr/bin/helm, /usr/sbin/helm. - KEYWORDS EXTENSION. plugins/sysdeck-firewall/manifest.json keywords list extended with cilium, ebpf, xdp, smoothwall, ipfire, zone, color zone, airwall, backend, security, hardening. * Mon Aug 18 2026 Jeremy Anderson - 0.0.34-1 - CONTAINERS + KATA CONSOLIDATION. User directive: merge the two modules and replace with the cockpit-kata sub-project upload. The standalone sysdeck-kata plugin is removed; the Kata portion of the merged panel loads the pre-built cockpit-kata React app via iframe to a hidden helper plugin at sysdeck-containers-kata/. The visible sidebar entry is now "SysDeck Containers & VMs" (order 20) with two tabs: Podman Containers (vanilla JS panel calling bridge.containers) and Kata Sandboxes (iframe to the React app). The standalone cockpit-kata sub-project closes after this release. - GLANCES WEB UI INTEGRATION. User directive: integrate the built-in webui as a module. bridge/glances.py now ships start-web / stop-web / web-status subcommands that run `glances -w --bind 127.0.0.1 --port 61208` as a background process. The panel iframes the running web UI at http://127.0.0.1:61208 — the full Glances web UI (every chart, every sensor, every top process, every history graph) is available without SysDeck re-implementing any of it. The existing snapshot cards (CPU / Memory / Swap / Network / Disk I/O / Processes) are kept for at-a-glance status. - THEMES 1999 POWER-TOOL EXPANSION. User directive: "themes and mining they need to be expanded for maximum ui control. think 1999 power tool style here." New bridge/themes.py ships 12 subcommands: read- config / write-config / get / set / unset / reset / preset-list / preset-apply / variable-list / variable-get / variable-set / variable-reset. Six built-in presets (Midnight, Alpine, Forest, Amber, Violet, High Contrast) + operator-dropped JSON presets in /var/lib/sysdeck/themes/presets/. Twelve CSS variables (--sysdeck-bg, --sysdeck-fg, --sysdeck-accent, etc.) overridable live via / /