Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .agents/workflows/review-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ For every modified file, evaluate against ALL of the following checklist:
- [ ] Does the change follow existing patterns in the codebase?
- [ ] Are naming conventions consistent (variables, functions, types)?
- [ ] Does it match the code style of surrounding files?
- [ ] Are non-obvious policy helpers commented? Check public API validators, persistent-state mutations, complex SQL/GORM queries, stats/business policy, security/privacy behavior, and concurrency assumptions.
- [ ] Do comments explain intent, accepted inputs, defaults, return meaning, or invariants where those are not obvious from nearby code?
- [ ] Are comments self-contained and production-ready? Flag any comment or test name that references development-process artifacts (task/ticket numbers, decision IDs, plan/backlog/review docs, audit findings, agent briefs, uncommitted files) or narrates how a decision was reached instead of stating the technical rationale. Doc pointers are fine only when they cite a committed file by its actual heading.

#### Completeness
- [ ] Are all necessary files modified (tests, docs, config)?
Expand All @@ -75,6 +78,8 @@ For every modified file, evaluate against ALL of the following checklist:
- [ ] Are new behaviors covered by tests?
- [ ] Do existing tests still pass with these changes?
- [ ] Are test assertions meaningful (not just "no error")?
- [ ] For large backend changes, compare package coverage before/after and identify new uncovered functions.
- [ ] Are complex SQL/GORM queries, migrations/backfills, counter mutations, concurrency paths, and public API validation covered by focused backend tests? E2E coverage is useful but not a substitute for these.

#### Documentation (`docs/`, README, `--help`)
- [ ] Do user-facing docs reflect the new/changed behavior?
Expand All @@ -89,6 +94,7 @@ For every modified file, evaluate against ALL of the following checklist:
- [ ] Are new/changed request/response fields documented with examples?
- [ ] Are removed or renamed fields noted for backward compatibility?
- [ ] Are new query parameters listed in the relevant filter table?
- [ ] Treat HTTP API documentation drift as a critical review finding.

#### CSS / Theming (if webapp styles were changed)
- [ ] Do changes use design tokens (`surface-*`, `accent-*`) instead of hardcoded colors?
Expand All @@ -114,6 +120,7 @@ For every modified file, evaluate against ALL of the following checklist:
- Verify the change aligns with documented patterns
- Check if AGENTS.md needs updating
- **If any HTTP handler, route, or API response struct was modified**: check `docs/reference/api.md` and flag any drift as a 🔴 Critical issue
- **If stats, counters, migrations, or SQL/GORM queries changed**: verify direct backend tests exist for the new logic and flag E2E-only coverage as a review issue

### 4. Lint and build

Expand Down Expand Up @@ -207,4 +214,4 @@ Do NOT auto-fix without asking. Present the findings first, let the user decide.
- Don't just rubber-stamp changes with "LGTM" unless they're truly clean
- Pay special attention to subtle issues: off-by-one, missing error handling, stdout/stderr confusion, goroutine leaks
- If a change seems incomplete (e.g., missing tests for new behavior), flag it
- Compare against ARCHITECTURE.md and project conventions
- Compare against ARCHITECTURE.md and project conventions
20 changes: 18 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ plik/
├── README.md ← project README (concise)
├── Makefile ← build orchestration
├── Dockerfile
├── .agent/ ← agentic workflows (/review-changes, /prepare-pr, /cut-release)
├── .agents/ ← agentic workflows (/review-changes, /prepare-pr, /cut-release)
├── server/ ← Go server (see server/ARCHITECTURE.md)
│ ├── main.go ← entry point
│ ├── plikd.cfg ← default config
Expand Down Expand Up @@ -139,8 +139,25 @@ make vuln # govulncheck (report only)
- **Helm persistence**: the chart has two independent PVCs — `persistence` for uploaded files (`/home/plik/server/files`) and `dbPersistence` for the SQLite database (`/home/plik/server/db`). Both default to `emptyDir` when disabled. The default `MetadataBackendConfig.ConnectionString` is `/home/plik/server/db/plik.db`.
- **Run tests before committing**: `make lint && make test`
- **Keep ARCHITECTURE.md files in sync**: Each root folder has its own — update the one closest to your change
- **Comment non-obvious policy and invariants**: Add concise comments for helpers that validate public API input, mutate persistent state, build complex SQL/GORM queries, encode security/privacy behavior, rely on concurrency assumptions, or implement statistics/business policy. Comments should explain accepted input domains, default behavior, return meaning, and why the rule exists when those details are not obvious from the name and surrounding call site. Prefer comments that explain why the rule exists over comments that restate the code.
- **Comments must be self-contained and production-ready**: A comment (or test name) may only reference things that live in the repository. Never reference development-process artifacts — task or ticket numbers, decision IDs, plan/backlog/review documents, audit findings, agent briefs, or any uncommitted file — and never narrate how a decision was reached ("as decided during review", "per the maintainer"). State the technical rationale itself; when the long-form policy lives in a committed doc (e.g. an ARCHITECTURE.md section), point there by its actual heading.
- **Test at the right layer**: E2E tests complement, but do not replace, focused backend tests for SQL/GORM queries, migrations/backfills, counter mutations, concurrency, and public API validation.
- **Release process**: Before creating a GitHub release, update the version in `README.md` and move `charts/plik/CHANGELOG.md` entries from `[Unreleased]` to the new version heading

