# AGENTS ## Overview - Languages: Go backend with `github.com/gin-gonic/gin`, `zap`, PostgreSQL migrations and REST handlers under `backend/internal`, plus a Vite+React+TypeScript frontend under `frontend/`. - Keep the system deterministic: Go 1.22+, PostgreSQL 14+, Node 18+, npm 10+ (use `node --version`/`npm --version` to confirm). - Secret material lives in `/etc/calypso/config.yaml` or environment variables (never commit private keys, JWT secrets, or database passwords). - The backend expects `CALYPSO_DB_PASSWORD` and `CALYPSO_JWT_SECRET` (minimum 32 characters) before running locally or in CI. ## Environment Setup - Run `sudo ./scripts/install-requirements.sh` from the repo root to provision system dependencies on Ubuntu-like hosts before building the backend. - `config.yaml.example` provides defaults; copy it to `/etc/calypso/config.yaml`, then edit the file or override with the `CALYPSO_*` environment variables before launching. - For frontend work, `npm install` from `frontend/` once before subsequent builds or `npm run dev` sessions. ## Backend Tooling (Go) ### Build & Run - `make build` (`go build -o bin/calypso-api ./cmd/calypso-api`) produces the local binary. - `make run` (`go run ./cmd/calypso-api -config config.yaml.example`) is the quickest local dev server; supply `-config` pointing at your working config. - Production cross-build: `make build-linux` sets `CGO_ENABLED=0 GOOS=linux GOARCH=amd64` and strips symbols. - `deploy/systemd/calypso-api.service` is a template—copy it to `/etc/systemd/system/`, `daemon-reload`, `enable`, and `start` to run as service. ### Testing - `make test` runs `go test ./...` across all packages; it is the authoritative test runner today. - To run a single test, narrow the package and pass `-run`: `cd backend && go test ./internal/auth -run '^TestHandler_Login$'` (use `.` to target the current package and regular expressions for test names). - `make test-coverage` generates `coverage.out` and opens a coverage report via `go tool cover -html=coverage.out`. ### Lint & Format - `make fmt` (`go fmt ./...`) keeps source gofmt-clean; run it after edits and before committing. - `make lint` requires `golangci-lint`; install it with `make install-deps` (which calls `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`). - `make deps` handles dependency downloads and tidies `go.mod`/`go.sum` when vendor changes are necessary. ## Backend Style Guidelines (Go) ### Imports - Group imports with standard library packages first, blank line, then third-party packages (gofmt enforces this). - Do not alias imports unless needed to disambiguate. Keep third-party import paths explicit (e.g., `github.com/gin-gonic/gin`). - Prefer explicit package names (`config`, `database`, `logger`) rather than dot-importing. ### Formatting & Files - Always run `gofmt` (or `make fmt`) before committing; gofmt enforces tabs for indentation and consistent formatting. - Keep files small (100–300 lines) and break responsibilities into `internal/common`, `internal/iam`, etc., mirroring the current layout. - Maintain doc comments on exported functions, types, and struct fields; these comments start with the identifier they describe. ### Types & Naming - Use PascalCase (UpperCamel) for exported types/fields/methods and camelCase for internal helpers. - Keep configuration structs descriptive (`ServerConfig`, `DatabaseConfig`) and align YAML tags with snake_case field names. - When introducing new structs, mimic the existing tag conventions (`yaml:"field_name"`). - Use short, meaningful receiver names (`func (h *Handler) ...`), ideally 1–3 characters, matching current files. - Name boolean helpers `isX`/`shouldY` and prefer `cfg`/`db`/`ctx` as short local variables for configuration, database, and contexts. ### Error Handling - Wrap errors with `fmt.Errorf("context: %w", err)` and return early. Do not swallow errors unless there is a clear action (e.g., logging + fallback). - Use `gin.Context` to respond with the appropriate HTTP status codes (`c.JSON(http.StatusBadRequest, gin.H{...})`). - Log warnings vs. errors via the shared `logger.Logger` (`h.logger.Warn(...)` for expected issues, `Error` for failures, `Info` for happy paths). - Avoid `panic` in handlers; only panic during initialization (e.g., new logger). Instead, log and return a 5xx status. - Keep timeouts and cancellation explicit using `context.WithTimeout` when communicating with databases or external systems. ### Logging & Observability - Use `zap`-backed `logger.NewLogger`, pass service-specific names (e.g., `router`, `auth-handler`), and attach structured pairs (`"user_id", user.ID`). - Allow log format and level to be configured via `CALYPSO_LOG_FORMAT`/`CALYPSO_LOG_LEVEL` if present. - Prefer adding caller info and stack traces on `Error`level logs when helpful. Do not sprinkle raw `fmt.Println` or `log.Print`. ### HTTP & Routing - All HTTP routing lives under `internal/common/router`; register handlers through route groups and middleware (auth/authZ) as seen in `router.NewRouter`. - Use Gin middleware for sessions, JWT validation, and auditing. Keep handler methods grouped by feature (auth, iam, tasks). - Keep handler structs simple: inject shared dependencies (`db`, `config`, `logger`) via constructors, then refer to them as fields. - For JSON requests/responses, mirror struct field names in `json` tags (snake_case) even if Go fields are camelCase. ### Database & Migrations - Use prepared statements or parameterized queries (see `iam` and `sessions` tables) with `$1` style placeholders. - Run migrations automatically from `internal/common/database/migrations/` via `database.RunMigrations` at startup. - Hunters: use `database.DB` wrapper for connection pool handling, close connections via `defer db.Close()`. - When adding new tables, drop SQL files into `db/migrations/` and re-run migrations via the running service or a migration helper. ### Security Notes - JWT secrets must be strong (>=32 characters). Use environment variables, never embed secrets in code or checked-in configs. - Database passwords, API keys, and secrets are populated via env vars (`CALYPSO_DB_PASSWORD`, `CALYPSO_JWT_SECRET`, `CALYPSO_JWT_SECRET`) or `config.yaml` on the host. - All mutating endpoints should log audit events and guard via IAM roles (see `iam` package for role resolution). - Bacula client registration and capability pushes require the `bacula-admin` role; enforce it via `requireRole("bacula-admin")` on `/api/v1/bacula/clients` routes. ## Frontend Tooling (React + TypeScript) ### Build & Run - Work inside `frontend/`. Start the dev server with `npm run dev` (proxies `/api` → `http://localhost:8080`). - Production builds use `npm run build`, which runs `tsc` before `vite build` to ensure typing correctness. - Serve production output via `npm run preview` if you need to smoke-test a `dist/` snapshot. ### Testing - No automated frontend tests run currently. If you add a test harness (Jest/Vitest), ensure `npm test` or equivalent is added to `package.json`. - For manual verification, interact with the dev server after logging into the backend API from `localhost:3000`. ### Lint & Formatting - `npm run lint` runs ESLint across `.ts/.tsx` files with the `@typescript-eslint` recommended rules and `react-hooks` enforcement. It also blocks unused directives and sets `--max-warnings 0`. - The linter requires you to keep hooks exhaustive and avoid unused vars; prefix intentionally unused arguments with `_` (e.g., `_event`). - `tsconfig.json` sets `strict` mode, `noUnusedLocals`, `noUnusedParameters`, and `noFallthroughCasesInSwitch`. Respect these by keeping types precise and handling every branch. ## Frontend Style Guidelines (TypeScript + React) ### Imports & Aliases - Use the `@/` alias defined in `tsconfig.json` for slices inside `src/` (e.g., `import Layout from '@/components/Layout'`). - Import React/third-party packages at the top, followed by `@/` aliased modules, maintaining alphabetical order within each group. - Prefer named exports for hooks and API utilities, default exports for page components (e.g., `export default function LoginPage()`). ### Formatting & Syntax - Use single quotes for strings, omit semicolons (the repo follows Prettier/Vite defaults), and keep 2-space indentation inside JSX. - Keep JSX clean: wrap complex class combinations and conditional fragments in template literals or helper functions, avoid inline object literal props unless necessary. - Keep React components in kebab-case file names (e.g., `Login.tsx`, `Layout.tsx`) and align component names with file names. ### Types & Naming - Use `interface` or `type` aliases for props and API shapes (e.g., `interface LoginRequest`), and add optional fields with `?`. - Names for state setters, handlers, and store selectors follow camelCase (`setAuth`, `handleSubmit`, `loginMutation`). - Use descriptive names for UI data (`navigation`, `userMenu`, `alerts`). Keep boolean flags prefixed with `is`, `has`, or `should`. ### State, Hooks, & Effects - Prefer `useState`/`useEffect` for local UI state and `Zustand` stores (`useAuthStore`) for shared state. - Keep `useEffect` dependency arrays explicit; avoid disabling exhaustive-deps rules unless documented. - Manage async server interactions with TanStack Query (`useMutation`, `useQuery`) and centralize API clients (`src/api/client.ts`). ### Styling & Layout - TailwindCSS classes are used for layout (`flex`, `gap`, `bg-...`). Keep className strings readable and group related classes together (spacing, color, typography). - Use utility classes for responsive behavior (`lg:hidden`, `px-4`). For dynamic class toggling, compute the string in JS before passing it to `className`. - Keep inline styles minimal; prefer CSS utilities and shared constants (colors, text sizing) defined in tailwind config. ### API & Networking - Use `src/api/client.ts` as the single Axios instance (`baseURL: '/api/v1'`). Attach request/response interceptors for auth token injection/401 handling. - Return typed promises from API helpers (e.g., `Promise`). Let callers handle success/errors via TanStack Query callbacks. - Use `authApi.getMe`, `authApi.login`, etc., for all backend interactions; do not re-implement Axios calls directly in UI components. ### Error Handling - Surface backend errors by reading `error.response?.data?.error` (as seen in `LoginPage`) and displaying friendly messages via inline UI alerts or toast components. - Clear authentication state on 401 responses using the Axios interceptor (`useAuthStore.getState().clearAuth()`), then redirect to `/login`. - Display loading or pending states (`loginMutation.isPending`) instead of disabling UI abruptly; provide visual feedback for async actions. ## Repository Practices - Keep database migration files under `db/migrations/` and refer to them from `internal/common/database/migrations/` for auto-run. - When adding commands or packages, update the relevant `Makefile` target or `package.json` script, and document the command in this AGENTS file. - Commit `go.mod`, `go.sum`, and `package-lock.json`/`package.json` updates together with code changes. - Avoid committing binaries (`bin/`), coverage artifacts (`coverage.out`), or built frontend output (`frontend/dist/`). These are ignored by `.gitignore` but double-check before commits. ## Cursor & Copilot Rules - There are no `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` files in this repository. Follow the guidelines laid out above.