Codync
A free, open-source 1:1 alternative to Grok Bot and Muse. Message Claude Code, Codex, Cursor and 40+ coding agents as persistent bots from your iPhone, Mac or Linux desktop. Group chats, threads, appr
Open-source, Go-based, AI-native CMS. Kernel + extension architecture, MCP-driven, sub-50ms public renders. GPL-3.0, no telemetry.
Translation pending
Developer toolsHTML · GoGitHub @erikkubicaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
High-performance, AI-native CMS built in Go. Kernel + extension architecture. MCP-first.
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗High-performance, AI-native CMS built in Go. Kernel + extension architecture. MCP-first.
Squilla is a content management system designed around one idea: an AI should be able to build and manage an entire website without human intervention. The kernel provides content nodes, rendering, auth, and a powerful CoreAPI. Everything else (media, email, SEO, forms) is an extension — gRPC plugins with their own data, logic, and admin UI.
The key differentiator: every CMS operation is exposed as an MCP tool. An AI agent can create node types, seed content, activate themes, and manage extensions through a structured API — no filesystem access, no shell commands, no HTML scraping.
Core = Linux kernel (infrastructure only). Extensions = Debian packages (own their full stack). Admin SPA = browser shell (just loads extension micro-frontends). Themes = templates + scripts + assets (registered on activation, no restart needed).
| Layer | Technology |
|---|---|
| Language | Go 1.24+ |
| HTTP | Fiber (routing, middleware) |
| ORM | GORM (PostgreSQL 16+) |
| Database | PostgreSQL (JSONB, GIN indexes) |
| Admin UI | React + TypeScript (Vite, Tailwind, shadcn/ui) |
| Templates | Go html/template |
| Scripting | Tengo (sandboxed VM, core/* modules) |
| Plugins | HashiCorp go-plugin (gRPC, bidirectional) |
| Storage | Local disk (S3 planned) |
| Security | Capability-based permissions, Ed25519 license verification |
# Clone and run with Docker
git clone <repo-url> && cd squilla
docker compose up --build
# App runs at http://localhost:3000
# Admin at http://localhost:3000/admin
# Default login: [email protected] / changeme
| Variable | Default | Description |
|---|---|---|
DB_HOST | localhost | PostgreSQL host |
DB_PORT | 5432 | PostgreSQL port |
DB_USER | squilla | Database user |
DB_PASSWORD | squilla | Database password |
DB_NAME | squilla | Database name |
THEME_PATH | themes/default | Path to active theme |
APP_ENV | production | development disables template caching |
PORT | 3000 | HTTP port |
DATABASE_URL | (unset) | Optional: postgres://user:pass@host:port/db?sslmode=disable. Overrides individual DB_* vars when set. |
ADMIN_EMAIL | [email protected] | Email for the auto-seeded admin user (first boot only). |
ADMIN_PASSWORD | (unset) | If unset, a random password is generated on first boot and printed to the app logs once. Set this to skip the random one. |
SQUILLA_SECRET_KEY | (unset) | AES-256 master key for at-rest encryption of secret settings. Must be a base64 string that decodes to exactly 32 raw bytes. Generate with openssl rand -base64 32. If unset, secret settings are stored in plaintext (dev only); production startup refuses to boot. |
SESSION_SECRET | (unset) | Session cookie signing key. Any sufficiently random string. Generate with openssl rand -base64 48. Required in production. |
MONITOR_BEARER_TOKEN | (unset) | Bearer token for /api/v1/stats monitoring endpoint. Any opaque string. Required in production. |
Squilla ships a coolify-compose.yml for near-zero-config deployment:
coolify-compose.yml.app service (Coolify fills SERVICE_FQDN_APP and provisions TLS).SQUILLA_SECRET_KEY in the Environment Variables tab — see the gotcha below.Coolify auto-generates the database credentials, session secret, and monitor token via its SERVICE_* magic variables — those work out of the box. The first admin password is generated on first boot and printed to the app container logs once (search the logs for first-boot admin credentials). To pre-set credentials, add ADMIN_EMAIL and ADMIN_PASSWORD env vars to the app service before the first deploy.
SQUILLA_SECRET_KEY requires manual overrideCoolify's SERVICE_BASE64_<NAME> magic variable produces a 32-character base64 string, which decodes to 24 raw bytes. The Squilla secrets service requires exactly 32 raw bytes (AES-256 spec), so the auto-generated value is rejected on boot and the container crash-loops with:
secrets init failed: SQUILLA_SECRET_KEY must be 32 raw bytes (base64-encoded): got 24 bytes, want 32
Fix: before first deploy, in the Coolify Environment Variables tab, set:
SQUILLA_SECRET_KEY=<paste output of: openssl rand -base64 32>
That produces a 44-character base64 string (32 raw bytes after decode), which the secrets service accepts. Verify with:
echo -n "$SQUILLA_SECRET_KEY" | base64 -d | wc -c # must print: 32
The SESSION_SECRET and MONITOR_BEARER_TOKEN env vars are not length-constrained — Coolify's SERVICE_BASE64_64_* (48 raw bytes) values work as-is.
The pre-built image is published to ghcr.io/erikkubica/squilla:latest (multi-arch, amd64 + arm64).
HARD RULE: If disabling/removing an extension would leave dead code in core, that code belongs in the extension, not core.
Core provides:
Extensions own:
Single Go interface providing all CMS capabilities. Three adapters:
core/* modules) — for .tgo scriptsSee docs/extension_api.md for the full reference.
Squilla exposes ~75 MCP tools across 17 domains, all under the core.<domain>.<verb> namespace. This is how AI agents interact with the CMS. The tables below highlight the most-used tools per domain — call core.guide for a live decision tree, recipes, and a CMS state snapshot, or core.extension.standards / core.theme.standards for authoring rules.
| Tool | Description |
|---|---|
core.node.create | Create a content node |
core.node.update | Update a node by ID |
core.node.get | Fetch a node by numeric ID |
core.node.query | Search/list nodes with filters; returns {nodes, total} |
core.node.delete | Permanently delete a node (use update with status='draft' to unpublish without deleting) |
core.nodetype.create | Register a custom post type |
core.nodetype.list / .get / .update / .delete | Manage node type definitions |
core.render.node_preview | Preview rendered page HTML (no events, no view counts) |
core.render.block / .layout | Smoke-test a block or layout in isolation |
| Tool | Description |
|---|---|
core.taxonomy.create / .list / .get / .update / .delete | Manage taxonomy definitions |
core.term.create / .list / .get / .update / .delete | Manage taxonomy term rows |
| Tool | Description |
|---|---|
core.menu.create / .list / .get / .update / .delete / .upsert | Manage menus; upsert is idempotent and resolves page:"<slug>" items to NodeIDs |
core.settings.get / .list / .set | Site settings |
core.media.upload / .import_url / .get / .query / .delete | Media library |
core.files.store / .delete | Raw file storage (no DB record) |
| Tool | Description |
|---|---|
core.theme.list / .active / .get | Inspect themes |
core.theme.activate / .deactivate | Hot-swap themes (no app restart) |
core.theme.deploy | Ship a packaged theme (base64 ZIP) into themes/<slug>/ and optionally activate — atomic dir swap, no docker cp, no git push |
core.theme.rescan | Re-scan themes/ and upsert rows for any directory dropped on disk |
core.layout.list / .get / .create / .update / .delete / .detach / .reattach | Manage page layouts |
core.block_types.list / .get / .create / .update / .delete / .detach / .reattach | Manage content block types |
core.field_types.list | List built-in field types and their how_to guides |
| Tool | Description |
|---|---|
core.extension.list / .get | Inspect extensions (active/inactive) |
core.extension.activate / .deactivate | Hot activate/deactivate (no app restart; subprocess only) |
core.extension.deploy | Ship a packaged extension (base64 ZIP) into extensions/<slug>/ and optionally hot-activate — atomic dir swap, plugin binaries chmod'd, no docker cp, no git push |
core.extension.rescan | Re-scan extensions/ and upsert rows for any directory dropped on disk |
| Tool | Description |
|---|---|
core.user.get / .query | Read-only user lookup |
core.data.get / .query / .create / .update / .delete | Low-level table access (prefer typed tools when available) |
core.email.send | Dispatch via active email provider extension |
core.http.fetch | Outbound HTTP (capability-gated) |
core.event.emit | Emit a custom event onto the bus |
core.filter.apply | Run a registered filter chain against a value |
| Tool | Description |
|---|---|
core.guide | Decision tree + recipes + live CMS state snapshot |
core.theme.standards | Theme authoring standards (Rules 1.5/1.6, etc.) |
core.extension.standards | Extension authoring standards (manifest, capabilities, hot deploy) |
Create a trip booking site from scratch:
1. core.theme.list → find theme ID
2. core.theme.activate(id) → activates theme, seeds node types + content
3. core.node.query(node_type="trip") → verify trips were created
4. core.render.node_preview(id=<trip_id>) → check rendering
Add a new content type:
1. core.nodetype.create(slug="recipe", label="Recipe", field_schema=[...])
2. core.node.create(node_type="recipe", title="Pho Bo", fields_data={...})
3. core.render.node_preview(id=<recipe_id>) → verify
Deploy a theme built outside the primary repo:
1. (locally) zip -r mytheme.zip mytheme/ # contains theme.json
2. core.theme.deploy(body_base64=<base64(mytheme.zip)>, activate=true)
3. core.render.node_preview(id=<home_node>) → verify the new look
Deploy an extension built outside the primary repo:
1. (locally) build the gRPC plugin for the host's OS/arch (e.g. linux/amd64)
2. (locally) zip -r myext.zip myext/ # extension.json + bin/myext + admin-ui/dist
3. core.extension.deploy(body_base64=<base64(myext.zip)>, activate=true)
4. core.extension.get(slug="myext") → confirm is_active=true
cmd/squilla/ Application entry point
internal/ Core kernel
coreapi/ CoreAPI interface + adapters (Tengo, gRPC, internal)
cms/ Content service, theme loader, extension loader
scripting/ Tengo VM runtime
models/ GORM models
auth/ Session auth, RBAC
events/ Event bus
mcp/ MCP tool server (~75 tools across 17 domains)
extensions/ Feature extensions (see extensions/README.md)
themes/ Theme repository
admin-ui/ React SPA shell
proto/ Protocol Buffer definitions
storage/ Local file storage
Themes are self-contained packages: layouts, partials, blocks, assets, scripts, and page templates.
themes/my-theme/
theme.json Manifest (layouts, blocks, assets, templates)
layouts/ Page layouts (default.html, trip.html, etc.)
partials/ Reusable fragments (site-header.html, site-footer.html)
blocks/ Content blocks (my-hero/view.html + block.json)
assets/ Static files (images/, styles/, scripts/)
scripts/ Tengo scripts (theme.tengo entry point)
templates/ Pre-populated page JSON files
When core.theme.activate(id) is called (or POST /admin/api/themes/:id/activate):
The #1 source of theme bugs is mismatch between seed data shape and template access patterns.
Rule: The seed script (theme.tengo) defines the data contract. Templates must match.
Example bug: seed stores tag as a string, template accesses tag.name:
// theme.tengo seeds: tag: "Foodie"
// template uses: {{ with $fd.tag }}{{ .name }}{{ end }} ← CRASH
// fix: {{ with $fd.tag }}{{ . }}{{ end }} ← correct
.fields vs .fields_dataDifferent template contexts use different key names for node fields:
| Where you are | Access pattern | Example |
|---|---|---|
| Layout template (current node) | .node.fields | {{ .node.fields.color }} |
| Block template (block fields) | .fields or direct | {{ .heading }} |
| Tengo filter result (list_nodes) | .fields_data | {{ .fields_data.color }} |
docs/theming.md — Complete theming guide (1400+ lines)docs/scripting_api.md — Tengo scripting referenceextensions/README.md — Extension development guideExtensions are feature packages. Two flavors:
See extensions/README.md for the complete guide and docs/extension_api.md for the API reference.
content_node with blocks_data and fields_data JSONBsnake_case Go files, .html templates, .tgo Tengo scripts| Doc | Description |
|---|---|
CLAUDE.md | AI coding assistant context (architecture, conventions) |
docs/architecture.md | Canonical architectural reference |
docs/extension_api.md | Building extensions (manifests, gRPC plugins, capabilities) |
docs/scripting_api.md | Tengo core/* modules for theme + extension scripts |
docs/theming.md | Complete theming guide (layouts, partials, blocks, assets) |
docs/forms.md | Forms extension public API reference |
docs/vdus.md | Server-Driven UI (admin shell + layout trees + SSE) |
docs/core_dev_guide.md | Kernel development workflows |
docs/core_features.md | Exhaustive feature inventory |
docs/database-schema.md | Database schema reference (27 GORM models, 38 migrations) |
docs/security.md | Security posture and PR-time checklist |
extensions/README.md | Extension development guide (alternative entry to docs/extension_api.md) |
themes/README.md | Theme development guide (layouts, blocks, partials, Tengo seeding, forms wiring) |
GNU General Public License v3.0 — see LICENSE.
CORS_ORIGINS | (unset) | Comma-separated list of allowed origins for CORS. Defaults to the public domain in Coolify deployments. |