## Agent Workflows

Project workflows live in `.agents/workflows/`. Use them when the matching task comes up:

| Workflow | Purpose |
|----------|---------|
| `plan.md` | Create an implementation plan before non-trivial features or refactors |
| `review-changes.md` | Critically review local diffs before committing or preparing a PR |
| `commit.md` | Commit or push changes with the mandatory user approval gate |
| `prepare-pr.md` | Run review/pre-flight checks, commit, push, and draft a PR |
| `cut-release.md` | Walk through the release checklist |
| `add-language.md` | Add a new webapp locale end to end |
| `review-language.md` | Review locale quality, placeholders, plurals, and semantic distinctions |

## Documentation

The documentation lives in two places:
Expand Down Expand Up @@ -171,4 +188,3 @@ make docs # Build docs (builds client, injects help+plikrc, b
| [releaser/ARCHITECTURE.md](releaser/ARCHITECTURE.md) | Release tooling: build pipeline, Docker stages, client/server compilation |
| [charts/plik/ARCHITECTURE.md](charts/plik/ARCHITECTURE.md) | Helm chart: structure, config/secrets separation, persistence, versioning |
| [.github/ARCHITECTURE.md](.github/ARCHITECTURE.md) | GitHub Actions workflows, CI/CD, Helm chart release flow |

29 changes: 23 additions & 6 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,13 @@ When processing a request, limits are resolved via the custom `Context`:
|--------|------|---------|-------------|
| GET | `/me` | `UserInfo` | Current user info |
| DELETE | `/me` | `DeleteAccount` | Delete own account |
| GET | `/me/token` | `GetUserTokens` | List tokens (paginated) |
| GET | `/me/token` | `GetUserTokens` | List tokens (paginated, sortable by `sort` (`date`/`size`/`lifetimeSize`), strict `400` on unknown sort) |
| POST | `/me/token` | `CreateToken` | Create upload token |
| DELETE | `/me/token/{token}` | `RevokeToken` | Revoke token |
| GET | `/me/uploads` | `GetUserUploads` | List uploads (paginated, sortable by `sort` (`date`/`size`) and `order` (`desc`/`asc`), filterable by `token` and badge settings: `oneShot`, `removable`, `stream`, `extendTTL`, `password`, `e2ee`) |
| GET | `/me/uploads` | `GetUserUploads` | List uploads (paginated, sortable by `sort` (`date`/`size`/`downloads`) and `order` (`desc`/`asc`), filterable by `token` and badge settings: `oneShot`, `removable`, `stream`, `extendTTL`, `password`, `e2ee`) |
| DELETE | `/me/uploads` | `RemoveUserUploads` | Remove all uploads |
| GET | `/me/stats` | `GetUserStatistics` | User stats |
| GET | `/me/stats` | `GetUserStatistics` | Counter-backed user stats: legacy flat `uploads`/`files`/`totalSize` plus a nested `usage` object (current + lifetime counters, stats-tracking start timestamp, download windows); `token` scopes `usage` to the current API token (no download windows) |
| GET | `/me/stats/downloads/daily` | `GetUserDownloadsDaily` | Current user's dense daily download series (`days` 1–31, default 30, strict 400); bare oldest-first array of `{day, downloads, bytes}` |

### User Management Endpoints (authenticated — session cookie required)

