Skip to main content
Volvox.Bot uses a two-layer configuration system. config.json defines the structure and default values. PostgreSQL stores runtime overrides that take effect immediately without a restart. When the bot starts, it seeds the database from config.json if the database is empty, then merges the two layers in memory.
Changes made via /config set or the web dashboard are written to PostgreSQL and applied immediately. You do not need to restart the bot.

How the two layers work

Layer 1 — config.json is the authoritative set of defaults. It is read from disk once at startup and never written at runtime. If the database is unavailable, the bot falls back to this file. Layer 2 — PostgreSQL stores any values that have been changed since the initial seed. Each changed key is stored as a row in the config table with a guild_id column. The special value 'global' is used for server-wide defaults; individual Discord guild IDs are used for per-server overrides. At runtime the bot deep-merges global defaults with any guild-level overrides so that each guild sees its own effective configuration.

config.json structure

The file contains one top-level key per feature. All keys are present in the default file; features that are not yet enabled have "enabled": false.
config.json
{
  "ai": {
    "enabled": true,
    "systemPrompt": "...",
    "channels": [],
    "historyLength": 20,
    "historyTTLDays": 30,
    "threadMode": { "enabled": false, "autoArchiveMinutes": 60, "reuseWindowMinutes": 30 },
    "blockedChannelIds": [],
    "feedback": { "enabled": false }
  },
  "triage": {
    "enabled": true,
    "defaultInterval": 3000,
    "maxBufferSize": 30,
    "triggerWords": ["volvox"],
    "classifyModel": "claude-haiku-4-5",
    "classifyBudget": 0.05,
    "respondModel": "claude-sonnet-4-6",
    "respondBudget": 0.2,
    "thinkingTokens": 1024,
    "dailyBudgetUsd": 10
  },
  "welcome": {
    "enabled": true,
    "channelId": "1438631182379253814",
    "message": "Welcome to Volvox, {user}! ...",
    "dynamic": { "enabled": true, "timezone": "America/New_York" }
  },
  "moderation": {
    "enabled": true,
    "alertChannelId": "...",
    "autoDelete": false,
    "dmNotifications": { "warn": true, "timeout": true, "kick": true, "ban": true },
    "escalation": { "enabled": false, "thresholds": [] },
    "protectRoles": { "enabled": true, "roleIds": [], "includeAdmins": true }
  },
  "starboard": {
    "enabled": false,
    "channelId": null,
    "threshold": 3,
    "emoji": "*",
    "selfStarAllowed": false
  },
  "logging": {
    "level": "info",
    "fileOutput": true,
    "database": { "enabled": false, "retentionDays": 30 }
  },
  "botStatus": {
    "enabled": true,
    "status": "online",
    "rotation": { "enabled": true, "intervalMinutes": 5, "messages": [] }
  },
  "permissions": {
    "enabled": true,
    "adminRoleIds": [],
    "moderatorRoleIds": [],
    "botOwners": [],
    "allowedCommands": { "ping": "everyone", "config": "admin" }
  },
  "memory": { "enabled": true, "maxContextMemories": 5, "autoExtract": true },
  "aiAutoMod": { "enabled": false },
  "engagement": { "enabled": false },
  "reputation": { "enabled": false },
  "reminders": { "enabled": false }
}

Configurable sections

Not every key in config.json is writable via the API or dashboard. The SAFE_CONFIG_KEYS allowlist in src/api/utils/configAllowlist.js controls which sections can be updated at runtime. Sections that are missing from this list (for example logging) are readable but not writable through the API. Writable sections (dashboard and API):
SectionDescription
aiAI assistant settings: channels, history, thread mode, feedback
triageMessage triage pipeline: models, budgets, keywords, timeouts
welcomeMember welcome messages and dynamic greeting behavior
moderationAlerts, DM notifications, escalation rules, role protection
starboardReaction-based starboard channel and threshold
permissionsRole assignments and per-command permission levels
memoryLong-term user memory via mem0
botStatusOnline status and activity rotation
aiAutoModAI-powered auto-moderation thresholds and actions
announceAnnouncement feature toggle
afkAFK status feature toggle
tldrChannel summary command settings
engagementMessage and reaction tracking
reputationXP system, level thresholds, role rewards
challengesDaily coding challenge posts
reviewCode review request tracking
remindersUser reminder command settings
githubGitHub feed integration
showcaseProject showcase feature
snippetCode snippet feature toggle
pollPoll feature toggle
helpHelp command feature toggle
auditLogAudit log retention settings
The logging section is readable via the API but not writable. Change logging.level through the LOG_LEVEL environment variable or by editing config.json and restarting.

Changing config at runtime

You can update configuration in two ways:

/config slash command

Use /config set, /config view, and /config reset directly in Discord. Requires the admin permission level.

Web dashboard

Use the Settings pages in the web dashboard. Changes are validated against the schema before being written.

/config slash command

The /config command has three subcommands:
1

View the current config

/config view
/config view section:ai
Without a section argument, the bot returns all sections (truncated if the output exceeds Discord’s embed limit). Use section: to inspect a specific section in full.
2

Set a value

/config set path:ai.enabled value:true
/config set path:triage.dailyBudgetUsd value:20
/config set path:welcome.channelId value:"123456789012345678"
The path argument uses dot notation. Values are auto-coerced: true/false become booleans, numeric strings become numbers, null becomes null. To force a literal string, wrap the value in escaped quotes: "\"my text\"".
3

Reset to defaults

/config reset
/config reset section:welcome
For global resets, values are restored from config.json. For guild-level resets, the guild override is deleted and the server falls back to the global default.
The path and section arguments support autocomplete. Start typing to filter matching config paths.

Per-guild overrides

Every guild that uses Volvox.Bot can have its own configuration. When a user runs /config set inside a Discord server, the change is scoped to that guild’s guild_id row in the database. The bot deep-merges each guild’s overrides onto the global defaults at request time, so only the keys a guild has explicitly changed differ from the global config. Running /config reset without a section clears all of a guild’s overrides and restores it to the global defaults.

Build docs developers (and LLMs) love