ci4-cms-erp / ci4ms
Modular CodeIgniter 4 CMS featuring RBAC admin, theming, blog/page management, elFinder media integration, and CLI tooling for rapid customization.
Package info
Language:JavaScript
Type:project
pkg:composer/ci4-cms-erp/ci4ms
Fund package maintenance!
Requires
- php: ^8.2
- bertugfahriozer/ci4commonmodel: ^1.2
- bertugfahriozer/ci4seopro: ^v1.0.0
- bertugfahriozer/sql2migration: ^1.0
- ci4-cms-erp/ext_module_generator: ^1.1
- claviska/simpleimage: 4.4.0
- codeigniter4/framework: 4.7.4
- codeigniter4/settings: 2.2
- codeigniter4/shield: 1.4.0
- codeigniter4/translations: 4.7.2
- ezyang/htmlpurifier: 4.19
- gregwar/captcha: 1.3.0
- maxmind-db/reader: ^1.13
- studio-42/elfinder: 2.1.68
Requires (Dev)
- fakerphp/faker: ^1.9
- mikey179/vfsstream: ^1.6
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.5.16
Suggests
- ext-redis: Redis-backed realtime SSE (notificationsconfig.realtimeEnabled)
This package is auto-updated.
Last update: 2026-07-28 22:52:18 UTC
README
CI4MS is a CodeIgniter 4-based CMS skeleton that delivers a production-ready, modular architecture with RBAC authorization and theme support. It combines CMS workflows, developer-focused CLI commands, an extensible module system, and customizable front-end themes in a single package.
Key Features
- Authentication & RBAC:
Modules\Authhandles user login, lockouts, and password resets via CodeIgniter Shield. Permissions map toauth_permissions_pagesrecords. - Modular backend: Each feature ships as an independent module (Blog, Pages, Menu, Media, Users, Settings, Theme, etc.) under
modules/*. - Flexible content management: Page and blog entries include SEO metadata, categories, tags, and full comment workflows.
- Media & files: Includes elFinder-powered media management, a built-in file editor, and an in-panel log viewer.
- Automatic Updates: Modernized
UpdateServiceprovides a "One-Click Update" system with atomic file operations, automated GitHub version discovery (bypassing 300-file limits), and secure rollback management. - Signed Updates (fail-closed): Every file an update writes must appear in a
manifest.jsoncarrying a detached Ed25519 signature from a key your installation already trusts, with a per-file SHA-256 check on top. A compromise of the GitHub account, the release, or the CDN is not enough to push code â the publisher's offline private key is required. There is no "continue anyway" option, downgrades through the updater are refused, and the shipped keyring is empty on purpose, so auto-update stays off until you add a key you have verified. See Release Signing & Trusted Keys. - Security Architecture: Global CSRF protection across all AJAX endpoints, strict HTTP security headers (CSP, HSTS, X-Frame-Options), executable file upload blacklists, and HTMLPurifier sanitization to prevent XSS and RCE attacks.
- Backup Support: Updates automatically trigger a full backup of modified files before applying patches, with a dedicated management interface for restores.
- Theme system: The
public/templates/*structure and theModules\Thememodule enable installing or upgrading themes from ZIP packages. - Setup & automation: Offers a web-based installer (
/install) plus a single CLI command (php spark ci4ms:setup) for automated installation, default data seeding, and route generation. Module scaffolding is available viaphp spark make:module. - Docker support: Ships with a production-ready
Dockerfile,docker-compose.yml, and a GitHub Actions CI workflow out of the box. - SEO helpers:
ci4seoprobuilds meta tags and JSON-LD, whileCommonLibrarycentralizes email, breadcrumbs, and inline shortcode utilities.
Requirements
- PHP 8.2 or newer (
intl,json,mbstring,gd,curl,opensslextensions required) - Composer 2.5+
- MySQL / MariaDB (or any CodeIgniter 4-supported driver)
- Writable directories:
writable/,public/uploads/, optionallypublic/templates/ - Optional: Redis +
ext-redis(phpredis) â only for realtime notifications, which are off by default. The extension must be built for the PHP version that serves the site; on a multi-version setuppecl install rediseasily targets the wrong one, and the feature then degrades silently to 60 s polling. See the Notifications module README.
See composer.json for the full dependency list (e.g. bertugfahriozer/ci4commonmodel, bertugfahriozer/sql2migration, ci4-cms-erp/ext_module_generator, claviska/simpleimage, gregwar/captcha, studio-42/elfinder).
ðŠī Project Activity
Installation
Fresh Project (recommended)
composer create-project ci4-cms-erp/ci4ms myproject
cd myproject
Clone Existing Repository
git clone <repo-url> ci4ms cd ci4ms composer install
Docker (recommended for development & CI)
cp env .env # configure database, baseURL, etc. cp app/Config/DefaultRoutes.php app/Config/Routes.php docker compose up -d --build docker exec ci4ms_app composer install docker exec ci4ms_app php spark ci4ms:setup
Refer to DOCKER_SETUP.md for full Docker configuration details.
Environment & Configuration
- Create your
.envfrom the template:
cp env .env
-
Update these core settings in
.env:app.baseURLdatabase.default.*- Optional:
cookie.*,honeypot.*,security.*
-
Prepare the routes file:
cp app/Config/DefaultRoutes.php app/Config/Routes.php
- If you prefer the web installer, open
/installin the browser and follow the wizard. Use the CLI step below to skip the wizard.
One-Command Setup (CLI)
php spark ci4ms:setup
This single command runs all migrations, seeds default data (modules, permissions, sample content), and creates the initial administrator account. No separate migrate or seed commands are needed.
Run the Dev Server
php spark serve
Access the backend via: https://<domain>/backend
Directory Layout
app/ Application code (controllers, config, libraries, filters)
modules/ Feature modules (Auth, Backend, Blog, etc.)
public/
index.php Front controller
be-assets/ Admin UI build artifacts (CSS/JS)
templates/ Front-end themes
media/ Media storage (must be writable)
writable/ Cache, logs, temporary files (must be writable)
vendor/ Composer packages
.docker/ Dockerfile, Apache, and PHP configuration
docs/ Developer documentation
Key files:
app/Commands/â CLI tooling (make:a*,create:route,ci4ms:setup).app/Filters/Ci4ms.phpâ Install guard, maintenance mode redirect, menu cache.app/Config/DefaultRoutes.phpâ Routes template; copy toRoutes.phpon setup.modules/*â Each module includes its ownConfig/Routes.php,Controllers,Models,Views,Language,Libraries,Filters.public/templates/â Theme assets; each theme requiresinfo.xmlandscreenshot.png.writable/â Cache, logs, temporary files.
Modules
| Module | Purpose | Highlights |
|---|---|---|
| Auth | Authentication lifecycle | Shield-based, CAPTCHA, email activation, reset tokens |
| Backend | Admin shell | Dashboard stats, shared base controller |
| Blog | Blog CRUD | Categories, tags, comments, bad-word filters |
| Pages | Static page management | SEO fields, inline shortcode parsing |
| Menu | Menu builder | Drag-and-drop ordering, slug helpers |
| Media | Media manager | elFinder integration, optional WebP conversion |
| Fileeditor | Project file editor | Safe read/write/rename; dangerous extension blacklist |
| Settings | System configuration | One-click updates, company/social/mail settings, i18n support |
| Users | User & role management | Shield groups, reset tracking |
| Methods | Route â permission mapping | Module toggling, router scan |
| Logs | Log viewer | Browses CodeIgniter log files inside the backend |
| ModulesInstaller | Module ZIP installer | Upload + cache invalidation |
| Theme | Theme manager | ZIP upload, DB migration support, duplicate checks |
| Install | Web installer | Creates .env, triggers migrations |
| Backup | Database backup manager | Create, download, and restore with SQL sanitization |
| DashboardWidgets | Dashboard statistics | Modular widget system for admin overview |
| LanguageManager | Language file manager | Edit and manage translation files from the backend |
| Notifications | In-app admin notifications | Bell dropdown, single-global-row targeting (user / group / broadcast), optional Redis-backed SSE realtime, per-user opt-out screen |
See docs/architecture.md for deeper architectural notes.
CLI Commands
| Command | Description |
|---|---|
php spark ci4ms:setup |
Full automated installation: migrations, seeding, default data |
php spark make:module Blog |
Scaffold a new module (Config, Controllers, Views, language files) |
php spark make:abview dashboard |
Generate a backend view from the AdminLTE template |
php spark create:route |
Rebuild app/Config/Routes.php from the template |
php spark migrate --all |
Run all pending migrations across modules |
php spark cache:clear |
Clear all application caches |
php spark ci4ms:geoip-update |
Download/update the local DB-IP City Lite database for session geo lookup (run monthly via cron) |
php spark ci4ms:release:keygen |
(publisher only) Generate an Ed25519 release signing keypair into a password-sealed keyfile |
php spark ci4ms:release:manifest |
(publisher only) Build and sign writable/release/manifest.json + .sig from the tracked tree |
php spark ci4ms:release:verify |
Verify a local manifest/signature pair, or a published release with --remote --tag v<x.y.z.w> |
Standard CodeIgniter commands (php spark db:seed, php spark key:generate, etc.) are also available.
Developer Notes
- Cache keys:
settings(24h),menus_{locale}(per-locale, 24h),{userId}_permissions. Clear withphp spark cache:clearorcache()->delete(). - Base controller: Extend
Modules\Backend\Controllers\BaseControllerfor new backend controllers; it prepares session user, navigation, mail settings, and shared data. - Permissions: Register new secured routes in
Modules\Methods(or via the database) so the permission filter recognizes them. - Slug generation:
seflink()handles transliteration (including Turkish characters). - Form security: Global CSRF is enabled; backend AJAX endpoints opt out via
BackendConfig::$csrfExcept. - Comment moderation:
CommonLibrary::commentBadwordFilteringhandles bad word filtering and moderation rules. - Theme uploads: Each theme must include
info.xmlandscreenshot.png; missing files trigger a backend warning.
Testing & Maintenance
composer testâ runs PHPUnit.- The GitHub Actions workflow (
.github/workflows/docker-test.yaml) automatically builds the Docker image and runs migrations on every push tomaster. - Maintenance mode: When
settings.maintenanceMode.scalar == 1, theCi4msfilter redirects visitors tomaintenance-mode. - Security:
Fileeditorenforcesrealpathguards and a dangerous extension blacklist (.php,.phtml,.phar,.htaccess) to prevent RCE; destructive operations (deleteFileOrFolder,renameFile) additionally validate against an extension allowlist to block renaming or deleting critical application files.Backuprestore uses SQL statement whitelist to block malicious queries (LOAD_FILE,GRANT, etc.).HTMLPurifierconfig is hardened against XSS bypass (data:URIs blocked,CSS.Trusteddisabled) andCustomRules::getClean()output is persisted on everycreateandupdateflow in Blog and Pages controllers to prevent Stored XSS. All$_SERVERreads replaced with CI4base_url()/site_url()helpers. ConfigureApp.php::$proxyIPsif behind Cloudflare/Nginx.
Release Signing & Trusted Keys
The auto-updater verifies a detached Ed25519 signature over the release manifest before it writes a single file, and then checks every downloaded file against its SHA-256 entry in that manifest. Because the trust decision is made against a key list stored inside your installation, an attacker who takes over the GitHub account, the release, or the CDN still cannot push code to you.
Auto-update is off until you configure trust. The repository ships with an empty keyring (Modules\Settings\Config\UpdateKeys::$keys = []), which is deliberate: a key shipped in the same repository it is supposed to protect proves nothing. Until you add a public key, the updater refuses every update and reports Settings.updateNoTrustedKeys.
Published signing key fingerprints
Before adding a key to your keyring, confirm its key_id and full hex SHA-256 fingerprint against the table below and against at least one channel that is not this repository (release announcement, project website, maintainer's own key publication). If the two do not match, do not add the key and open a security report.
key_id |
SHA-256 fingerprint (hex) | Status | Added |
|---|---|---|---|
| not yet published | not yet published | â | â |
Publisher: replace the placeholder row with the real
key_idand fingerprint printed byphp spark ci4ms:release:keygen. Publish the full hex fingerprint, never a truncated one, and add a new row (rather than editing the old one) when rotating â during a rotation window both keys are listed, the old one moving torevokedonce the transition is complete. Arevokedkey causes the entire manifest to be rejected, even if a valid active signature is present alongside it.
Checking what your own installation trusts
The backend Settings page lists the release signing keys your installation currently trusts, with each entry's key_id, fingerprint, and status (active / revoked). Compare that list against the published fingerprints above; anything present there that is not published here did not come from the publisher. The keyring itself is a plain PHP config file â modules/Settings/Config/UpdateKeys.php â so it can also be reviewed directly and kept under your own change control.
Private keys are never held by the application, never committed, and never present in CI: signing happens offline via php spark ci4ms:release:keygen / ci4ms:release:manifest against a password-sealed keyfile stored outside ROOTPATH and public/. Anyone can independently verify a published release with php spark ci4ms:release:verify --remote --tag v<x.y.z.w>. The full pipeline, gates, and release ritual are documented in docs/architecture.md.
Third-Party Data Attribution
- Session geo lookup (optional, disabled by default) uses the free DB-IP City Lite database. If you enable it (Settings â Session Location Tracking, or the installer checkbox) and run
php spark ci4ms:geoip-update, you must comply with the database license: IP Geolocation by DB-IP, distributed under CC BY 4.0. Keep this attribution visible in your deployment. No IP data leaves your server â lookups are performed locally against the downloaded database.
Additional Docs
docs/architecture.mdâ Architecture, flow, permissions, and extension guidance.docs/developer-handbook.mdâ Environment setup, coding standards, deployment checklist.docs/theme_development.mdâ Theme folder structure, routing, andbase.phpvariables.docs/web-server-hardening.mdâ Apache/nginx rules that stop uploaded files from executing. Read this if you deploy on anything other than Apache â.htaccessis ignored by nginx, Caddy and FrankenPHP.DOCKER_SETUP.mdâ Docker environment configuration and usage.CHANGELOG.mdâ Full release history.
Questions or contributions? Open an issue or pull request.
ð Security Hall of Fame
A huge thank you to the security researchers who have helped make ci4ms more secure by finding and reporting vulnerabilities.
| Contributor | Contribution | Date |
|---|---|---|
| Lars van Mil | Identified Critical RCE and Information Disclosure vulnerabilities. | Jan 2026 |
| 0xAlchemist | Identified Critical Stored DOM XSS vulnerabilities across Company Info, Social Media, and Mail Settings modules, and a Session Invalidation flaw, leading to Account Takeover, Privilege Escalation, and potential Platform Compromise. | Feb 2026 |
| peeefour | Identified Stored DOM XSS vulnerabilities leading to Account Takeover. | Feb 2026 |
| Hunter. | Identified Critical Stored XSS in Backend & Blog modules allowing Session Hijacking. | Feb 2026 |
| m1scher | Assisted with vulnerability triaging and security testing. | Feb 2026 |
| alpernae | Assisted with vulnerability triaging and security testing. | Feb 2026 |
| offset | Identified Critical vulnerabilities including multiple Stored XSS (Blog & Pages content via broken html_purify validation), Authorization Bypass in Fileeditor destructive operations (delete/rename extension allowlist missing), Install Guard Bypass, and CRLF Injection. |
Apr â May 2026 |
| fg0x0 | Identified Critical Arbitrary File Write (Zip Slip RCE) vulnerabilities in Theme::upload and Backup::restore modules. | Apr 2026 |
| 0xAlchemist , peeefour and DexterHK | Identified Critical Full Account Takeover and Privilege Escalation via Stored DOM Blind XSS in Backup Management (v2). | Apr 2026 |
| dapickle | Identified Critical Authenticated RCE in Theme installation, Arbitrary Database Table Drop in Theme module, and a Session Management Bypass. | Apr 2026 |
| iltosec | Identified Broken Access Control in Media module, Unsafe Reflection in Dashboard Widgets, RCE via template-function parsing in Pages, and Stored XSS in Pages Cover Image URL leading to Account Takeover (the residual instance of the same Cover Image URL Stored XSS class in Blog Categories was subsequently hardened as well). | Jun 2026 |
If you find a security vulnerability, please report it via Security Policy.
ð Bug Reporters
Thanks to the community members who report functional bugs and help us catch regressions before they hit more users.
| Contributor | Contribution | Date |
|---|---|---|
| spreaderman | Reported two installation-blocking regressions in v0.31.10.0: the web installer returning 404 GET install/dbsetup after the configuration step, and php spark ci4ms:setup aborting on the users.profileIMG migration due to a TEXT column with a default value (rejected by MySQL/MariaDB strict mode). |
May 2026 |
| SIENSIS | Reported the fresh-install web installer failing silently because .env is written mid-request: migrations ran against an empty database name and the encrypter raised a "needs a starter key" error. |
Jul 2026 |
| SIENSIS | Reported that every login crashed with a 500 when a DNS-level blocker (e.g. Pi-hole) or an outage made the ip-api.com geo lookup unreachable â a failed fetch returned false and json_decode(false) raised an uncaught TypeError under strict_types. This led to replacing the third-party call with a local, opt-in geo lookup. |
Jul 2026 |
Found a non-security bug? Please open an issue with reproduction steps.