KLXM Studio – Home
Get in touch

Developers

Make it your system.

A lean PHP core without a framework, open interfaces and clearly documented extension points – from kits to your own CLI command.

Tech-Stack

What KLXM Studio is built with.

Versions according to composer.lock and tools/package.json of version 1.0.0.

AreaTechnologyDetails
RuntimePHP ≥ 8.4, no frameworkFront controller, PSR-4 autoload (Core\ → app/) via Composer; extensions sodium, gd, mbstring, dom, pdo.
DatabaseSQLite or MySQL/MariaDBPer website SQLite (default, one file) or MySQL/MariaDB ('db' in config/sites/{key}.php); backup via VACUUM INTO or mysqldump. Search index, chat, shared tables, media pools, support and AI logs always stay SQLite – pdo_sqlite is required, SQLite ≥ 3.35 (Debian 12/13, Ubuntu 22.04/24.04; AlmaLinux/RHEL 8/9 too old). Doctrine DBAL 4.4.4 (MIT) for table schemas.
ServerApache, nginx or php -SNo .htaccess – only public/ is in the web root; runs on Plesk and ordinary PHP hosting, locally with php -S.
Searchloupe/loupe 1.1.0 (MIT)Full text with typo tolerance on SQLite; optionally semantic/hybrid via vectors (Symfony AI Store).
AISymfony AI 0.14 (MIT)platform + store; providers Ollama, Mistral, OpenAI and generic/OpenAI-compatible; local transcription with whisper.cpp.
SecurityWebAuthn, TOTP, libsodium, CSPPasskeys with lbuchs/webauthn 2.2.0 (MIT), TOTP with QR code (chillerlan/php-qrcode 5.0.5, MIT/Apache-2.0), libsodium for encrypted requests and secrets, Content Security Policy without unsafe-inline.
Calendar & syncrlanvin/php-rrule 3.0.0 · sabre/dav 4.7.1Recurrence (MIT); CalDAV/CardDAV in the dav extension (BSD-3-Clause).
HTTP & Mailsymfony/http-client 8.0.16 · symfony/mailer 8.0.15Proxy for maps and thumbnails, AI providers, SMTP delivery (MIT).
FrontendVanilla JS, esbuild 0.25.12Scripts as IIFE and ES modules (.mjs), built with esbuild in tools/ (pnpm); admin UI isolated from the kit via Shadow DOM; CSS budgets per kit.
Vendor-AssetsEditor.js 2.31.7 · PDF.js 6.3.289 · MapLibre GL JS 6.11.2Block editor (Apache-2.0) with editorjs-drag-drop 1.1.16 (MIT), PDF viewer (Apache-2.0), maps (BSD-3-Clause); Lato font (OFL).
IconsPhosphor Icons 2.1.1 duotone (MIT)511 icons in 23 topics, as a sprite per website.
LicenceMITCore, kits and extensions; your own kits and extensions under any licence. Third-party software see THIRD-PARTY-NOTICES.md.

Extension points

Where you can hook in.

