Kernel + Extension Architecture
Why we model the CMS like Linux.
If there's one piece of jargon worth understanding before reading the Squilla source, it's the kernel-vs-extension split. Every architectural choice in the codebase falls out of it.
The hard rule
If disabling an extension would leave dead code in core, that code belongs in the extension, not the kernel.
That's the rule. We apply it ruthlessly. Image optimisation? Extension. Email templates? Extension. WebP conversion, sitemap generation, contact forms, file storage variants, content-block libraries? All extensions. The kernel only contains things that any extension might reuse: the content-node model, the rendering pipeline, the auth layer, the CoreAPI, the event bus, the filter chain, the public-route proxy.
Why this matters
Two reasons. First, the kernel stays small and stays fast — there's no “bloat that came along for the ride.” Second, every feature is a sovereign package: it owns its tables, its UI, its scripts, its migrations. The kernel never reaches inside an extension's database or imports an extension's package. They communicate through the CoreAPI, gRPC, and the event bus.
What that buys you operationally
- Crash isolation. Plugins run in their own processes via HashiCorp go-plugin. A panicking extension can't take down the kernel.
- Hot activation. Activate or deactivate without restart. The kernel atomically swaps the in-memory layout tree, runs the extension's pending migrations, starts or stops its plugin subprocess, and fires lifecycle events.
- Per-extension capability scopes. Each extension declares the capabilities it needs in its manifest. The CoreAPI guard rejects calls outside the declared set. There is no override.
What it costs
Discipline. It's tempting to drop a feature into core because it's faster than wiring up gRPC. We pay that cost up front because feature debt in the kernel is the kind of debt you never repay.
If you're starting a Squilla extension, read the existing ones first — they're structured exactly like third-party extensions would be, because that's the only way the abstraction stays honest.