Install
$ agentstack add mcp-hegner123-modulacms ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Modula
[](https://github.com/hegner123/modulacms/actions/workflows/go.yml) [](https://github.com/hegner123/modulacms/actions/workflows/windows.yml)
A headless CMS written in Go, built on three core values: performance, flexibility, and transparency.
Requirements
| Requirement | Details | |-------------|---------| | Go | 1.24+ | | CGO | Must be enabled (CGO_ENABLED=1) | | C compiler | GCC or Clang (for the SQLite driver) | | OS | Linux or macOS | | Build runner | just |
CGO is required because the SQLite driver (mattn/go-sqlite3) is a C library. Even if you use MySQL or PostgreSQL, the binary still compiles with the SQLite driver.
Quick Start
1. Build and install
git clone https://github.com/hegner123/modulacms.git
cd modulacms
just build
cp out/bin/modula /usr/local/bin/modula
Verify: modula version
2. Create a project
mkdir ~/mysite && cd ~/mysite
modula init
modula init runs an interactive setup wizard that prompts for database driver, connection details, ports, and admin credentials. It creates modula.config.json, initializes the database schema, seeds three bootstrap roles (admin, editor, viewer) with 72 permissions, and registers the project in ~/.modula/configs.json.
For non-interactive setup with defaults:
modula init --yes --admin-password your-password
Defaults: SQLite database (modula.db in working directory), HTTP on :8080, HTTPS on :4000, SSH on :2233, development environment. The admin credentials are printed to the startup log.
3. Start the server
modula serve
Three servers start concurrently:
| Server | Default Address | Purpose | |--------|-----------------|---------| | HTTP | localhost:8080 | REST API + admin panel | | HTTPS | localhost:4000 | TLS-secured API (autocert in production) | | SSH | localhost:2233 | Terminal UI |
Graceful shutdown: first SIGINT/SIGTERM triggers a 30-second shutdown; second signal forces exit.
4. Connect
Web admin panel: http://localhost:8080/admin/
Log in with system@modulacms.local and the password from init.
Terminal UI: ssh localhost -p 2233
REST API:
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "system@modulacms.local", "password": "YOUR_PASSWORD"}' \
-c cookies.txt
curl http://localhost:8080/api/v1/datatype -b cookies.txt
Once registered, manage a project from any directory:
modula connect # default project, default env
modula connect mysite # specific project
modula connect mysite prod # specific project + env
Build & Development
just dev # Build local binary with version info via ldflags
just run # Build and run
just build # Production binary to out/bin/
just build-target darwin arm64 # Cross-compile for specific OS/arch
just build-all # Build all release targets (darwin/linux, amd64/arm64)
just check # Compile-check without producing artifacts
just clean # Remove build artifacts
just vendor # Update vendor directory
Testing
just test # Run all tests (creates/cleans testdb/ and backups/)
just coverage # Tests with coverage report
just lint # Run all linters (Go, Dockerfile, YAML)
# Single package or test
go test -v ./internal/db
go test -v ./internal/db -run TestSpecificName
# S3 integration tests (requires MinIO)
just test-minio # Start MinIO container
just test-integration # Run integration tests
just test-minio-down # Stop MinIO
# Cross-backend DB integration tests
just docker-infra # Start Postgres, MySQL, MinIO
just test-integration-db # Run cross-backend tests
Docker
Unified via just dc :
# Backends: full, sqlite, mysql, postgres, prod
# Actions: up, down, reset, dev, fresh, logs, destroy (full only), minio-reset (postgres only)
just dc full up # Full stack (CMS + all databases + MinIO)
just dc full down # Stop containers, keep volumes
just dc full reset # Stop containers and delete volumes
just dc full dev # Rebuild and restart CMS container only
just dc full fresh # Reset volumes then rebuild everything
just dc full logs # Tail CMS logs
just dc full destroy # Remove containers, volumes, and images
just dc sqlite up # SQLite stack
just dc mysql up # MySQL stack
just dc postgres up # PostgreSQL stack
Other Docker commands:
just docker-infra # Infrastructure only (Postgres, MySQL, MinIO)
just docker-build # Build standalone CMS image (for CI)
just docker-release # Tag and push image with version tags
The Docker image exposes ports 8080 (HTTP), 4000 (HTTPS), and 2233 (SSH), with volumes for /app/data, /app/certs, /app/.ssh, /app/backups, and /app/plugins.
Architecture
Runtime
The serve command starts three concurrent servers sharing a single DbDriver instance:
HTTP (default :8080) ─┐
HTTPS (default :8443) ─┤── stdlib ServeMux (Go 1.22+) ── Middleware Chain ── Handlers ── DbDriver
SSH (default :2222) ─┘ Charmbracelet Wish ── Bubbletea TUI ─────────────────────────── DbDriver
Graceful shutdown: first SIGINT/SIGTERM triggers a 30-second shutdown; second signal forces exit. Shutdown order: HTTP servers, plugin system, database connections.
Request Flow
Client Request
-> Request ID
-> Logging
-> CORS
-> Authentication (cookie session or Bearer API key)
-> Rate Limiting (auth endpoints: 10 req/min per IP)
-> Permission Injection (RBAC)
-> Route Handler
-> DbDriver Interface
-> Database-specific wrapper (SQLite / MySQL / PostgreSQL)
-> sqlc-generated queries
Tri-Database Pattern
One codebase supports three databases through a layered abstraction:
- SQL schemas in
sql/schema/define tables and queries per dialect (SQLite, MySQL, PostgreSQL) - sqlc generates type-safe Go code into
internal/db-sqlite/,internal/db-mysql/,internal/db-psql/ DbDriverinterface (~150 methods ininternal/db/db.go) provides the contract- Wrapper structs (
Database,MysqlDatabase,PsqlDatabase) implement the interface, converting between sqlc types and application types
Switch databases by setting db_driver in modula.config.json to "sqlite", "mysql", or "postgres".
Content Model
Content uses a tree structure with sibling pointers for O(1) navigation and reordering:
parent_id: parent nodefirst_child_id: leftmost childnext_sibling_id/prev_sibling_id: doubly-linked sibling list
Content items have a status lifecycle: draft -> pending -> published -> archived.
Data Model
27 schema directories define the full entity model:
| Entity Group | Tables | |-------------|--------| | Content | contentdata, contentfields, contentrelations, admin variants | | Schema | datatypes, fields, datatypefields, admin variants | | Media | media, mediadimensions | | Routing | routes, adminroutes | | Users & Auth | users, roles, permissions, rolepermissions, tokens, useroauth, sessions, usersshkeys | | i18n | locales | | Webhooks | webhooks, webhookdeliveries | | System | backups, changeevents, tables |
All entity IDs are 26-character ULIDs wrapped in distinct Go types (ContentID, UserID, FieldID, etc.) that provide compile-time type safety.
RBAC Authorization
Role-based access control with resource:operation granular permissions:
| Role | Permissions | Description | |------|-------------|-------------| | admin | 47 (all) | Bypasses all permission checks | | editor | 28 | CRUD on content, media, routes, datatypes, fields | | viewer | 3 | Read-only: content, media, routes |
The PermissionCache maintains an in-memory role-to-permissions map with lock-free reads and 60-second periodic refresh. System-protected roles and permissions cannot be deleted or renamed.
Audited Commands
All database mutations are wrapped in transactions that atomically record change_events rows capturing:
- Operation type (INSERT, UPDATE, DELETE)
- Old and new JSON values
- User ID, request ID, IP address
- Hybrid Logical Clock timestamps for distributed ordering
Admin Panel
Server-rendered HTMX + templ web interface. No SPA: all pages are server-rendered with HTMX for interactivity.
- Content: tree navigation, block editor with drag-and-drop, inline field editing
- Schema: datatypes, fields, and field-datatype associations
- Media: upload, browse, image preview with dimension presets
- Users & Roles: user management, role assignment, permission configuration
- Routes: URL slug management
- Plugins: browse, enable, disable, view details
- Webhooks: create, test, view delivery history
- Locales: i18n configuration and locale management
- Settings: server configuration
- Audit Log: change event browser
- Import: bulk import from external CMS platforms
- Sessions & Tokens: active session and API token management
Light DOM web components (mcms-*) provide dialog, data-table, field-renderer, media-picker, tree-nav, toast, confirm, and search widgets.
just admin generate # Regenerate templ Go code
just admin watch # Watch .templ files for changes
just admin bundle # Bundle block editor JS via esbuild
API
All endpoints are prefixed with /api/v1/ and follow standard REST conventions. Content delivery uses slug-based routing at /api/v1/content/{slug}.
Authentication
POST /api/v1/auth/login # Session login
POST /api/v1/auth/logout # Session logout
GET /api/v1/auth/me # Current user profile
POST /api/v1/auth/register # Registration
POST /api/v1/auth/reset # Password reset
GET /api/v1/auth/oauth/login # OAuth flow initiation
GET /api/v1/auth/oauth/callback # OAuth callback
Content Management
GET|POST /api/v1/contentdata # List / Create
GET|PUT|DELETE /api/v1/contentdata/{id} # Get / Update / Delete
POST /api/v1/content/create # Create content with fields (cascade)
POST /api/v1/content/batch # Batch operations
POST /api/v1/contentdata/move # Move node in tree
POST /api/v1/contentdata/reorder # Reorder siblings
GET|POST /api/v1/contentfields # Content field values
GET|POST /api/v1/contentrelations # Content relationships
Publishing & Versioning
POST /api/v1/content/publish # Publish content (creates snapshot)
POST /api/v1/content/unpublish # Unpublish content
POST /api/v1/content/schedule # Schedule future publish
GET /api/v1/content/versions # List versions
POST /api/v1/content/versions # Create manual version
DELETE /api/v1/content/versions/{id} # Delete version
POST /api/v1/content/restore # Restore from version
Admin content mirrors exist at /api/v1/admin/content/ for draft management.
Content Delivery
GET /api/v1/content/{slug} # Published content by slug
GET /api/v1/content/{slug}?preview=true # Live draft (requires auth)
GET /api/v1/content/{slug}?locale=en # Locale-specific delivery
GET /api/v1/content/{slug}?format=clean # Format override
GET /api/v1/globals # All global content trees
GET /api/v1/query/{datatype} # Query by datatype
The format query parameter controls response structure: contentful, sanity, strapi, wordpress, clean, or raw.
Schema
GET|POST /api/v1/datatype # Datatypes
GET|POST /api/v1/fields # Field definitions
GET|POST /api/v1/datatypefields # Datatype-field associations
GET|POST /api/v1/tables # Custom tables
Media
GET /api/v1/media # List (paginated)
POST /api/v1/media # Upload (multipart/form-data)
DELETE /api/v1/media/{id} # Delete
GET /api/v1/media/health # S3 connectivity check
DELETE /api/v1/media/cleanup # Remove orphaned S3 objects
GET|POST /api/v1/mediadimensions # Dimension presets
Routes & Locales
GET|POST /api/v1/routes # Route management
GET /api/v1/locales # Public locale list
CRUD /api/v1/admin/locales # Admin locale management
Users & Access Control
GET|POST /api/v1/users # User management
POST /api/v1/users/reassign-delete # Reassign content and delete user
GET|POST /api/v1/roles # Roles
GET|POST /api/v1/permissions # Permissions
GET|POST /api/v1/role-permissions # Role-permission mappings
GET|POST|DELETE /api/v1/ssh-keys # SSH key management
GET|POST|DELETE /api/v1/sessions # Session management
GET|POST|DELETE /api/v1/tokens # API token management
Webhooks
CRUD /api/v1/admin/webhooks # Webhook management
POST /api/v1/admin/webhooks/{id}/test # Test delivery
GET /api/v1/admin/webhooks/{id}/deliveries # Delivery history
POST /api/v1/admin/webhooks/deliveries/{id}/retry # Retry delivery
Import & Configuration
POST /api/v1/import/contentful # Import from Contentful
POST /api/v1/import/sanity # Import from Sanity
POST /api/v1/import/strapi # Import from Strapi
POST /api/v1/import/wordpress # Import from WordPress
POST /api/v1/import/clean # Import Modula format
POST /api/v1/import # Bulk import
GET /api/v1/admin/config # Get config (redacted)
PATCH /api/v1/admin/config # Update config
GET /api/v1/admin/config/meta # Config field metadata
GET /api/v1/admin/plugins # List plugins
GET /api/v1/admin/plugins/routes # Plugin route approval
Terminal UI
The SSH-accessible TUI is built with Charmbracelet Bubbletea following the Elm Architecture (Model-Update-View). Each screen implements a Screen interface with its own state, update, and view methods.
- Content: browse, create, edit content with tree navigation (regular and admin views)
- Datatypes & Fields: define and manage content schemas
- Media: upload and manage media assets with file picker
- Users: user management with role assignment
- Routes: URL slug configuration
- Plugins: browse, enable, disable, reload Lua plugins
- Webhooks: webhook management and delivery monitoring
- Pipelines: pipeline entry management
- Deploy: content sync between environments
- Configuration: edit server configuration
- Database: database info and table browser
- Quick Start: guided setup wizard
UI features: responsive panel layouts with three screen modes (normal/wide/full), accordion focus, panel tabs, scroll indicators, adaptive statusbar, compact/full header with breadcrumbs.
Connect System
The connect command provides a project registry for managing multiple CMS instances and environments from a single CLI. Each project can have multiple environments (local, dev, staging, prod), each pointing to a different modula.config.json. The registry lives at ~/.modula/configs.json.
###
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: hegner123
- Source: hegner123/modulacms
- License: MIT
- Homepage: https://modulacms.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.