Every snippet is taken from the source code of version 1.0.0.

  • Kits

    A kit is a self-contained package: blocks, central settings, design tokens, conditional CSS per block and structured data. New kit: php bin/console kit:create <name> [--from=…] – the starter kit is the default. Technically a kit lives in themes/{name}/theme.php – the folder name stays for compatibility (theme:create still works).

    themes/essenz/theme.php
    'jsonld' => 'essenz_jsonld',
    'conditional_css' => [
        'css/b-faq.css' => ['faq'],
        'js/video.js' => ['video'],
    ],
    'design' => require __DIR__ . '/design.php',
  • Blocks as PHP templates

    One renderer per block type in themes/{name}/blocks – with inline editing via $b->edit(). The block automatically appears in the editor, the REST API and the MCP server.

    themes/basis/blocks/richtext.php
    <div class="wrap wrap--text">
      <?= basis_head($b) ?>
      <div class="prose"<?= $b->edit('text', 'rich') ?>><?= rich($d['text']) ?></div>
    </div>
  • Block builder

    Custom blocks without PHP: fields, a safe template language with escaping and scoped CSS; export as a kit block. Excerpt from this website’s “command line” block:

    Admin → Blocks
    <ol class="cli-list">
      {% for c in commands %}<li class="cli-row">
        <code class="cli-cmd">{{ c.cmd }}</code>
        <span class="cli-text">{{ c.text }}</span>
      </li>{% endfor %}
    </ol>
  • Extensions

    An extension ships features, permissions, migrations, menu entries, routes, blocks, proxy sources and CLI commands – enabled per website via 'extensions' => ['dav']. Extensions live in extensions/{name} or come as Composer packages of type “mycms-extension” (composer require vendor/package), which the core discovers automatically.

    extensions/dav/extension.php
    'boot' => function (Core\Extension $x): void {
        $x->feature('dav', 'CalDAV/CardDAV (Kalender und Kontakte in Apps)', ['dav.use']);
        $x->permissions('CalDAV/CardDAV', ['dav.use' => '…']);
        $x->migration(1, fn(Core\Database $db) => MyCms\Dav\Dav::migrate($db));
        $x->nav('/admin/dav', 'Kalender & Kontakte in Apps', 'dav', 'dav.use');
  • Example: Consent Kit

    A complete extension with its own README, LICENSE (MIT) and THIRD-PARTY-NOTICES: consent management with 38 templates, a web component in Shadow DOM, Consent Mode v2 and a CSP that only widens after consent. Ported from KLXM’s REDAXO add-on consent_kit.

    extensions/consent_kit
    // config/sites/{key}.php bzw. config/config.local.php
    'extensions' => ['consent_kit'],
    // abschalten, ohne die Erweiterung zu entfernen:
    'features' => ['consent' => false],
  • Output hooks

    Extensions can post-process the website HTML, add sources to the CSP (never unsafe-inline) and add links to the legal line.

    app/Extension.php
    $x->htmlFilter(fn(string $html, array $ctx) => $html)
    $x->csp(fn() => ['script-src' => ['https://…']])
    $x->footerLinks(fn() => [['label' => …, 'href' => '#…']])
  • REST-API

    OpenAPI 3.1 at /api/v1/openapi.json. Tokens with read or write level, expiry and mode “apply directly” or “for approval” – only a SHA-256 hash is stored.

    curl
    curl -H "Authorization: Bearer $TOKEN" https://ihre-domain.de/api/v1/me
  • MCP server

    Model Context Protocol over Streamable HTTP with 42 tools, including list_pages, update_block, save_entry, upload_media, set_design, publish_page – with the same tokens and rules as the API.

    Claude Code
    claude mcp add --transport http mycms https://ihre-domain.de/mcp --header "Authorization: Bearer cms_…"
  • Command line

    Around 50 commands for accounts, websites, network, kits, fonts, migration, backups, shared data, search and AI; extensions add their own.

    bin/console
    php bin/console site:create kunde www.kunde.de,kunde.de basis
    php bin/console migrate --all
    php bin/console health
    php bin/console blocks:selftest
  • Data tables

    Custom tables with 23 field types, conditions, detail pages and public forms – shared between several websites of an installation.

    bin/console
    php bin/console data:share <handle> --members=a,b [--see-members] [--merge]
    php bin/console shared:list
  • External sources

    RSS 2.0/1.0, Atom, JSON (path), XML (XPath) and OpenImmo 1.2 (XML, ZIP or pull URL) are fetched server-side, mapped with transforms into entries of a data table and synced; images go to the media library, synced entries are read-only. Protected against SSRF, XXE and ZIP slip.

    Mapping (JSON) · bin/console
    {"id_path": "guid | link",
     "rows": {"titel": {"path": "title", "tx": "text"},
              "datum": {"path": "pubDate | dc:date", "tx": "date"},
              "bild":  {"path": "enclosure@url | media:content@url", "alt": "title"}}}
    
    */15 * * * * php bin/console sources:sync --all
  • Languages

    Fixed texts use lt('…') (website) or __('…') (admin); translations are a PHP array per language, i18n:missing lists gaps.

    themes/essenz/lang/site/en.php
    'Datenschutz' => 'Privacy',
    
    # php bin/console i18n:missing en --site-texts
  • Design tokens

    Every token becomes a CSS variable or class; the style editor shows presets and checks contrast, also via API and MCP (get_design/set_design).

    themes/essenz/design.php
    $color('accent', 'Signal (Füllfarbe)', '--e-a', '#D85B19', '#F0782F', ['with' => 'background', 'min' => 3],
  • Feature flags per website

    Presets and individual features per website; disabled features lock their permissions – navigation, API and MCP follow automatically.

    config/sites/{key}.php
    'preset'   => 'full' | 'content' | 'minimal',
    'features' => ['data.schema' => false, 'api' => false],
    'blocks'   => ['deny' => ['video'], 'allow' => null],

Anatomy of a kit

Get started

Local in four steps.

From the README quick start. The developer manual lives in the admin at /admin/hilfe/technik.

  1. Get the source

    Public repository: github.com/klxm/studio – bugs and requests at github.com/klxm/studio/issues.

    git
    git clone https://github.com/klxm/studio.git
    cd studio
  2. Dependencies and build

    Composer 2 for PHP, Node 22 + pnpm 10 only for the build – the server needs neither Node nor pnpm.

    shell
    composer install
    cd tools && pnpm install && pnpm build && cd ..
  3. Run

    The first request creates the database and config/config.local.php (app_key, setup_token) and seeds the kit’s starter content.

    shell
    php -S localhost:8000 -t public public/index.php
  4. Create an account

    Or open /admin/setup with the setup token. Then set up encryption, email delivery and kit details.

    shell
    php bin/console user:create name@example.org admin

Contact

Talk to KLXM Crossmedia.

A demo, use in your project or an extension tailored to your needs – get in touch. We answer personally.

KLXM Crossmedia GmbH
Am Schürmannshütt 40g
47441 Moers
Germany