Commit Graph
16 Commits
Author SHA1 Message Date
maggesandClaude Sonnet 5 993e187f59 Add container pause/resume/kill/rename + reusable context-menu component
First increment of the big WebUI feature-parity spec (see task list) —
row-level actions were about to run out of icon-button space, so this adds
a small anchored dropdown menu (app.js openContextMenu) for secondary
per-container actions instead of cramming more buttons into every row.

All four new actions verified live against the real podman API before
wiring up the UI (same discipline as the CSRF/pull/compose bugs found
earlier — field names and response shapes checked against the running
socket, not assumed from docs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 11:56:46 +00:00
maggesandClaude Sonnet 5 5b47b4cc0a Add catatonit/nftables/docker-compose packages, fix CSRF/streaming/storage bugs found by live testing
- Package #9-11: catatonit (pod infra init), nftables (netavark firewall
  backend), docker-compose (external compose provider for `podman compose`)
  — all vendored prebuilt binaries, versions.env pinned, propagated through
  build-packages.sh/release.sh/podman.plg/verify+update-packages.sh.
- Fix WebUI: every POST action was silently failing (empty response body)
  because Unraid's own CSRF protection was never satisfied — app.js now
  sends the page's csrf_token as X-CSRF-Token.
- Fix WebUI: PodmanClient::pullImage() assumed a single JSON response, but
  /images/pull actually streams newline-delimited JSON — every successful
  pull was throwing "Expected a JSON object/array response".
- Fix WebUI: compose.php's up/down status detection had the same
  single-JSON-vs-NDJSON bug for `podman compose ps`, plus stderr was
  corrupting the parse.
- Add cache-busting (?v=<mtime>) to Podman.page's script/style tags so a
  redeployed JS/CSS fix isn't served stale from browser cache.
- Add a reusable modal dialog (app.js openFormModal) replacing
  prompt()/alert() for New Volume/Network/Pull Image.
- Add host-path (bind-mount) support when creating a named volume.
- Add Create Container (image, name, network mode incl. custom networks,
  ports, volumes, env, restart policy, privileged, start-after-create),
  auto-pulling the image on first use since /containers/create doesn't.

All fixes verified live against a real podman system service and, where
reachable, via the actual WebUI over the real socket — not just unit-level.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 11:51:17 +00:00
maggesandClaude Sonnet 5 5944ddf722 Fix WebUI: podman_parse_time() rejected int timestamps from list endpoints
Lint / ShellCheck (push) Successful in 10s
Lint / Validate .plg XML (push) Successful in 9s
Lint / EditorConfig (push) Successful in 5s
Found by exercising the AJAX endpoints directly against a real running
podman service (php -d display_errors=1 -r '...containers.php...') —
containers.php's list action crashed with an uncaught TypeError the
moment a real container existed. podman's libpod API is inconsistent
about container/volume timestamp encoding: inspect-style endpoints
return RFC3339 strings, but list-style endpoints (containers/json,
volumes/json) return raw Unix-epoch integers for the same logical
field. podman_parse_time() only accepted ?string, so any list call
with a real container blew up outright rather than merely
mis-rendering. Widened it to string|int|null and handle both.

Verified: containers.php's list action now returns correct JSON for
real running/exited containers, including their createdAt timestamps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 23:12:04 +00:00
maggesandClaude Sonnet 5 51b7262b72 Fix five real bugs found by actually installing and running the plugin
Lint / ShellCheck (push) Successful in 10s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Successful in 4s
First live end-to-end install on real Unraid hardware (all 8 built
packages installed via upgradepkg, rc.podman started, containers
pulled/run/networked/port-mapped) — surfaced five genuine bugs no
amount of container-based CI testing could have caught, since none of
them exist inside the vbatts/slackware:15.0 build container:

1. rc.podman never created $PODMAN_LOG_DIR before redirecting the
   podman system service's output into it, so the service failed to
   even start ("No such file or directory"). Added it alongside the
   existing PODMAN_RUN_DIR mkdir.

2. config/storage.conf hardcoded a [storage] table, and
   podman-config.sh's `sync` step appended a second one at boot with
   the real graphroot/runroot — TOML forbids defining the same table
   twice. Removed the template's [storage] entirely; sync already
   generates the whole thing.

