Configuration reference
There are exactly two sources: jingmei.config.json for settings and .env for secrets. The config file only names environment variables (tokenEnv, apiKeyEnv, routingSecretEnv), never the secrets themselves; process environment variables override .env. Write every ID as a JSON string.
Top level
| Field | Meaning |
|---|---|
dataDir | Data directory, default data; relative to the project root, ~/ allowed |
routingSecretEnv | Env var holding the routing HMAC secret, default ROUTING_SECRET; must be set |
visionModel | Optional "provider/model" (split at the first /). Describes images as text only for personas whose main model cannot see images; must accept image input |
discord.guilds[] | { guildId, channelIds }: server ID and allowed channel IDs (17–20 digits); threads under those channels work too |
telegram.chatIds | Allowed group IDs, e.g. "-1001234567890" |
voice | Optional Fish Audio: apiKeyEnv, referenceId (32-hex voice ID), model (s2.1-pro-free default, or s2.1-pro) |
imageGeneration | Optional { model }: the Antigravity model ID used for drawing, default gemini-3.1-flash-image. The drawing tool is enabled only when the antigravity provider is signed in, see Drawing |
kline | Optional { enabled }: enabled defaults to false, see Candlestick charts |
textImage | Optional { enabled, thresholdChars }: enabled defaults to false; thresholdChars is the length limit of a text reply (integer 50–8000, default 300), see Long text as an image |
features | Optional; boolean switches history, memory, soul, search, audit, all true by default; see Optional features |
telemetry | Optional, { enabled }: anonymous instance stats; enabled defaults to true, and DO_NOT_TRACK=1 in the environment also turns it off; see Usage statistics |
jev | Optional, see Jev |
localJev | Optional in-process LLM→Jev wrapper: required baseUrl (http(s)) and model, optional apiKeyEnv (omit for unauthenticated local services). Requires an OpenAI-compatible endpoint with logprobs. If the section is absent and DEEPSEEK_API_KEY resolves, defaults to DeepSeek / deepseek-flash |
events | Optional; presence enables topics. Required summaryModel: "provider/model" (first slash splits; model and authentication checked at startup). embeddingModel defaults to fast-bge-small-zh-v1.5 (512 dimensions), and must be supported by fastembed; it also supplies the message-search vectors (without topics, message search uses the default model). Requires a remote Jev or local LLM decision client |
celebrations[] | Optional greeting targets, see below |
personas[] | Characters, at least one |
Web search has no section of its own: it is on whenever DEEPSEEK_API_KEY is present, and features.search: false turns it off.
personas[]
| Field | Meaning |
|---|---|
id | Unique, a-z 0-9 _ - only |
name | Display name; a message containing it addresses the character |
personaPath | Persona file, must be readable |
reactionImages | Optional local catalog directory containing catalog.json; resolved from the project root like personaPath, also accepting absolute paths and ~/ |
provider / model | Pi provider and model ID |
reasoningEffort | off (default), minimal, low, medium, high, xhigh, max; a level the model doesn't support fails at startup |
routingP | 0–1, deterministic candidate probability for ordinary messages, still subject to gates and enabled content scoring; the sum over characters in one group/server must not exceed 1. At 0, the character never chimes in randomly but can still be addressed explicitly or identified as the content's addressee |
aliases | Optional extra names that address the character (≤ 64 characters each) |
spaces | Optional restriction to some groups/servers, e.g. ["discord:<guildId>", "telegram:<chatId>"]; omit for all |
sendReactionImages | Whether bundled and local catalog reaction images may be sent, default true |
voiceEnabled | Whether to use voice when configured, default true |
imageGenerationEnabled | Whether to draw when Antigravity is signed in, default true |
discord / telegram | The character's account on that platform: { tokenEnv, adminUserIds? }. At least one is required, and each platform used needs its top-level section. adminUserIds may use /context and /compact |
Keep local catalogs private: place the whole directory at personas/feiba/, add "reactionImages": "personas/feiba" to that character's object, and keep "sendReactionImages": true. Example layout:
personas/
idk.local.md
feiba/
catalog.json
001_innocent.png
...catalog.json maps ids to metadata, e.g. {"innocent":{"file":"feiba/001_innocent.png","name":"Innocent","caption":"Who, me?"}}. Files are relative to the catalog directory; if the first segment exactly matches the directory name (feiba/ here), it is stripped first. Thus the old assets/reactions/feiba/ directory can be copied wholesale to personas/feiba/ without editing its 120 catalog paths; keep the directory name feiba.
Startup validation collects errors: ids use only a-z 0-9 _ and cannot collide with hello/laugh/think/hug; name and caption must be strings; files must be readable, with .png, .jpg, or .jpeg extensions (case-insensitive). Absolute paths, .., backslashes, and symlinks escaping the directory are rejected. Extra metadata is ignored. Available ids enter the character's tool schema in stable order; selection guidance belongs in its persona file, not a repeated full list in the system prompt. Restart after changing the directory or catalog to reload it.
celebrations[]
| Field | Meaning |
|---|---|
space | "discord:<guildId>" or "telegram:<chatId>", a configured space |
channelId | Discord: one of that server's allowed channels; Telegram: optional, must equal the group ID if set |
personaId | The character who sends greetings; must be able to speak in that space |
timeZone | IANA time zone, e.g. Australia/Sydney |
calendar | china (New Year's Day, Spring Festival, Labour Day, Dragon Boat, Mid-Autumn, National Day), australia (New Year's Day, Australia Day, Good Friday, Easter Sunday, ANZAC Day, Christmas, Boxing Day) or both |
Lunar holidays are calculated from the Gregorian date in the target time zone, independently of the operating system; leap months do not repeat greetings.
Birthdays are greeted only in groups/servers with a greeting target; February 29 birthdays are greeted on February 28 in common years.
