- HTTP handlers are plain `net/http` functions using chi route params.
- DB access comes from request context via `handlers.WithPool` and `poolFrom(r)`.
- JSON responses use `writeJSON` in `internal/handlers/util.go`.
- JSON request bodies use `jsonDecode`, limited to 1 MiB.
- Resource IDs are UUIDs (`github.com/google/uuid`).
- Tags are stored in `tags` + `resource_tags`; new tag slugs are deterministic hashes from `tagSlug`.
- Public resource listing only returns `resources.status = 'published'` and `is_public = TRUE`.
- Admin routes require `Authorization: Bearer <admin-jwt>` from `/api/admin/login`.
- Wallet routes produce normal user JWTs, not admin JWTs.
## Database/migration notes
-`migrations/001_init.sql` is designed for a fresh database and is not fully idempotent.
-`migrations/002_wallet_auth.sql` is idempotent.
-`RUN_WALLET_AUTH_SCHEMA` defaults to true and lets startup create the wallet nonce table. In production with read-only/public DB users, apply migration separately and set it to false.