mateclaw/mateclaw-server/src/main/resources/docs/en/config.md
mikemikimike 576eb8055a
fix(auth): require secure JWT configuration and persist desktop secret (#666)
Require explicitly configured secure JWT secrets for server deployments and persist a random per-installation desktop secret across restarts. Preserve explicit environment overrides for strict server validation. Fixes #665.
2026-10-19 04:26:06 -05:00

14 KiB

Configuration

Three places to configure MateClaw: application.yml, environment variables, and the database.

Most settings live in application.yml (Spring Boot's default config file), with sensitive values overridden by environment variables. Anything you want to change at runtime — model providers, search keys, feature toggles — lives in the mate_system_setting table and is edited through the Settings page.

Deep-dive topics have their own pages — Tool Guard rules in Security & Approval, model providers in Models, memory tuning in Memory.


Profiles

Profile Database Activated by
default H2 file at ./data/mateclaw No action needed
mysql MySQL 8.0+ spring.profiles.active=mysql or SPRING_PROFILES_ACTIVE=mysql
postgres PostgreSQL 16+ SPRING_PROFILES_ACTIVE=postgres
kingbase KingbaseES SPRING_PROFILES_ACTIVE=kingbase (opt-in driver required)

The public Docker Compose stack activates postgres. Desktop builds use default; the mysql profile remains supported for existing or self-managed deployments.


Core application.yml sections

Server

server:
  port: 18088                    # HTTP port
  servlet:
    context-path: /

Database — H2 (development)

spring:
  datasource:
    url: jdbc:h2:file:./data/mateclaw;MODE=MYSQL
    username: sa
    password:
    driver-class-name: org.h2.Driver
  h2:
    console:
      enabled: true              # Available at /h2-console (disable in production)

Database — MySQL (supported self-managed deployment)

spring:
  profiles:
    active: mysql
  datasource:
    url: jdbc:mysql://localhost:3306/mateclaw?useSSL=false&serverTimezone=UTC
    username: root
    password: ${DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver

AI model — managed in the UI, not YAML

::: tip Model configuration is 100% UI-driven. Don't put spring.ai.* blocks in application.yml — every provider, key, and model config lives in Settings → Models, backed by the mate_model_provider and mate_model_config tables. :::

Provider, key, and model rows in the database are the primary configuration. On a fresh install, log in and add the first provider under Settings → Models → Add Provider. DASHSCOPE_API_KEY remains a compatibility fallback for DashScope auto-configuration, but it does not replace the provider row; do not assume equivalent environment variables are read for other providers. Full reference in Models.

Virtual threads (JDK 21)

spring:
  threads:
    virtual:
      enabled: true

Enabled by default. Tomcat request threads, @Scheduled tasks, and @Async methods all run on virtual threads. SSE long-connections no longer hold platform threads, and I/O-bound async tasks (memory extraction, auditing, skill install, etc.) no longer queue behind a 16-thread pool.

Spring AI Observability

spring:
  ai:
    chat:
      observations:
        log-prompt: false       # don't write prompt content into spans (security)
        log-completion: false   # don't write completion content into spans

When enabled, /actuator/metrics/gen_ai.client.operation and /actuator/metrics/gen_ai.client.token.usage automatically record latency and token usage for every LLM call. Requires spring-boot-starter-actuator (already included).

Context window

mate:
  agent:
    conversation:
      window:
        default-max-input-tokens: 128000
        compact-trigger-ratio: 0.75
        preserve-recent-pairs: 2
        summary-max-tokens: 300

Details in Memory.

Memory extraction and consolidation

mate:
  memory:
    auto-summarize-enabled: true
    min-messages-for-summarize: 4
    min-user-message-length: 10
    skip-cron-conversations: true
    summary-max-tokens: 1000
    max-transcript-messages: 30
    cooldown-minutes: 5
    emergence-enabled: true
    emergence-day-range: 7

LLM Wiki

mate:
  wiki:
    enabled: true
    max-chunk-size: 30000
    max-context-chars: 10000
    max-pages-per-raw: 15
    max-parallel-raw-materials: 3
    max-parallel-phase-b-pages: 3
    auto-process-on-upload: true
    upload-dir: ./data/wiki-uploads

Details in LLM Wiki.

Tool Guard (rule-based)

Tool Guard's global switch, default policy, and rules are not configured in application.yml — they live in the database (mate_tool_guard_config / mate_tool_guard_rule) and are edited from the admin Security page or via REST:

Method Path What it does
GET / PUT /api/v1/security/guard/config Global switch + default policy (allow / deny / require_approval)
GET /api/v1/security/guard/rules/builtin Built-in rules
GET / POST /api/v1/security/guard/rules List / create custom rules
PUT /api/v1/security/guard/rules/{ruleId} Update a rule
PUT /api/v1/security/guard/rules/{ruleId}/toggle Enable/disable a single rule

Each rule matches on tool name + argument pattern and yields an allow / deny / require_approval action, ordered by priority. Details in Security & Approval.

File Guard

File Guard has two layers:

  1. Allowed / denied path rules — like Tool Guard, stored in the database and edited from the admin Security page; REST is GET / PUT /api/v1/security/guard/config/file-guard. Not in application.yml.
  2. Global fallback sandbox root — the only piece that lives in application.yml. When a conversation has no per-workspace base path configured, file/shell tools are confined to this root (fail-closed default):
mateclaw:
  workspace:
    sandbox:
      enabled: true                       # set false to restore the legacy unconstrained behaviour
      root: ${user.dir}/data/workspace    # fallback sandbox root, created at startup

Environment overrides: MATECLAW_WORKSPACE_SANDBOX_ENABLED / MATECLAW_WORKSPACE_SANDBOX_ROOT.

JWT authentication

mateclaw:
  jwt:
    secret: ${JWT_SECRET:}
    expiration: 86400000

::: warning JWT_SECRET is required at startup in every environment. Set a unique random value of at least 32 UTF-8 bytes; generate one with openssl rand -base64 48. Never commit it. Rotating it invalidates existing tokens. :::

Skill workspace

mateclaw:
  skill:
    workspace:
      root: ${user.home}/.mateclaw/skills
      auto-init: true
      delete-policy: archive
      bundled-skills-path: skills

Multimodal default providers

mate:
  image:
    default-provider: dashscope
  video:
    default-provider: dashscope
  tts:
    default-provider: cosyvoice
  stt:
    default-provider: paraformer
  music:
    default-provider: dashscope

Details in Multimodal.


Environment variables

::: warning Manage LLM keys in the console Provider, key, and model rows in Settings → Models are primary. DASHSCOPE_API_KEY remains only as a compatibility fallback for DashScope auto-configuration; do not assume equivalent environment variables are read for other providers. :::

Variable Required Purpose
SERPER_API_KEY — Google Serper search key (search tools not yet UI-managed)
TAVILY_API_KEY — Tavily search key (same as above)
JWT_SECRET Yes JWT signing secret
MATECLAW_CORS_ALLOWED_ORIGINS — CORS allowlist (recommended in production)
DB_PASSWORD / DB_ADMIN_PASSWORD Docker PostgreSQL application / bootstrap-admin passwords (must differ)
DB_USERNAME / DB_ADMIN_USERNAME — PostgreSQL application / bootstrap-admin usernames
DB_HOST / DB_PORT / DB_NAME — Database address, port, and name
SPRING_PROFILES_ACTIVE — Docker Compose sets postgres; self-managed deployments may use mysql / kingbase

Setting them

Linux / macOS:

export JWT_SECRET="$(openssl rand -base64 48)"
export SERPER_API_KEY=your-serper-key   # optional

Windows (PowerShell):

$env:JWT_SECRET = openssl rand -base64 48

Docker (.env file):

DB_PASSWORD=secure-password-here
DB_ADMIN_PASSWORD=different-secure-password-here
# Generate with: openssl rand -base64 48
JWT_SECRET=replace-with-generated-value

After startup, open http://localhost:18080, sign in as admin / admin123, and add your first LLM provider under Settings → Models → Add Provider.


Database schema init

MateClaw uses Flyway for schema migrations:

  1. db/migration/h2/V*__*.sql — H2-dialect migration scripts
  2. db/migration/mysql/V*__*.sql — MySQL-dialect migration scripts
  3. After migrations, seed data is loaded from db/data-*.sql — idempotent

Flyway selects the migration dialect from the active Spring profile. Startup runs migrate with checksum validation enabled by default in all bundled profiles. Applied migration files must not be edited: checksum drift or a failed migration stops startup rather than silently changing the schema history.

Desktop upgrade compatibility: an existing desktop database whose applied scripts differ from the new release can now fail to start. A new database and an unchanged migration history need no repair. Keep a backup that can be opened with the previous application version; do not delete the database to clear a validation error.

If validation fails:

  1. Stop the application and back up the database. Keep the exact previous and target release artifacts, and inspect the failing migration and schema history.
  2. Compare the old and new SQL with the actual schema/data. Prefer restoring the expected migration file or a maintainer-provided forward migration. repair changes migration metadata; it does not execute changed DDL, undo partial DDL/data changes, or prove the schema is correct. Resolve any partial migration effects before proceeding.
  3. Only after confirming that history repair is appropriate, set mateclaw.flyway.auto-repair=true (environment variable MATECLAW_FLYWAY_AUTOREPAIR=true) for one controlled startup. Repair uses the configured migration locations and is followed by normal migration. Keep validation enabled.
  4. Remove the opt-in immediately afterwards, restart normally, and verify validation and application behavior before allowing writes. If recovery fails, restore the backup with its matching application version.

The opt-in defaults to false, including Desktop. It is not an unattended upgrade policy. spring.flyway.validate-on-migrate remains externally configurable, but setting it to false bypasses the validation guard and is not a repair procedure.

Table conventions

  • All tables prefixed mate_
  • snake_case columns, camelCase Java fields (auto-mapped by MyBatis Plus)
  • Every table has create_time, update_time, deleted
  • Logical delete: deleted = 0 active, deleted = 1 soft-deleted

H2 console in dev

http://localhost:18088/h2-console:

Field Value
JDBC URL jdbc:h2:file:./data/mateclaw
Username sa
Password (empty)

Switching to MySQL

CREATE DATABASE mateclaw CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
export SPRING_PROFILES_ACTIVE=mysql
export DB_PASSWORD=your-password
mvn spring-boot:run

Runtime settings (mate_system_setting)

Things you want to change without restarting:

Key Type Purpose
default_agent_id Long Agent used when none specified
default_model_config_id Long Default model configuration
max_conversation_turns Integer Max turns per conversation
enable_memory Boolean Enable memory extraction
search_enabled Boolean Global web search toggle
search_provider String serper / tavily / duckduckgo / searxng
search_fallback_enabled Boolean Fall through to next provider on failure
serper_api_key String Serper key (masked in UI)
tavily_api_key String Tavily key (masked in UI)
language String zh-CN / en-US default UI language
stream_enabled Boolean SSE streaming output
debug_mode Boolean Show extra debug info in UI

All editable from Settings → System. Changes take effect immediately.

Search service configuration

Web search config has been migrated from application.yml to the System Settings page. Changes take effect immediately without restart.

::: tip API keys are displayed masked. When saving, keys are only overwritten when a new value is entered. Blank input means "keep existing key". :::


Logging

logging:
  level:
    vip.mate: INFO
    vip.mate.agent: DEBUG
    vip.mate.agent.graph: DEBUG
    org.springframework.ai: INFO
    root: INFO

For deep debugging, set vip.mate: TRACE. Log volume is high — don't leave it on in production.


CORS

Development: Vite's dev server handles CORS via its proxy. Production: frontend is embedded in the JAR, no CORS needed.

If you deploy the frontend separately:

mateclaw:
  cors:
    allowed-origins:
      - http://localhost:5173
      - https://your-domain.com

Configuration precedence

Settings resolve in this order (highest priority first):

  1. Environment variables
  2. Command-line arguments (--server.port=9090)
  3. application-{profile}.yml
  4. application.yml
  5. Database mate_system_setting (for runtime-configurable values)

Next