# CPalius CMF 2.2.2

**Released:** 2026-09-23 · **Channel:** stable · **Critical:** no

Turkish: [2.2.2-tr.md](2.2.2-tr.md)

Roles stay the default. AACP can now attach sparse grant/deny exceptions to one user without minting a new role or writing `system.*` through a checkbox wall. Dead public URLs can 301/302 in Studio, and missing pages use a themed 404 instead of Symfony's "Oops!".

## What's new

**Per-user capability overlay.** Role YAML is unchanged. Each user may inherit, grant, or deny a registered capability. Deny wins over the role union, a T1.4 record grant, and the T1.5 Access event. `system.*` cannot be granted through the overlay (a raw SQL grant row is ignored). The last admin, and your own account, cannot be denied `system.aacp.access`, `system.users.manage`, `system.module.manage`, or `system.update.manage`. Reset deletes overlay rows only. One SELECT per user per request. Design: `cp-core/src/Core/Security/USER_CAPABILITY_OVERRIDES.md`.

**Forum section ACL is unchanged.** Overlay speaks module capability strings. Board-level Forum permissions stay in `ForumPermissionService`.

**Themed 404 / 403 / 500.** TwigBundle exception pages replace the Symfony default and send `noindex`.

**Studio SEO redirects.** Map a dead path to a URL (301 or 302). Skips admin/AACP/profiler. Seo 1.1.0.

## Upgrade notes

`cp:update` applies `Version20260923010000` (`cp_user_capability_overrides`) and the Seo `cp_seo_redirects` SQL. After files land, empty `cp-core/var/cache/prod/` (leave the folder).

Overlay appears on AACP user **edit** only, below the role form.

## Known limits

A raw SQL deny of `system.aacp.access` on the last admin is an operator disaster, same as deleting that admin. The writer refuses it; the voter does not re-scan every user on each `is_granted`.
