Files

6.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Additional Instructions

  • personal overrides: @~/.claude/CLAUDE.md

Commands

# Build
go build -o crowdsec-dashy ./cmd/server

# Production build (static binary, stripped)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o crowdsec-dashy ./cmd/server

# Run
./crowdsec-dashy

# Lint / vet
go vet ./...

# Tests (none yet — project is in early build phases)
go test ./...

# Docker
docker compose up -d
docker compose build

Architecture

Go 1.26. Zero external Go dependencies. Stdlib only (net/http, html/template, os/exec, encoding/json).

Data access — two parallel layers

Layer Package Coverage
CrowdSec LAPI (REST) internal/crowdsec/lapi.go Decisions CRUD, Alerts, health
cscli exec wrapper internal/crowdsec/cli.go Bouncers, Machines, Hub, Metrics

All shared structs (Decision, Alert, Bouncer, Machine, HubItem, MetricsSection, etc.) live in internal/crowdsec/types.go.

Bouncers, machines, hub, and metrics are not exposed via REST — they require cscli hitting the database directly. Features that need cscli check Deps.CLIAvailable and render an inline warning banner when the binary is absent.

Package layout

cmd/server/main.go                entrypoint — loads config, authenticates LAPI, starts HTTP server
internal/config/config.go         env-var loading; CscliAvailable() stat-checks the binary path
internal/crowdsec/
  types.go                        all shared structs
  lapi.go                         REST client (JWT auth, auto-retry on 401)
  cli.go                          cscli exec wrapper (strict allow-list)
internal/handlers/
  renderer.go                     Renderer, PageData, Deps, SidebarNav
  dashboard.go / api.go / ...     one file per page + JSON API handler
internal/middleware/middleware.go  BasicAuth, Logger, SecureHeaders, Recovery
internal/router/router.go         constructs Deps, wires all routes
web/templates/layouts/base.html   sidebar shell
web/templates/pages/*.html        one file per page
web/static/css/app.css            all component styles
web/static/js/dashboard.js        live stat polling + sparklines
web/static/js/tables.js           client-side filter/sort/bulk-select

Request flow

HTTP request
  → middleware chain (BasicAuth → Logger → SecureHeaders → Recovery)
  → router.go (stdlib mux)
  → handler struct (receives Deps: Renderer, LAPI, CLI, CLIAvailable, PollInterval)
  → handler renders to bytes.Buffer first, then writes to ResponseWriter

Dependency injection

Deps struct (internal/handlers/renderer.go:246) is constructed once in router.New() and passed to every handler constructor. Handlers are structs, not functions.

Template system

Renderer (internal/handlers/renderer.go) parses all page templates at startup: each page in web/templates/pages/*.html is combined with web/templates/layouts/base.html into a named *template.Template. Templates are keyed by filename without extension (e.g. "dashboard", "decisions"). All render calls go through Renderer.Render(w, name, data) using the buffer-then-write pattern.

Template functions available: inc, dec, dict, decisionBadgeClass, originBadgeClass, hubStatusClass, safeHTML, boolIcon, truncate, join.

LAPI authentication

On startup, lapi.Login() POSTs to /v1/watchers/login → JWT stored in memory. All subsequent requests carry Authorization: Bearer <jwt>. On 401, doJSON() auto-retries with a fresh login once before failing.

cscli exec security

All args passed as a slice (never through shell). Two allow-lists enforced before exec.CommandContext:

  • allowedSubcommands (first arg): bouncers, machines, collections, parsers, scenarios, postoverflows, hub, metrics, version
  • allowedActions (second arg): list, add, delete, install, remove, update, upgrade, validate
  • All other user-supplied values validated against safeArg regex ^[a-zA-Z0-9_./:@\-]+$

POST pattern

All state-changing POST handlers use PRG (Post/Redirect/Get). Flash messages are attached to PageData via WithFlash(type, msg) and displayed on the redirected GET. All POST handlers must apply http.MaxBytesReader before parsing the body.

Internal JSON API

Used by frontend JS — all return JSON, protected by the same BasicAuth middleware.

Method Path Description
GET /api/v1/stats Dashboard summary counts
GET /api/v1/health LAPI health + cscli availability

Adding a new page

  1. internal/handlers/mypage.go — handler struct with ServeHTTP or named methods
  2. web/templates/pages/mypage.html — starts with {{template "base" .}}
  3. Add NavItem to SidebarNav in renderer.go
  4. router.gomux.Handle("/mypage", handlers.NewMyPageHandler(deps))

UI design

Dark industrial aesthetic. Palette: near-black surface #080b10, blue-grey panels #0f1520, cyan accent #00d4ff, threat-red #ff3b3b, safe-green #00e676. Fonts: JetBrains Mono for data, IBM Plex Sans for labels. All styles in web/static/css/app.css. No external JS frameworks — sparklines drawn with inline SVG <polyline>.

Dashboard live stats poll /api/v1/stats via web/static/js/dashboard.js. Client-side table filter/sort/bulk-select in web/static/js/tables.js.

Environment variables

Variable Default Notes
PORT :8080 Listen address
CROWDSEC_API_URL http://localhost:8080 LAPI base URL
CROWDSEC_API_LOGIN (required) Machine login
CROWDSEC_API_PASSWORD (required) Machine password
CSCLI_PATH /usr/local/bin/cscli Binary path; CLI features disabled if absent
UI_USERNAME admin Basic Auth username
UI_PASSWORD changeme Basic Auth password
UI_SESSION_SECRET (required, ≥32 chars) HMAC signing key
POLL_INTERVAL_SEC 15 Dashboard poll interval (seconds)