# AGENTS.md — Bookhoard server Standing rules for any agent or developer working in this repo. Project-wide context lives in the sibling repos' AGENTS.md files (../bookhoard-app/AGENTS.md is the Android client's). ## Library visibility (family-lens rule) — ALWAYS apply Per-user library visibility (`library_visibility` table, default-ALLOW: every user sees every library unless an admin hides one) must be enforced on **every endpoint that returns media metadata or content**. It is NOT automatic — enforcement is per-handler. - **New endpoints that list/filter media or series** MUST filter by the requesting user's visible-library set, and MUST reject an explicit `library_id` outside that set with 403 (never silently pass). - Helpers (do not hand-roll): - `handlers`: `(*MediaHandler).visibleLibrarySet` / `(*SeriesHandler).visibleLibrarySet` — membership map + id slice. - `router`: `visibleLibraryIDsFor` — same for SSR routes. - `resolveLibrary` (helpers.go) only honors requested library ids inside the visible set. - SQL pattern: `AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))` — see `ListMediaItems`, `GetDistinctSeries`, `SearchMediaItems`. - Content endpoints (file bytes, page images) additionally gate per item like `ReaderHandler.ensureItemVisible` / the download endpoint. - Tests must pin visibility: hidden library → 403 on the endpoint, and absent from unscoped aggregates. History: 2026-10-13 (`431fec0`) the series/media listing surfaces leaked hidden libraries to any authenticated account — series endpoints were name-only across ALL libraries. Fixed; do not regress. ## Tests - Use `setupTestServer()` from `cmd/server/tests/test_helpers_test.go` (see the Conventions section below for the pool/cleanup rules). - Integration tests run against the SHARED dev database: never write destructive SQL without preserving the dev rows (libraries Manga/ Comics/Ebooks, testuser's annotations and progress are real data). - Test-created users must use `@tests.bookhoard.internal` or `@example.com` emails and "test"-named collections/libraries so the harness cleanup can find them. - **NEVER rename or edit the shared admin (`testuser`) in tests** — a renamed admin escapes the cleanup preserve-list and cascades real data (this wiped the dev annotations once; see user_test.go TestUpdateProfileSelfEdit for the dedicated-user pattern). ## Timezones - The server owns display conversion: clients send instants (RFC3339), the server renders in the requesting user's timezone. - Use `utils.FormatTimeInTimezone` / `utils.UserLocation` — never `time.Now().Format(...)` bare (that is server-UTC). - Analytics day-buckets must bucket in the user's timezone (`CreatedAt.In(loc)`), and date-range end bounds must be end-of-day. ## Conventions (absorbed from the retired PROJECT_GUIDELINES.md — ## only the rules that still matched reality) - **Runtime is Docker** (Makefile auto-detects docker, falls back to podman). The old "podman only" rule is dead. - **The server binary is only built inside the Docker build** (`make rebuild-app`); host-side Go builds are for tests/vet only. - **Schema changes go in place** — edit `database/schema/schema.sql`, never create migration files, until a release splits them. - **Business logic lives in services**, not handlers; handlers parse requests and call services (all DB access goes through services). - **Integration tests**: one `setupTestServer()` per test function; test pools use `max_conns=1`; never create a `pgxpool` per subtest (connection exhaustion); use `t.Cleanup()`-based helpers; table-driven subtests over duplicate functions.