Deployments: deploy, restart and git sync now run in the background, one at
a time per app (a newer request replaces a queued one). Each run is recorded
in SQLite with its log, streamed to the UI while it runs, and can be
cancelled. Any earlier deployment can be deployed again, which rolls back to
its commit, or to its saved compose file for compose apps.
Auto deploy: POST /hooks/<app>, verified with the app's secret (Forgejo,
Gitea and GitHub HMAC signatures, or the secret as a token for CI). With a
Forgejo token stored, the panel adds the webhook to the repository itself.
The NixOS module routes /hooks/* past Authelia. Caddy matches the cleaned
path but forwards the original, so the panel refuses dot segments and only
accepts webhook deliveries from that route (tagged with X-Panel-Hook).
Domains: a route can point at a compose service's container port
("web:8080"). The panel picks a free 127.0.0.1 port and panelctl publishes
it through a generated .panel-ports.yaml override, so compose files need no
ports: section. Existing host:port upstreams keep working.
Logs stream live over server-sent events, with service and text filters.
A sampler keeps an hour of CPU and memory per container for the new
Monitoring tab. The Terminal tab opens `podman exec` in a container over a
WebSocket, using xterm.js bundled by the Nix package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
The panel now lives in its own repository instead of the nixos config.
The flake provides:
- packages.<system>.default: panelctl plus a panel-api wrapper, with the
frontend under share/panel and runtime tools appended to PATH
- nixosModules.default: services.reudy-panel (service, directories,
shared routes group, Caddy vhost with optional Authelia forward_auth,
routes import and reload path unit, Forgejo integration)
- overlays.default, and checks that build the package and evaluate a
host using the module
panelctl takes the routes file owner from PANEL_USER/PANEL_GROUP
instead of hardcoding reudy:panelroutes (defaults unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
Forgejo:
- panel.nix passes the local Forgejo's public, API and ssh URLs (derived
from forgejo.nix) to panel-api.
- Settings dialog: connect a Forgejo access token (verified against
/api/v1/user, stored 0600 in state/panel/forgejo-token).
- New-app dialog gets a Forgejo repository picker with search and a branch
dropdown; private repos are cloned over https with the stored token, or
over ssh with the deploy key when no token is connected. The app name and
domain are filled in from the repository name.
- Commit and compare links in the Source tab point at Forgejo; cards show
the provider ("Forgejo · main").
Git over ssh:
- ssh:// and git@host:owner/repo URLs are accepted; the panel generates an
ed25519 deploy key in state/panel/ssh and uses it for clone/fetch
(BatchMode, accept-new host keys). openssh added to the service path.
- Credential redaction only applies to http(s) URLs, so ssh usernames are
kept; git errors now report the meaningful line instead of git's advice.
Environment variables:
- Stored per app in state/env/<app>.env (0600), outside the repo and stack.
- panelctl passes them to every compose command via env(1), so ${VAR}
interpolation works; by default deploy/restart also generate a compose
override listing the keys under every service's environment (values are
read from compose's environment, never quoted into YAML).
- Environment tab (and a section in the new-app dialog) with .env paste
import, hidden values, validation of names (reserved podman/compose vars
rejected), hints for ${VAR}s the compose file uses but aren't set, and
Save / Save & deploy. Removing an app deletes its variables.
The API still accepts the old source_type "github" / github_* fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf
Web UI (panel/frontend/index.html) rewritten:
- Cards update in place from a single /status poll instead of being rebuilt
on every action, so open tabs, unsaved compose/route edits, logs and the
file browser position survive refreshes. Polling speeds up while an
operation runs and pauses in background tabs; a header indicator shows
when the panel last synced and detects an expired Authelia session.
- New-app dialog (starter / compose / git), suggested port and domain,
proper confirm dialogs (the old "OK = keep volumes" remove prompt is gone),
toasts, an activity drawer with operation output, overflow menu, search,
status filters, keyboard shortcuts, deep links, dark mode and mobile layout.
- Tabs: overview (containers + routes), compose editor (dirty tracking,
Ctrl+S), logs with follow, validated routes editor, file browser with
drag-and-drop upload, backups, and a git source tab (deployed commit,
check for updates, sync & deploy).
API (panel/panel-api.py):
- ThreadingHTTPServer so a long deploy no longer blocks every other request.
- Per-app operation lock; concurrent writes to a busy app return 409.
- GET /status: all apps, routes and container status in one request
(statuses gathered in parallel); status reports running/partial/stopped.
- Git sync is fetch + hard reset instead of pull-or-reclone, keeps the stored
token, reports before/after commits; GET /apps/<name>/repo[?fetch=1].
- Any http(s) git host (e.g. Forgejo), default branch detection, git
timeouts, no credential prompts, tokens redacted from errors, and manifest
values validated before being written into the bash-sourced manifest.
panelctl:
- flock around routes.caddy rewrites (util-linux added to the service path).
- deploy returns compose output so failures are visible in the UI.
- inspect-volumes no longer fails for apps without named podman volumes,
which broke the file browser.
Docs: README/API.md updated; fixed outdated panelctl init examples.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbWSNkXxZhYf7eqHTyx3Bf