init
release-tag / release-image (push) Failing after 1m20s

This commit is contained in:
2026-08-31 17:09:21 +02:00
parent 57e5114b60
commit 45ca18b74e
38 changed files with 6169 additions and 1 deletions
+327 -1
View File
@@ -1,2 +1,328 @@
# dockwatch
## v9.1 packaging fix
v9.1 fixes the v9 source archive packaging. The v9 ZIP accidentally omitted `internal/stacks/`, which could leave an older local copy of that package in place when extracting over an existing checkout and cause method-signature build errors. v9.1 is a complete source archive and includes `internal/stacks/stacks.go` and its tests. Always extract it into a fresh directory.
# Dockwatch v9.1
> Go module: `git.send.nrw/sendnrw/dockwatch`
Dockwatch is a single-binary Go control plane for Docker Compose, Docker resources and uptime monitoring. It combines Dockge/Dockhand-style operational workflows with Uptime-Kuma-style monitoring while keeping its own implementation and UI.
The same binary runs as `standalone`, `master` or `agent`. SQLite uses `modernc.org/sqlite`, so the application itself builds with `CGO_ENABLED=0`.
## What is included
### Compose / stack management
- multiple Compose stacks per environment
- local Docker plus remote agents
- staged `docker compose config --quiet` validation before a save
- atomic managed-file writes and rollback on save failure
- full bidirectional Compose designer backed by a YAML AST
- recursive editing of all YAML maps/arrays/scalars, including unknown/future Compose fields
- `compose.yaml`, root `.env`, `envs/`, `secrets/` and `configs/`
- Up / Start / Stop / Restart / Down / Pull / Update / Force recreate
- service/container status
- live SSE logs with pause, follow, filter and download
- interactive PTY/WebSocket `docker compose exec` terminal
- normalized service/dependency/network/volume graph from `docker compose config --format json`
- registry digest based image-update checks
- safe stack deletion: only Dockwatch-managed definition files are removed by default
- explicit, typed-name confirmation for full stack-folder purge
The editor keeps unsaved drafts in browser storage. Background refresh never overwrites a dirty editor.
### Git-backed stacks
Git sources support:
- local or remote-agent environment
- repository, branch, workdir and Compose filename
- manual sync
- optional deploy after sync
- signed webhooks (`X-Hub-Signature-256`, `X-Gitlab-Token` or `X-Webhook-Token`)
- encrypted webhook secret at rest
- last commit / last sync / last error
A Git sync is now **managed-file based**. Dockwatch stores a small `.dockwatch/git-manifest.json` in the stack directory and only replaces/removes files that belong to the Git checkout. Unrelated runtime or bind-mount data beside the Compose project is preserved. Syncs of the same source are serialized and a failed apply rolls back the touched file set.
Symlink stack destinations and symlink paths inside Git-managed writes are rejected.
### Docker resources
**Containers**
- list/filter
- start / stop / restart / force-remove
- operator-only inspect
- one-shot CPU/memory/network/block stats
**Images**
- list/filter
- pull / remove / prune
- registry login/logout
- persistent Docker auth via `DOCKER_CONFIG=/data/docker-config`
**Volumes**
- list/filter
- create / remove / prune
- driver and labels
**Networks**
- list/filter
- create / remove / prune
- driver, labels, `internal`, `attachable`
Docker positional arguments and Compose service names are validated before invoking the CLI so option-like values cannot be interpreted as Docker CLI flags.
### Monitoring
Probe types:
- HTTP(S)
- TCP
- DNS
- Docker container running state
- Docker container running + optional `healthy` requirement
Monitor lifecycle:
- create / edit / delete
- pause / resume
- manual or timed maintenance
- maintenance notes
- manual **Check now**
- interval and timeout
- HTTP method and accepted status range
- request headers/body
- keyword / inverted keyword assertion
- optional HTTP TLS verification bypass
- local or remote-agent execution
- heartbeat history
- last latency/status message
- 24-hour uptime
- retention cleanup
The scheduler uses a lightweight monitor query and does not recompute 24-hour uptime statistics every two seconds. Manual checks are serialized with scheduled checks and do not accidentally resume paused/maintenance monitors. Shutdown-cancelled checks are not persisted as false outages.
### Services and public status pages
Probes can be grouped into user-facing Services. Aggregation is strict:
- any probe `down` -> Service `down`
- otherwise maintenance -> Service `maintenance`
- pending/paused states are retained
- otherwise Service `up`
Service dialogs can assign/unassign probes directly.
Admins can publish selected Services on public pages:
```text
/status/<slug>
/public/api/status/<slug>
```
The public JSON API uses a dedicated DTO and exposes only public status information (name, status, uptime, latency and last-check timestamp), not targets, node IDs, request headers, bodies or credentials. HTML/JSON status responses are not cached.
### Notifications
Providers:
- generic JSON webhook
- ntfy
- Gotify
- SMTP
SMTP supports:
- `STARTTLS`
- implicit `SSL/TLS`
- `None`
- authentication on/off independently of TLS
- optional certificate-verification bypass for private/self-signed infrastructure
SMTP connections inherit the notification deadline after connect as well, so a stalled server cannot leave a delivery goroutine hanging indefinitely. SMTP/password/token/secret values are AES-GCM encrypted at rest. Notification configuration is admin-only in both API and UI.
### PocketID / OIDC and roles
Roles are mapped from OIDC/PocketID groups:
- `viewer` – dashboards/status/inventory, sanitized monitor configuration
- `operator` – stack configuration/logs/terminal, Docker actions, monitor/service operations, Git stacks
- `admin` – environments, notifications, status-page administration and audit log
Viewer API responses do not expose monitor request headers/body/keywords. Stack definition detail and live logs require Operator access because `.env`, secrets and runtime logs may contain sensitive data.
Required OIDC setup:
```env
AUTH_DISABLED=false
BASE_URL=https://dockwatch.example.com
OIDC_ISSUER=https://id.example.com
OIDC_CLIENT_ID=...
OIDC_CLIENT_SECRET=...
APP_SECRET=replace-with-at-least-32-random-characters
OIDC_ADMIN_GROUP=dockwatch-admins
OIDC_OPERATOR_GROUP=dockwatch-operators
```
Redirect URI:
```text
https://dockwatch.example.com/auth/callback
```
OIDC discovery, token exchange and verification are bounded by `HTTP_TIMEOUT_SECONDS`. Session/state cookies are `HttpOnly`, `SameSite=Lax` and become `Secure` when `BASE_URL` uses HTTPS.
### Master / agent
Modes:
```text
APP_MODE=standalone
APP_MODE=master
APP_MODE=agent
```
An agent exposes only `/agent/v1/*` and requires an `AGENT_TOKEN` of at least 24 characters. Agent tokens are AES-GCM encrypted in the master's SQLite database.
Remote-capable features include:
- stacks and actions
- logs and PTY terminal
- Compose graph
- image update checks
- containers/images/networks/volumes
- monitoring probes
- Git clone/sync/deploy
See `examples/compose-master.yml` and `examples/compose-agent.yml`.
## Full Compose designer
The Compose tab uses two synchronized representations:
1. the raw YAML source of truth
2. a recursive visual designer
YAML changes are parsed after a short debounce. Visual changes patch the selected YAML AST path instead of reserializing a simplified Compose model. Unknown keys, `x-*` extensions and comments are therefore preserved where possible. Invalid/incomplete YAML pauses visual synchronization and never overwrites the source editor.
The designer is not limited to a hard-coded subset: arbitrary maps, arrays and scalar types remain editable, including current/future Compose fields and long syntax objects.
Server-side save validation still uses Docker Compose itself after all related `.env`, secret, env-file and config files have been staged.
## Reliability and security work in v9
The v9 review includes, among other changes:
- fixed fresh-database migration ordering before monitor indexes are created
- SQLite WAL/busy-timeout/foreign-key configuration with a small connection pool
- strict environment parsing instead of silently accepting invalid numeric/boolean values
- graceful HTTP shutdown and sensible server timeouts
- security headers and same-origin checks for browser mutations
- request JSON size limit and single-value decoding
- admin-only audit activity and notification configuration
- audit retention cleanup
- public-status DTO that does not leak internal monitor configuration
- safe IP parsing for audit entries
- sensitive viewer responses reduced
- Compose save snapshot/rollback
- safe stack delete vs. explicit purge
- symlink protection on stack/Git file paths
- atomic managed-file writes
- Git sync serialization and managed-file manifest
- Git copy file-descriptor leak fixed
- Node URL validation rejects credentials/query/fragment
- remote terminal WebSocket URL/query handling fixed
- registry/Docker CLI positional-argument validation
- scheduler query and shutdown behaviour improved
- defensive frontend collection handling (`null` cannot break list pages)
- admin-only navigation entries hidden for non-admin users
- persisted dark/light theme and collapsible/mobile sidebar
- API connectivity indicator and manual refresh
- denser Docker/monitor/service tables and better empty/error states
## Configuration
`.env.example` contains the defaults. Important values:
```env
APP_MODE=standalone
LISTEN_ADDR=:8080
BASE_URL=http://localhost:8080
DATA_DIR=/data
STACKS_DIR=/stacks
AUTH_DISABLED=true
APP_SECRET=change-me-change-me-change-me-32bytes
CHECK_CONCURRENCY=8
CHECK_RETENTION_DAYS=30
HTTP_TIMEOUT_SECONDS=10
AUDIT_RETENTION_DAYS=180
```
`AUTH_DISABLED=true` is for local development only. Do not expose that configuration publicly.
## Run with Docker Compose
```bash
cp .env.example .env
docker compose up -d --build
```
Runtime mounts normally include:
```text
/data SQLite, Docker registry config
/stacks Compose projects
/var/run/docker.sock Docker Engine access
```
Giving Dockwatch access to the Docker socket grants highly privileged control of that Docker host. Protect the UI and agent endpoint accordingly.
## Build from source
The pinned OIDC/OAuth2 releases require **Go 1.25**. The Docker build uses `golang:1.25-alpine`.
```bash
go mod tidy
go test ./...
CGO_ENABLED=0 go build ./cmd/dockwatch
```
Build metadata can be injected through the supplied Makefile/Dockerfile (`VERSION`, `COMMIT`, `BUILD_DATE`) and is visible in `/healthz` and the UI.
## Database
SQLite database:
```text
/data/dockwatch.db
```
`modernc.org/sqlite` is used, so no SQLite CGO binding is required. Migrations are additive and include users/sessions, nodes, monitors/checks/services, public status pages, audit events, notification channels and Git sources.
## Verification for this archive
The artifact-building environment cannot reach `proxy.golang.org`, so it cannot download the real external modules or generate a trustworthy `go.sum` here. The repository intentionally does **not** ship fake checksums or test stubs.
For quality control, the project is copied into a temporary test workspace where API-compatible local stubs replace only the external dependencies. Those stubs are not included in the ZIP. The checks used for v9 include:
```text
# all packages type/compile checked with temporary external-module stubs
go test -run='^$' ./...
go test ./internal/config ./internal/gitops ./internal/httpapi \
./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks
go vet ./...
go test -race ./internal/config ./internal/gitops ./internal/httpapi ./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks
node --check web/app.js
```
The SQLite migration SQL is additionally smoke-tested against Python's SQLite engine because the temporary `modernc.org/sqlite` stub does not implement a real SQL driver.
On a normal networked development machine or during `docker build`, run `go mod tidy && go test ./...` once against the real pinned dependencies.