Amvionlie CMS
Where the Future Begins

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.php

Declare 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