## 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/ /public/api/status/ ``` 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.