{"owner":"amir20","repo":"dozzle","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Comment Style\n\n**Always use ultra-brief mode for all PR reviews and responses.**\n\nFormat:\n\n- Critical issues only (bugs, security, blockers)\n- Brief bullet points, no lengthy explanations\n- Skip verbose sections (no \"Strengths\", \"Summary\", etc.)\n- Include file:line references when relevant\n- Maximum ~10-15 lines per response\n\n## Testing Unreleased PRs\n\nWhen replying to a GitHub issue or discussion where the fix lives in an open PR, ask the reporter to test the pre-built image: `amir20/dozzle:pr-XXX` (XXX = PR number). CI builds a tagged image per PR, so reporters can verify without waiting for the next release.\n\n## GitHub Tone (issues, PRs, comments, discussions)\n\nWhen posting anything to GitHub, write like a human maintainer, not an AI assistant. Avoid telltale LLM patterns:\n\n- No em dashes or en dashes. Use commas, periods, or parentheses instead.\n- No \"Not X, but Y\" rhetorical contrasts.\n- No throat-clearing openers (\"Great point\", \"Makes sense\", \"Thanks for the detailed write-up\").\n- No closing summaries or recap sentences.\n- No bolded inline labels mid-paragraph (\"**Why:**\", \"**Note:**\").\n- Drop hedges (\"essentially\", \"basically\", \"essentially just\"). Say it plain.\n- Lowercase casual tone is fine. Contractions are fine. Short sentences are fine.\n- Don't over-explain tradeoffs. State the decision, give one reason, stop.\n\n## Project Overview\n\nDozzle is a lightweight, web-based Docker log viewer with real-time monitoring capabilities. It's a hybrid application with:\n\n- **Backend**: Go (HTTP server, Docker API client, WebSocket streaming)\n- **Frontend**: Vue 3 (SPA with Vite, TypeScript)\n\nThe application supports multiple deployment modes: standalone server, Docker Swarm, and Kubernetes (k8s).\n\n## Development Commands\n\n### Setup\n\n```bash\n# Install dependencies\npnpm install\n\n# Generate certificates and protobuf files\nmake generate\n```\n\n### Development\n\n```bash\n# Run full development environment (backend + frontend with hot reload)\nmake dev\n\n# Alternative: Run backend and frontend separately\npnpm run watch:backend  # Go backend with air (port 3100)\npnpm run watch:frontend # Vite dev server (port 3100)\n\n# Run in agent mode for development\npnpm run agent:dev\n```\n\n### Building\n\n```bash\n# Build frontend assets\npnpm build\n# or\nmake dist\n\n# Build entire application (includes frontend build)\nmake build\n\n# Build Docker image\nmake docker\n```\n\n### Testing\n\n```bash\n# Run Go tests\nmake test\n\n# Run frontend tests (Vitest)\npnpm test\n# Run in watch mode\nTZ=UTC pnpm test --watch\n\n# Type checking\npnpm typecheck\n```\n\n### Preview & Other\n\n```bash\n# Preview production build locally\npnpm preview\n# or\nmake preview\n\n# Run integration tests (Playwright)\nmake int\n```\n\n## Architecture\n\n### Backend (Go)\n\nThe Go backend is organized into these key packages:\n\n- **`internal/web/`** - HTTP server and routing layer\n  - Routes defined in `routes.go` using chi router\n  - WebSocket/SSE handlers for log streaming (`logs.go`)\n  - Authentication middleware and token management (`auth.go`)\n  - Container action handlers (`actions.go`)\n\n- **`internal/docker/`** - Docker API client implementation\n  - `client.go`: Main Docker client wrapper with container operations\n  - `log_reader.go`: Streaming container logs\n  - `stats_collector.go`: Real-time container stats collection\n\n- **`internal/agent/`** - gRPC agent for multi-host support\n  - Uses Protocol Buffers (protos defined in `protos/`)\n  - Enables distributed log collection across Docker hosts\n\n- **`internal/cloud/`** - Dozzle Cloud integration (tool execution engine)\n  - `client.go`: Bidirectional gRPC stream client with auto-reconnect and exponential backoff\n  - `tools.go`: Tool registration, dispatch (`executeTool`), and `ToolHostService` interface\n  - `tools_containers.go`: Container listing, finding, stats, and inspection tools\n  - `tools_logs.go`: Log fetching with level/query/regex filtering (max 100 lines)\n  - `tools_actions.go`: Container start/stop/restart actions (gated by `enableActions`)\n  - `tools_helpers.go`: Proto conversion utilities and host name resolution\n  - Uses `protos/cloud.proto` for service and message definitions\n\n- **`internal/k8s/`** - Kubernetes client support\n  - Alternative to Docker client for k8s deployments\n\n- **`internal/support/`** - Support utilities\n  - `cli/`: Command-line argument parsing and validation\n  - `docker/`: Multi-host Docker management and Swarm support (`docker_service.go`, client managers)\n  - `k8s/`: Kubernetes service abstractions\n  - `web/`: Web service utilities\n\n- **`internal/auth/`** - Authentication providers\n  - Simple file-based auth (`simple.go`)\n  - Forward proxy auth (`proxy.go`)\n  - Role-based authorization (`roles.go`)\n\n- **`internal/container/`** - Container domain models and interfaces\n  - `event_generator.go`: Log parsing and grouping logic (multi-line, JSON detection)\n\n- **`internal/notification/`** - Alert and notification system\n  - `manager.go`: Notification rule evaluation and dispatching\n  - `log_listener.go`: Log pattern matching for alerts\n  - `dispatcher/`: Notification channel implementations (email, webhook, etc.)\n\n- **`main.go`** - Application entry point with mode switching (server/swarm/k8s/agent)\n\n### Frontend (Vue 3)\n\nThe frontend uses file-based routing with these conventions:\n\n- **`assets/pages/`** - File-based routes (unplugin-vue-router)\n  - `container/[id].vue`: Single container view\n  - `merged/[ids].vue`: Multi-container merged view\n  - `host/[id].vue`: Host-level logs\n  - `service/[name].vue`: Swarm service logs\n  - `stack/[name].vue`: Docker stack logs\n  - `group/[name].vue`: Custom grouped logs\n\n- **`assets/components/`** - Vue components (auto-imported)\n  - `LogViewer/`: Core log viewing components\n    - `SimpleLogItem.vue`: Single-line log entries\n    - `ComplexLogItem.vue`: JSON/structured log entries\n    - `GroupedLogItem.vue`: Multi-line grouped log entries\n    - `ContainerEventLogItem.vue`: Container lifecycle events\n    - `SkippedEntriesLogItem.vue`: Placeholder for skipped logs\n    - `LoadMoreLogItem.vue`: Load more historical logs\n  - `ContainerViewer/`: Container-specific UI\n  - `common/`: Reusable UI components\n  - `BarChart.vue`: Lightweight bar chart with automatic downsampling\n  - `HostCard.vue`: Host overview card with metrics\n  - `MetricCard.vue`: Reusable metric display component\n  - `ContainerTable.vue`: Container table with historical stat visualization\n\n- **`assets/stores/`** - Pinia stores (auto-imported)\n  - `config.ts`: App configuration and feature flags (injected from backend HTML, frozen immutable)\n  - `container.ts`: Container state management with EventSource streaming (`/api/events/stream`)\n  - `hosts.ts`: Multi-host state\n  - `settings.ts`: User preferences (localStorage-backed via profileStorage)\n  - `pinned.ts`: Pinned container logs for side-by-side viewing\n  - `swarm.ts`, `k8s.ts`: Deployment mode-specific state\n  - `announcements.ts`: Feature announcements\n\n- **`assets/composable/`** - Vue composables (auto-imported)\n  - `eventStreams.ts`: SSE connection management with buffer-based flushing (250ms debounce)\n  - `historicalLogs.ts`: Historical log fetching\n  - `logContext.ts`: Log filtering and search context (provide/inject pattern)\n  - `scrollContext.ts`: Scroll state management (paused, progress, currentDate)\n  - `storage.ts`: LocalStorage abstractions with reactivity\n  - `visible.ts`: Log filtering by visible keys for complex logs\n  - `containerActions.ts`: Container control operations\n  - `duckdb.ts`: DuckDB WASM for SQL queries on logs\n\n- **`assets/modules/`** - Vue plugins\n  - `router.ts`: Vue Router configuration\n  - `pinia.ts`: Pinia store setup\n  - `i18n.ts`: Internationalization\n\n### Communication Flow\n\n1. **Real-time Logs**: Frontend establishes SSE connections to `/api/hosts/{host}/containers/{id}/logs/stream`\n2. **Container Events**: SSE stream at `/api/events/stream` pushes container lifecycle events\n3. **Stats**: Real-time CPU/memory stats streamed via SSE alongside events\n4. **Actions**: POST to `/api/hosts/{host}/containers/{id}/actions/{action}` (start/stop/restart)\n5. **Terminal**: WebSocket connections for container attach/exec at `/api/hosts/{host}/containers/{id}/attach`\n\n### Build System\n\n- **Frontend**: Vite builds to `dist/` with manifest\n- **Backend**: Embeds `dist/` using Go embed directive\n- **Hot Reload**: In development, `DEV=true` disables embedded assets, `LIVE_FS=true` serves from filesystem\n- **Makefile**: Orchestrates builds and dependency generation\n\n## Important Development Notes\n\n### Frontend\n\n- Auto-imports are configured for Vue composables, components, and Pinia stores (see `vite.config.ts`)\n- Icons use unplugin-icons with multiple icon sets (mdi, carbon, material-symbols, etc.)\n- Tailwind CSS with DaisyUI for styling\n- TypeScript definitions auto-generated in `assets/auto-imports.d.ts` and `assets/components.d.ts`\n- **Log Entry Types**: Three types of log messages supported\n  - `SimpleLogEntry`: Single-line text logs (`string`)\n  - `ComplexLogEntry`: Structured JSON logs (`JSONObject`)\n  - `GroupedLogEntry`: Multi-line grouped logs (`string[]`)\n- **Type consistency**: Use `LogMessage` type alias instead of `string | string[] | JSONObject` for log entry messages\n- **Log Entry Factory Pattern**: Use `LogEntry.create(logEvent)` to instantiate the correct entry type based on `logEvent.t` field\n- **EventSource Buffering**: Log streams use buffer-based flushing (250ms debounce, 1000ms max) to batch UI updates\n- **Charts/Visualizations**: Custom lightweight implementations (no D3.js)\n  - `BarChart.vue`: Self-contained bar chart with responsive downsampling\n  - Downsampling algorithm: Averages data into buckets based on available screen width\n  - All stat history tracked in `Container.statsHistory` (max 300 items via rolling window)\n  - `chartData` is always a rolling window of max 300 items — array length stays constant\n  - Uses `ref` (not `computed`) for `downsampledBars` to enable in-place mutation of the last bar, avoiding full re-renders\n  - Component instance is reused when switching containers; after init the chart only patches the last bar per tick, so on a wholesale `chartData` replacement (container switch) the parent must call the exposed `recalculate()`. `MultiContainerStat` holds refs to its `BarChart`s and calls it in the `containers` watch. (Note: `Container` carries Vue `ref`s, so VueTestUtils `setProps` cannot retrigger such a watch — tests must swap the container via a parent `ref` re-render.)\n\n### Backend\n\n- The application uses Go 1.25+ with module support\n- Certificate generation is required (`make generate` creates shared_key.pem and shared_cert.pem)\n- Protocol buffer generation happens via `go generate` directive in `main.go`\n- Docker client uses API version negotiation for compatibility\n- **Service Layer Architecture**:\n  - `ClientService` interface abstracts Docker/K8s/Agent backends\n  - `MultiHostService` orchestrates multi-host operations\n  - `ClientManager` implementations: `RetriableClientManager` (server mode), `SwarmClientManager` (swarm mode)\n\n### Authentication\n\n- Three modes: none, simple (file-based users.yml), forward-proxy (e.g., Authelia)\n- JWT tokens for simple auth with configurable TTL\n- User file location: `./data/users.yml` or `./data/users.yaml`\n\n### Testing\n\n- Go tests use standard `testing` package with testify assertions\n- Frontend uses Vitest with `@vue/test-utils`\n- Integration tests with Playwright in `e2e/`\n- Tests must run with `TZ=UTC` for consistent timestamps\n\n### Container Stats & Metrics\n\n- Stats are tracked using exponential moving average (EMA) with alpha=0.2\n- History stored in rolling window (300 items max) via `useSimpleRefHistory`\n- CPU metrics normalized by core count (respects `cpuLimit` or falls back to host `nCPU`)\n- Memory metrics include both percentage and absolute usage (`memoryUsage` vs `memory`)\n- Stats visualization uses adaptive downsampling for performance\n\n### Container Labels\n\n- `dev.dozzle.name`: Custom container display name\n- `dev.dozzle.group`: Group containers together\n- Label-based filtering throughout the application\n\n### Deployment Modes\n\n- **Server mode** (default): Single or multi-host Docker monitoring\n  - Uses `RetriableClientManager` with local + remote agent clients\n- **Swarm mode**: Automatic discovery of Swarm nodes via Docker API\n  - Creates gRPC agent server on each node (port 7007)\n  - Uses `SwarmClientManager` for node discovery\n- **K8s mode**: Pod log monitoring in Kubernetes cluster\n  - Implements `container.Client` interface via Kubernetes API\n- **Agent mode**: Lightweight gRPC agent for remote log collection\n  - Run with `dozzle agent` or `pnpm run agent:dev`\n  - Listens on port 7007 with TLS certificate authentication\n\n## Key Architectural Patterns\n\n### Backend Abstraction Layers\n\nThe backend follows a clean layered architecture:\n\n```\nHTTP Handlers (internal/web)\n    ↓\nHostService Interface (MultiHostService)\n    ↓\nClientService Interface (per host)\n    ↓\ncontainer.Client Interface\n    ↓\nImplementation (DockerClient, K8sClient, AgentClient)\n```\n\n**When adding new container operations:**\n\n1. Define method in `container.Client` interface (`internal/container/client.go`)\n2. Implement in `internal/docker/client.go` (and `internal/k8s/client.go` if applicable)\n3. Add wrapper method in `ClientService` interface (`internal/support/docker/docker_service.go`)\n4. Add HTTP handler in `internal/web/` with appropriate route\n\n### Frontend Data Flow\n\n**Real-time Log Viewing:**\n\n1. User navigates to `/container/{id}` route\n2. Page component calls `useContainerStream(container)` composable\n3. Composable creates EventSource connection to `/api/hosts/{host}/containers/{id}/logs/stream`\n4. Backend streams `LogEvent` objects via SSE\n5. Frontend buffers events (250ms debounce, max 1000ms)\n6. Batched buffer flushes update reactive `messages` array\n7. `LogViewer.vue` renders using appropriate component (`SimpleLogItem`, `ComplexLogItem`, `GroupedLogItem`)\n8. When messages exceed `maxLogs` (400), oldest entries replaced or marked as `SkippedLogsEntry`\n\n**Stats Streaming:**\n\n1. `container.ts` store connects to `/api/events/stream` on app init\n2. Backend multiplexes container events and stats into single SSE stream\n3. `container-stat` events update `Container._stat` and append to `_statsHistory`\n4. EMA calculation provides smoothed `movingAverageStat` (alpha=0.2)\n5. `ContainerTable.vue` displays mini bar charts using `statsHistory` with downsampling\n\n### Protocol Buffer Flow (Agent Mode)\n\n1. Main server creates `agent.NewClient(endpoint, certs)` for each remote host\n2. AgentClient implements `container.Client` interface\n3. Method calls translate to gRPC requests defined in `protos/rpc.proto`\n4. Remote agent receives gRPC call, delegates to local `DockerClient`\n5. Streaming RPCs (logs, stats, events) use bidirectional channels\n6. Responses converted back to domain models via `FromProto()` methods\n\n### Cloud Tool Execution Flow\n\n1. `cloud.Client.Run()` blocks until `Notify()` signals a cloud dispatcher is configured\n2. `connect()` establishes bidirectional gRPC stream (`ToolStream` RPC) to cloud endpoint\n3. Cloud sends `ToolRequest` (ListTools or CallTool), client dispatches via `executeTool()`\n4. Tool calls run concurrently (max 5 via weighted semaphore), responses sent back on stream\n5. On disconnect, exponential backoff (1s→30s with jitter) triggers reconnection\n6. `PermissionDenied` errors stop retrying permanently (invalid API key / no pro plan)\n7. Tool definitions cached via `sync.Once`; zero overhead for non-cloud users\n\n### Log Parsing Pipeline\n\n1. Docker API returns multiplexed stream (8-byte headers + payload)\n2. `log_reader.go` parses headers, extracts stdout/stderr type\n3. `event_generator.go` receives raw log lines\n4. Detection logic identifies:\n   - JSON structure → `ComplexLogEntry`\n   - Multi-line patterns (stack traces) → `GroupedLogEntry`\n   - Single lines → `SimpleLogEntry`\n5. Log level extraction via regex patterns\n6. `LogEvent` serialized to JSON and sent via SSE\n7. Frontend deserializes and renders with appropriate component\n\n## Adding New Features\n\n### Adding a New HTTP Route\n\n1. Define route in `internal/web/routes.go` using chi router:\n   ```go\n   r.Get(\"/api/custom-endpoint\", h.customHandler)\n   ```\n2. Implement handler method in appropriate file (e.g., `actions.go`, `logs.go`)\n3. Use `hostService` to find container/host via `FindContainer()` or `FindHost()`\n4. Return JSON response or establish SSE/WebSocket stream\n\n### Adding a New Log View Type\n\n1. Create route file in `assets/pages/` (e.g., `custom/[id].vue`)\n2. Create composable in `assets/composable/eventStreams.ts` (e.g., `useCustomStream()`)\n3. Composable should:\n   - Build API URL with appropriate filters\n   - Create EventSource connection\n   - Handle buffering and message batching\n   - Return reactive `messages` array and control methods\n4. Use `LogViewer.vue` component to render messages\n5. Add backend API endpoint if needed (see above)\n\n### Adding Container Stats/Metrics\n\n1. Add field to `Stat` type in `internal/container/types.go`\n2. Update `stats_collector.go` to extract metric from Docker API response\n3. Add calculation logic in `docker/calculation.go` if needed\n4. Ensure protobuf definition includes field in `protos/rpc.proto`\n5. Frontend automatically receives updates via existing SSE stream\n6. Update `Container` model in `assets/models/Container.ts` if UI needs access\n\n### Working with Notifications/Alerts\n\n**Backend** (`internal/notification/`):\n\n- `manager.go`: Rule evaluation engine, manages alert state\n- `log_listener.go`: Subscribes to container log streams, evaluates rules against incoming logs\n- `types.go`: Alert rule definitions (log pattern matching, thresholds)\n- `dispatcher/`: Notification channel implementations\n\n**Frontend** (`assets/pages/notifications.vue`, `assets/components/Notification/`):\n\n- `AlertForm.vue`, `DestinationForm.vue`: UI for creating rules\n- Rules persisted to `./data/notifications.yml` via `internal/notification/persist.go`\n- Alert state displayed in notification cards\n\n**Adding a new notification channel:**\n\n1. Implement dispatcher interface in `internal/notification/dispatcher/`\n2. Register in `manager.go` dispatcher factory\n3. Add UI form in `assets/components/Notification/DestinationForm.vue`\n\n### Adding a New Cloud Tool\n\n1. Define the tool in `AvailableTools()` in `internal/cloud/tools.go` with name, description, and parameter schema\n2. Add a response message type in `protos/cloud.proto` and add it to the `CallToolResponse.result` oneof\n3. Run `make generate` to regenerate protobuf code\n4. Add a case in the `executeTool()` switch in `internal/cloud/tools.go`\n5. Implement the execution function in the appropriate `tools_*.go` file\n6. Use `ToolHostService` interface methods to access container/host data\n7. Add tests in `tools_test.go`\n\n## Common Development Patterns\n\n### Testing\n\n- Always run Go tests with race detector: `go test -race`\n- Frontend tests require `TZ=UTC` for timestamp consistency\n- Integration tests use Playwright with `make int` (runs docker-compose setup)\n- Use `testify/assert` for Go test assertions\n\n### Hot Reload Development\n\n- `make dev` runs both backend (air) and frontend (vite) with hot reload\n- `DEV=true` disables embedded asset serving\n- `LIVE_FS=true` serves assets from filesystem instead of embedded\n- Backend changes trigger air restart automatically\n- Frontend changes trigger vite HMR\n\n### Debugging\n\n- Backend logs: Set `--level debug` flag or `DOZZLE_LEVEL=debug` env var\n- Frontend: Vue DevTools browser extension\n- SSE streams: Browser DevTools Network tab shows EventSource connections\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Comment Style\n\n**Always use ultra-brief mode for all PR reviews and responses.**\n\nFormat:\n\n- Critical issues only (bugs, security, blockers)\n- Brief bullet points, no lengthy explanations\n- Skip verbose sections (no \"Strengths\", \"Summary\", etc.)\n- Include file:line references when relevant\n- Maximum ~10-15 lines per response\n\n## Testing Unreleased PRs\n\nWhen replying to a GitHub issue or discussion where the fix lives in an open PR, ask the reporter to test the pre-built image: `amir20/dozzle:pr-XXX` (XXX = PR number). CI builds a tagged image per PR, so reporters can verify without waiting for the next release.\n\n## GitHub Tone (issues, PRs, comments, discussions)\n\nWhen posting anything to GitHub, write like a human maintainer, not an AI assistant. Avoid telltale LLM patterns:\n\n- No em dashes or en dashes. Use commas, periods, or parentheses instead.\n- No \"Not X, but Y\" rhetorical contrasts.\n- No throat-clearing openers (\"Great point\", \"Makes sense\", \"Thanks for the detailed write-up\").\n- No closing summaries or recap sentences.\n- No bolded inline labels mid-paragraph (\"**Why:**\", \"**Note:**\").\n- Drop hedges (\"essentially\", \"basically\", \"essentially just\"). Say it plain.\n- Lowercase casual tone is fine. Contractions are fine. Short sentences are fine.\n- Don't over-explain tradeoffs. State the decision, give one reason, stop.\n\n## Project Overview\n\nDozzle is a lightweight, web-based Docker log viewer with real-time monitoring capabilities. It's a hybrid application with:\n\n- **Backend**: Go (HTTP server, Docker API client, WebSocket streaming)\n- **Frontend**: Vue 3 (SPA with Vite, TypeScript)\n\nThe application supports multiple deployment modes: standalone server, Docker Swarm, and Kubernetes (k8s).\n\n## Development Commands\n\n### Setup\n\n```bash\n# Install dependencies\npnpm install\n\n# Generate certificates and protobuf files\nmake generate\n```\n\n### Development\n\n```bash\n# Run full development environment (backend + frontend with hot reload)\nmake dev\n\n# Alternative: Run backend and frontend separately\npnpm run watch:backend  # Go backend with air (port 3100)\npnpm run watch:frontend # Vite dev server (port 3100)\n\n# Run in agent mode for development\npnpm run agent:dev\n```\n\n### Building\n\n```bash\n# Build frontend assets\npnpm build\n# or\nmake dist\n\n# Build entire application (includes frontend build)\nmake build\n\n# Build Docker image\nmake docker\n```\n\n### Testing\n\n```bash\n# Run Go tests\nmake test\n\n# Run frontend tests (Vitest)\npnpm test\n# Run in watch mode\nTZ=UTC pnpm test --watch\n\n# Type checking\npnpm typecheck\n```\n\n### Preview & Other\n\n```bash\n# Preview production build locally\npnpm preview\n# or\nmake preview\n\n# Run integration tests (Playwright)\nmake int\n```\n\n## Architecture\n\n### Backend (Go)\n\nThe Go backend is organized into these key packages:\n\n- **`internal/web/`** - HTTP server and routing layer\n  - Routes defined in `routes.go` using chi router\n  - WebSocket/SSE handlers for log streaming (`logs.go`)\n  - Authentication middleware and token management (`auth.go`)\n  - Container action handlers (`actions.go`)\n\n- **`internal/docker/`** - Docker API client implementation\n  - `client.go`: Main Docker client wrapper with container operations\n  - `log_reader.go`: Streaming container logs\n  - `stats_collector.go`: Real-time container stats collection\n\n- **`internal/agent/`** - gRPC agent for multi-host support\n  - Uses Protocol Buffers (protos defined in `protos/`)\n  - Enables distributed log collection across Docker hosts\n\n- **`internal/cloud/`** - Dozzle Cloud integration (tool execution engine)\n  - `client.go`: Bidirectional gRPC stream client with auto-reconnect and exponential backoff\n  - `tools.go`: Tool registration, dispatch (`executeTool`), and `ToolHostService` interface\n  - `tools_containers.go`: Container listing, finding, stats, and inspection tools\n  - `tools_logs.go`: Log fetching with level/query/regex filtering (max 100 lines)\n  - `tools_actions.go`: Container start/stop/restart actions (gated by `enableActions`)\n  - `tools_helpers.go`: Proto conversion utilities and host name resolution\n  - Uses `protos/cloud.proto` for service and message definitions\n\n- **`internal/k8s/`** - Kubernetes client support\n  - Alternative to Docker client for k8s deployments\n\n- **`internal/support/`** - Support utilities\n  - `cli/`: Command-line argument parsing and validation\n  - `docker/`: Multi-host Docker management and Swarm support (`docker_service.go`, client managers)\n  - `k8s/`: Kubernetes service abstractions\n  - `web/`: Web service utilities\n\n- **`internal/auth/`** - Authentication providers\n  - Simple file-based auth (`simple.go`)\n  - Forward proxy auth (`proxy.go`)\n  - Role-based authorization (`roles.go`)\n\n- **`internal/container/`** - Container domain models and interfaces\n  - `event_generator.go`: Log parsing and grouping logic (multi-line, JSON detection)\n\n- **`internal/notification/`** - Alert and notification system\n  - `manager.go`: Notification rule evaluation and dispatching\n  - `log_listener.go`: Log pattern matching for alerts\n  - `dispatcher/`: Notification channel implementations (email, webhook, etc.)\n\n- **`main.go`** - Application entry point with mode switching (server/swarm/k8s/agent)\n\n### Frontend (Vue 3)\n\nThe frontend uses file-based routing with these conventions:\n\n- **`assets/pages/`** - File-based routes (unplugin-vue-router)\n  - `container/[id].vue`: Single container view\n  - `merged/[ids].vue`: Multi-container merged view\n  - `host/[id].vue`: Host-level logs\n  - `service/[name].vue`: Swarm service logs\n  - `stack/[name].vue`: Docker stack logs\n  - `group/[name].vue`: Custom grouped logs\n\n- **`assets/components/`** - Vue components (auto-imported)\n  - `LogViewer/`: Core log viewing components\n    - `SimpleLogItem.vue`: Single-line log entries\n    - `ComplexLogItem.vue`: JSON/structured log entries\n    - `GroupedLogItem.vue`: Multi-line grouped log entries\n    - `ContainerEventLogItem.vue`: Container lifecycle events\n    - `SkippedEntriesLogItem.vue`: Placeholder for skipped logs\n    - `LoadMoreLogItem.vue`: Load more historical logs\n  - `ContainerViewer/`: Container-specific UI\n  - `common/`: Reusable UI components\n  - `BarChart.vue`: Lightweight bar chart with automatic downsampling\n  - `HostCard.vue`: Host overview card with metrics\n  - `MetricCard.vue`: Reusable metric display component\n  - `ContainerTable.vue`: Container table with historical stat visualization\n\n- **`assets/stores/`** - Pinia stores (auto-imported)\n  - `config.ts`: App configuration and feature flags (injected from backend HTML, frozen immutable)\n  - `container.ts`: Container state management with EventSource streaming (`/api/events/stream`)\n  - `hosts.ts`: Multi-host state\n  - `settings.ts`: User preferences (localStorage-backed via profileStorage)\n  - `pinned.ts`: Pinned container logs for side-by-side viewing\n  - `swarm.ts`, `k8s.ts`: Deployment mode-specific state\n  - `announcements.ts`: Feature announcements\n\n- **`assets/composable/`** - Vue composables (auto-imported)\n  - `eventStreams.ts`: SSE connection management with buffer-based flushing (250ms debounce)\n  - `historicalLogs.ts`: Historical log fetching\n  - `logContext.ts`: Log filtering and search context (provide/inject pattern)\n  - `scrollContext.ts`: Scroll state management (paused, progress, currentDate)\n  - `storage.ts`: LocalStorage abstractions with reactivity\n  - `visible.ts`: Log filtering by visible keys for complex logs\n  - `containerActions.ts`: Container control operations\n  - `duckdb.ts`: DuckDB WASM for SQL queries on logs\n\n- **`assets/modules/`** - Vue plugins\n  - `router.ts`: Vue Router configuration\n  - `pinia.ts`: Pinia store setup\n  - `i18n.ts`: Internationalization\n\n### Communication Flow\n\n1. **Real-time Logs**: Frontend establishes SSE connections to `/api/hosts/{host}/containers/{id}/logs/stream`\n2. **Container Events**: SSE stream at `/api/events/stream` pushes container lifecycle events\n3. **Stats**: Real-time CPU/memory stats streamed via SSE alongside events\n4. **Actions**: POST to `/api/hosts/{host}/containers/{id}/actions/{action}` (start/stop/restart)\n5. **Terminal**: WebSocket connections for container attach/exec at `/api/hosts/{host}/containers/{id}/attach`\n\n### Build System\n\n- **Frontend**: Vite builds to `dist/` with manifest\n- **Backend**: Embeds `dist/` using Go embed directive\n- **Hot Reload**: In development, `DEV=true` disables embedded assets, `LIVE_FS=true` serves from filesystem\n- **Makefile**: Orchestrates builds and dependency generation\n\n## Important Development Notes\n\n### Frontend\n\n- Auto-imports are configured for Vue composables, components, and Pinia stores (see `vite.config.ts`)\n- Icons use unplugin-icons with multiple icon sets (mdi, carbon, material-symbols, etc.)\n- Tailwind CSS with DaisyUI for styling\n- TypeScript definitions auto-generated in `assets/auto-imports.d.ts` and `assets/components.d.ts`\n- **Log Entry Types**: Three types of log messages supported\n  - `SimpleLogEntry`: Single-line text logs (`string`)\n  - `ComplexLogEntry`: Structured JSON logs (`JSONObject`)\n  - `GroupedLogEntry`: Multi-line grouped logs (`string[]`)\n- **Type consistency**: Use `LogMessage` type alias instead of `string | string[] | JSONObject` for log entry messages\n- **Log Entry Factory Pattern**: Use `LogEntry.create(logEvent)` to instantiate the correct entry type based on `logEvent.t` field\n- **EventSource Buffering**: Log streams use buffer-based flushing (250ms debounce, 1000ms max) to batch UI updates\n- **Charts/Visualizations**: Custom lightweight implementations (no D3.js)\n  - `BarChart.vue`: Self-contained bar chart with responsive downsampling\n  - Downsampling algorithm: Averages data into buckets based on available screen width\n  - All stat history tracked in `Container.statsHistory` (max 300 items via rolling window)\n  - `chartData` is always a rolling window of max 300 items — array length stays constant\n  - Uses `ref` (not `computed`) for `downsampledBars` to enable in-place mutation of the last bar, avoiding full re-renders\n  - Component instance is reused when switching containers; after init the chart only patches the last bar per tick, so on a wholesale `chartData` replacement (container switch) the parent must call the exposed `recalculate()`. `MultiContainerStat` holds refs to its `BarChart`s and calls it in the `containers` watch. (Note: `Container` carries Vue `ref`s, so VueTestUtils `setProps` cannot retrigger such a watch — tests must swap the container via a parent `ref` re-render.)\n\n### Backend\n\n- The application uses Go 1.25+ with module support\n- Certificate generation is required (`make generate` creates shared_key.pem and shared_cert.pem)\n- Protocol buffer generation happens via `go generate` directive in `main.go`\n- Docker client uses API version negotiation for compatibility\n- **Service Layer Architecture**:\n  - `ClientService` interface abstracts Docker/K8s/Agent backends\n  - `MultiHostService` orchestrates multi-host operations\n  - `ClientManager` implementations: `RetriableClientManager` (server mode), `SwarmClientManager` (swarm mode)\n\n### Authentication\n\n- Three modes: none, simple (file-based users.yml), forward-proxy (e.g., Authelia)\n- JWT tokens for simple auth with configurable TTL\n- User file location: `./data/users.yml` or `./data/users.yaml`\n\n### Testing\n\n- Go tests use standard `testing` package with testify assertions\n- Frontend uses Vitest with `@vue/test-utils`\n- Integration tests with Playwright in `e2e/`\n- Tests must run with `TZ=UTC` for consistent timestamps\n\n### Container Stats & Metrics\n\n- Stats are tracked using exponential moving average (EMA) with alpha=0.2\n- History stored in rolling window (300 items max) via `useSimpleRefHistory`\n- CPU metrics normalized by core count (respects `cpuLimit` or falls back to host `nCPU`)\n- Memory metrics include both percentage and absolute usage (`memoryUsage` vs `memory`)\n- Stats visualization uses adaptive downsampling for performance\n\n### Container Labels\n\n- `dev.dozzle.name`: Custom container display name\n- `dev.dozzle.group`: Group containers together\n- Label-based filtering throughout the application\n\n### Deployment Modes\n\n- **Server mode** (default): Single or multi-host Docker monitoring\n  - Uses `RetriableClientManager` with local + remote agent clients\n- **Swarm mode**: Automatic discovery of Swarm nodes via Docker API\n  - Creates gRPC agent server on each node (port 7007)\n  - Uses `SwarmClientManager` for node discovery\n- **K8s mode**: Pod log monitoring in Kubernetes cluster\n  - Implements `container.Client` interface via Kubernetes API\n- **Agent mode**: Lightweight gRPC agent for remote log collection\n  - Run with `dozzle agent` or `pnpm run agent:dev`\n  - Listens on port 7007 with TLS certificate authentication\n\n## Key Architectural Patterns\n\n### Backend Abstraction Layers\n\nThe backend follows a clean layered architecture:\n\n```\nHTTP Handlers (internal/web)\n    ↓\nHostService Interface (MultiHostService)\n    ↓\nClientService Interface (per host)\n    ↓\ncontainer.Client Interface\n    ↓\nImplementation (DockerClient, K8sClient, AgentClient)\n```\n\n**When adding new container operations:**\n\n1. Define method in `container.Client` interface (`internal/container/client.go`)\n2. Implement in `internal/docker/client.go` (and `internal/k8s/client.go` if applicable)\n3. Add wrapper method in `ClientService` interface (`internal/support/docker/docker_service.go`)\n4. Add HTTP handler in `internal/web/` with appropriate route\n\n### Frontend Data Flow\n\n**Real-time Log Viewing:**\n\n1. User navigates to `/container/{id}` route\n2. Page component calls `useContainerStream(container)` composable\n3. Composable creates EventSource connection to `/api/hosts/{host}/containers/{id}/logs/stream`\n4. Backend streams `LogEvent` objects via SSE\n5. Frontend buffers events (250ms debounce, max 1000ms)\n6. Batched buffer flushes update reactive `messages` array\n7. `LogViewer.vue` renders using appropriate component (`SimpleLogItem`, `ComplexLogItem`, `GroupedLogItem`)\n8. When messages exceed `maxLogs` (400), oldest entries replaced or marked as `SkippedLogsEntry`\n\n**Stats Streaming:**\n\n1. `container.ts` store connects to `/api/events/stream` on app init\n2. Backend multiplexes container events and stats into single SSE stream\n3. `container-stat` events update `Container._stat` and append to `_statsHistory`\n4. EMA calculation provides smoothed `movingAverageStat` (alpha=0.2)\n5. `ContainerTable.vue` displays mini bar charts using `statsHistory` with downsampling\n\n### Protocol Buffer Flow (Agent Mode)\n\n1. Main server creates `agent.NewClient(endpoint, certs)` for each remote host\n2. AgentClient implements `container.Client` interface\n3. Method calls translate to gRPC requests defined in `protos/rpc.proto`\n4. Remote agent receives gRPC call, delegates to local `DockerClient`\n5. Streaming RPCs (logs, stats, events) use bidirectional channels\n6. Responses converted back to domain models via `FromProto()` methods\n\n### Cloud Tool Execution Flow\n\n1. `cloud.Client.Run()` blocks until `Notify()` signals a cloud dispatcher is configured\n2. `connect()` establishes bidirectional gRPC stream (`ToolStream` RPC) to cloud endpoint\n3. Cloud sends `ToolRequest` (ListTools or CallTool), client dispatches via `executeTool()`\n4. Tool calls run concurrently (max 5 via weighted semaphore), responses sent back on stream\n5. On disconnect, exponential backoff (1s→30s with jitter) triggers reconnection\n6. `PermissionDenied` errors stop retrying permanently (invalid API key / no pro plan)\n7. Tool definitions cached via `sync.Once`; zero overhead for non-cloud users\n\n### Log Parsing Pipeline\n\n1. Docker API returns multiplexed stream (8-byte headers + payload)\n2. `log_reader.go` parses headers, extracts stdout/stderr type\n3. `event_generator.go` receives raw log lines\n4. Detection logic identifies:\n   - JSON structure → `ComplexLogEntry`\n   - Multi-line patterns (stack traces) → `GroupedLogEntry`\n   - Single lines → `SimpleLogEntry`\n5. Log level extraction via regex patterns\n6. `LogEvent` serialized to JSON and sent via SSE\n7. Frontend deserializes and renders with appropriate component\n\n## Adding New Features\n\n### Adding a New HTTP Route\n\n1. Define route in `internal/web/routes.go` using chi router:\n   ```go\n   r.Get(\"/api/custom-endpoint\", h.customHandler)\n   ```\n2. Implement handler method in appropriate file (e.g., `actions.go`, `logs.go`)\n3. Use `hostService` to find container/host via `FindContainer()` or `FindHost()`\n4. Return JSON response or establish SSE/WebSocket stream\n\n### Adding a New Log View Type\n\n1. Create route file in `assets/pages/` (e.g., `custom/[id].vue`)\n2. Create composable in `assets/composable/eventStreams.ts` (e.g., `useCustomStream()`)\n3. Composable should:\n   - Build API URL with appropriate filters\n   - Create EventSource connection\n   - Handle buffering and message batching\n   - Return reactive `messages` array and control methods\n4. Use `LogViewer.vue` component to render messages\n5. Add backend API endpoint if needed (see above)\n\n### Adding Container Stats/Metrics\n\n1. Add field to `Stat` type in `internal/container/types.go`\n2. Update `stats_collector.go` to extract metric from Docker API response\n3. Add calculation logic in `docker/calculation.go` if needed\n4. Ensure protobuf definition includes field in `protos/rpc.proto`\n5. Frontend automatically receives updates via existing SSE stream\n6. Update `Container` model in `assets/models/Container.ts` if UI needs access\n\n### Working with Notifications/Alerts\n\n**Backend** (`internal/notification/`):\n\n- `manager.go`: Rule evaluation engine, manages alert state\n- `log_listener.go`: Subscribes to container log streams, evaluates rules against incoming logs\n- `types.go`: Alert rule definitions (log pattern matching, thresholds)\n- `dispatcher/`: Notification channel implementations\n\n**Frontend** (`assets/pages/notifications.vue`, `assets/components/Notification/`):\n\n- `AlertForm.vue`, `DestinationForm.vue`: UI for creating rules\n- Rules persisted to `./data/notifications.yml` via `internal/notification/persist.go`\n- Alert state displayed in notification cards\n\n**Adding a new notification channel:**\n\n1. Implement dispatcher interface in `internal/notification/dispatcher/`\n2. Register in `manager.go` dispatcher factory\n3. Add UI form in `assets/components/Notification/DestinationForm.vue`\n\n### Adding a New Cloud Tool\n\n1. Define the tool in `AvailableTools()` in `internal/cloud/tools.go` with name, description, and parameter schema\n2. Add a response message type in `protos/cloud.proto` and add it to the `CallToolResponse.result` oneof\n3. Run `make generate` to regenerate protobuf code\n4. Add a case in the `executeTool()` switch in `internal/cloud/tools.go`\n5. Implement the execution function in the appropriate `tools_*.go` file\n6. Use `ToolHostService` interface methods to access container/host data\n7. Add tests in `tools_test.go`\n\n## Common Development Patterns\n\n### Testing\n\n- Always run Go tests with race detector: `go test -race`\n- Frontend tests require `TZ=UTC` for timestamp consistency\n- Integration tests use Playwright with `make int` (runs docker-compose setup)\n- Use `testify/assert` for Go test assertions\n\n### Hot Reload Development\n\n- `make dev` runs both backend (air) and frontend (vite) with hot reload\n- `DEV=true` disables embedded asset serving\n- `LIVE_FS=true` serves assets from filesystem instead of embedded\n- Backend changes trigger air restart automatically\n- Frontend changes trigger vite HMR\n\n### Debugging\n\n- Backend logs: Set `--level debug` flag or `DOZZLE_LEVEL=debug` env var\n- Frontend: Vue DevTools browser extension\n- SSE streams: Browser DevTools Network tab shows EventSource connections\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Comment Style\n\n**Always use ultra-brief mode for all PR reviews and responses.**\n\nFormat:\n\n- Critical issues only (bugs, security, blockers)\n- Brief bullet points, no lengthy explanations\n- Skip verbose sections (no \"Strengths\", \"Summary\", etc.)\n- Include file:line references when relevant\n- Maximum ~10-15 lines per response\n\n## Testing Unreleased PRs\n\nWhen replying to a GitHub issue or discussion where the fix lives in an open PR, ask the reporter to test the pre-built image: `amir20/dozzle:pr-XXX` (XXX = PR number). CI builds a tagged image per PR, so reporters can verify without waiting for the next release.\n\n## GitHub Tone (issues, PRs, comments, discussions)\n\nWhen posting anything to GitHub, write like a human maintainer, not an AI assistant. Avoid telltale LLM patterns:\n\n- No em dashes or en dashes. Use commas, periods, or parentheses instead.\n- No \"Not X, but Y\" rhetorical contrasts.\n- No throat-clearing openers (\"Great point\", \"Makes sense\", \"Thanks for the detailed write-up\").\n- No closing summaries or recap sentences.\n- No bolded inline labels mid-paragraph (\"**Why:**\", \"**Note:**\").\n- Drop hedges (\"essentially\", \"basically\", \"essentially just\"). Say it plain.\n- Lowercase casual tone is fine. Contractions are fine. Short sentences are fine.\n- Don't over-explain tradeoffs. State the decision, give one reason, stop.\n\n## Project Overview\n\nDozzle is a lightweight, web-based Docker log viewer with real-time monitoring capabilities. It's a hybrid application with:\n\n- **Backend**: Go (HTTP server, Docker API client, WebSocket streaming)\n- **Frontend**: Vue 3 (SPA with Vite, TypeScript)\n\nThe application supports multiple deployment modes: standalone server, Docker Swarm, and Kubernetes (k8s).\n\n## Development Commands\n\n### Setup\n\n```bash\n# Install dependencies\npnpm install\n\n# Generate certificates and protobuf files\nmake generate\n```\n\n### Development\n\n```bash\n# Run full development environment (backend + frontend with hot reload)\nmake dev\n\n# Alternative: Run backend and frontend separately\npnpm run watch:backend  # Go backend with air (port 3100)\npnpm run watch:frontend # Vite dev server (port 3100)\n\n# Run in agent mode for development\npnpm run agent:dev\n```\n\n### Building\n\n```bash\n# Build frontend assets\npnpm build\n# or\nmake dist\n\n# Build entire application (includes frontend build)\nmake build\n\n# Build Docker image\nmake docker\n```\n\n### Testing\n\n```bash\n# Run Go tests\nmake test\n\n# Run frontend tests (Vitest)\npnpm test\n# Run in watch mode\nTZ=UTC pnpm test --watch\n\n# Type checking\npnpm typecheck\n```\n\n### Preview & Other\n\n```bash\n# Preview production build locally\npnpm preview\n# or\nmake preview\n\n# Run integration tests (Playwright)\nmake int\n```\n\n## Architecture\n\n### Backend (Go)\n\nThe Go backend is organized into these key packages:\n\n- **`internal/web/`** - HTTP server and routing layer\n  - Routes defined in `routes.go` using chi router\n  - WebSocket/SSE handlers for log streaming (`logs.go`)\n  - Authentication middleware and token management (`auth.go`)\n  - Container action handlers (`actions.go`)\n\n- **`internal/docker/`** - Docker API client implementation\n  - `client.go`: Main Docker client wrapper with container operations\n  - `log_reader.go`: Streaming container logs\n  - `stats_collector.go`: Real-time container stats collection\n\n- **`internal/agent/`** - gRPC agent for multi-host support\n  - Uses Protocol Buffers (protos defined in `protos/`)\n  - Enables distributed log collection across Docker hosts\n\n- **`internal/cloud/`** - Dozzle Cloud integration (tool execution engine)\n  - `client.go`: Bidirectional gRPC stream client with auto-reconnect and exponential backoff\n  - `tools.go`: Tool registration, dispatch (`executeTool`), and `ToolHostService` interface\n  - `tools_containers.go`: Container listing, finding, stats, and inspection tools\n  - `tools_logs.go`: Log fetching with level/query/regex filtering (max 100 lines)\n  - `tools_actions.go`: Container start/stop/restart actions (gated by `enableActions`)\n  - `tools_helpers.go`: Proto conversion utilities and host name resolution\n  - Uses `protos/cloud.proto` for service and message definitions\n\n- **`internal/k8s/`** - Kubernetes client support\n  - Alternative to Docker client for k8s deployments\n\n- **`internal/support/`** - Support utilities\n  - `cli/`: Command-line argument parsing and validation\n  - `docker/`: Multi-host Docker management and Swarm support (`docker_service.go`, client managers)\n  - `k8s/`: Kubernetes service abstractions\n  - `web/`: Web service utilities\n\n- **`internal/auth/`** - Authentication providers\n  - Simple file-based auth (`simple.go`)\n  - Forward proxy auth (`proxy.go`)\n  - Role-based authorization (`roles.go`)\n\n- **`internal/container/`** - Container domain models and interfaces\n  - `event_generator.go`: Log parsing and grouping logic (multi-line, JSON detection)\n\n- **`internal/notification/`** - Alert and notification system\n  - `manager.go`: Notification rule evaluation and dispatching\n  - `log_listener.go`: Log pattern matching for alerts\n  - `dispatcher/`: Notification channel implementations (email, webhook, etc.)\n\n- **`main.go`** - Application entry point with mode switching (server/swarm/k8s/agent)\n\n### Frontend (Vue 3)\n\nThe frontend uses file-based routing with these conventions:\n\n- **`assets/pages/`** - File-based routes (unplugin-vue-router)\n  - `container/[id].vue`: Single container view\n  - `merged/[ids].vue`: Multi-container merged view\n  - `host/[id].vue`: Host-level logs\n  - `service/[name].vue`: Swarm service logs\n  - `stack/[name].vue`: Docker stack logs\n  - `group/[name].vue`: Custom grouped logs\n\n- **`assets/components/`** - Vue components (auto-imported)\n  - `LogViewer/`: Core log viewing components\n    - `SimpleLogItem.vue`: Single-line log entries\n    - `ComplexLogItem.vue`: JSON/structured log entries\n    - `GroupedLogItem.vue`: Multi-line grouped log entries\n    - `ContainerEventLogItem.vue`: Container lifecycle events\n    - `SkippedEntriesLogItem.vue`: Placeholder for skipped logs\n    - `LoadMoreLogItem.vue`: Load more historical logs\n  - `ContainerViewer/`: Container-specific UI\n  - `common/`: Reusable UI components\n  - `BarChart.vue`: Lightweight bar chart with automatic downsampling\n  - `HostCard.vue`: Host overview card with metrics\n  - `MetricCard.vue`: Reusable metric display component\n  - `ContainerTable.vue`: Container table with historical stat visualization\n\n- **`assets/stores/`** - Pinia stores (auto-imported)\n  - `config.ts`: App configuration and feature flags (injected from backend HTML, frozen immutable)\n  - `container.ts`: Container state management with EventSource streaming (`/api/events/stream`)\n  - `hosts.ts`: Multi-host state\n  - `settings.ts`: User preferences (localStorage-backed via profileStorage)\n  - `pinned.ts`: Pinned container logs for side-by-side viewing\n  - `swarm.ts`, `k8s.ts`: Deployment mode-specific state\n  - `announcements.ts`: Feature announcements\n\n- **`assets/composable/`** - Vue composables (auto-imported)\n  - `eventStreams.ts`: SSE connection management with buffer-based flushing (250ms debounce)\n  - `historicalLogs.ts`: Historical log fetching\n  - `logContext.ts`: Log filtering and search context (provide/inject pattern)\n  - `scrollContext.ts`: Scroll state management (paused, progress, currentDate)\n  - `storage.ts`: LocalStorage abstractions with reactivity\n  - `visible.ts`: Log filtering by visible keys for complex logs\n  - `containerActions.ts`: Container control operations\n  - `duckdb.ts`: DuckDB WASM for SQL queries on logs\n\n- **`assets/modules/`** - Vue plugins\n  - `router.ts`: Vue Router configuration\n  - `pinia.ts`: Pinia store setup\n  - `i18n.ts`: Internationalization\n\n### Communication Flow\n\n1. **Real-time Logs**: Frontend establishes SSE connections to `/api/hosts/{host}/containers/{id}/logs/stream`\n2. **Container Events**: SSE stream at `/api/events/stream` pushes container lifecycle events\n3. **Stats**: Real-time CPU/memory stats streamed via SSE alongside events\n4. **Actions**: POST to `/api/hosts/{host}/containers/{id}/actions/{action}` (start/stop/restart)\n5. **Terminal**: WebSocket connections for container attach/exec at `/api/hosts/{host}/containers/{id}/attach`\n\n### Build System\n\n- **Frontend**: Vite builds to `dist/` with manifest\n- **Backend**: Embeds `dist/` using Go embed directive\n- **Hot Reload**: In development, `DEV=true` disables embedded assets, `LIVE_FS=true` serves from filesystem\n- **Makefile**: Orchestrates builds and dependency generation\n\n## Important Development Notes\n\n### Frontend\n\n- Auto-imports are configured for Vue composables, components, and Pinia stores (see `vite.config.ts`)\n- Icons use unplugin-icons with multiple icon sets (mdi, carbon, material-symbols, etc.)\n- Tailwind CSS with DaisyUI for styling\n- TypeScript definitions auto-generated in `assets/auto-imports.d.ts` and `assets/components.d.ts`\n- **Log Entry Types**: Three types of log messages supported\n  - `SimpleLogEntry`: Single-line text logs (`string`)\n  - `ComplexLogEntry`: Structured JSON logs (`JSONObject`)\n  - `GroupedLogEntry`: Multi-line grouped logs (`string[]`)\n- **Type consistency**: Use `LogMessage` type alias instead of `string | string[] | JSONObject` for log entry messages\n- **Log Entry Factory Pattern**: Use `LogEntry.create(logEvent)` to instantiate the correct entry type based on `logEvent.t` field\n- **EventSource Buffering**: Log streams use buffer-based flushing (250ms debounce, 1000ms max) to batch UI updates\n- **Charts/Visualizations**: Custom lightweight implementations (no D3.js)\n  - `BarChart.vue`: Self-contained bar chart with responsive downsampling\n  - Downsampling algorithm: Averages data into buckets based on available screen width\n  - All stat history tracked in `Container.statsHistory` (max 300 items via rolling window)\n  - `chartData` is always a rolling window of max 300 items — array length stays constant\n  - Uses `ref` (not `computed`) for `downsampledBars` to enable in-place mutation of the last bar, avoiding full re-renders\n  - Component instance is reused when switching containers; after init the chart only patches the last bar per tick, so on a wholesale `chartData` replacement (container switch) the parent must call the exposed `recalculate()`. `MultiContainerStat` holds refs to its `BarChart`s and calls it in the `containers` watch. (Note: `Container` carries Vue `ref`s, so VueTestUtils `setProps` cannot retrigger such a watch — tests must swap the container via a parent `ref` re-render.)\n\n### Backend\n\n- The application uses Go 1.25+ with module support\n- Certificate generation is required (`make generate` creates shared_key.pem and shared_cert.pem)\n- Protocol buffer generation happens via `go generate` directive in `main.go`\n- Docker client uses API version negotiation for compatibility\n- **Service Layer Architecture**:\n  - `ClientService` interface abstracts Docker/K8s/Agent backends\n  - `MultiHostService` orchestrates multi-host operations\n  - `ClientManager` implementations: `RetriableClientManager` (server mode), `SwarmClientManager` (swarm mode)\n\n### Authentication\n\n- Three modes: none, simple (file-based users.yml), forward-proxy (e.g., Authelia)\n- JWT tokens for simple auth with configurable TTL\n- User file location: `./data/users.yml` or `./data/users.yaml`\n\n### Testing\n\n- Go tests use standard `testing` package with testify assertions\n- Frontend uses Vitest with `@vue/test-utils`\n- Integration tests with Playwright in `e2e/`\n- Tests must run with `TZ=UTC` for consistent timestamps\n\n### Container Stats & Metrics\n\n- Stats are tracked using exponential moving average (EMA) with alpha=0.2\n- History stored in rolling window (300 items max) via `useSimpleRefHistory`\n- CPU metrics normalized by core count (respects `cpuLimit` or falls back to host `nCPU`)\n- Memory metrics include both percentage and absolute usage (`memoryUsage` vs `memory`)\n- Stats visualization uses adaptive downsampling for performance\n\n### Container Labels\n\n- `dev.dozzle.name`: Custom container display name\n- `dev.dozzle.group`: Group containers together\n- Label-based filtering throughout the application\n\n### Deployment Modes\n\n- **Server mode** (default): Single or multi-host Docker monitoring\n  - Uses `RetriableClientManager` with local + remote agent clients\n- **Swarm mode**: Automatic discovery of Swarm nodes via Docker API\n  - Creates gRPC agent server on each node (port 7007)\n  - Uses `SwarmClientManager` for node discovery\n- **K8s mode**: Pod log monitoring in Kubernetes cluster\n  - Implements `container.Client` interface via Kubernetes API\n- **Agent mode**: Lightweight gRPC agent for remote log collection\n  - Run with `dozzle agent` or `pnpm run agent:dev`\n  - Listens on port 7007 with TLS certificate authentication\n\n## Key Architectural Patterns\n\n### Backend Abstraction Layers\n\nThe backend follows a clean layered architecture:\n\n```\nHTTP Handlers (internal/web)\n    ↓\nHostService Interface (MultiHostService)\n    ↓\nClientService Interface (per host)\n    ↓\ncontainer.Client Interface\n    ↓\nImplementation (DockerClient, K8sClient, AgentClient)\n```\n\n**When adding new container operations:**\n\n1. Define method in `container.Client` interface (`internal/container/client.go`)\n2. Implement in `internal/docker/client.go` (and `internal/k8s/client.go` if applicable)\n3. Add wrapper method in `ClientService` interface (`internal/support/docker/docker_service.go`)\n4. Add HTTP handler in `internal/web/` with appropriate route\n\n### Frontend Data Flow\n\n**Real-time Log Viewing:**\n\n1. User navigates to `/container/{id}` route\n2. Page component calls `useContainerStream(container)` composable\n3. Composable creates EventSource connection to `/api/hosts/{host}/containers/{id}/logs/stream`\n4. Backend streams `LogEvent` objects via SSE\n5. Frontend buffers events (250ms debounce, max 1000ms)\n6. Batched buffer flushes update reactive `messages` array\n7. `LogViewer.vue` renders using appropriate component (`SimpleLogItem`, `ComplexLogItem`, `GroupedLogItem`)\n8. When messages exceed `maxLogs` (400), oldest entries replaced or marked as `SkippedLogsEntry`\n\n**Stats Streaming:**\n\n1. `container.ts` store connects to `/api/events/stream` on app init\n2. Backend multiplexes container events and stats into single SSE stream\n3. `container-stat` events update `Container._stat` and append to `_statsHistory`\n4. EMA calculation provides smoothed `movingAverageStat` (alpha=0.2)\n5. `ContainerTable.vue` displays mini bar charts using `statsHistory` with downsampling\n\n### Protocol Buffer Flow (Agent Mode)\n\n1. Main server creates `agent.NewClient(endpoint, certs)` for each remote host\n2. AgentClient implements `container.Client` interface\n3. Method calls translate to gRPC requests defined in `protos/rpc.proto`\n4. Remote agent receives gRPC call, delegates to local `DockerClient`\n5. Streaming RPCs (logs, stats, events) use bidirectional channels\n6. Responses converted back to domain models via `FromProto()` methods\n\n### Cloud Tool Execution Flow\n\n1. `cloud.Client.Run()` blocks until `Notify()` signals a cloud dispatcher is configured\n2. `connect()` establishes bidirectional gRPC stream (`ToolStream` RPC) to cloud endpoint\n3. Cloud sends `ToolRequest` (ListTools or CallTool), client dispatches via `executeTool()`\n4. Tool calls run concurrently (max 5 via weighted semaphore), responses sent back on stream\n5. On disconnect, exponential backoff (1s→30s with jitter) triggers reconnection\n6. `PermissionDenied` errors stop retrying permanently (invalid API key / no pro plan)\n7. Tool definitions cached via `sync.Once`; zero overhead for non-cloud users\n\n### Log Parsing Pipeline\n\n1. Docker API returns multiplexed stream (8-byte headers + payload)\n2. `log_reader.go` parses headers, extracts stdout/stderr type\n3. `event_generator.go` receives raw log lines\n4. Detection logic identifies:\n   - JSON structure → `ComplexLogEntry`\n   - Multi-line patterns (stack traces) → `GroupedLogEntry`\n   - Single lines → `SimpleLogEntry`\n5. Log level extraction via regex patterns\n6. `LogEvent` serialized to JSON and sent via SSE\n7. Frontend deserializes and renders with appropriate component\n\n## Adding New Features\n\n### Adding a New HTTP Route\n\n1. Define route in `internal/web/routes.go` using chi router:\n   ```go\n   r.Get(\"/api/custom-endpoint\", h.customHandler)\n   ```\n2. Implement handler method in appropriate file (e.g., `actions.go`, `logs.go`)\n3. Use `hostService` to find container/host via `FindContainer()` or `FindHost()`\n4. Return JSON response or establish SSE/WebSocket stream\n\n### Adding a New Log View Type\n\n1. Create route file in `assets/pages/` (e.g., `custom/[id].vue`)\n2. Create composable in `assets/composable/eventStreams.ts` (e.g., `useCustomStream()`)\n3. Composable should:\n   - Build API URL with appropriate filters\n   - Create EventSource connection\n   - Handle buffering and message batching\n   - Return reactive `messages` array and control methods\n4. Use `LogViewer.vue` component to render messages\n5. Add backend API endpoint if needed (see above)\n\n### Adding Container Stats/Metrics\n\n1. Add field to `Stat` type in `internal/container/types.go`\n2. Update `stats_collector.go` to extract metric from Docker API response\n3. Add calculation logic in `docker/calculation.go` if needed\n4. Ensure protobuf definition includes field in `protos/rpc.proto`\n5. Frontend automatically receives updates via existing SSE stream\n6. Update `Container` model in `assets/models/Container.ts` if UI needs access\n\n### Working with Notifications/Alerts\n\n**Backend** (`internal/notification/`):\n\n- `manager.go`: Rule evaluation engine, manages alert state\n- `log_listener.go`: Subscribes to container log streams, evaluates rules against incoming logs\n- `types.go`: Alert rule definitions (log pattern matching, thresholds)\n- `dispatcher/`: Notification channel implementations\n\n**Frontend** (`assets/pages/notifications.vue`, `assets/components/Notification/`):\n\n- `AlertForm.vue`, `DestinationForm.vue`: UI for creating rules\n- Rules persisted to `./data/notifications.yml` via `internal/notification/persist.go`\n- Alert state displayed in notification cards\n\n**Adding a new notification channel:**\n\n1. Implement dispatcher interface in `internal/notification/dispatcher/`\n2. Register in `manager.go` dispatcher factory\n3. Add UI form in `assets/components/Notification/DestinationForm.vue`\n\n### Adding a New Cloud Tool\n\n1. Define the tool in `AvailableTools()` in `internal/cloud/tools.go` with name, description, and parameter schema\n2. Add a response message type in `protos/cloud.proto` and add it to the `CallToolResponse.result` oneof\n3. Run `make generate` to regenerate protobuf code\n4. Add a case in the `executeTool()` switch in `internal/cloud/tools.go`\n5. Implement the execution function in the appropriate `tools_*.go` file\n6. Use `ToolHostService` interface methods to access container/host data\n7. Add tests in `tools_test.go`\n\n## Common Development Patterns\n\n### Testing\n\n- Always run Go tests with race detector: `go test -race`\n- Frontend tests require `TZ=UTC` for timestamp consistency\n- Integration tests use Playwright with `make int` (runs docker-compose setup)\n- Use `testify/assert` for Go test assertions\n\n### Hot Reload Development\n\n- `make dev` runs both backend (air) and frontend (vite) with hot reload\n- `DEV=true` disables embedded asset serving\n- `LIVE_FS=true` serves assets from filesystem instead of embedded\n- Backend changes trigger air restart automatically\n- Frontend changes trigger vite HMR\n\n### Debugging\n\n- Backend logs: Set `--level debug` flag or `DOZZLE_LEVEL=debug` env var\n- Frontend: Vue DevTools browser extension\n- SSE streams: Browser DevTools Network tab shows EventSource connections\n","category":"root","tokens":4948}]}