A CMS that an AI can actually run.
Squilla is a Go kernel with everything else as a hot-loaded extension. Every operation — content, themes, blocks, taxonomies, settings, media — is exposed as an MCP tool. No filesystem hacks. No HTML scraping. Just a structured API and a render preview.
Read the standardsThe shape of the platform
Six surfaces. One contract.
Squilla looks small from the outside because everything that isn't infrastructure ships as an extension. The kernel only owns nodes, auth, rendering, the event bus, and the CoreAPI — every other capability is a Debian-style package with its own tables, migrations, and admin UI.
Kernel + extensions
If removing a feature leaves dead code in core, that code belonged in an extension. Media, email, forms, sitemaps — all opt-in, capability-guarded packages.
MCP-first
Every CMS operation is an MCP tool. core.guide returns a goal→tool decision tree; core.theme.standards and core.extension.standards are the authoring contract.
Hot drop-in
Drop a theme or extension folder into the running container — an fs watcher debounces, rescans, and registers it. No restart. No rebuild.
gRPC plugins
Extensions ship as Go binaries spawned via HashiCorp go-plugin. Crashes are isolated. Activation hot-spawns the subprocess in milliseconds.
Tengo scripting
Sandboxed embedded VM with core/* modules — nodes, taxonomies, terms, menus, settings, events, http, assets. Themes self-bootstrap from a single .tengo file.
Capability-guarded
Every CoreAPI call goes through a capability guard reading the caller's manifest. Declare data:read, you can read; you don't get data:write for free.
Scaffold a working site in four MCP calls.
No filesystem walks. No SQL. No restarts. Each step is one tool call, idempotent, and verifiable via core.render.node_preview.
Activate a theme
core.theme.list to find the ID, core.theme.activate to swap the asset registry atomically. The site serves the new look on the next request.
Define a content type
core.nodetype.create with a field_schema. Pages, posts, recipes, trips — whatever fits. Custom field types contributed by extensions show up automatically.
Upload media + create the node
core.media.upload returns a slug-addressable record; core.node.create accepts blocks_data and a featured_image object. AI agents reference media by slug for theme portability.
Verify the render
core.render.node_preview returns the full layout + blocks + theme CSS as the public site would serve it. Side-effect-free, safe to call repeatedly.
The kernel is the boring part. That's the point.
Squilla's core ships exactly what every CMS needs and nothing more: content nodes, auth, rendering, an event bus, and a capability-guarded CoreAPI. Everything that could be opinionated — media management, email delivery, SEO, forms, image optimization — lives outside, in extensions.
- Disable an extension and core stays clean
- Replace email providers without touching templates
- Crashes in one plugin can't take down the others
An admin shell that's just… a shell.
The Squilla admin SPA owns auth, the sidebar, the dashboard, and the extension loader. That's it. Every feature page — Media Library, Email Manager, Forms, Sitemaps — is a React micro-frontend shipped by its extension and loaded via import maps from the running container.
Add an extension, see a new sidebar entry. Remove it, the entry vanishes. The shell never grows.
// scripts/theme.tengo — seed taxonomy terms idempotently
taxonomies := import("core/taxonomies")
terms := import("core/terms")
taxonomies.register({
slug: "trip_tag", label: "Trip tag",
label_plural: "Trip tags", node_types: ["trip"]
})
seed := func(slug, name) {
existing := terms.list("trip", "trip_tag")
for t in existing { if t.slug == slug { return } }
terms.create({
node_type: "trip", taxonomy: "trip_tag",
slug: slug, name: name
})
}
seed("foodie", "Foodie")
seed("adventure", "Adventure")
seed("relaxing", "Relaxing")
Themes self-bootstrap. theme.tengo runs once on activation and again on every restart while the theme is active — existence checks make re-runs safe.
Things developers said while building with Squilla.
"I asked Claude to add a 'recipe' content type with categories and seed three pages. It made nine MCP calls and the site rendered. I went to make tea."
"I docker-cp'd a new theme folder into a running container expecting to restart. The theme just appeared in the admin. I sat there for a minute."
"The capability guard caught my extension trying to write settings before I declared settings:write. The error message told me which line of extension.json to edit."
Questions you probably have
Why not just put everything in core? expand_more
Because then the CMS becomes the thing you fight when your needs change. Squilla's hard rule: if removing a feature would leave dead code in core, that code belongs in an extension. Media, email, forms, even content blocks — all opt-in. Disable what you don't need; the kernel stays the same size.
What does "hot drop-in" actually mean? expand_more
Drop a theme or extension folder onto the filesystem of a running container — via docker cp, a volume mount, a git pull — and an fsnotify watcher picks it up, debounces, and re-runs the loader scan. The new package shows up in core.theme.list / core.extension.list immediately. Activation is also hot: theme switches swap the asset registry atomically, extension activation spawns the gRPC plugin subprocess in milliseconds.
Do I have to write Go to build an extension? expand_more
No. Tengo-only extensions are a thing — see resend-provider, which is just a manifest plus one .tengo file that subscribes to email.send and shells out via core/http. Reach for a Go binary when you need owned database tables, large request bodies, or native libraries.
Where do I start? expand_more
Call core.guide from any MCP client. You'll get a goal→tool decision tree, a live snapshot of the CMS, and the full tool index in one payload. From there, core.theme.standards and core.extension.standards are the authoring contracts.
Drop a folder in. Watch it light up.
Squilla is open about how it works because the architecture is the product. The kernel is small, the contracts are public, and every behavior in this site was set up via MCP — including this homepage.
Open the admin