Expand All @@ -270,10 +271,13 @@ When processing a request, limits are resolved via the custom `Context`:
| Method | Path | Handler | Description |
|--------|------|---------|-------------|
| POST | `/user` | `CreateUser` | Create user |
| GET | `/stats` | `GetServerStatistics` | Server stats |
| GET | `/users` | `GetUsers` | List users (paginated, filterable by `provider` and `admin` status) |
| GET | `/stats` | `GetServerStatistics` | Counter-backed server stats: legacy flat totals (`users`, `uploads`, `anonymousUploads`, `files`, `totalSize`, `anonymousTotalSize`), top-level `lifetimeUsers`, and nested `usage`/`anonymousUsage` objects (current + lifetime counters, downloads, feature usage; download windows on server `usage` only) |
| GET | `/stats/downloads/daily` | `GetServerDownloadsDaily` | Server-wide dense daily download series (`days` 1–31, default 30, strict 400); bare oldest-first array of `{day, downloads, bytes}` |
| GET | `/stats/trending/uploads` | `GetTrendingUploads` | Trending uploads (`window=all\|1d\|7d\|30d`, `limit`) |
| GET | `/stats/trending/files` | `GetTrendingFiles` | Trending files (`window=all\|1d\|7d\|30d`, `limit`) |
| GET | `/users` | `GetUsers` | List users (paginated, sortable by `sort` (`date`/`size`/`lifetimeSize`), filterable by `provider` and `admin` status) |
| GET | `/users/search` | `SearchUsers` | Search users by login/name/email (LIKE query, `q`, `limit`, `provider`, `admin`) |
| GET | `/uploads` | `GetUploads` | List all uploads (paginated, filterable by `user`, `token`, `sort`, badge settings: `oneShot`, `removable`, `stream`, `extendTTL`, `password`, `e2ee`) |
| GET | `/uploads` | `GetUploads` | List all uploads (paginated, sortable by `sort` (`date`/`size`/`downloads`), filterable by `user`, `token`, and badge settings: `oneShot`, `removable`, `stream`, `extendTTL`, `password`, `e2ee`) |

---

Expand Down Expand Up @@ -421,8 +425,21 @@ GORM-based with auto-migrations via gormigrate.
| `tokens` | `Token` | Upload tokens, FK to users |
| `settings` | `Setting` | Server settings (e.g., auth signing key) |
| `cli_auth_sessions` | `CLIAuthSession` | Ephemeral CLI device auth sessions (auto-cleaned) |
| `usage_stats` | `UsageStats` | Flat scoped current/lifetime counters keyed by `(user_id, token)`: `("", "")` for server, `("__anonymous__", "")` for anonymous usage, `(userID, "")` for users, and `(ownerUserID, token)` for API tokens |
| `download_stats_daily` | `DownloadStatsDaily` | Daily download rollups keyed by day/entity type/entity ID for trending windows; cleanup keeps today plus 30 previous UTC-day buckets |
| `migrations` | (gormigrate) | Schema migration history |

### Stats Model

Plik's stats system is database-backed so several server instances can safely share one metadata database. Counters are updated in the same metadata transactions that create uploads, complete files, remove retained data, and record counted downloads. The detailed implementation contract lives in [server/ARCHITECTURE.md](server/ARCHITECTURE.md#stats-architecture); the system-level invariants are:

- **Current stats** describe retained state now: active uploads, uploaded files, retained size, current feature usage, current TTL distribution, and current file-size distribution.
- **Lifetime stats** describe events since tracking started: lifetime uploads, lifetime files, lifetime size, lifetime feature usage, lifetime TTL distribution, and lifetime file-size distribution.
- **StartedAt** is the "Stats since ..." timestamp. Migration-created rows start at migration/backfill time because already purged metadata cannot be reconstructed.
- **Anonymous usage** is tracked through the synthetic `("__anonymous__", "")` usage row instead of a real user.
- **Downloads** are logical intent events, not bytes. Full-file `GET` requests count, start-range `GET` requests count, non-start ranges and malformed ranges do not. Direct file download stats are recorded after auth/status/backend-open/range-policy checks and before streaming completes; archive download stats are recorded after a successful archive close.
- **Trending and server download windows** use upload/file download counts and daily rollups for `1d`, `7d`, and `30d` UTC-day windows. Trending is admin-only. Cleanup keeps today plus 30 previous UTC-day rollup buckets, one extra bucket beyond the 30-day display window.

---

## Documentation
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/server-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ plikd file delete --all

## Cleanup

Remove expired uploads and purge deleted files from the data backend:
Remove expired uploads, purge deleted files from the data backend, and prune old download statistics used for trends:

```bash
plikd clean
Expand Down
Loading
Loading