panel/API.md
Jakub Dorfman e15298fd3d feat: add initial implementation of the frontend panel with app management features
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 23:53:17 +02:00

3.1 KiB

panel-api

HTTP API wrapper around panelctl with a web UI.

Default bind: 127.0.0.1:9911

Endpoints

Health & UI

Method Path Description
GET / Web UI (served from frontend/index.html)
GET /health Health check

Apps — Read

Method Path Description
GET /apps List all apps
GET /apps/<name> Show single app manifest
GET /apps/<name>/status Container status (running/stopped)
GET /apps/<name>/compose Read compose.yaml content
GET /apps/<name>/logs?tail=N Fetch last N log lines (default 100)
GET /apps/<name>/backups List available backups
GET /apps/<name>/backups/<file> Download backup zip

Apps — Write

Method Path Description
POST /apps/init Create a new app
POST /apps/<name>/deploy Deploy (compose up + caddy reload)
POST /apps/<name>/restart Restart (compose down + up)
POST /apps/<name>/stop Stop (compose down)
POST /apps/<name>/render-route Re-render Caddy route
POST /apps/<name>/compose Save compose.yaml content
POST /apps/<name>/validate-compose Validate compose file
POST /apps/<name>/backup Create volume backup (zip)
POST /apps/<name>/restore Restore from backup
POST /apps/<name>/remove Remove app

Example payloads

Create app (single domain)

{
  "name": "whoami",
  "domain": "whoami.srazka.com",
  "port": 18080,
  "auth": true
}

Create app (multiple domains)

{
  "name": "myapp",
  "domain": "app.srazka.com,www.app.srazka.com",
  "port": 18081,
  "auth": true
}

Or using the domains array format:

{
  "name": "myapp",
  "domains": ["app.srazka.com", "www.app.srazka.com"],
  "port": 18081,
  "auth": true
}

Create app (wildcard domain)

{
  "name": "wildcard",
  "domain": "*.srazka.com",
  "port": 18082,
  "auth": false
}

Note: Wildcard domains require DNS challenge configuration in Caddy.

Save compose

{
  "content": "services:\n  app:\n    image: nginx:latest\n    ports:\n      - '127.0.0.1:18080:80'\n"
}

Remove and keep volumes

{
  "keepVolumes": true
}

Restore from backup

{
  "file": "whoami-20260101-120000.zip"
}

Response format

All JSON responses include an ok boolean:

{
  "ok": true,
  "apps": [...]
}

Error responses:

{
  "ok": false,
  "error": "description",
  "stderr": "panelctl error output"
}

Status response

{
  "ok": true,
  "name": "whoami",
  "running": true,
  "containers": [
    {
      "name": "whoami-app-1",
      "state": "running",
      "image": "docker.io/traefik/whoami:latest"
    }
  ]
}

Local test

curl -s http://127.0.0.1:9911/health | jq .
curl -s http://127.0.0.1:9911/apps | jq .
curl -s http://127.0.0.1:9911/apps/whoami/status | jq .
curl -s http://127.0.0.1:9911/apps/whoami/logs?tail=50 | jq .
curl -s http://127.0.0.1:9911/apps/whoami/backups | jq .