The console is a NiceGUI application mounted at /admin/ on the existing private FastAPI service. / and the previous server URLs redirect into it. Sign in with Discord using a server administrator account. Bot and web still run separately against the same SQLite file.
Use Server setup to create worlds and hubs, link worlds, bind channels, choose casts, and enable ambient participation. Each bound channel shows as one line (channel, world or hub, cast size, and an Ambient tag when on); select it to edit its guidelines, default cast, and ambient setting, then choose Save changes in the bar at the bottom. Reply footer, server timezone, avatar asset channel, and Allow /catchup in channels without characters are together under Server settings, saved the same way. That switch is off by default; when on, members can use /catchup in any channel they can read, and those channels' recent messages are sent to the memory model to write the summary. Keep a turn log (off by default) and Keep usage and log for (7, 14 or 30 days, or Full month, which keeps the current month in the server timezone; default 14 days) are there too; see Monitoring. Rebinding a channel keeps its ambient setting and removes cast members who are not available in the new world or hub; unlinking a world from a hub removes that world's characters from the hub's channel casts.
Expand a world or hub to edit its World guidelines or Hub guidelines. Each bound channel has Channel guidelines. Use these for the setting, participants' shared fictional roles, tone, and conventions such as occasional fourth-wall jokes. Channel guidance takes precedence over the current world/hub guidance when they conflict. Threads inherit their parent channel's guidance. In hubs, the hub's guidelines describe the current scene; a guest's home-world guidelines are not added. Each field accepts up to 6,000 UTF-8 bytes. A hub card also has a Tone (In character or Off duty) with its own Save hub tone button; off duty hubs make characters chat casually without pushing their world's plot, and world channels are always in character.
Guidelines are managed instructions included after preset/card instructions, independent of lore matching and lore budgets. They remain present when prompt trimming occurs and work with existing custom presets. All speakers in a turn share one guideline snapshot. Prompt previews include the selected character's home-world guidance, or the current world/channel guidance when Sample channel (optional) is selected.
Delete world or Delete hub shows a confirmation. First move/delete home characters, including archived characters, and rebind any channels elsewhere. Deletion removes owned lore, guidelines, encounters, and hub/book links; shared lorebooks, entries moved elsewhere, and past messages remain. Delete book in Imports removes a named lorebook, including empty books, its remaining entries, and its world/hub links after confirmation. Deleted world/book IDs are not reused.
Characters edits card fields, including main and post-history instructions, moves home worlds with confirmation, and archives or restores characters. Editing a character shows a bar at the bottom of the page (Reset and Save changes) while any field differs from what is saved; other changes are refused until you save or reset. Card instructions are added to the server's selected prompt structure. Post-history instructions are placed after conversation history when the provider permits it.
Each character card has a collapsed Play style section: eight sliders from 0 to 100 (50 is neutral), grouped as Style (Looseness, Aggression, Bluffing, Risk), Reads (Suspicion, Stubbornness) and Mind (Judgment, Unpredictability), with a one-line summary of how the character plays. It shapes how the character plays card games when the model is not choosing its moves. Judgment is a steady habit (a foolish character keeps misjudging its hand), while Unpredictability is the chance of an odd move now and then. Until you save one, the sliders show the style the character plays with today (derived from the character); Save play style keeps your own and Reset to default goes back to the derived one. These buttons save at once and do not use the bottom bar; a change made to the character meanwhile makes the save refuse until you reload. Each save is audited as character.play_style. Generate from description asks the server's director model (else dialogue model) to rate the eight traits from the character's description and personality. The numbers fill the sliders as an unsaved draft with a one-line reason beside each (reasons are not stored); review them and press Save play style. The button shows a busy state while the model works, counts as one model call toward the spending cap (two if the first answer is unreadable) (usage feature "Play style drafts"), is refused at the hard cap, and a failure (no model, cap, timeout, unreadable answer) shows a message and leaves the sliders alone. The character text is untrusted: only the clamped 0 to 100 numbers are used. Each generation is audited as character.play_style_generate. A play style belongs to this bot's games and is not part of imported or exported card data.
Choose Create character, enter a name and home world, and choose Create to start with empty card fields and the default emotion slots. Create a world in Server setup first if none exists. Names must be unique within the server.
Delete character (in the ⋯ menu beside the character name, below Archive) opens a confirmation. Delete permanently removes the character, its owned lore, personal memories, encounters, saved avatar data, and channel/thread cast assignments. Past chat history and Discord messages remain; lore previously moved to another owner also remains. Archive is available when you want to restore a character later. Deletion rejects changes made since the page was loaded, and character IDs are never reused for a new character.
Under Server settings, Largest cast and Favorites per member (whole numbers from 1 to 15, default 5) set how many characters a cast can hold and how many favorites a member can keep. A larger cast gives the director more characters to choose from, so more characters may answer a message; each answer is a separate model call and every prompt lists the cast. At most 3 characters answer one message. With step in, a member's favorites outside the cast can also answer them, so more favorites can mean more model calls and longer prompts. Lowering a limit keeps existing casts and favorites; they can shrink but not grow. Saved with the bar; if the limits were changed meanwhile, the save is refused and you reload. A channel's Default cast label shows the current largest cast. The Archived favorites can answer their members switch (off by default) lets members keep archived characters as favorites. An archived favorite answers only the members who chose it, in channels where its world is available; it never joins a cast, a game or /summon. Turning it off hides them again without removing them; saved with the bar and audited as settings.archived_favorites.
Choose an owner on each side of Lore: character, channel, world/hub, lorebook, or an existing thread lore owner. Search filters content and keywords; lists show 50 entries per page.
Server: Server-wide lore is also available and applies to eligible characters throughout that server. Use Import JSON entries under either owner list to preview a SillyTavern or RisuAI file and add entries directly to that owner. The Direct lore entry import section in Imports provides the same flow. These imports create independent entries without creating a lorebook, replacing existing entries, or removing entries absent from the file. Identical content/rules/pin state are skipped, including duplicates within one upload. Unsupported imported features stay preserved and inactive. A changed destination requires a fresh preview. Named lorebooks retain their separate synchronization workflow.
Drag an entry by its handle onto the other drop area, including its padding or an empty list. The workspace shows Saving lore changes… and waits for that move to finish before accepting another drag. Lists update after the saved change, and the destination opens the page containing the moved entries. Drops within the same owner do not change insertion priority.
Each entry is one compact row with an Edit entry (pencil) button and a ⋯ menu holding Move left/right and Delete. Check individual entries or use Select page, then choose Move selected left/right or Delete selected. Selection can span pages and owners; Clear selection resets it. Deletes show a confirmation with the selected count and a preview. Bulk moves and deletes either complete for the entire selection or leave it unchanged if an entry was edited, moved, or removed in another session. After a conflict, both lists reload and selection clears so you can review current data.
The Edit entry button also offers Move and Copy. Copies are independent; moves retain their activation identity, rule settings, and source-message provenance. Transfers only work within the selected server. Moving channel lore to a character makes it follow that character; moving character lore into a channel shares it with eligible characters there.
The editor includes enabled, constant, pinned, keyword, and priority fields plus advanced matching, probability, grouping, recursion, timing, character filters, and placement settings. Keyword arrays use JSON so a comma inside a regex is preserved. Within each prompt position, entries are assembled in ascending priority: higher values appear later in the context. Higher priority values also win when the World Info budget is constrained. Priority zero is valid.
Unsupported imported features remain visible and inactive. Use the preserved import-fields editor to remove unavailable dependencies, and choose a supported placement. Export retains effective settings and unknown source fields.
Owner exports retain imported source IDs and include a llmcord_pinned field for restoring pin state in llmcord. Reimporting an exported manual entry into the same owner presents a collision decision rather than silently duplicating or overwriting it.
The Import lorebook button in Lore opens Imports. Under Named lorebooks, enter a lorebook name, choose Server lorebook or Channel lorebook, and click Create lorebook. Expand the lorebook, choose Upload JSON lorebook for preview, resolve any conflicts, and click Apply lorebook sync. Enable server lorebooks in the desired worlds or hubs; channel lorebooks apply to their bound channel and threads. Edit entries in Lore opens the lorebook's entries in the Lore workspace.
Imports previews V2/V3 JSON/PNG character cards and standalone lorebook JSON. Lorebooks accept entry arrays, SillyTavern entries objects/arrays, and RisuAI version-1 exports (type: risu, ver: 1, data: [...]). The preview identifies the format. Select a home world or destination book before uploading. Review changes and resolve every conflict before applying. Previews expire after 15 minutes; a changed owner revision requires another preview.
RisuAI primary/secondary keywords, selective matching, insertion order (including zero), always-active flags, and regex toggles are mapped to runtime rules. Keyword matching in the lore editor can explicitly select literal keys, regex patterns, or automatic detection of /pattern/flags syntax. Original entry fields, folder references, and unknown metadata are retained. Folder records are inactive metadata; the editor does not recreate RisuAI's folder tree. Other RisuAI modes, @@ decorators, and content macros beyond {{char}}/{{user}} are preserved and flagged as inactive until rewritten or remapped. Exports use the existing llmcord/SillyTavern entry structure and retain source fields; this adds import support, not a RisuAI-format exporter.
RisuAI entry IDs are used when present. Exports without IDs use array indices for reimport matching, so keep their entry order stable when syncing into an existing book. The original export wrapper is retained with the book.
Reimports preserve manual additions. An imported entry that was edited, moved, or deleted requires a decision if its source changes. Keeping the current entry does not recreate it in its previous owner. Character text and manually uploaded default avatars also have conflict choices. Moving a character's home world can prune ineligible casts.
For older databases, known embedded-card entries are tracked conservatively. Unmatched legacy lore remains independent, and uncertain card-text changes require a choice.
Each server has its own preset library and active revision. The built-in default is read-only: edit it and choose Save draft in the bar that appears at the bottom to create a copy. Choosing a preset in Preset library loads it (save or reset your edits first; changing preset while there are unsaved edits is refused). Other presets can be renamed by changing their name and saving, duplicated with Save as new preset, or deleted once inactive; those actions, and Activate saved revision, are in the ⋯ menu of the Preset library heading. Preview and export keep working on unsaved edits. An imported bundle opens as an unsaved preview: Save draft stores it as a new preset, and Reset returns to the preset you had open.
Each bundle contains separate configurations for dialogue, director selection, memory extraction, summaries, and image descriptions. Purposes missing from an imported native bundle inherit the built-in defaults.
The built-in dialogue prompt asks for a character's next fictional Discord reply, with knowledge, motives, and reactions grounded in their card. Chat questions are things the character may react to rather than tasks they must always solve. A short reminder after history reinforces the character's voice, followed by any card-specific post-history instructions. This follows SillyTavern's main prompt and post-history approach; the wording is tailored to casual Discord conversations. Saved custom presets keep their own instructions.
For a stronger individual voice, add contrasting dialogue examples to the character card: an ordinary interaction, an unexpected request, and something the character actually knows about. Examples should demonstrate their phrasing and temperament, not just describe them. Start a fresh conversation when comparing prompt changes; earlier replies can encourage the model to repeat their style.
Each block shows as one line (name, role, placement, and a Disabled tag when it is off); select it to open its fields, drag its handle or use Move up / Move down in its ⋯ menu to reorder, and use the same menu to remove it. Blocks support ordering, enabling, custom text, context markers, roles, and dialogue-history injection depth/order. Trimming priority is separate from display order. Required input markers, structured-output schemas, and the emotion-header contract remain present. Eligibility, consent, world-lore isolation, and branch visibility are enforced before the renderer receives data.
Save draft (in the unsaved-changes bar, next to Reset) creates an immutable revision. Activate saved revision applies that revision to the next scene turn. A turn captures its preset once: every speaker, image description, summary, and memory call in that turn keeps that revision. Changing providers later can require a new compatibility mapping before the preset will render successfully.
Use Assembled request preview with a sample character, input, and history. It shows roles, expanded text, token estimates, omissions, and provider adaptations without calling a model. It does not read members' stored personal memories.
Import Chat Completion presets containing prompts and prompt_order. Choose an order profile if several are present. SillyTavern character IDs are profile labels, not Discord identities. Imports populate dialogue only; other purposes keep defaults.
Supported substitutions are {{char}}, {{user}}, {{description}}, {{personality}}, {{scenario}}, {{mesExamples}}, {{mesExamplesRaw}}, {{summary}}, and {{group}}. Expansion runs once. These macros, and the time macros below, also expand in character card fields and lore text (one pass; an unknown {{…}} there is left as written). Because expansion runs once, {{description}} inside prompt text inserts the card field as written, while a Character description block expands the field's own macros. Chat-derived text (history, summaries, personal facts, encounters) is never expanded. In the prompt text editor, known macros are tinted and an unknown {{…}} is red with a wavy underline. The editor lists an unknown macro as unsupported, and the preset cannot be activated until you rewrite or remove it.
Time macros (dialogue blocks, card fields and lore text only) use the timezone of the member whose message started the turn: their own timezone setting for this server (/time set), else the server default (Server setup → Server settings → Server timezone), else UTC. The time is when that message was sent (not when the reply is written), so every character speaking in that turn sees the same time. The prompt preview uses the current time in the server's timezone. {{time}} gives 5:39 PM, {{date}} October 8, 2026, {{weekday}} Thursday, {{isotime}} 17:39, {{isodate}} 2026-10-08, and {{local_time}} Thursday, October 8, 2026, 5:39 PM (Asia/Seoul). The built-in preset has a Time block after Location (Local time for {{user}}: {{local_time}}); presets saved before this change keep their blocks, so add one there if you want it. Standard character, scenario, examples, history, and World Info markers are mapped to runtime sources. Scenario/personality formatting and the World Info {0} wrapper are supported.
The import preview adds editable llmcord context markers, including card instructions, local memories, summaries, recent messages, and depth-injected lore. Review these alongside imported ordering. Unknown macros, markers, extension dependencies, generation triggers, and prefills need explicit disabling or remapping before activation. Edit a block's preserved fields to clear unsupported injection_trigger values or set extension to false. A separate checkbox disables an imported assistant prefill.
Anthropic and Google's Gemini endpoint use system instructions at the top level. For imported late or depth-injected system blocks, choose Move to top-level system instructions, Convert late system block to user instructions, or disable the block. The built-in default uses the top-level adaptation and reports it in preview and traces. On Google's OpenAI-compatible endpoint, system blocks are combined into one message in their original system order after budget trimming, so earlier character details and response contracts are preserved. The assembled preview shows this same request.
Export full native bundle preserves all purposes, mappings, revisions' content, and import metadata. Export SillyTavern dialogue preset exports only dialogue and requires a JSON array of explicitly omitted nonportable block IDs, or an equivalent supported marker mapping. Different before/after World Info wrappers cannot be represented by SillyTavern's one global wrapper.
Imported generation settings, models, endpoints, and credentials are retained as source data for export but never applied. Runtime model and sampling configuration remains in config.yaml. Text Completion/Instruct templates, STscript, and the full SillyTavern macro language are unsupported.
In Server setup → Server settings, select a private text channel for avatar assets and choose Save changes. Explicitly deny View Channel to @everyone, then allow the bot to view it, read history, send messages, and attach files. A private channel controls channel access; published images are Discord CDN assets.
Each character starts with neutral, happy, sad, angry, surprised, and embarrassed slots. Labels and descriptions are editable; additional slots use stable lowercase keys. Neutral is required but its image is optional. Other slots may be removed. Emotion images are separate from the static fallback.
In Characters → Fallback static avatar, upload an image; it is saved as soon as the upload finishes. Imported card portraits automatically serve as static fallbacks. This avatar is used when the selected emotion has no usable image, including characters with no emotion images. No asset channel or separate publication is needed for the fallback. Remove fallback avatar clears it; Remove emotion image clears an image without deleting its emotion slot. Both ask for confirmation, as do Remove emotion, Delete preset, and every other delete.
Upload static PNG, JPEG, or WebP images up to 8 MiB and 16 megapixels. Images are normalized to PNG thumbnails no larger than 256 pixels. Uploading an emotion image saves it (with the emotion's current label and description); Save emotion saves label and description edits. Then choose Publish / repair image. Only published usable slots are offered to the model, alongside neutral. Replacing an image retains older published assets for historical messages.
The dialogue model selects one emotion before visible text streams. The bot chooses the avatar before creating its placeholder, and continuation chunks use the same avatar. Headers are removed from saved dialogue, summaries, and extraction inputs. Unknown/malformed headers select neutral; a missing or unavailable emotion image uses the static fallback. With neither image configured, Discord shows the webhook's default icon. Assets are checked on first use after a bot restart; repair deleted assets from the console. Response traces identify whether the avatar came from an emotion image, the static fallback, or Discord's default.
The Monitoring tab shows this server's model usage and, when it is on, the turn log. Both keep entries for the period chosen in Keep usage and log for; older entries are deleted by the daily cleanup.
Usage has a Period (Last 24 hours, Last 7, 14 or 30 days, or This month; periods longer than the server keeps are not offered). A daily chart stacks input and output tokens by model, in the server timezone. Below it, tables break requests, tokens and estimated cost down By model, By channel and By feature (Replies, Ambient turns, Summons, Memory updates, Catch-ups). Calls recorded before this breakdown existed show as "Unknown (before this update)"; a deleted channel shows as "Deleted channel (ID …1234)" with the last four digits of its ID. Memory, summary and image calls made inside a reply turn count under that turn's feature; the background memory update after a turn counts as Memory updates. Tracking persists across restarts; it cannot reconstruct earlier provider usage.
Log lists recorded model calls newest first: time, channel, stage (for example "reply · dialogue" or "reply · failed at dialogue"), model, tokens, and a short preview. Filter by Channel, Errors only, or Reference ID (the code in a failure message, such as 06f1ee; pasting (ref 06f1ee) works too; press Enter to search). Expand an entry to see its full request, response and error, and use Load more for older entries. It is read-only.
The log records nothing until a server admin turns on Keep a turn log. It then records every model call for this server: the director, each speaker, image descriptions, memory extraction, scene summaries and /catchup, plus every failed turn with its reference ID. Before saving, the bot hides known personal facts (as [personal fact hidden]) and removes API keys and tokens, and caps very long text. Limits to know: only exact copies of stored facts are hidden (case aside), so a paraphrase can remain; the memory extraction request still contains the member's own recent messages, which is what the model reads to find facts; and requests contain the channel's recent conversation, as the model saw it. Only this server's admins can see its log. When a turn or command fails and the log is on, the notice members see points admins here and carries the reference ID to search for.
Operators also see one read-only line at the top of this tab, "Bot-wide spending: $X of $Y hard cap this period" (or "$X this period; no hard cap set"), with an Open Bot settings link. Other server admins never see it, and nothing there can be changed from the server page.
Generation status shows the dialogue model and its tracked input-plus-output tokens over a rolling 24 hours for the current server, including director, memory, summary, and image calls using that same profile/model.
The Currency tab manages this server's member currency (see /balance and /admin currency in the server guide). Members earn it with the daily check-in (/daily, off until an amount is set) and will earn and spend it in future minigames; admins can also give and take it.
- Currency name: what this server calls its currency (default "coins"; 1 to 32 characters on one line, without
@,<or markdown symbols). Save it with the bar at the bottom. If it was renamed meanwhile (in another tab, by another admin, or with/admin currency name), the save is refused; reload and try again. - Daily check-in: the amount members get from
/daily(0 turns check-ins off), the extra amount added per streak day, and the number of streak days that bonus keeps growing for (whole numbers; default 0, 0 and 7). The day ends at midnight in the server's timezone, and a missed day starts the streak again. Saved with the bar like the currency name; if the settings were changed meanwhile (another tab or/admin currency daily), the save is refused; reload and try again. - Games: game channels, bets and table talk moved to the Games tab. The Currency tab shows a pointer to it.
- Balances: members with a balance, highest first, 50 per page. Names come from Discord; a member who left shows as "Member …NNNN" (the last digits of their ID). If Discord names cannot be loaded, the tab shows IDs.
- Character wallets: the Character refill cap (a whole number from 0 to 1,000,000,000,000, default 1,000). Each character gets the daily check-in amount once per server day, without the streak bonus, until it reaches the cap; winnings can take it above. When the daily amount is 0 the tab says characters do not refill. Saved with the bar; a change made meanwhile makes the save refuse until you reload.
- Character balances: a table of characters that have a wallet (Character, Balance; richest first, paged like member balances). Characters appear after they first need money. Their ledger entries appear in the Ledger under the character's name (or "Deleted character (#id)"), with the member column used for either kind, and can be reversed like member entries.
- Give or take currency: enter a member ID (or paste a mention), an amount from 1 to 1,000,000 and a reason (required, up to 200 characters). A balance never goes below zero; taking more than a member has is refused.
- Ledger: every change, newest first, with time, member, amount, balance after, reason and admin; Load more shows older entries. Entries are never edited or deleted. To undo a mistake, choose Reverse and give a reason: this adds an opposite entry ("Reverses #N"), and the original shows "Reversed by #M". An entry can be reversed once, and a reversal is refused if it would leave the balance below zero.
Balances and the ledger stay when a member leaves the server. The dashboard audit log records each change's action, member, amount and entry, but not its reason, which stays in the ledger.
The Games tab holds the settings for the minigames, after the Currency tab. Each setting is saved with the bar at the bottom and audited; if it was changed meanwhile (another tab, another admin or /admin games), the save is refused and you reload.
Vault (Experimental)
A section at the end of the Games tab, off by default. It saves with its own bar entries (Vault and Weekly operation). Vault: the enable switch, the allowed protocols (Standard and Open are on by default; Total Match and Position-Only are harder and opt-in), the turn budget per protocol, the reward for solving a personal vault, the partner advice limit per vault, and whether a clue is worded by the deterministic template or by a model. Weekly operation: its own enable switch, the operation channel (a plain text channel), the week start day in the server timezone, the protocol of each of the three stages, the contribution, team and support-mission rewards, and the minimum number of contributions a member needs to qualify for the team reward. Rewards default to 0. Tool prices are set in the Shop tab. See Vault for how members play.
All games
The Smallest bet and Largest bet (whole numbers from 1 to 100,000; the smallest cannot be above the largest; default 1 and 1,000), and the Game channels where members can play /blackjack and /game start. Rounds in the end-of-table summary (whole number from 1 to 20, default 5; saved as "Summary rounds saved", audited as games.summary_rounds) sets how many of the last rounds a closing table message lists. Characters talk at the table (on by default; saved as "Character table talk saved"): on, a character at a blackjack table asks the model for its move and may say a short line, one model call per decision; off, characters play by the house rule (hit below 17) and stay quiet. Character moves (Model picks the move by default, or Play style only; saved as "Character moves saved", audited as games.character_moves): with Play style only, I Doubt It characters never call the model to choose a move and play the policy; blackjack characters keep the house rule, and with table talk on the model is still called but only to add a short line. Table talk in I Doubt It threads still uses the model. Yut characters follow the same two settings: Play style only moves them by their saved play style (aggression and risk), and Model picks the move lets the model choose among the legal moves (a throw needs no model call) with a fallback to the play style. In I Doubt It the saved play style's bluffing and suspicion drive the policy in both modes (which seats are asked, and the fallback play). With Model picks the move, a character that has a saved play style also gets it described in words in the model prompt. Table talk calls per day (whole number from 1 to 10,000, default 100; saved as "Table talk limit saved") caps those model calls per server day (approximately, when several tables play at once; failed calls count too); after that characters play by the house rule and stay quiet until the next day, and the note shows how many calls were used today. A round in which a character cannot move at all is cancelled and every bet is refunded. Bets are saved with the bar ("Bet limits saved"); channels are saved separately ("Game channels saved"). Taking a channel off closes its open table, cancels an unfinished round and refunds the bets; the toast then says how many tables were closed. A game channel that Discord no longer lists shows as its ID so it can be removed. If either was changed meanwhile (another tab or /admin games), the save is refused; reload and try again.
- Cast sees item results (Experimental) (off by default; saved as "Cast sees item results saved", audited as
games.cast_sees_item_resultswith only on or off): on, characters in a channel see the last three item results (8-ball, fortune cookie, dice, tarot and others) used there in the past 30 minutes, as a "Recent happenings" note in their prompt with the item name and result text only. They mention it when asked or when they would join in anyway; it never forces a reply. After a rewind (replying to an older message) only results from before that message are shown. Characters from a world that is not the channel's world (or not linked into its hub) never get it. - Cast watches games (Experimental) (off by default; saved as "Cast watches games saved", audited as
games.cast_watches_gameswith only on or off): on, characters who are not seated at an open game table can see its public state (seats, bets or stacks, visible cards, whose turn, last moves; never anyone's hand, face-down cards or the dealer's hole card) as a "Game table you are watching" note in normal chat, and can answer when asked. In an I Doubt It or Texas Hold'em table thread, a mention or a reply lets the channel's other cast members, and the asker's favorites from the same world, speak; at each key moment (a doubt result, a showdown, the end of the game) one of them may add a single reaction, within the same talk limits as the seated characters. Blackjack has no thread, so its cast only sees the public state. Replying to an older message hides the table note. Characters from a world that is not the channel's world (or not linked into its hub) never get it. Seated characters are unchanged.
Blackjack
- Blackjack is on (on by default; saved as "Blackjack setting saved", audited as
games.blackjack_enabled): off,/blackjackis refused with "Blackjack is off on this server." and the server's open blackjack tables close with every bet refunded; the toast says "Blackjack is off. Closed 1 open table." or "Blackjack is off. Closed N open tables." The tables close in the same step as the switch. The bot repaints a table message closed from the dashboard ("Table closed: blackjack was turned off on this server.") within about 30 seconds (its sweep), and at once when someone presses a button on it. Only on the dashboard;/admin gameshas no switch for it. - Blackjack rules (saved as "Blackjack rules saved", audited as
games.blackjack_ruleswith the full rules): Dealer stands on (16, 17 or 18), Dealer hits soft 17 (a switch, available only when the dealer stands on 17), Blackjack pays (3:2 or 6:5), Ties (Push: bet comes back, or Dealer wins), Insurance and even money and Late surrender (switches). A gray "Table rules line" under the controls previews the line members see on the table. Rule changes apply from the next round; open rounds keep the rules they were dealt with. If the rules were changed elsewhere meanwhile, the save is refused ("The blackjack rules were changed elsewhere. Reload the page and try again.") - Read moves from chat (Experimental) (off by default; saved as "Read moves from chat saved", audited as
games.spoken_moves): on, when it is a member's turn at a blackjack table the bot reads their chat message in that channel and, if it looks like a move, answers with a move check whose buttons only that member can press (for example Hit / Just table talk, or Hit / Stand / Just table talk when unsure). Nothing is played until they press a button, and table talk still reaches characters. English and Korean. The first message it reads loads a small language model (about 0.5 GB of memory) into the bot. Members can turn it off for themselves with/game spoken_moves. See Blackjack. - Decision-model check for spoken moves (Experimental) (off by default; saved as "Decision-model check for spoken moves saved", audited as
games.spoken_veto): on, a message that the local reader takes for a spoken move is sent to OpenRouter (Microsoft Decision-1), a third-party service, to double-check it; the check can only remove or reorder the local reader's moves, never add one. Only the message text and the legal moves are sent, only for members who turned on spoken moves, and only on their own turn. It has no effect until the bot's operator sets it up in the environment. Each member gets a one-line notice before their first message is sent and can opt out with/game spoken_veto off. If the service fails or is slow, the local reader's buttons are used. - Keep phrases for tuning (off by default; saved as "Keep phrases for tuning saved", audited as
games.spoken_keep): works only while Read moves from chat is on. On, a message the bot got wrong is kept: the member pressed Just table talk, picked the second move of an unsure check, or got no check (or a check without that move) and then played the move with a table button. Never from members who turned move checks off. Kept phrases are deleted after the history retention period, and at most 500 are kept per server (oldest first out). Saving it off asks "Turn off keeping?" when phrases are kept, and deletes them all; example phrases stay. - Kept phrases (newest first): the message, what the check offered ("Offered Hit", "Offered Hit or Stand", "No check"), what the member meant, and when. No member is shown. Pick the right move (or Table talk) and press Make example to turn it into an example phrase (audited as
games.spoken_example_addwith the move only, never the text), or Delete it (asks first; audited asgames.spoken_phrase_delete). - Example phrases for this server (up to 200): when a chat message is very close to one of these, the bot uses this example's move instead of its own guess (a Table talk example keeps it quiet). Remove asks first (audited as
games.spoken_example_delete). Example phrases belong to the server and stay when keeping is turned off or a member turns move checks off. If the list was changed elsewhere meanwhile, the action is refused ("The example phrases were changed elsewhere. Reload the page and try again.").
I Doubt It
- I Doubt It is on (saved as "I Doubt It setting saved", audited as
games.doubt_enabled): off,/game start game:doubtis refused and the server's open I Doubt It tables close with every ante refunded; the toast says "I Doubt It is off. Closed 1 open table." or "Closed N open tables." - I Doubt It rules (saved as "I Doubt It rules saved", audited as
games.doubt_ruleswith the full rules): Ante (whole number from 0 to 1,000,000,000; 0 = no ante, the default), Turn timeout (seconds) (15 to 300, default 60) and the Claim rule, shown read-only as "Sequence" (claims follow A, 2 … K, then A again). The doubt window (15 seconds), the deck count (one deck for 3–5 players, two for 6–8) and when the turn message is posted again (after 8 messages) are fixed. Changes apply to tables opened afterwards; an open table keeps the rules it was opened with. If the rules were changed elsewhere meanwhile, the save is refused ("The I Doubt It rules were changed elsewhere. Reload the page and try again.") - Read doubts from chat (Experimental) (off by default; saved as "Read doubts from chat saved", audited as
games.spoken_doubt): on, during the 15-second doubt window after a claim, when a seated member other than the claimer writes something like "liar!", "I doubt it" or "구라" in the table thread, the bot replies with a check whose buttons only that member can press (Doubt it! / Just table talk). Nothing is played until they press, the window does not wait, and the message still reaches characters. English and Korean, by word list only (no language model, nothing kept for tuning). Members can turn checks off for themselves with/game spoken_moves. See I Doubt It.
One Card
- Enabled (saved as "One Card setting saved", audited as
games.onecard_enabled): off,/game start game:onecardis refused and the server's open One Card tables close with every ante refunded. - One Card rules (saved as "One Card rules saved", audited as
games.onecard_ruleswith the full rules): Ante (0 to 1,000,000,000; 0 = none, the default), Turn timeout (seconds), Last card restriction (No restriction, No attack cards, or No attack cards or Jokers: a player cannot finish on those), Spade Ace counts +10 (off: the ♠A is +5), Defense chain (off: an attack can only be answered by drawing), King skips two seats (only while 4 or more players are still in), Bankruptcy with a Bankruptcy limit (15 to 30 cards, default 20; a player who reaches it is out and their cards go back to the draw pile), Call-out penalty (1 to 3 cards, default 1) and Declaration phrases (one per line) (1 to 8 phrases; default 원카드 and One Card). Changes apply to tables opened afterwards. A stale save is refused ("The One Card rules were changed elsewhere. Reload the page and try again."). - Declarations and call-outs are read in the table thread whenever One Card is on; they do not use the Experimental spoken-move switches.
Yut
- Enabled (saved as "Yut setting saved", audited as
games.yut_enabled): off,/game start game:yutis refused and the server's open Yut tables close with every ante refunded. - Yut rules (saved as "Yut rules saved", audited as
games.yut_ruleswith the full rules): Ante (0 to 1,000,000,000; 0 = none, the default), Turn timeout (seconds) (15 to 300, default 60), Back-do (빽도) throw (on by default; off: the marked stick counts as a plain 도) and Pieces per player (2 to 5, default 4). Changes apply to tables opened afterwards. A stale save is refused ("The Yut rules were changed elsewhere. Reload the page and try again.").
Texas Hold'em
- Texas Hold'em is on (saved as "Texas Hold'em setting saved", audited as
games.holdem_enabled): off,/game start game:holdemis refused and the server's open Hold'em tables close with every stack cashed out into wallets; the toast says how many tables were closed. - Texas Hold'em rules (saved as "Texas Hold'em rules saved", audited as
games.holdem_ruleswith the full rules): Small blind (whole number from 1 to 1,000,000, default 5; the big blind is double), Smallest buy-in (big blinds) (20 or more, default 40) and Largest buy-in (big blinds) (1,000 or less, default 200; the smallest cannot be above the largest), Action timeout (seconds) (15 to 300, default 45) and Allow assist (off by default: when on, a table's host may turn on Assist, which shows win odds to the player privately). Changes apply to new tables only; an open table keeps the rules it opened with (except that turning Allow assist off ends Assist at its next hand).
Characters talk at the table and Table talk calls per day above also apply to I Doubt It: they decide whether characters choose their plays and doubts by model and talk in the table thread. See I Doubt It.
By default each completed Discord message has a footer with model, reply input/output tokens, and estimated USD cost. Continuation messages repeat the same reply-level figures; they are not separate model calls. Footers are excluded from saved conversation text and memory extraction. Provider-reported counts include thinking tokens when reported as output. Missing counts and unknown prices display as unavailable rather than being inferred from visible text. Turn the footer off per server with the "Show model and cost footer on replies" switch under Server setup → Server settings (then Save changes); usage is still recorded and the Usage figures are unaffected.
For Google's gemini-3.8-flash endpoint, estimates use standard paid list rates: $0.75/M input, $3.75/M output, and $0.075/M cached input through December 31, 2026, doubling from January 1, 2027 (UTC). These estimates are not billing statements and exclude credits, taxes, and non-token charges. Set a model profile's billing_tier: free for a free-tier estimate, or configure input_cost_per_million, output_cost_per_million, and optional cached_input_cost_per_million in config.yaml. Other models require configured prices. Set stream_usage: false only for compatible backends that reject usage reporting.
Operators are the people who run the bot itself, listed by Discord user ID in LLMCORD_OPERATOR_IDS (see live testing). They are separate from server admins. An operator sees Bot settings in the account menu; for anyone else that page does not exist. Every save there checks the operator list again and is recorded in the audit log.
Bot settings has three tabs: Spending (the caps below), Usage by server and Backend (model profiles, below). Add ?tab=usage or ?tab=backend to open one directly.
Usage by server lists every server's model calls: requests, input and output tokens, estimated USD and calls without a cost estimate, costliest first, with a total row. Choose a Period: This spending period (from the reset day, UTC; the default), Last 7 days or Last 30 days. A server appears by its Discord name when the operator's own Discord account can see it, otherwise as "Server (ID …1234)" with the last four digits; calls with no server show as "No server". Each server keeps usage only for its own period (Keep usage and log for in its Server settings), so this table can show less than the Spending tab. The table is read-only.
Spending caps apply to the whole bot, across every server, and use the same estimated USD cost as the reply footer:
- Soft cap: when spending this period reaches it, each operator gets one Discord DM. Nothing is paused.
- Hard cap: each operator gets one DM, and new character replies, ambient turns, catch-ups and memory updates pause until the reset day or until an operator raises the cap. A turn that already started finishes.
/summonanswers with a private notice; ambient chat stays silent. Mentions and replies stay silent unless Post a notice in the channel is switched on, which posts at most once an hour per channel. - Reset day: the period starts on this day of the month (1–28, UTC). Both caps start off (blank); leave a field blank to turn that cap off.
Calls without a cost estimate (models with no configured price, or a provider that did not report tokens) count as $0, and the panel shows how many there were this period. Set prices in config.yaml to include them. Daily spend totals are kept for 400 days, independent of history retention, so clearing old conversations does not lower the period's spend.
A model profile names a provider, a model and its settings; the three roles (Dialogue, Director, Memory) each use one profile. config.yaml still defines the starting profiles and roles. On the Backend tab an operator can add a profile, edit any profile (editing a config.yaml profile saves a dashboard copy that overrides it: "From: Dashboard (overrides config.yaml)"), and assign the roles. The role selects list profiles, not models (each option reads profile · model, for example google/flash · gemini-3.8-flash), so to give a role another model, add a profile for it first. Reset to config.yaml removes the dashboard copy; Delete removes a dashboard-only profile, which must not be assigned to a role. Changes apply from the next message without a restart; a turn that already started keeps the profiles it began with, including the memory work it starts. The prompt preview follows the same profiles.
API keys are never entered or shown. The API key field takes a reference such as ${OPENAI_API_KEY} to a variable in the bot's environment whose name ends in _API_KEY; the page shows only "set" or "missing" for the dashboard's own environment. Each key may only be sent to its own provider: OPENAI_API_KEY to api.openai.com, ANTHROPIC_API_KEY to api.anthropic.com, and GEMINI_API_KEY or GOOGLE_API_KEY to generativelanguage.googleapis.com, over HTTPS. To use a key with another endpoint, list it in LLMCORD_KEY_HOSTS (see live testing). A profile that breaks this rule is refused when saved, and the bot refuses it again before each call, so a later environment change cannot leak a key. Members then see "The bot's model setup needs attention" with a reference ID; the detail is in the bot log and the turn log. Profiles in config.yaml are trusted as written and are not checked this way. Anthropic profiles always use api.anthropic.com and take no Base URL.
Fetch models in the editor asks the provider for its model list with the form's current settings and fills a searchable Fetched models picker; choosing one sets Model (and fills a blank Context tokens, and Supports images when you have not changed it). You can still type any model ID, providers that don't list their models just say so, and nothing is saved. It uses the same key rules as Test connection and does not follow redirects.
Look up prices (under Advanced, next to the three cost fields) fills Input, Output and Cached input cost per million with suggested prices from a public price list: OpenRouter's own list for an openrouter.ai Base URL, otherwise LiteLLM's, matched on the exact model ID (the provider's own entry first, and an entry belonging to a different provider is never used). A found price replaces all three cost fields (a missing cached price clears that field). It shows the source and date, sends no API key, and changes nothing until you save the profile, so check the numbers first. The bot never looks prices up by itself; it uses only the prices saved on a profile. A model with no price in the list says so and leaves your values alone.
Test connection in the editor sends one short request with the form's current (unsaved) settings from the dashboard process and shows "Connected. replied in N ms." or the provider's error, with any key value removed. It uses the dashboard's environment, is not counted in usage, and gives up after about 20 seconds without retries. A profile without a key is not pinned, so the test (like the bot) will call any address typed into Base URL, including ones only the dashboard's host can reach; only operators can run it. Every save, delete, role change and test is recorded in the audit log with profile and field names only, never values; fetching a model list is recorded with the profile name, the result and the count only, and a price lookup with the profile name, whether the list could be read, whether a price was found and the source. A dashboard profile stores every field, including today's defaults, so it does not follow later changes to the built-in defaults.
The dashboard reads config.yaml when it starts; restart it after editing the file so the Backend tab shows the same profiles as the bot.
The dashboard and the bot speak English and Korean (한국어).
- Dashboard language. Each viewer picks their own under Language in the account menu (top right). The page reloads in that language and remembers it in a cookie for a year. Without a choice it follows the viewer's Discord account language, then the browser's language, then English. It changes only what that viewer sees.
- Bot language (Server setup → Server settings, saved with the bar, audited as
settings.language): the language of the bot's public messages in this server, such as game tables, game updates and notices. Follow each member's Discord language (the default) uses the server's Discord language if it is a Community server, else English. Replies only one member sees always use that member's Discord language. - Never translated: character lines, anything people typed (names, lore, guidelines, prompts), slash command and option names (
/admin space bindstays English), and audit and log text. - Built-in item names and results (dice, 8-ball, fortune cookie, tarot) come in both languages and follow the audience of the message.
Use one web worker. Keep Tailscale Serve forwarding the existing localhost port, including its live Socket.IO/WebSocket traffic. NiceGUI assets are served locally; the dashboard content-security policy uses per-response script nonces and allows the framework's local runtime evaluation. Existing non-dashboard routes retain their policy.
Live events bind to the authenticated session and recheck administrator permission. Upload endpoints additionally validate session binding, CSRF, origin, and body size. Logs and response traces record preset identities, included blocks, omissions, and adaptations rather than complete prompts.
Schema upgrades preserve existing entry IDs, legacy activation keys, avatars, conversations, and traces. Before upgrading an older database, the migration service creates a dated *.pre-vN-*.sqlite3 backup named for the version it upgrades to (for example *.pre-v6-* from v5). Schema 6 (spending caps) and later need the bot and the dashboard upgraded together; schema 7 adds the server switch for /catchup outside character channels; schema 8 records which channel and feature (reply, ambient, summon, memory, catchup) each model call belonged to; schema 9 stores model profiles and role assignments edited on the Backend tab. Keep a manual backup too. Rolling back requires the matching older application and pre-upgrade database; older code must not open the upgraded schema.
Live Discord delivery, real OAuth/Tailscale forwarding, and the ARM64 container still require validation on your private server and target Pi. See verification.