MADORIMADORI

Configuration

Madori is configured via a single TypeScript file at your project root: madori.config.ts. This file controls content paths, Control Panel settings, GraphQL behaviour, and authentication. All options have sensible defaults — you only need to configure what you want to change.


Configuration Reference

Full Config Schema

import type { MadoriConfigInput } from './src/lib/config/schema'

const config: MadoriConfigInput = {
  contentPath: './content',
  resourcesPath: './resources',
  usersPath: './users',
  assetsPath: './public/assets',

  git: {
    enabled: false,
    automatic: true,
    push: false,
    trackedPaths: [
      { root: 'content', exclude: ['forms/**'] },
      { root: 'resources' },
    ],
  },

  cp: {
    enabled: true,
    path: '/cp',
  },

  graphql: {
    enabled: true,
    path: '/api/graphql',
    introspection: process.env.NODE_ENV !== 'production',
  },

  staticCache: {
    enabled: false,
    driver: 'application',
    storagePath: './storage/static-cache',
    exclude: [],
    queryStrings: 'ignore',
    warmOnInvalidate: false,
  },

  sites: [
    { handle: 'default', url: 'https://www.example.com', locale: 'en-US', default: true },
  ],

  seo: {
    enabled: true,
    metadata: true,
    structuredData: true,
    sitemap: true,
    robots: true,
    humans: true,
    reports: true,
    redirects: true,
    errorTracking: false,
    socialImages: false,
    allowExternalCanonicals: false,
    allowedRedirectOrigins: [],
    trailingSlash: 'never',
    reportRetentionDays: 90,
    reportSnapshotLimit: 50,
    operationalStoragePath: './storage/seo',
  },

  auth: {
    driver: 'password',
    store: 'file',
    provider: 'yaml',
    storeConfig: {
      sessionsDir: './.sessions',
      sessionDurationMs: 86400000,
    },
  },
}

export default config

Path Options

Option Type Default Description
contentPath string ./content Directory where content entries, globals, forms, and navigation are stored
resourcesPath string ./resources Directory where blueprints, fieldsets, roles, and definitions live
usersPath string ./users Directory where user YAML files are stored
assetsPath string ./public/assets Directory where uploaded assets are stored

Git Content Sync

Git sync is disabled by default. When enabled, Madori commits successful content changes to each repository containing a configured tracked root. Pushing to a remote is separately opt-in.

Option Type Default Description
git.enabled boolean false Enable content Git synchronization
git.automatic boolean true Queue commits after content changes
git.push boolean false Push successful commits to configured remote
git.debounceMs number 2000 Coalesce rapid changes before committing
git.trackedPaths { root: string, exclude?: string[] }[] content and resources Built-in roots (content, resources, assets, users) or explicit paths; assets and users are opt-in
git.remote string origin Existing Git remote name
git.branch string unset Optional branch to push
git.commitPrefix string [Madori] Prefix for generated commit messages
git.statePath string ./storage/git-sync Durable pending-sync state

Madori never stores Git credentials. Configure authentication in Git itself (for example, an SSH deploy key or credential helper). Tracked paths may point into separate repositories, but only explicitly configured paths are staged. users and assets remain excluded unless added to trackedPaths; consider privacy and large-file storage before enabling them.

For setup, GitHub authentication, separate repositories, recovery, and troubleshooting, see Git Content Sync.

Control Panel Options

Option Type Default Description
cp.enabled boolean true Enable or disable the Control Panel entirely
cp.path string /cp URL path prefix for the Control Panel

GraphQL Options

Option Type Default Description
graphql.enabled boolean true Enable or disable the GraphQL API
graphql.path string /api/graphql URL path for the GraphQL endpoint
graphql.introspection boolean true in dev, false in prod Allow schema introspection queries

When introspection is disabled, __schema and __type fields are rejected during GraphQL validation; ordinary fields, including __typename, remain available. GraphiQL is unavailable, but normal GET and POST queries still work subject to authentication and permissions.

The bundled Next.js routes and Control Panel links use /cp and /api/graphql. Changing cp.path or graphql.path alone does not relocate those routes; keep the defaults unless you also adapt application routing, links, and route protection.

Static HTML Cache

Option Type Default Description
staticCache.enabled boolean false Enable bounded caching for eligible anonymous HTML responses
staticCache.driver application | file application Cache storage implementation
staticCache.storagePath string storage/static-cache/ Cache storage location
staticCache.exclude string[] [] Glob patterns excluded from caching
staticCache.queryStrings ignore | separate ignore Query-string cache-key policy
staticCache.warmOnInvalidate boolean false Warm invalidated URLs when configured
staticCache.invalidationRules array [] Content trigger to URL/glob mappings

Cache applies only to eligible public HTML on the configured site origin. Requests with cookies, authorization, RSC/prefetch variants, or private/no-store/no-cache responses pass through. Use one writable application process per storage location; the cache coordination is not a distributed lock across independent deployments.

Sites and SEO Options

sites defines public site contexts used by URL resolution, metadata, alternate links, sitemaps, and redirects. Exactly one site must be marked default; each URL must be an HTTP(S) origin without credentials, query parameters, or fragments. Use separate handles for domain-based sites or locales served from a shared host.

