Last Updated: April 9, 2026
- Reuse limiter stacks such as
limiter.Authandlimiter.Login, and uselimiter.AbortJSONfor429responses. - Lean on
api.ClientIP,header.BearerToken, and the sharedAbort*helpers instead of duplicating request parsing. - Compare secrets in constant time and set
Cache-Control: no-storeon sensitive responses. - Register routes in
internal/server/routes.go. - New list endpoints should default to
count=100, cap at1000, requireoffset >= 0, and document their parameters explicitly. - When a test or fixture needs portal mode, set
PHOTOPRISM_NODE_ROLE=portaltogether withPHOTOPRISM_JOIN_TOKEN.
- Map user roles through
acl.ParseRole(s)andacl.UserRoles[...]; map client roles throughacl.ClientRoles[...]. - Treat
RoleAliasNone(none) and the empty string asRoleNone; default unknown client roles toRoleClient. - When checking JWT or client scopes, use
acl.ScopePermitsandacl.ScopeAttrPermitsinstead of handwritten parsing. - Use
AuthenticateAdmin,AuthenticateUser, orOAuthTokenhelpers in tests rather than building auth flows manually. - Build client-session requests with
entity.AddClientSession(..., authn.GrantClientCredentials, nil)andAuthenticatedRequest(...). - Admin sessions may see
AdvertiseUrlandDatabase; client and user sessions must not.SiteUrland the clientstorageNamespacederived from it are safe to expose.
- Annotate only routed handlers in
internal/api/*.go; use full/api/v1/...paths and skip helper functions. - Regenerate API docs with
make fmt-go swag-fmt swagormake swag-json;make swagmay fetch modules, so confirm network access first. - While iterating, prefer focused runs such as
go test ./internal/api -run Cluster -count=1. - Isolate config paths with
t.TempDir()and reuseNewConfig,CliTestContext, andNewApiTest()helpers. - For negative permission checks, prefer OAuth client tokens over non-admin user fixtures.
- Register
CreateSession(router)only once per test router; a second registration panics on duplicate routes.