# Changelog

## Unreleased

### Roles and permissions system

Replaced the single moderator promote/demote toggle with a full role hierarchy
and a runtime-editable, granular permission system.

- **Role hierarchy**: SuperAdmin (env-only) > Admin > Moderator > Writer > Guest
  (absence of a role). SuperAdmin always holds every permission and can never be
  edited, demoted, or removed through the UI.
- **New env var `SUPERADMIN_PUBKEYS`** (comma-separated hex or npub) replaces the
  old env-admin concept. Falls back to `ADMIN_PUBKEYS` when unset so existing
  deployments keep working.
- **Permission registry**: a central list of granular keys in
  `backend/src/constants/permissions.ts` is the single source of truth. Adding a
  key there surfaces it automatically in the API and the Roles matrix page.
- **Authorization**: a single `requires(permission)` guard replaces the old
  role-name middleware and is applied to every protected endpoint. Roles and
  permissions are resolved live per request, so a stale JWT no longer drives
  access. SuperAdmin bypasses all checks.
- **Data model**: `User.role` is now nullable (null means Guest) and uses the
  lowercase enum `admin`, `moderator`, `writer`. A new `RolePermission` table
  maps roles to permission keys. A migration backfills legacy uppercase roles and
  seeds default permission sets; it is idempotent and safe on a live database.
- **New endpoints**: `GET /api/auth/me`, `GET /api/admin/permissions`,
  `GET /api/admin/roles`, `PUT /api/admin/roles/:role/permissions`, and
  `PUT /api/users/:pubkey/role` (replacing `POST /api/users/promote` and
  `POST /api/users/demote`).
- **Dashboard**: User Management now uses a per-user role selector with hierarchy
  enforcement and a locked SuperAdmin badge. A new SuperAdmin-only Roles page
  renders a permission-by-role matrix. Nav items are gated on resolved
  permissions. The NIP-05 reserved-username assignment is unchanged.

#### Migration notes

After pulling, from `backend` run `npm run migrate:deploy` (or, for databases
created with `db push`, `npm run db:baseline-and-migrate`). Set
`SUPERADMIN_PUBKEYS` in your `.env`. Existing moderators become `moderator`,
existing database admins become `admin`, and prior env admins become SuperAdmin.