Option Type Default Description
sites[].handle string default Stable site identifier used in SEO documents and API requests
sites[].url string http://localhost:3000 Public origin for canonical and alternate URLs
sites[].locale string en-US Locale emitted in alternate metadata
sites[].default boolean false Select exactly one fallback site
seo.enabled boolean true Master SEO switch; disables SEO output and endpoints
seo.metadata boolean true Emit title, description, canonical, robots, and social metadata
seo.structuredData boolean true Emit validated JSON-LD
seo.sitemap boolean true Enable /sitemap.xml generation
seo.robots boolean true Enable /robots.txt generation
seo.humans boolean true Enable /humans.txt generation
seo.reports boolean true Enable SEO audit report APIs and snapshots
seo.redirects boolean true Enable authored redirect management and runtime redirects
seo.errorTracking boolean true Record bounded, normalized public 404 observations
seo.socialImages boolean false Emit resolved social-image overrides
seo.allowExternalCanonicals boolean false Permit explicitly authored external canonical URLs
seo.allowedRedirectOrigins string[] [] Exact external origins permitted as redirect destinations; redirects stay local by default
seo.trailingSlash always | never | preserve never Canonical path normalization policy
seo.reportRetentionDays number 90 Retention window for operational report snapshots
seo.reportSnapshotLimit number 50 Maximum retained report snapshots
seo.operationalStoragePath string ./storage/seo Runtime SEO storage; keep outside content Git paths

SEO defaults are authored in versioned files under resources/seo/; redirects are versioned under content/seo/redirects/. 404 observations are persisted in not-found-observations.json and audit snapshots under the configured operational storage path; do not commit that operational directory to content Git. Runtime caches and counters are process/runtime concerns rather than a documented persistent storage contract. See SEO Architecture for the storage contract.

Authentication Options

Option Type Default Description
auth.driver string password Authentication driver — validates credentials
auth.store string file Session storage backend
auth.provider string yaml User data provider
auth.storeConfig.sessionsDir string ./.sessions Directory for session files
auth.storeConfig.sessionDurationMs number 86400000 (24h) Session expiry duration in milliseconds

Directory Structure

my-site/
├── content/
│   ├── collections/      # Entry files (Markdown + YAML frontmatter)
│   ├── globals/          # Global data (YAML)
│   ├── forms/            # Form submissions (YAML)
│   ├── navigation/       # Navigation trees (YAML)
│   └── taxonomies/       # Taxonomy terms (YAML)
├── resources/
│   ├── blueprints/       # Field schemas
│   │   ├── collections/
│   │   ├── globals/
│   │   ├── taxonomies/
│   │   └── forms/
│   ├── collections/      # Collection definitions
│   ├── definitions/      # Navigation definitions
│   ├── fieldsets/        # Reusable field groups
│   ├── roles/            # Permission roles
│   └── taxonomies/       # Taxonomy definitions
├── users/                # User accounts (YAML)
├── public/assets/        # Uploaded files
└── madori.config.ts      # Project configuration

Usage Examples

Minimal Configuration

The simplest valid config uses all defaults:

import type { MadoriConfigInput } from './src/lib/config/schema'

const config: MadoriConfigInput = {}

export default config

This gives you a fully functional CMS with the Control Panel at /cp and GraphQL at /api/graphql.

Custom Paths

Change where content and resources are stored:

const config: MadoriConfigInput = {
  contentPath: './data/content',
  resourcesPath: './data/resources',
  assetsPath: './public/media',
}

export default config

Disable GraphQL in Production

Keep GraphQL available in development but disable it in production:

const config: MadoriConfigInput = {
  graphql: {
    enabled: process.env.NODE_ENV !== 'production',
    path: '/api/graphql',
    introspection: false,
  },
}

export default config

Custom Control Panel Path

Mount the Control Panel at a non-default URL:

const config: MadoriConfigInput = {
  cp: {
    enabled: true,
    path: '/admin',
  },
}

export default config

The Control Panel is now accessible at http://localhost:3000/admin.

Extended Session Duration

Keep editors logged in for 7 days instead of the default 24 hours:

const config: MadoriConfigInput = {
  auth: {
    driver: 'password',
    store: 'file',
    provider: 'yaml',
    storeConfig: {
      sessionsDir: './.sessions',
      sessionDurationMs: 7 * 24 * 60 * 60 * 1000, // 7 days
    },
  },
}

export default config

Common Patterns

Environment-Specific Configuration

Use environment variables to adjust configuration per environment:

const config: MadoriConfigInput = {
  graphql: {
    enabled: true,
    path: '/api/graphql',
    introspection: process.env.NODE_ENV !== 'production',
  },

  cp: {
    enabled: process.env.DISABLE_CP !== 'true',
    path: '/cp',
  },
}

export default config

Headless Mode (API Only)

Disable the Control Panel entirely for a headless setup where content is managed via files or external tools:

const config: MadoriConfigInput = {
  cp: {
    enabled: false,
  },

  graphql: {
    enabled: true,
    path: '/api/graphql',
  },
}

export default config

Monorepo Setup

In a monorepo where content lives separately from the application:

const config: MadoriConfigInput = {
  contentPath: '../../packages/content/data',
  resourcesPath: '../../packages/content/resources',
  usersPath: '../../packages/content/users',
  assetsPath: './public/assets',
}

export default config

Secure Production Defaults

A production-hardened configuration:

const config: MadoriConfigInput = {
  graphql: {
    enabled: true,
    path: '/api/graphql',
    introspection: false,
  },

  auth: {
    driver: 'password',
    store: 'file',
    provider: 'yaml',
    storeConfig: {
      sessionsDir: './.sessions',
      sessionDurationMs: 4 * 60 * 60 * 1000, // 4 hours
    },
  },
}

export default config

Managing Settings in the Control Panel

The Control Panel includes a Settings page at /cp/settings. Runtime settings (site_name, locale, and timezone) are stored separately from application configuration. The configuration editor writes supported fields to madori.config.ts, including paths, sites, SEO, Git, and cache options. Changes are validated before writing; configuration changes require a rebuild and process restart in production. Authored content and SEO documents have separate runtime write paths.

Git-Ignored Sessions Directory

Add the sessions directory to .gitignore to avoid committing session data:

.sessions/

This is included by default in Madori's generated .gitignore.