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>
webui/
Dynamix-style WebUI pages, following Unraid's plugin GUI convention of
/usr/local/emhttp/plugins/<name>/. webui/plugins/podman/ is staged to
that path by the unraid-podman scaffolding package (see
packages/unraid-podman/unraid-podman.SlackBuild), which podman.plg installs
alongside the other ten packages.
Status: implemented, covering all ten sections from docs/ARCHITECTURE.md, section 18: Dashboard, Containers, Pods, Images, Volumes, Networks, Logs, Terminal, Compose, Settings. Not yet exercised against a real Unraid/Podman install — see docs/ROADMAP.md for what "implemented" does and doesn't cover yet.
webui/mockups/prototype.html is the static, non-PHP clickable mockup this
implementation was built against — kept as the visual reference; it is not
staged into the package.
Structure
webui/
├── mockups/
│ └── prototype.html # static approved mockup, not shipped
└── plugins/
└── podman/
├── Podman.page # page shell: header, sub-nav, one container per panel
├── include/
│ ├── PodmanClient.php # libpod REST API client (talks to podman.sock only)
│ ├── Config.php # reads podman.cfg (mirrors podman-common.sh)
│ ├── bootstrap.php # shared include + error handling for ajax/*.php
│ └── helpers.php # formatting + JSON-response helpers
├── ajax/ # one endpoint per resource, each require()s bootstrap.php
│ ├── containers.php # list/start/stop/restart/remove/logs
│ ├── pods.php
│ ├── images.php
│ ├── volumes.php
│ ├── networks.php
│ ├── exec.php # Terminal — see its header comment for API scope
│ ├── compose.php # Compose — the one deliberate CLI exception, see header
│ ├── settings.php # plugin's own config, not a libpod resource
│ └── system.php # Dashboard aggregation
├── javascript/
│ ├── app.js # shared AJAX helper + sub-tab router
│ └── <panel>.js # one module per panel, registers with app.js
├── styles/podman.css # design tokens ported 1:1 from the mockup
├── event/ # official Unraid array-event hooks (see plugin/event/)
└── images/ # plugin icon assets
Design constraints (see ARCHITECTURE.md for full rationale)
- Every panel talks to
podman system serviceover its Unix socket viaPodmanClient— noexec()/shell_exec()of thepodmanbinary anywhere ininclude/or in the Containers/Pods/Images/Volumes/Networks/ Logs endpoints. - Two documented, deliberate exceptions, not oversights:
ajax/exec.php(Terminal) uses the real exec REST API, but as one-command-in/output-out rather than a true interactive PTY — libpod's interactive exec needs a persistent hijacked connection that doesn't fit PHP-FPM's request lifecycle. See that file's header comment.ajax/compose.php(Compose) shells out to thepodman composeCLI viaproc_open()with an argv array (never a shell string) — because no REST endpoint for Compose exists in libpod at all. See that file's header comment.
- The API socket is root-equivalent; no unauthenticated network exposure beyond what Unraid's own WebUI auth already provides — see .github/SECURITY.md.
- Dark/light mode via CSS custom properties (
prefers-color-scheme+[data-theme]override), matching Unraid's own theme mechanism — no separate theme toggle inside the plugin page.