bae6fd8b9f
Beschrijf dat systemd_user.reachable afgeleid is van helper.ok, dat de container zelf geen D-Bus/systemctl aanroepen doet, en dat alle systemctl-acties (incl. daemon-reload) via de helper-socket lopen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
124 lines
5.3 KiB
Markdown
124 lines
5.3 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
podman-mvp is a Portainer-like web dashboard for managing rootless user-session Podman containers. It runs as a two-container Podman pod: a FastAPI backend (`mvp-backend`) that talks to Podman over a Unix socket, and a static Apache frontend (`mvp-webui`) that reverse-proxies `/api/` to the backend.
|
|
|
|
## Architecture
|
|
|
|
### Backend — FastAPI modular monolith (`control/`)
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `app.py` | Bootstrap only — creates FastAPI app, wires routers, no feature logic |
|
|
| `common.py` | Shared helpers: Podman HTTP, systemctl, utilities |
|
|
| `app_system.py` | System/platform router: `/health`, `/daemon-reload`, systemctl unit actions |
|
|
| `app_containers.py` | Containers router: dashboard, inspect, logs, stats stream, exec sessions |
|
|
| `app_pods.py` | Pods router: dashboard, pod actions |
|
|
| `app_networks.py` | Networks router |
|
|
| `app_images.py` | Images router |
|
|
| `app_files.py` | Files/workloads router: tree, read, save |
|
|
|
|
Backend communicates with Podman through the Unix socket at `/run/user/1000/podman/podman.sock` using `requests_unixsocket`. Podman API base: `http+unix://%2Frun%2Frun%2Fuser%2F1000%2Fpodman%2Fpodman.sock/v5.4.2`.
|
|
|
|
### Frontend — Static Apache (`webui/`)
|
|
|
|
- `webui/html/index.html` — single-page app shell
|
|
- `webui/html/assets/js/tabs/` — per-tab JavaScript modules (containers, networks, images, files)
|
|
- `webui/conf/httpd.conf` — Apache config, proxies `/api/` → `http://127.0.0.1:8000/api/`
|
|
|
|
## Build & Deploy
|
|
|
|
```bash
|
|
# Build backend image
|
|
podman build -t mvp-control:latest control/
|
|
|
|
# Create pod
|
|
podman pod create --name mvp-pod -p 8080:8000 -p 8081:8081 --userns=keep-id
|
|
|
|
# Run backend
|
|
podman run -d --pod mvp-pod --name mvp-backend \
|
|
--ipc=host --pid=host \
|
|
-e XDG_RUNTIME_DIR=/run/user/1000 \
|
|
-v /run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:rw \
|
|
-v /run/user/1000/podman-mvp:/run/podman-mvp \
|
|
-v /home/kodi/.config/containers:/app/workloads:rw \
|
|
mvp-control:latest
|
|
|
|
# Run frontend
|
|
podman run -d --pod mvp-pod --name mvp-webui \
|
|
-v $HOME/.config/podman-mvp/webui/html:/usr/local/apache2/htdocs:ro \
|
|
-v $HOME/.config/podman-mvp/webui/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro \
|
|
docker.io/library/httpd:2.4
|
|
```
|
|
|
|
## Verification Commands
|
|
|
|
```bash
|
|
# Syntax check all backend modules
|
|
python3 -m py_compile control/app.py control/common.py control/app_system.py \
|
|
control/app_containers.py control/app_pods.py control/app_networks.py \
|
|
control/app_files.py control/app_images.py
|
|
|
|
# Smoke test key endpoints (all via proxy on :8081)
|
|
curl -fsS http://127.0.0.1:8081/api/health | jq
|
|
curl -fsS http://127.0.0.1:8081/api/containers-dashboard >/dev/null && echo OK
|
|
curl -fsS http://127.0.0.1:8081/api/pods-dashboard >/dev/null && echo OK
|
|
curl -fsS http://127.0.0.1:8081/api/files/tree >/dev/null && echo OK
|
|
curl -fsS http://127.0.0.1:8081/api/networks/meta | jq
|
|
```
|
|
|
|
All test/verification URLs must target `127.0.0.1:8081` (the proxy), not port 8000 directly.
|
|
|
|
## Health Check (`/api/health`)
|
|
|
|
`GET /api/health` geeft drie deelresultaten terug:
|
|
|
|
| Veld | Wat het meet | Techniek |
|
|
|---|---|---|
|
|
| `podman.ok` | Podman API bereikbaar | HTTP GET `/libpod/info` op Unix socket |
|
|
| `helper.ok` | podman-helper socket bereikbaar | TCP connect op `/run/podman-mvp/podman-helper.sock` |
|
|
| `systemd_user.reachable` | Afgeleid van `helper.ok` | Identiek — helper draait als host-user en voert `systemctl --user` uit, dus bereikbaarheid van helper impliceert bereikbaarheid van systemd |
|
|
|
|
`ok` (toplevel) is `true` als én `podman.ok` én `helper.ok` waar zijn.
|
|
|
|
De container voert zelf **geen** `systemctl --user` of D-Bus aanroepen uit. Alle systemctl-acties (start/stop/restart/daemon-reload) gaan via de helper-socket. D-Bus en `/run/user/1000/bus` zijn niet gemount.
|
|
|
|
## Hard Rules
|
|
|
|
### Module placement
|
|
- `app.py` is bootstrap-only — no endpoints, no feature logic, no Podman/systemctl calls.
|
|
- New system/platform endpoints → `app_system.py`.
|
|
- New domain feature endpoints → the corresponding `app_<domain>.py`.
|
|
- Shared helpers → `common.py`, never duplicated into routers.
|
|
- `allow_list` / `allowed_units.txt` has been removed and must NOT be reintroduced.
|
|
- `app_system.py` broad wildcard routes (`/{action}/{unit}`) must be defined **last**.
|
|
|
|
### API contract (`contracts/API_GOLDEN.md`)
|
|
- Never remove or rename existing JSON response keys.
|
|
- Never change existing key data types.
|
|
- Extend via new optional fields or new endpoints only.
|
|
- UI-critical endpoints requiring pre-approval before any change: `/containers-dashboard`, `/pods-dashboard`, `/images`, `/networks/meta`.
|
|
|
|
### Security
|
|
- No `shell=True` in subprocess calls.
|
|
- All subprocess commands must be explicit lists.
|
|
|
|
### Infrastructure (propose before changing)
|
|
- Pod name, port mappings, `userns=keep-id`.
|
|
- DBus/XDG_RUNTIME_DIR mounts, Podman socket path, host PID/IPC namespaces.
|
|
- `control/Containerfile`, `webui/conf/httpd.conf`.
|
|
|
|
## Change Workflow
|
|
|
|
For non-trivial changes, follow PR_RULES.md:
|
|
1. Analyse existing behaviour with curl.
|
|
2. Propose minimal plan identifying affected files.
|
|
3. Confirm API contract safety.
|
|
4. Provide curl validation commands showing expected output change.
|
|
5. Implement after agreement.
|
|
|
|
Minimize diff size. Do not reformat unrelated code. No large rewrites or hidden refactors.
|