Architecture

Extensions

Extensions are versioned packages that add features to Paradigm CMS without modifying core.

Directory layout

The runtime selects the configured version, or the latest semantic version available in the folder. Every extension follows this structure:

extensions/<extension_slug>/<version>/
├── extension-manifest.json
├── README.md
├── src/
│   ├── init.php
│   ├── config.php
│   ├── install.php
│   ├── config/
│   │   ├── objects/
│   │   │   └── <object_name>.json
│   │   └── widget.php
│   ├── API/
│   │   ├── config.php
│   │   ├── requests/
│   │   │   ├── <action>.php
│   │   │   └── <entity>/
│   │   │       ├── create.php
│   │   │       ├── update.php
│   │   │       ├── delete.php
│   │   │       ├── list.php
│   │   │       └── get.php
│   │   └── REST/
│   │       └── <resource>/
│   │           ├── get.php
│   │           ├── post.php
│   │           ├── put.php
│   │           └── delete.php
│   ├── blocks/
│   │   ├── admin/
│   │   │   ├── dashboard.php
│   │   │   └── <entity>/
│   │   │       ├── list.php
│   │   │       ├── create.php
│   │   │       └── edit.php
│   │   ├── public/
│   │   │   └── <block_name>.php
│   │   └── widget/
│   │       └── <block_name>.php
│   ├── classes/
│   ├── layouts/
│   │   └── admin/
│   │       └── index.php
│   ├── assets/
│   │   ├── js/
│   │   ├── css/
│   │   └── images/
│   ├── widget/
│   │   └── views/
│   └── lib/

Extension manifest

Each extension root contains an extension-manifest.json with metadata:

{
    "slug": "extension_name",
    "version": "1.0.0",
    "repository": "https://github.com/org/repo"
}

init.php — hook config class

The init.php file defines a config class that hooks into the CMS lifecycle. This is the only place a PHP class is allowed in extension code (aside from classes/).

<?php

class extension_slug_config {

    function end_of_head() {
        $html = '';
        echo $html;
    }

    function admin_end_of_head() {
        $html = '';
        echo $html;
    }

}

?>

API/config.php — action mapping

Maps human-readable action names to handler file names:

<?php

$API_CONFIG = array(
    '{entity}' => array(
        'actions' => array(
            'create' => 'create_{entity}',
            'list' => 'list_{entities}',
            'update' => 'update_{entity}',
            'delete' => 'delete_{entity}'
        )
    )
);

?>

install.php — schema initialization

Optional file that runs during extension activation to seed baseline data:

<?php

if (!defined('ROOT_DIR')) { header("HTTP/1.1 403 Not Found"); exit(); }

$existingPage = PAPI_get_object('core:page', array(
    array(array('slug', '=', '{page_slug}'))
), 'internal');

if ($existingPage === false) :
    PAPI_create_object('core:page', array(
        'label' => '{Page Label}',
        'slug' => '{page_slug}',
        'status' => 'active'
    ), 'internal');
endif;

Schema setup

The core schema updater reads JSON object definitions from src/config/objects/ and builds database tables and meta tables. Schema files use singular names (article.json) and the infoKeys/metaKeys format. See Database schema for the full format.

Enabling extensions

Extensions must be enabled in siteConfig.php under the extensions key. The schema updater refuses to run if the extension is not enabled.

Activation workflow

Extensions can provide an activate request. The core installer triggers extension:activate for required extensions during initial setup. Core activation demonstrates the pattern by updating schema and creating baseline pages.

AI-first extension checklist

  • Provide a clear README.md at the extension root describing APIs and blocks.
  • Keep request names short and consistent for predictable extension:request lookups.
  • Document block slugs alongside file paths so AI can navigate quickly.
  • Version extensions semantically to support deterministic upgrades.