# 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.
`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`.