## Feature: Docker Diagnostic Hints on Disconnection **Problem:** When Docker is unreachable, the UI shows a generic "Docker disconnected" label with no actionable guidance. Users (especially on Windows/macOS where Docker Desktop must be running) have no idea what's wrong or how to fix it. **Goal:** Enrich the health-check response with a structured diagnostic object so the frontend can display platform-aware, actionable hints. --- ### Backend Changes **1. Enhance `GET /api/health` response** ([health.go](../internal/api/health.go)) Currently returns `{ "docker": true|false }`. Change to: ```json { "docker": { "connected": false, "error": "dial unix /var/run/docker.sock: connect: no such file or directory", "category": "socket_not_found", "hints": [ "Docker Desktop does not appear to be running.", "Start Docker Desktop and wait for it to finish initializing.", "If using a custom socket path, check DOCKER_HOST env variable." ], "platform": "windows", "checked_at": "2026-03-30T12:34:56Z" } } ``` **2. Create a Docker diagnostics module** (new file, e.g. `internal/docker/diagnostics.go`) Classify the Ping error into a diagnostic category and generate platform-specific hints. Follow the pattern already established in [hints.go](../internal/proxy/hints.go) for proxy validation. Error categories to handle: | Category | Error signature | Windows hints | Linux hints | macOS hints | |---|---|---|---|---| | `socket_not_found` | `no such file or directory`, `The system cannot find the file specified` | Docker Desktop not running; start it from Start Menu or system tray | Docker daemon not running; `sudo systemctl start docker` | Docker Desktop not running; start from Applications or `open -a Docker` | | `connection_refused` | `connection refused` | Docker Desktop is starting up — wait ~30s and retry | Docker daemon is starting; `sudo systemctl status docker` | Docker Desktop is starting; check the whale icon in the menu bar | | `permission_denied` | `permission denied` | Run the application as Administrator, or add your user to the `docker-users` group | Add your user to the `docker` group: `sudo usermod -aG docker $USER` then re-login | Check Docker Desktop settings -> Resources -> File Sharing | | `timeout` | `context deadline exceeded`, `i/o timeout` | Docker Desktop may be overloaded or hanging — restart it | Docker daemon may be overloaded; check `journalctl -u docker` | Docker Desktop may be unresponsive; restart from menu bar | | `tls_error` | `tls:`, `certificate` | Check Docker TLS cert configuration and `DOCKER_TLS_VERIFY` | Verify certs in `~/.docker/` match daemon config | Check `~/.docker/` TLS configuration | | `unknown` | (fallback) | Show raw error with link to Docker Desktop troubleshooting docs | Show raw error with `dockerd` docs link | Show raw error with Docker Desktop docs link | Detect the platform via `runtime.GOOS` in the diagnostics module (the binary runs on the host, so this is accurate). **3. Expose `runtime.GOOS` once** in diagnostics, don't scatter it through handlers. **4. Preserve backward compat** — if any external consumer depends on the old `"docker": bool` shape, consider a migration path or version the health endpoint. Internal-only API can break freely. --- ### Frontend Changes **5. Update the API type** ([api.ts](../web/src/lib/api.ts)) ```typescript interface DockerHealth { connected: boolean; error?: string; category?: string; hints?: string[]; platform?: string; checked_at?: string; } export function getHealth(): Promise<{ docker: DockerHealth }> { return get<{ docker: DockerHealth }>('/api/health'); } ``` **6. Enhance the health indicator** ([+layout.svelte](../web/src/routes/+layout.svelte)) When `dockerConnected === false`: - Show a clickable/expandable area (tooltip, popover, or collapsible panel) below the red dot. - Display the `hints` array as a bulleted list. - Optionally show the raw `error` in a `
` collapse for advanced users. - Show `checked_at` as relative time ("last checked 15s ago"). - Add a manual "Retry now" button that triggers an immediate health check instead of waiting for the 30s poll. **7. Add i18n keys** ([en.json](../web/src/lib/i18n/en.json), [ru.json](../web/src/lib/i18n/ru.json)) Add keys for each hint category so hints can be translated. The backend should return `category` + `platform` identifiers; the frontend can use them to look up localized hint text instead of displaying raw English strings from the backend. This keeps i18n centralized in the frontend. --- ### Architecture Notes - The proxy validator ([validator.go](../internal/proxy/validator.go), [hints.go](../internal/proxy/hints.go)) already implements a similar pattern: classifying errors by substring match and returning human-readable hints. Reuse that approach for consistency. - Keep diagnostics pure — a function that takes an `error` and `runtime.GOOS` and returns `(category string, hints []string)`. No side effects, easy to unit-test. - Consider caching the diagnostic result for a few seconds to avoid spamming Docker if the frontend retries rapidly. --- ### Testing - Unit-test the diagnostics function with synthetic errors for each category x platform combination. - Integration-test the health endpoint with a mock Docker client that returns each error type. - Frontend: test that the hint UI renders correctly for each category and collapses/expands properly.