# CLAUDE.md

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

## Project Overview

LabGraph is a HomeLab infrastructure documentation engine. It maps physical hardware to virtual services using a graph model (Nodes + Edges). The stack is Django + Celery on the backend, Next.js 14 + DaisyUI on the frontend, all orchestrated via Docker Compose.

## Running the Stack

```bash
# Start everything (from repo root)
docker compose up -d

# Include Flower (Celery monitoring at localhost:5555)
docker compose --profile dev-tools up -d

# View logs
docker compose logs -f web
docker compose logs -f celery-worker

# Rebuild after Dockerfile or requirements.txt changes
docker compose build
docker compose up -d
```

The web service runs `backend/scripts/entrypoint.sh` which runs migrate → collectstatic → gunicorn.

## Backend Commands (inside container)

```bash
# Run migrations
docker compose exec web python manage.py migrate

# Create superuser
docker compose exec web python manage.py createsuperuser

# Make migrations after model changes
docker compose exec web python manage.py makemigrations

# Open Django shell
docker compose exec web python manage.py shell

# Inspect Celery workers
docker compose exec celery-worker celery -A config.celery inspect ping
docker compose exec celery-worker celery -A config.celery inspect active
```

## Frontend Commands

```bash
cd frontend
npm run dev          # Dev server at localhost:3000
npm run type-check   # TypeScript check (tsc --noEmit)
npm run lint         # ESLint
npm run build        # Production build
```

## Environment Setup

Copy `.env.example` to `.env` (repo root). Copy `frontend/.env.local.example` to `frontend/.env.local`.

Required variables that have no defaults and will crash on startup if missing:
- `SECRET_KEY` — Django secret key (`python3 -c "import secrets; print(secrets.token_urlsafe(50))"`)
- `POSTGRES_PASSWORD` — PostgreSQL password (used directly by docker-compose)
- `REDIS_PASSWORD` — Redis password (used directly by docker-compose)
- `NEXTAUTH_SECRET` — NextAuth JWT signing key (`openssl rand -base64 32`)

Critical: `DATABASE_URL`, `REDIS_URL`, and `CELERY_BROKER_URL` must use the Docker service hostnames `db` and `redis`, not `localhost`. The Redis URL format is `redis://:PASSWORD@redis:6379/0` (colon before password, no username).

## Architecture

### Backend (`backend/`)

**Settings** are split into `config/settings/base.py` (shared) → `config/settings/development.py` / `production.py`. Settings are loaded via `django-environ` from the root `.env` file (`BASE_DIR.parent / ".env"`). Never import `base` directly — always use `development` or `production`.

**Celery** is configured in `config/celery.py` and referenced by docker-compose as `-A config.celery`. Three queues: `discovery` (nmap/Proxmox scans), `heartbeat` (ICMP/TCP liveness), `default` (maintenance). Beat uses `DatabaseScheduler`, so periodic tasks are stored in the DB and can be edited at runtime via admin.

**Graph model** in `apps/core/models.py`:
- `Node` — any infrastructure component (location → hardware → hypervisor → VM/container → application). All have UUID PKs, a `node_type` TextChoices enum, `status`, optional `ip_address`, and a `metadata` JSONField for per-type flexible data.
- `Edge` — directed source→target relationship with `edge_type` (parent_child, network, dependency, physical). Unique constraint on `(source, target, edge_type)`.
- `Network` — VLAN/subnet records (separate from Nodes).
- `WikiPage` — OneToOne to Node, stores raw Markdown; `rendered_html()` converts via the `Markdown` package.
- `HeartbeatLog` — high-volume time-series; pruned nightly by `tasks/maintenance.py`.

**Input validation** uses Pydantic v2 schemas (`apps/core/schemas.py`) at the API boundary before data reaches the ORM. DRF serializers (to be added in Phase 2) handle output.

**Celery tasks** live in `tasks/` (not inside an app). Phase 1 stubs are in `tasks/discovery.py`, `tasks/heartbeat.py`, `tasks/maintenance.py`. Autodiscovery is configured for the `tasks` package.

**RLS**: `apps/core/apps.py` connects a `post_migrate` signal that enables PostgreSQL Row-Level Security on `core_node`. The handler checks `information_schema.tables` first — it fires after every app's migrations, not just after core.

**Health check** at `GET /api/health/` (no auth required) checks DB (`SELECT 1`) and Redis (cache round-trip). Returns `{"status": "ok"|"degraded", "services": {...}, "version": "1.0.0"}`. Used by docker-compose healthcheck.

**OpenAPI schema** at `GET /api/schema/`, Swagger UI at `GET /api/docs/`.

### Frontend (`frontend/`)

Next.js 14 App Router. TypeScript strict mode with `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` — avoid `any`.

**Auth flow**: NextAuth (`src/lib/auth.ts`) uses `CredentialsProvider`. In development, any non-empty credentials are accepted (stub). Phase 2 wires `authorize()` to `POST /api/auth/login/` on the Django backend. `NEXTAUTH_SECRET` must be stable in `.env.local` — changing it invalidates all existing sessions.

**API client** (`src/lib/api.ts`): all backend calls go through `apiFetch()` which validates responses against Zod schemas before returning. Throws `ApiError` on non-2xx, `ZodError` on schema mismatch.

**Types** (`src/types/index.ts`): Zod schemas are the source of truth; TypeScript types are inferred with `z.infer<>`. The schemas mirror the Django models exactly — keep them in sync when models change.

**Routing**: `next.config.mjs` proxies `/backend/*` → Django. Route groups: `(auth)` for login, `(dashboard)` for protected pages. Auth guard is done server-side with `getServerSession(authOptions)` + `redirect()`.

**Styling**: Tailwind CSS + DaisyUI. Theme is set via `html[data-theme]` in `layout.tsx`. DaisyUI component classes (e.g. `btn`, `card`, `alert`) — no custom CSS unless DaisyUI can't cover it.

## Phase Status

- **Phase 1** ✅ Complete — Docker stack, Django models, Celery stubs, Next.js scaffold
- **Phase 2** 🔄 In Progress — DRF ViewSets, discovery scrapers (Proxmox/nmap), heartbeat (icmplib), Django auth endpoint
- **Phase 3** ⏳ Pending — Nested inventory list, React Flow topology graph, Markdown wiki editor

## Key Constraints

- The `requirements.txt` lives in `backend/` (Docker build context is `./backend`).
- `next.config.ts` is not supported in Next.js 14 — use `next.config.mjs`.
- YAML `>` folded scalars in docker-compose do NOT fold newlines on more-indented continuation lines — use a shell script instead of multi-line `sh -c` commands.
- `HeartbeatLog` is high-volume — always filter by `node` and use the `(node, -timestamp)` index; never do full-table scans.
- `Node.metadata` is a free-form JSONField — validate its contents with the Pydantic schemas in `apps/core/schemas.py` before writing.
