Permissions Contract Basics
Every protected operation must have a registered permission contract. A route or save handler should never check an undeclared permission.
File Location
Preferred external contract:
addons/{addon_key}/src/permissions_contract.phpDeclare the provider in `manifest.php` and `bootstrap/install_contract.php`. Do not copy the full permission catalog into those files.
Permission Naming
Permission keys are lowercase dot-delimited identifiers.
Good examples:
- `example_notes.view`
- `example_notes.manage`
- `wiki.publish`
- `permissions.groups.manage`
- `ticket_support.ticket.submit`
- `ticket_support.ticket.view_own`
Avoid shared generic keys such as `manage` or `edit` without an owner prefix.
Preferred Contract Shape
<?php
declare(strict_types=1);
if (!defined('HC_ACCESS')) {
exit;
}
function amv_example_notes_permissions_contract(): array
{
return [
'addon_key' => 'example_notes',
'permissions' => [
'example_notes.view' => [
'label' => 'View Example Notes',
'description' => 'View notes in the Example Notes addon.',
],
'example_notes.manage' => [
'label' => 'Manage Example Notes',
'description' => 'Create, edit, publish, and archive notes.',
],
'example_notes.note.submit' => [
'label' => 'Submit Example Notes',
'description' => 'Submit frontend notes when the site allows public/member intake.',
],
],
'recommended_roles' => [
'administrator' => ['example_notes.view', 'example_notes.manage', 'example_notes.note.submit'],
'editor' => ['example_notes.view', 'example_notes.manage'],
'member' => ['example_notes.note.submit'],
],
];
}
return amv_example_notes_permissions_contract();Install Contract Pointer
Use this shape in `bootstrap/install_contract.php`:
'permissions_contract_present' => true,
'permissions_contract' => [
'provider' => 'addons/example_notes/src/permissions_contract.php',
'registration_function' => 'amv_example_notes_permissions_contract',
],That is a pointer, not a second permission catalog.
Frontend Permissions
Frontend permissions live in the same permission contract as admin permissions.
A frontend route or public target may require keys such as:
- `ticket_support.ticket.submit`
- `ticket_support.ticket.view_own`
- `ticket_support.ticket.reply_own`
The route/public-target access metadata may reference those keys, but it must not invent them locally.
Required Agreement
These must agree by reference, not by duplicated catalogs:
- manifest points to `src/permissions_contract.php`
- install contract points to `src/permissions_contract.php`
- `src/permissions_contract.php` owns the permission catalog
- route metadata references keys from that catalog
- admin/public handlers check keys from that catalog
- documentation names the same keys
Permission renames are migrations. Do not casually rename permission keys once assigned to roles/users.
Related: Reference Samples/Permissions Contract Sample, Addon Development/Admin Routes, Centralized Systems/Frontend Access and RBAC.
Updated: 2026-05-07 20:06:59