Files
jbergner f3a687543c
release-tag / release-image (push) Successful in 2m28s
v9.6.0
2026-09-01 22:46:20 +02:00

600 lines
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dockwatch v9.6.0
> 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`.
## v9.6.0 System-Updates
- neuer Admin-Bereich **System-Updates** für den lokalen Host und ausgewählte Master/Agent-Nodes
- Update-Erkennung und Installation über apt, dnf/yum, pacman sowie weiterhin zypper/apk als bereits unterstützte Paketmanagerfamilien
- Arch-Abfragen nutzen bevorzugt `checkupdates` aus `pacman-contrib`, damit keine isolierte `pacman -Sy`-Synchronisierung und damit kein Partial-Upgrade-Risiko entsteht
- Arch-Systemupdates werden ausschließlich als vollständiges `pacman -Syu --noconfirm` ausgeführt
- AUR-Unterstützung erkennt `paru` (bevorzugt) oder `yay`; `HOST_AUR_USER` legt den Nicht-root-Benutzer fest, unter dem AUR-Abfrage/Build läuft
- AUR-Installation fordert die Paket-Eskalation des Helpers non-interaktiv an (`--sudoflags=-n`); Hosts müssen diesen Benutzer entsprechend für den Paketmanager autorisieren
- neue Master/Agent-Endpunkte für Update-Liste, Paketmetadaten-Refresh und Installation; alle Mutationen bleiben Admin-only und laufen durch Origin-Schutz/Audit-Logging
- Browser kann weiterhin keine freien Paketnamen oder Shell-Kommandos einreichen, sondern nur die vordefinierten Scopes `system`, `aur` oder `all`
- statische Assets verwenden `v=9.6.0` zur sicheren Cache-Aktualisierung
## v9.5.3 deutsche Weboberfläche
- die Weboberfläche ist nun durchgängig deutsch beschriftet; interne API-/Compose-Werte bleiben unverändert
- Navigation, Compose-Designer, Monitoring, Dienste, Statusseiten, Docker-Ressourcen, Git, Benachrichtigungen und Host-Sicherheit wurden sprachlich vereinheitlicht
- Status- und Rollenwerte werden im UI deutsch dargestellt, ohne die API-Werte (`up`, `down`, `admin`, `operator` usw.) zu verändern
- die öffentliche Statusseite ist ebenfalls deutsch
- häufige serverseitige Diagnosemeldungen werden für die Anzeige im WebUI verständlich ins Deutsche übertragen
- statische Assets verwenden `v=9.5.3` zur sicheren Cache-Aktualisierung
## v9.5.1 UI / Compose editor fixes
- Compose scalar edits no longer re-render the complete visual tree after every keystroke, so text/port fields keep focus while typing. Structural changes (add/remove/type changes) still re-parse the YAML AST.
- Compose designer controls now use the application theme instead of browser-default white form controls in dark mode.
- Stack container cards format Compose publisher JSON into readable port mappings and wrap long image/command values safely.
- Docker inventory tables now have horizontal overflow handling, sensible column widths, compact label summaries, and a sticky action column so buttons are no longer squeezed off-screen.
- Static assets are cache-busted to `v9.5.1`.
## v9.5 firewall-provider layer
- Host Security firewall management now uses a provider model: **Auto detect / UFW / firewalld / native nftables**
- Auto detect adopts an already-active UFW or firewalld frontend instead of refusing all changes simply because one exists
- multiple truly competing active frontends are still treated as a hard conflict
- UFW rules are added with deterministic `dockwatch:` comments and Dockwatch removes only those managed rules; it never runs `ufw reset`
- firewalld uses the selected native zone and managed rich rules while preserving unrelated services, ports, sources and rich rules
- native nftables keeps the isolated `table inet dockwatch` model and never flushes the global ruleset
- existing provider state/rules are displayed read-only in the firewall editor before changes are applied
- provider selection, default inbound management, firewalld zone selection, ALLOW/DENY/REJECT/LIMIT rules and rule reordering are available in the unified UI
- provider changes retain the timed rollback/explicit commit safety model; rollback restores the previous Dockwatch-managed policy and provider default snapshot where applicable
- package installation/upgrade now installs the selected firewall frontend (`ufw`, `firewalld` or `nftables`) rather than always installing nftables
## v9.4.1 fixes
- fixed monitor creation when no monitor is selected (`state.monitor == null`)
- static UI assets are served with `Cache-Control: no-store`, preventing stale frontend code after upgrades
- Compose visual-editor patches now use a real queue so rapid edits on different fields are not lost
- the **Volumes** section now reconciles every service mount with top-level `volumes:` declarations
- named volumes missing a top-level declaration can be added individually or with **Add missing declarations**
- bind mounts, anonymous mounts and dynamic mounts are shown in the reconciliation view but are never silently converted into named volumes
- unused top-level volume declarations are highlighted and can be removed explicitly without deleting the Docker volume itself
## 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
### Container identity / host UID-GID checks
Container rows include an **Identity** action. Dockwatch inspects the selected container and reports:
- configured Compose/image user and the effective runtime UID/GID where resolvable
- whether the process currently runs as UID 0
- a conservative root assessment based on `privileged`, Docker socket mounts, passed-through devices and added Linux capabilities
- bind-mount sources and their host UID/GID ownership when host access is configured
- single-container detail checks plus a throttled **Identity audit** across all containers in the selected environment
- whether the container UID/GID already maps to a local host account/group
Dockwatch deliberately does **not** claim that a root container can always be converted to non-root. Application-internal filesystem permissions, entrypoints and image-specific `PUID`/`PGID` conventions cannot be proven from Docker metadata alone. It also never rewrites Compose `user:` automatically.
A matching local host username is **not required by Docker**. Linux file ownership is numeric; creating a host account can nevertheless make bind-mount ownership, backups and administration easier.
Host inspection is opt-in. For read-only inspection, set:
```env
HOST_ROOT=/host
ALLOW_HOST_USER_MANAGEMENT=false
ALLOW_HOST_PERMISSION_MANAGEMENT=false
```
and mount the host root read-only at `/host`. `examples/compose-host-identity.override.yml` shows this setup.
Admins may optionally create a locked/non-login local host account using the container's server-side re-resolved UID/GID. This requires both a writable host-root mount and:
```env
ALLOW_HOST_USER_MANAGEMENT=true
```
See `examples/compose-host-user-management.override.yml`. The browser cannot supply an arbitrary UID/GID: Dockwatch re-inspects the container immediately before the change and derives the IDs itself. Existing numeric users/groups are reused and UID/name collisions are refused. Docker rootless/user-namespace remapping is detected where possible; automatic same-numbered host-account creation is refused when IDs are remapped. This operation is intentionally admin-only and disabled by default.
### Identity & Bind Mount Permissions
Dockwatch can now diagnose the actual bind-mount permission problem instead of stopping at "container runs as UID X". The container **Identity** dialog and each stack's **Permissions** tab show, per bind mount:
- effective PID 1 UID/GID and a separate expected **bind UID/GID**
- `PUID/PGID` or `USER_ID/GROUP_ID` when the image exposes those paired conventions
- host owner UID/GID and POSIX mode bits
- static writeability (`w+x` for directories, `w` for files)
- extended POSIX ACL detection when `getfacl` is available
- an optional non-mutating runtime `test -w` using the expected numeric identity
- read-only mounts, rootless/userns remapping and unsafe symlinked host paths as hard repair blockers
The repair flow is **Analyze → Preview → Repair → Verify**. For recursive ownership repair Dockwatch scans the tree first and shows how many files/directories differ. Automatic recursive repair is refused above 200,000 entries. Symlinks are never followed or chowned. `:ro` mounts are never repaired automatically.
Permission repair is a separate high-trust opt-in from host-user creation:
```env
HOST_ROOT=/host
ALLOW_HOST_PERMISSION_MANAGEMENT=true
```
and `/` must intentionally be mounted read-write at `/host`. See `examples/compose-host-permissions.override.yml`. The browser supplies only the container and its mount destination (for example `/config`); the backend re-reads `docker inspect` and resolves the real host source itself. Arbitrary host paths and arbitrary UID/GID values cannot be submitted for repair.
Ownership repair can operate on only the bind root or recursively. `chmod` is separate, optional, explicit, and only applies to the bind root; Dockwatch never automatically applies `chmod 777` and never recursively rewrites modes. After a repair Dockwatch re-runs the ownership/writeability analysis and reports the result.
**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.
### Host Security layer
Dockwatch can optionally act as a host-security control plane for the selected local or remote-agent environment. The feature is **disabled by default** and uses three independent capabilities so that audit, configuration changes and package management can be granted separately.
The **System → Host Security** page provides a posture overview and managed workflows for:
**Firewall (Auto / UFW / firewalld / nftables)**
- **Auto detect** adopts the already-active host firewall frontend: UFW first when it is the sole active frontend, firewalld when it is the sole active frontend, otherwise native nftables is used when available
- an explicit provider can be selected when you intentionally want to migrate or standardize a host
- only genuinely competing active frontends are treated as conflicts; for example UFW + firewalld, or UFW + an already-loaded Dockwatch nftables table
- the editor shows installed/active frontends plus the provider's existing runtime rules/state before any mutation
- common managed policy supports trusted IPv4/IPv6 CIDRs and ordered TCP/UDP **ALLOW / DENY / REJECT / LIMIT** rules
- provider-global default inbound management is an explicit opt-in for UFW/firewalld; nftables keeps its default policy inside Dockwatch's own table
- **UFW:** Dockwatch uses native `ufw` commands, tags its rules with deterministic `dockwatch:` comments and removes only those tagged rules; it never runs `ufw reset` and preserves foreign UFW rules
- **firewalld:** Dockwatch targets a selected zone, uses native rich rules, and preserves unrelated zone services/ports/sources/rich rules; default zone target changes are opt-in
- **nftables:** Dockwatch continues to own only `table inet dockwatch`, validates with `nft -c`, never runs `flush ruleset`, and does not alter Docker NAT/FORWARD chains
- when an enabled policy is applied to an inactive UFW/firewalld provider, Dockwatch activates the selected frontend inside the rollback window; rollback restores the previous active state
- **timed rollback** (30–600 seconds, UI default 90 seconds) after apply; changes must be explicitly kept after management connectivity is verified
- package installation/upgrade uses the selected provider package instead of always installing nftables
- browser clients never submit arbitrary firewall command text; the backend renders and executes only the typed policy model
**Fail2Ban**
- package/runtime/boot status
- package installation/upgrade when separately enabled
- managed global ban/find/max-retry/backend settings
- ignore IP/CIDR list
- configurable jails with filter, port, backend, log path and per-jail retry threshold
- server-side `fail2ban-client -t` validation before activation/reload
- Dockwatch owns only `/etc/fail2ban/jail.d/dockwatch.local`; distro and administrator configuration is preserved
**Linux Audit (auditd)**
- package/runtime/boot status
- managed watches for identity files, sudoers, SSH configuration, Docker socket/configuration, systemd units and kernel-module configuration
- additional explicitly configured file watches
- path/key/permission validation
- `augenrules --check` validation and `augenrules --load` activation
- Dockwatch owns only `/etc/audit/rules.d/90-dockwatch.rules`; unrelated audit rules remain untouched
**Installation and maintenance**
- install or upgrade the selected firewall provider (`ufw`, `firewalld` or `nftables`), `fail2ban` and `auditd`/`audit` using the detected host package manager
- supported package-manager families: apt, dnf, yum, zypper, apk and pacman
- enable, disable, restart and (where meaningful) reload the corresponding host services
- current package/service/config-drift information in the UI
- backups of Dockwatch-managed host configuration before replacement
- all mutation endpoints are administrator-only and flow through Dockwatch's existing origin/CSRF guard and activity audit log
Dockwatch intentionally manages its own drop-in/configuration scope rather than rewriting the host's entire security configuration. It is a focused administration layer, not a replacement for a complete CIS/STIG benchmark, SELinux/AppArmor policy management, an enterprise EDR, or distro-specific security tooling.
#### Capability modes
Read-only file/configuration audit:
```yaml
services:
dockwatch:
environment:
HOST_ROOT: /host
HOST_SECURITY_ENABLED: "true"
ALLOW_HOST_SECURITY_CHANGES: "false"
ALLOW_HOST_PACKAGE_MANAGEMENT: "false"
volumes:
- /:/host:ro
```
A ready-to-use example is `examples/compose-host-security-audit.override.yml`. In this mode Dockwatch cannot mutate the host. Runtime/service checks that require entering the host namespaces may be unavailable and are reported as such.
Full host-security management is intentionally high privilege:
```yaml
services:
dockwatch:
pid: host
privileged: true
environment:
HOST_ROOT: /host
HOST_SECURITY_ENABLED: "true"
ALLOW_HOST_SECURITY_CHANGES: "true"
ALLOW_HOST_PACKAGE_MANAGEMENT: "true"
# Arch/AUR optional, e.g. deploy
HOST_AUR_USER: ""
HOST_SECURITY_HOST_PID: "1"
volumes:
- /:/host:rw
```
See `examples/compose-host-security.override.yml`. Dockwatch verifies that `HOST_ROOT` and `/proc/<HOST_SECURITY_HOST_PID>/root` refer to the same host before enabling its namespace executor. Host commands are executed through `nsenter` in the target host namespaces; the browser can select only predefined operations and cannot submit arbitrary shell commands.
Because this mode grants Dockwatch broad host-administration capability, use OIDC, restrict the `admin` role carefully, protect the Dockwatch database/`APP_SECRET`, and do not expose an agent token or the UI to untrusted networks.
The same security APIs are available through the existing master/agent relay, so each agent can audit/manage **its own host** when that agent has been configured with the appropriate host-security capability. A master does not silently inherit host privileges on an agent.
#### System package updates
The **System-Updates** page uses the same selected environment as Docker/Host Security. Read-only update discovery requires the host-security namespace executor. Installing updates additionally requires `ALLOW_HOST_SECURITY_CHANGES=true` and `ALLOW_HOST_PACKAGE_MANAGEMENT=true`.
Supported update strategies:
- **apt**: simulate `dist-upgrade` for discovery; refresh with `apt-get update`; install with non-interactive `apt-get dist-upgrade -y --no-remove`
- **dnf**: `check-update`; install with `dnf upgrade --refresh -y`
- **yum**: `check-update`; install with `yum update -y`
- **pacman**: prefer `checkupdates` for safe discovery; install official packages only through full `pacman -Syu --noconfirm`
- **AUR**: detect `paru` or `yay`, query with `-Qua`, update AUR-only with `-Sua`, or combine repository+AUR updates with `-Syu`
- **zypper/apk** remain supported because the Host Security package installer already supports those families
For AUR, configure a real non-root host account:
```yaml
environment:
HOST_AUR_USER: deploy
```
That user must be able to run the helper and perform its package-manager escalation without an interactive password prompt. Dockwatch passes `--sudoflags=-n`; it never stores or submits a sudo password.
### 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
- container identity analysis, bind-mount permission repair and optional host-account creation on the agent host
- monitoring probes
- Git clone/sync/deploy
- host-security audit/management when explicitly enabled on that agent
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 / v9.2
The v9.2 identity extension is opt-in, admin-gated and preserves the existing least-surprise rule: diagnostics are read-only by default and no container user or host account is changed automatically.
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
HOST_ROOT=
ALLOW_HOST_USER_MANAGEMENT=false
ALLOW_HOST_PERMISSION_MANAGEMENT=false
HOST_SECURITY_ENABLED=false
ALLOW_HOST_SECURITY_CHANGES=false
ALLOW_HOST_PACKAGE_MANAGEMENT=false
HOST_AUR_USER=
HOST_SECURITY_HOST_PID=1
```
`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.
## v9.3.1 build-context fix
v9.3 accidentally used the broad ignore pattern `dockwatch` in both `.gitignore` and `.dockerignore`. Because patterns without a slash match path components recursively, that could hide `cmd/dockwatch/` from Git and from the Docker build context. v9.3.1 removes that pattern, writes local Makefile builds to `bin/dockwatch`, ignores only `bin/`/`dist/`, and makes the Dockerfile fail early with a clear message if `cmd/dockwatch/main.go` is ever missing from the build context.
## 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 this source snapshot 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 ./internal/hostsecurity
go vet ./...
go test -race ./internal/config ./internal/gitops ./internal/httpapi ./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks ./internal/hostsecurity
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.
### Module metadata / `go mod tidy`
The Docker builder runs `go mod tidy` after the full source tree has been copied and before compiling. This is intentional: Go 1.17+ module graph pruning may require transitive dependencies to be recorded as indirect requirements once the actual imported package graph is known. A checkout with an older/incomplete `go.mod` or without `go.sum` therefore still builds reproducibly in CI instead of failing late with `updates to go.mod needed`.
For repository maintenance, run:
```bash
go mod tidy
git add go.mod go.sum
git commit -m "chore: tidy Go modules"
```
CI can use `make verify` to fail when `go.mod`/`go.sum` are not committed in tidy form.
## v9.3.2 module-build fix
v9.3.2 fixes Docker/CI builds that stopped at `go: updates to go.mod needed; to update it: go mod tidy`. The builder now runs `go mod tidy` after the complete source tree has been copied and before `go build`, so indirect requirements required by Go module graph pruning are materialized in the build stage. The Makefile also includes `tidy` and `verify` targets for maintaining committed `go.mod`/`go.sum` files.
### Build-context note (v9.3.3)
Runtime directories in `.dockerignore` and `.gitignore` are root-anchored (`/stacks/`, `/data/`, `/bin/`, `/dist/`). This is intentional: unanchored patterns such as `stacks/` also match the source package `internal/stacks/` and can make Go try to resolve the project's own internal package as a remote module during `go mod tidy`.
### v9.5.2 UI polish
- Firewall rule editor uses a wide responsive modal and keeps long CIDRs, comments and provider output inside the dialog.
- Security details and notices wrap long unbroken values safely.
- Light mode now has consistent navigation, buttons, inputs, tables, tabs, code/editor surfaces, notices, modals and Host Security cards instead of inheriting dark-only hard-coded colors.