# Modulacms

> Headless CMS built in Go. Single binary, tri-database, developer-first. Built on three values: Performance, Flexibility, and Transparency.

- **Type:** MCP server
- **Install:** `agentstack add mcp-hegner123-modulacms`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [hegner123](https://agentstack.voostack.com/s/hegner123)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [hegner123](https://github.com/hegner123)
- **Source:** https://github.com/hegner123/modulacms
- **Website:** https://modulacms.com

## Install

```sh
agentstack add mcp-hegner123-modulacms
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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](https://github.com/casey/just#installation) |

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

```bash
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

```bash
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:

```bash
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

```bash
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/](http://localhost:8080/admin/)

Log in with `system@modulacms.local` and the password from init.

**Terminal UI:** `ssh localhost -p 2233`

**REST API:**

```bash
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:

```bash
modula connect              # default project, default env
modula connect mysite       # specific project
modula connect mysite prod  # specific project + env
```

## Build & Development

```bash
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

```bash
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  `:

```bash
# 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:

```bash
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:

1. **SQL schemas** in `sql/schema/` define tables and queries per dialect (SQLite, MySQL, PostgreSQL)
2. **sqlc** generates type-safe Go code into `internal/db-sqlite/`, `internal/db-mysql/`, `internal/db-psql/`
3. **`DbDriver` interface** (~150 methods in `internal/db/db.go`) provides the contract
4. **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 node
- `first_child_id`: leftmost child
- `next_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** | content_data, content_fields, content_relations, admin variants |
| **Schema** | datatypes, fields, datatype_fields, admin variants |
| **Media** | media, media_dimensions |
| **Routing** | routes, admin_routes |
| **Users & Auth** | users, roles, permissions, role_permissions, tokens, user_oauth, sessions, user_ssh_keys |
| **i18n** | locales |
| **Webhooks** | webhooks, webhook_deliveries |
| **System** | backups, change_events, 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.

```bash
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](https://github.com/hegner123)
- **Source:** [hegner123/modulacms](https://github.com/hegner123/modulacms)
- **License:** MIT
- **Homepage:** https://modulacms.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-hegner123-modulacms
- Seller: https://agentstack.voostack.com/s/hegner123
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
