// about
A low-code process platform for teams that outgrew their CMS.
Self-hosted, source-available, and in development. PHP 8.5 on Symfony 8, storage-agnostic, rendered server-side, as an SPA or headless over the API.
// the name
It shipped in the 2000s as CoolMS.CMS. The suffix stopped being true.
It still manages your website. It also runs your processes, decides your edge cases, captures your leads and orchestrates your back office. The CMS is the part you will notice least — which is why the suffix went and the name stayed.
// four decisions
Everything below is checkable from outside
These four shape the rest. Each is stated and then shown — a config you can open, a command you can run, a status code you can fetch for yourself. An architecture claim that leaves no trace is one you have no way to test, and this page used to be four of those.
// a monolith built to come apart
A module may not import its own level, or any level above it
That is what makes a level a seam rather than a diagram. Cross-module references go by UUID instead of foreign keys and a transactional outbox already carries the events, so lifting a module into its own service is a deployment change rather than a rewrite. The levels are not a description of the code — they are declared in the static analysis config, and the rule reads them.
The layering, read from phpstan.dist.neon where the boundary check takes its argument. Counted on 2026-10-07; the foundation level holds the four modules everything else may depend on.
level modules for example
// storage-agnostic on purpose
The rule that matters keeps Doctrine out of everything but the adapter
Domain classes may import Doctrine's mapping attributes and nothing else of it; operational ORM classes are refused outside a module's Infrastructure/Doctrine namespace. It runs at phpstan level 8. The module boundary itself — a module may use the Domain of a module at its own level or below, never another module's Application or Infrastructure, and the graph has no cycle — is checked by one tool in the commit gate, and its 46 existing violations (counted 2026-09-22, at the end of the day that began at 131) are pinned by name in a baseline that may only shrink — which is what enforcement actually looks like on a codebase older than the rule, and worth saying rather than implying a clean sheet.
The checks, by their own names in the source, on 2026-09-22: three layer-purity rules in tools/PHPStan/CoolmsArchitectureRule.php (the original numbering; its cross-module rules 1, 4 and 5 retired that day in favour of the boundary check) and the boundary check itself, tools/check-module-boundaries.php.
check what it refuses
// a real virtual filesystem
The pages of this site, listed as files, with owners and modes
Not a metaphor and not a permission model bolted on beside one. The panel below is this site's own content directory, listed through the platform's shell. The s in drwxrwsr-x is the setgid bit: a new page inherits the editors' group rather than its author's, which is a filesystem guarantee rather than a role check somebody remembered to apply.
coolms:vfs:ls -l /content/coolms-site, run on 2026-09-08. Five entries, which is this page, the history page, the team page, the landing page and the shared assets.
mode name type
// rendered more than one way
The same installation answers a browser, an SPA, an API and an agent
Same content, same permissions, no second stack to keep in step. The panel below asks all four anonymously, and the two 401s are the point rather than a gap: the API and the agent surface refuse to answer what the page will not show a visitor who is not signed in. One permission model, tested from four directions.
Requested without credentials on 2026-09-08. Status and content type exactly as returned.
surface route anonymous
// where it is
Alpha, source-available, self-hosted.
There is no hosted signup and no managed offering. The way to run CoolMS today is to install it yourself, and the way to ask a question is to open one on GitHub.