3. config/policy.json had a "_comment" pseudo-field for
   documentation, but containers/image's policy parser rejects any
   unknown top-level key outright. JSON has no comment syntax; moved
   the rationale into docs/ARCHITECTURE.md instead.

4. netavark >= 2.0 dropped its iptables firewall driver entirely
   (verified: passing "iptables" is flatly rejected) — nftables or
   firewalld are the only remaining options, and firewalld needs
   systemd/dbus, which Unraid has neither of. Set firewall_driver =
   "nftables" explicitly and documented that Unraid OS doesn't ship
   the `nft` binary this needs (a slackware64 nftables package works;
   not yet wired into the build/install pipeline — see follow-up).

5. Every container failed with "crun: pivot_root: Invalid argument".
   Root cause: Unraid's / is permanently the kernel's initial "rootfs"
   pseudo-filesystem (Unraid never pivots to a real one at boot — the
   whole OS runs from RAM), and pivot_root(2) unconditionally rejects
   that as the old root. This is not new: Docker/runc hits the exact
   same kernel restriction on this exact host and silently falls back
   to an MS_MOVE-based chroot; crun has no such fallback, only a
   --no-pivot flag with no config-file equivalent. Added
   plugin/sbin/crun-no-pivot.sh, a thin wrapper that scans crun's full
   argument list (podman puts global flags before the subcommand, so
   the subcommand isn't reliably $1) and injects --no-pivot right
   after create/run, and pointed containers.conf's crun runtime at it.
   Also fixed the podman.plg postinstall's chmod glob
   (`podman-*.sh` -> `*.sh`), which would have skipped this new
   non-podman-prefixed sbin script.

Verified end-to-end on the real host: pull, run, real network
connectivity (wget through the container's bridge), and a published
port actually serving HTTP (curl through -p 8099:80 to nginx) all
work. --no-pivot's security tradeoff (disabling one particular
container-escape mitigation) was explicitly discussed with and
approved by the user before committing, given it must be the default
for any container to start at all on this platform.

Follow-up not yet done: nftables (needed for #4) is not yet a
packages/ component in the reproducible build pipeline — it was only
installed manually on the test host for this verification run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 23:06:53 +00:00
maggesandClaude Sonnet 5 6cd541de23 Fix CI: downgrade upload/download-artifact to v3 for Gitea compatibility
Build Packages / Build .txz packages (push) Successful in 7m28s
Lint / ShellCheck (push) Successful in 11s
Lint / Validate .plg XML (push) Successful in 12s
Lint / EditorConfig (push) Successful in 4s
All 8 packages built and verified successfully in task 96 — the only
remaining failure was the very last step: actions/upload-artifact@v4
errored with GHESNotSupportedError. Gitea Actions' built-in artifact
storage doesn't implement the newer v2 upload/download API that
upload-artifact@v4/download-artifact@v4 require. Downgraded both (they
must match — v3 and v4 artifacts aren't cross-compatible) to v3, which
uses the older protocol Gitea does support.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 22:26:57 +00:00
maggesandClaude Sonnet 5 c3b8ae8ed9 Fix CI: json-c for crun, and fetch crun's git submodules as pinned tarballs
Build Packages / Build .txz packages (push) Failing after 7m21s
Lint / ShellCheck (push) Successful in 10s
Lint / Validate .plg XML (push) Successful in 9s
Lint / EditorConfig (push) Successful in 5s
Verified end-to-end with a full local build of all 8 packages on the
actual runner host (docker exec against vbatts/slackware:15.0, bind-
mounted repo on the cache pool) instead of round-tripping through
Gitea Actions for each fix — much faster for a chain of issues this
deep. All 8 packages now build successfully from a clean checkout.

Three fixes, all in crun (the last package still failing):

1. "Package requirements (json-c >= 0.14) were not met" — added
   json-c to the toolchain bootstrap.

2. crun depends on the libocispec git submodule, which in turn depends
   on the image-spec and runtime-spec git submodules. GitHub's source
   archive tarball never includes submodule content (no .git directory
   for `git submodule update` to work against — the existing `|| true`
   masked this failing silently). Fetched all three as their own
   pinned tarballs instead, at the exact commits crun 1.28 references
   (cross-checked via the GitHub contents API), matching how every
   other dependency in this project is already pinned.

3. crun.c unconditionally #includes git-version.h, which crun's own
   Makefile only generates via `git describe` (again, no .git) or from
   a pre-existing .tarball-git-version.h — the file crun's own `make
   dist` would have written, which we never run. Write that file
   ourselves in the exact format the Makefile already expects; this is
   the documented fallback path bundled release tarballs rely on, not
   a workaround around it.

Also includes the podman go-md2man pre-seed fix and setup-slackware-
buildenv.sh python3 addition from the previous commit's follow-up
testing (both already verified working in this same local build run).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 22:17:02 +00:00
maggesandClaude Sonnet 5 f1962acc8c Fix CI: correct the pinned Go toolchain SHA256
Build Packages / Build .txz packages (push) Failing after 6m3s
Lint / ShellCheck (push) Successful in 12s
Lint / Validate .plg XML (push) Successful in 13s
Lint / EditorConfig (push) Successful in 5s
GO_SRC_SHA256 was wrong since it was first pinned in Task 1 — this
path had never actually been exercised in any prior CI run or local
test because every earlier failure happened before reaching the Go
install step, or (once it did run) nothing had checked the pinned
value against go.dev's own published checksum yet. Task 88's log
caught it: the real go1.26.5 linux-amd64 tarball hashes to
5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053
(cross-checked against https://go.dev/dl/?mode=json directly), not
the previously pinned value. Also independently re-verified
LIBSECCOMP_SRC_SHA256 against the real v2.6.1 tarball while looking at
this — that one was already correct.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 21:37:48 +00:00
maggesandClaude Sonnet 5 23a1ab22a6 Fix CI: unshadow the pinned Go, and add python3/protoc/go-md2man
Build Packages / Build .txz packages (push) Failing after 2m59s
Lint / ShellCheck (push) Successful in 12s
Lint / Validate .plg XML (push) Successful in 12s
Lint / EditorConfig (push) Successful in 5s
Task 84's log got further than any run so far — 4 of 8 packages
(aardvark-dns, passt, fuse-overlayfs, unraid-podman) built successfully
— and surfaced four distinct, genuine build-time issues for the rest:

1. podman: go.mod parsing failed with "invalid go version '1.25.6':
   must match format 1.23". Root cause: slackpkg's batch-mode
   `install gcc` (verified directly) pulls in every gcc-<lang> sibling
   package Slackware's gcc SlackBuild produces — including gcc-go, an
   ancient bundled go1.16.5. Our Go bootstrap only installed the pinned
   $GO_VERSION when `command -v go` found nothing, so gcc-go's go1.16.5
   silently won. Fixed by always installing/overwriting the pinned Go
   and prepending it to PATH, regardless of what else provides `go`.

2. crun: "no suitable Python interpreter found" — added python3.

3. netavark: build.rs (via prost-build) needs a `protoc` binary;
   Slackware packages no protobuf/protoc at all. Added the official
   prebuilt release binary, pinned + checksummed (no `unzip` on this
   image either, so extracted with `python3 -m zipfile` instead of
   adding yet another package).

4. conmon: `make install`'s docs target needs go-md2man, not packaged
   by Slackware and no prebuilt release exists upstream. `go install`
   it, pinned to a tagged release, now that our own Go is reliably on
   PATH.

All four verified directly against vbatts/slackware:15.0 on the actual
runner host before this commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 21:32:27 +00:00
maggesandClaude Sonnet 5 e912180e8d Fix two real bugs in the package build scripts, found via a live CI run
Build Packages / Build .txz packages (push) Failing after 5m5s
Lint / ShellCheck (push) Successful in 13s
Lint / Validate .plg XML (push) Successful in 9s
Lint / EditorConfig (push) Successful in 4s
The toolchain bootstrap fixes finally got build-packages.sh far enough
to attempt actual package builds, surfacing two genuine bugs (not
environment/toolchain issues) in task 80's log:

1. sb_fetch_and_verify() echoed its "Fetching..."/"SHA256 verified..."
   progress messages to stdout, same stream its return value (the
   tarball path) is returned on. Every caller captures that return value
   via `tarball=$(sb_fetch_and_verify ...)`, so command substitution
   swallowed the progress lines into $tarball too, and the resulting
   multi-line garbage got handed to `tar -xf` as a single bogus
   filename. Fixed by sending the progress echoes to stderr.

2. unraid-podman.SlackBuild read plugin/podman.plg's version via
   `grep -oP` with a variable-length lookbehind ({1,10} to match
   flexible whitespace). PCRE requires fixed-length lookbehind; this
   works on a PCRE2 grep (e.g. most dev machines) but fails outright
   ("lookbehind assertion is not fixed length") on Slackware's PCRE1
   grep. Replaced with a portable sed capture group.

Both verified directly against vbatts/slackware:15.0 on the runner host
before this commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 15:14:45 +00:00
maggesandClaude Sonnet 5 3133d45d74 Fix CI: add cmake and its transitive runtime deps to the toolchain bootstrap
Build Packages / Build .txz packages (push) Failing after 3m40s
Lint / ShellCheck (push) Successful in 9s
Lint / Validate .plg XML (push) Successful in 9s
Lint / EditorConfig (push) Successful in 10s
yajl (needed by conmon, bootstrapped from source since Slackware ships
no package for it) builds via CMake, not the cmake-free autoconf script
its ./configure wrapper name suggests. Tracing the failure through the
actual vbatts/slackware:15.0 image on the runner host surfaced a chain
of packages slackpkg does not auto-resolve (Slackware packages carry no
dependency metadata at all): cmake needs libarchive, which needs lz4 and
libxml2; the patched make/gmake this mirror serves needs guile, which
needs gc; compiling anything needs kernel-headers for <linux/errno.h>;
and this build's binutils (ar/ranlib) needs flex, while objdump needs
elfutils. All added to the same slackpkg install list as the rest of
the toolchain, keeping everything on one mutually consistent version
set. Verified end-to-end against vbatts/slackware:15.0 on the actual
runner host at each step of this dependency chain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 15:07:18 +00:00
maggesandClaude Sonnet 5 c6947007aa Fix CI: install Node.js so actions/checkout can run in the container
Build Packages / Build .txz packages (push) Failing after 3m30s
Lint / ShellCheck (push) Successful in 11s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Successful in 5s
The git fix alone got further, but actions/checkout@v4 (and later,
actions/upload-artifact@v4) are Node-based actions — Gitea Actions
execs their JS bundle with the "node" binary from inside the job's
own container rather than injecting a runtime of its own, and
vbatts/slackware:15.0 has no nodejs package anywhere on the official
Slackware mirror. Install a pinned, checksum-verified Node.js release
straight from nodejs.org in the same "Install git" step, matching the
existing Go/Rust bootstrap style in setup-slackware-buildenv.sh.
Verified end-to-end (node --version succeeds after a fresh install) in
vbatts/slackware:15.0 on the actual runner host.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:30:12 +00:00
maggesandClaude Sonnet 5 acb9fb704b Fix CI: bootstrap the full Slackware build toolchain via slackpkg
Build Packages / Build .txz packages (push) Failing after 41s
Lint / ShellCheck (push) Successful in 11s
Lint / Validate .plg XML (push) Successful in 11s
Lint / EditorConfig (push) Successful in 5s
The build-packages.yml run was failing for two compounding reasons,
found by testing directly against vbatts/slackware:15.0 on the actual
runner host:

1. The image ships neither git nor its HTTPS runtime libs, so
   actions/checkout failed immediately.
2. It's a minimal rootfs with none of the 'D' (development) series —
   no gcc, make, autoconf, pkg-config, curl, glib2, libcap, or fuse3 —
   contrary to setup-slackware-buildenv.sh's assumption that a "full"
   Slackware install already provides these.

An earlier fix attempt hand-pinned git + its deps (nghttp2, brotli,
cyrus-sasl) by exact file + SHA256 from the base 15.0 release
directory. That drifted out of sync with the newer, patched curl
slackpkg installs later in the same container — same shared library,
two different builds, causing a runtime symbol lookup error. Both
steps now resolve every package through slackpkg's own prioritized
mirror instead, keeping the whole toolchain on one mutually consistent
version set. Verified end-to-end (git ls-remote and curl both succeed
over HTTPS against the real Gitea instance, full toolchain present)
in a fresh vbatts/slackware:15.0 container.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:11:29 +00:00
maggesandClaude Sonnet 5 f56704a7fb Fix CI: install git in the Slackware build container before checkout
vbatts/slackware:15.0 ships neither git nor its runtime shared libs
(nghttp2, brotli, cyrus-sasl) or ca-certificates, so actions/checkout
was failing immediately with "base image is missing git/tar". Install
git and its dependencies from Slackware's own official mirror, pinned
by exact filename and verified SHA256, and build the CA bundle so git
can trust HTTPS remotes. Verified end-to-end against the real Gitea
instance (git ls-remote succeeds) in a fresh vbatts/slackware:15.0
container.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 13:57:19 +00:00
maggesandClaude Sonnet 5 2e6832903b Fix CI: silence false-positive ShellCheck/EditorConfig findings
Build Packages / Build .txz packages (push) Failing after 4s
Lint / ShellCheck (push) Successful in 11s
Lint / Validate .plg XML (push) Successful in 12s
Lint / EditorConfig (push) Successful in 5s
- podman-common.sh: file-wide shellcheck disable=SC2034 for its
  constants, which are intentionally defined for other scripts to
  source, not used within the file itself (ShellCheck can't see
  cross-file usage in that direction). Confirmed as the actual cause
  of the failing ShellCheck job via the real Gitea Actions log.
- podman-storage.sh: use the retry counter in cmd_unmount's log
  message instead of leaving it genuinely unused (real SC2034 hit,
  not a false positive).
- lint.yml: replace the editorconfig-checker/action-* marketplace
  action with a pinned, checksum-verified direct binary download —
  more robust than relying on third-party action resolution on
  self-hosted Gitea Actions runners — and disable its IndentSize
  check, which false-positives on Markdown's nested-list indentation
  and shell heredoc bodies. scripts/dev/lint.sh mirrors the same check
  for local use. All three lint.yml jobs verified passing locally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 11:18:25 +00:00
maggesandClaude Sonnet 5 e2fefcdf9c Add reproducible build system, native Unraid plugin, and WebUI
Build Packages / Build .txz packages (push) Failing after 9s
Lint / ShellCheck (push) Failing after 43s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Failing after 6s
- versions.env pins podman, conmon, crun, netavark, aardvark-dns, passt,
  and fuse-overlayfs to verified upstream source checksums; SlackBuild
  recipes, scripts/build-packages.sh, checksums.sh, release.sh, and
  update-versions.sh implement the reproducible pipeline; GitHub Actions
  workflows build in a Slackware container and publish releases without
  committing any binaries.

- plugin/podman.plg installs/updates/removes all eight packages (the
  seven components plus the plugin's own unraid-podman scaffolding
  package) via upgradepkg, using the official Unraid array-event hook
  mechanism (event/disks_mounted, event/stopping) instead of editing
  /boot/config/go. rc.podman and the sbin/ helper scripts implement
  storage creation, config seeding/sync, preflight checks, autostart
  with per-container Safe-Mode, and package verify/update/rollback.

- webui/plugins/podman implements the Dashboard, Containers, Pods,
  Images, Volumes, Networks, Logs, Terminal, Compose, and Settings
  panels against the approved mockup (webui/mockups/prototype.html),
  talking to podman system service exclusively via PodmanClient.php
  (libpod REST API over the Unix socket), with two documented
  exceptions: Terminal's one-shot exec model and Compose's use of the
  podman compose CLI, since libpod has no REST equivalent for either.

- docs/ARCHITECTURE.md and docs/ROADMAP.md record the design decisions
  and honest current status (syntax-checked, unit- and
  integration-tested against fake sockets/servers; not yet run against
  a real Unraid/Podman/Slackware system).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:51:14 +00:00
magges 58ffc0c226 first commit 2026-07-11 12:24:23 +02:00