# CLAUDE.md

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

## Important Nots
Do not take screenshots yourself, ask the user to take screenshots of your visual changes if you want to so that you can verify them.

## Commands

**Go Executable:**
```bash
/home/linuxbrew/.linuxbrew/bin/go
```

**Run dev server:**
```bash
# First-time setup: copy the env template
cp scripts/env/dev.env.example scripts/env/dev.env
# Edit scripts/env/dev.env to set WARPBOX_ADMIN_TOKEN and other values, then:
./scripts/run/dev.sh
```

**Run directly (one-off):**
```bash
cd backend
go run ./cmd/warpbox
```

**Run all tests:**
```bash
cd backend
go test ./...
```

**Run a single test or package:**
```bash
cd backend
go test ./libs/services/... -run TestDeleteTokenVerification
go test ./libs/handlers/... -v
```

**Build:**
```bash
cd backend
go build ./cmd/warpbox
```

## Architecture

Warpbox is a self-hosted file-sharing app. All code lives under `backend/`. There is no frontend build step — the server renders Go templates and serves static assets directly.

### Startup flow

`cmd/warpbox/main.go` → `config.Load()` → `httpserver.New()` → `server.ListenAndServe()`

`httpserver.New()` wires everything together:
1. Creates `web.Renderer` (template engine)
2. Creates `UploadService` (opens bbolt DB, creates `files/` and `db/` dirs)
3. Creates `AuthService` (reuses same `*bbolt.DB`)
4. Creates `SettingsService` (reuses same `*bbolt.DB`)
5. Starts background jobs via `jobs.StartAll`
6. Creates `handlers.App` with all services
7. Registers all routes on a `http.ServeMux`
8. Wraps the mux in middleware chain: `Recoverer → RequestID → SecurityHeaders → Gzip → Logger`

### Services

The three services share a single bbolt database. Each owns distinct buckets:

| Service | Buckets |
|---|---|
| `UploadService` | `boxes` |
| `AuthService` | `users`, `user_emails`, `sessions`, `invites`, `collections` |
| `SettingsService` | `settings`, `usage` |

`UploadService` owns the DB handle. `AuthService` and `SettingsService` receive `uploadService.DB()`.

### Data model

- **Box** — one upload session. Has expiry, optional download limit, optional password (SHA-256 salted hash), optional owner (`OwnerID`), optional collection. Stored as JSON in the `boxes` bucket.
- **File** — belongs to a Box. Stored on disk as `data/files/{boxID}/@each@{fileID}.ext`. Thumbnails at `@thumb@{fileID}.jpg`.
- Box metadata is also written to `data/files/{boxID}/.warpbox.box.json` on every save.
- **User** passwords are hashed with argon2id. Session tokens are SHA-256 hashed before storage.
- **Delete tokens** for anonymous boxes are one-time random IDs, stored only as a SHA-256 hash.

### Handlers

`handlers.App` holds all three services plus config, logger, and renderer. `RegisterRoutes` maps every URL pattern. Handler files are split by concern: `upload.go`, `download.go`, `dashboard.go` (user `/app`), `admin.go`, `auth.go`, `manage.go`, `pages.go`.

### Upload policy enforcement

`SettingsService` stores per-day usage records keyed by `ip:{ip}:{date}` or `user:{userID}:{date}`. The upload handler (`handlers/upload.go`) checks these against `UploadPolicySettings` before accepting a multipart form. Admins bypass all per-upload and daily limits.

### Background jobs

`jobs.StartAll` launches goroutines for:
- **Cleanup** (`WARPBOX_CLEANUP_ENABLED`): deletes expired boxes and boxes that hit their download limit.
- **Thumbnails** (`WARPBOX_THUMBNAIL_ENABLED`): generates JPEG thumbnails for image/video files that don't have one yet.

### Configuration

All config comes from env vars via `config.Load()`. The dev script sources `scripts/env/dev.env`. `WARPBOX_BASE_URL` is required and must not be empty. Size values accept an optional `MB`/`Mb` suffix and support fractions (e.g. `0.5` = 512 KiB).

### Logging

Structured JSONL logs go to `data/logs/{YYYY-MM-DD}.log` via `log/slog`. Every log entry includes `source` (e.g. `"user-upload"`, `"admin"`) and `severity` fields. User-activity events include a numeric `code` field (e.g. `2001` = upload complete, `2101` = box deleted).

### Template rendering

`web.Renderer` parses all templates from `backend/templates/` at startup using `html/template`. Page data is passed as `web.PageData`. The base layout is `templates/layouts/base.html`. The current logged-in user is injected into every page render via `a.currentPublicUser(r)`.

## First-run bootstrap

On a fresh `data/` directory, visit `/register` to create the first admin account. After bootstrap, normal registration is closed. Admins create invite links from `/admin/users`. The `WARPBOX_ADMIN_TOKEN` env var provides emergency fallback access at `/admin/login`.
