@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user