AI-powered CMS Core for personal blogs, creator homepages & content websites.
Overview
MX Space Core is a headless CMS server built with NestJS, PostgreSQL, and Redis. Beyond standard blog features (posts, pages, notes, comments, categories, feeds, search), it ships with a full AI content workflow — summary generation, multi-language translation, comment moderation, and writing assistance — powered by pluggable LLM providers.
Historical MongoDB → PostgreSQL data migration lives in packages/mongo-pg-cli. Forward schema migrations live in apps/core/src/database/migrations/ and run as a one-shot release-phase step (see the release-phase migration design).
Quick Start
Prerequisites
Dependency
Version
Node.js
>= 22
pnpm
Latest (via Corepack)
PostgreSQL
16+
Redis
7.x
Local Development
# Enable Corepack for pnpm
corepack enable
# Install dependencies
pnpm install
# Start PostgreSQL + Redis (via Docker)
docker compose up -d postgres redis
# Start dev server (port 2333)
pnpm dev
The API is available at http://localhost:2333. In development mode, routes have no /api/v2 prefix.
Docker Deployment
The fastest way to get a production instance running:
# Clone and enter the project
git clone https://github.com/mx-space/core.git && cd core
# Edit environment variables
cp docker-compose.server.yml docker-compose.prod.yml
# Edit docker-compose.prod.yml — set JWT_SECRET, ALLOWED_ORIGINS, etc.
# Start all services
docker compose -f docker-compose.prod.yml up -d
Or use the prebuilt image directly:
docker pull innei/mx-server:latest
The image supports linux/amd64 and linux/arm64.
Available Commands
Run from the repository root:
Command
Description
pnpm dev
Start development server (watch mode)
pnpm build
Build the core application
pnpm bundle
Create production bundle (tsdown)
pnpm test
Run test suite (Vitest)
pnpm lint
Run ESLint with auto-fix
pnpm typecheck
TypeScript type checking
pnpm format
Format code with Prettier
Running Tests
# Run all tests
pnpm test
# Run a specific test file
pnpm test -- test/src/modules/user/user.service.spec.ts
# Run tests matching a pattern
pnpm test -- --testNamePattern="should create user"
# Watch mode
pnpm -C apps/core run test:watch
Environment Variables
Variable
Description
Default
JWT_SECRET
Secret for JWT signing
Required
ALLOWED_ORIGINS
CORS allowed origins (comma-separated)
—
PG_URL
Full PostgreSQL connection string
—
PG_HOST
PostgreSQL host
127.0.0.1
PG_PORT
PostgreSQL port
5432
PG_USER
PostgreSQL user
mx
PG_PASSWORD
PostgreSQL password
mx
PG_DATABASE
PostgreSQL database name
mx_core
PG_MAX_POOL_SIZE
PostgreSQL connection pool size
20
PG_SSL
Enable PostgreSQL SSL
false
REDIS_HOST
Redis host
localhost
REDIS_PORT
Redis port
6379
REDIS_PASSWORD
Redis password
—
SNOWFLAKE_WORKER_ID
Snowflake ID worker ID (0–1023)
Required
ENCRYPT_ENABLE
Enable field encryption
false
ENCRYPT_KEY
64-char hex encryption key
—
THROTTLE_TTL
Rate limit window (seconds)
10
THROTTLE_LIMIT
Max requests per window
100
PORT
Server port
2333
TZ
Timezone
Asia/Shanghai
DISABLE_CACHE
Disable Redis caching
false
Configuration can also be provided via CLI arguments or YAML files. See apps/core/src/app.config.ts for the full config schema.
API Response Format
Every successful JSON response has the shape { data, meta? }; every error has the shape { error: { code, message, details? } }.
A controller returning a bare value T → { data: T } (via global ResponseInterceptor).
Returning withMeta(value, meta) (see ~/common/response/envelope.types) → { data, meta }. Detection is by an internal Symbol, so returning a literal { data, ... } is double-wrapped — CI enforces this via scripts/check-controller-response-envelope.ts.
Returning undefined → 204 No Content.
@HTTPDecorators.RawResponse — opt out of the envelope/casing pipeline for non-JSON (streams, HTML, RSS, redirects).
Case conversion — code is camelCase end-to-end (DTOs, services, Drizzle column TS props). Incoming requests are normalized to camelCase by RequestCaseNormalizationPipe; outgoing data/meta are converted back to snake_case at the wire boundary. The wire format stays snake_case (e.g., createdAt → created_at). Use @BypassCaseTransform([paths]) to keep free-form JSON subtrees untouched.
Errors — throw AppException subclasses (BizException, CannotFindException, etc.) with a stable SCREAMING_SNAKE code; AppExceptionFilter maps them to the unified error envelope.
Upgrading
v11 → v12
v12 migrates the database from MongoDB to PostgreSQL. This is a hard cutover: all data must be migrated through the provided CLI before starting the new version. See Upgrading to v12.
v10 → v11
v11 refactors the Aggregate API: categories and pageMeta are removed from GET /aggregate; a new GET /aggregate/site endpoint is added for lightweight site metadata. See Upgrading to v11.
v9 → v10
v10 includes a breaking auth system refactor. See Upgrading to v10.
MX Space Core
AI-powered CMS Core for personal blogs, creator homepages & content websites.
Overview
MX Space Core is a headless CMS server built with NestJS, PostgreSQL, and Redis. Beyond standard blog features (posts, pages, notes, comments, categories, feeds, search), it ships with a full AI content workflow — summary generation, multi-language translation, comment moderation, and writing assistance — powered by pluggable LLM providers.
Key Features
x-api-keyheader)Tech Stack
apps/admin)Monorepo Structure
Core Architecture (
apps/core/src/)Quick Start
Prerequisites
Local Development
The API is available at
http://localhost:2333. In development mode, routes have no/api/v2prefix.Docker Deployment
The fastest way to get a production instance running:
Or use the prebuilt image directly:
The image supports
linux/amd64andlinux/arm64.Available Commands
Run from the repository root:
pnpm devpnpm buildpnpm bundlepnpm testpnpm lintpnpm typecheckpnpm formatRunning Tests
Environment Variables
JWT_SECRETALLOWED_ORIGINSPG_URLPG_HOST127.0.0.1PG_PORT5432PG_USERmxPG_PASSWORDmxPG_DATABASEmx_corePG_MAX_POOL_SIZE20PG_SSLfalseREDIS_HOSTlocalhostREDIS_PORT6379REDIS_PASSWORDSNOWFLAKE_WORKER_IDENCRYPT_ENABLEfalseENCRYPT_KEYTHROTTLE_TTL10THROTTLE_LIMIT100PORT2333TZAsia/ShanghaiDISABLE_CACHEfalseConfiguration can also be provided via CLI arguments or YAML files. See
apps/core/src/app.config.tsfor the full config schema.API Response Format
Every successful JSON response has the shape
{ data, meta? }; every error has the shape{ error: { code, message, details? } }.T→{ data: T }(via globalResponseInterceptor).withMeta(value, meta)(see~/common/response/envelope.types) →{ data, meta }. Detection is by an internalSymbol, so returning a literal{ data, ... }is double-wrapped — CI enforces this viascripts/check-controller-response-envelope.ts.undefined→204 No Content.@HTTPDecorators.RawResponse— opt out of the envelope/casing pipeline for non-JSON (streams, HTML, RSS, redirects).Case conversion — code is camelCase end-to-end (DTOs, services, Drizzle column TS props). Incoming requests are normalized to camelCase by
RequestCaseNormalizationPipe; outgoingdata/metaare converted back to snake_case at the wire boundary. The wire format stays snake_case (e.g.,createdAt→created_at). Use@BypassCaseTransform([paths])to keep free-form JSON subtrees untouched.Errors — throw
AppExceptionsubclasses (BizException,CannotFindException, etc.) with a stableSCREAMING_SNAKEcode;AppExceptionFiltermaps them to the unified error envelope.Upgrading
v11 → v12
v12 migrates the database from MongoDB to PostgreSQL. This is a hard cutover: all data must be migrated through the provided CLI before starting the new version. See Upgrading to v12.
v10 → v11
v11 refactors the Aggregate API:
categoriesandpageMetaare removed fromGET /aggregate; a newGET /aggregate/siteendpoint is added for lightweight site metadata. See Upgrading to v11.v9 → v10
v10 includes a breaking auth system refactor. See Upgrading to v10.
Related Projects
apps/admin@mx-admin/admin— React 19 admin dashboard (in-repo, built into the server release)mxsCLI for posts/notes/pages/config (OIDC device auth)License
apps/— AGPLv3 with Additional TermsSee LICENSE for full details.