mikkeli 2e23cb64ca Capture world GATING, not just the worlds
A lorebook is inert until something selects it, and SillyTavern gates three ways
-- globally, per character card, and per persona. All of that selection lives in
settings.json, which is untracked, so versioning worlds/*.json alone captured
the content and lost the wiring. A restore would have handed back every lorebook
with no record of which were active or what the token budget was.

_text/world-settings.json now carries the selection and the tuning that decides
how entries fire: budget, scan depth, recursion, whole-word matching, and the
character-vs-global strategy. Non-secret, small, and regenerated by the same
pre-commit hook.
2026-08-12 22:30:54 +09:00

SillyTavern world artifacts

Personas, lorebooks, character cards and presets for the SillyTavern instance on halogen — versioned in place, in SillyTavern's own live data directory.

Tracked What
worlds/ lorebooks / World Info — already JSON, tracked as-is
characters/ character cards, as SillyTavern stores them (PNG)
_text/cards/ the same cards extracted to readable JSON — this is the diff worth reading
_text/personas.json personas, lifted out of settings.json
QuickReplies/, OpenAI Settings/, User Avatars/ JSON presets and persona images
./st-export.py && git add -A && git commit    # after editing anything in the UI

data/ is a bind mount into the SillyTavern container. A symlink from data/default-user/worlds to a host path outside that mount resolves inside the container, where the target does not exist — so it would be a broken link to SillyTavern while looking correct on the host. Bind-mounting each artifact directory would work, at the cost of a mount entry per directory. Tracking in place avoids the whole class of problem.

⚠ The .gitignore is an ALLOWLIST, and that is load-bearing

It denies everything and re-includes named paths. This directory is SillyTavern's live user data, not a curated export: chat logs, caches, thumbnails, and whatever a future version decides to write here all land in it.

secrets.json is the reason. It is empty today, because API keys go through LiteLLM with a placeholder — and it arms itself the moment a key is typed into the UI. This repo is public. Under a denylist that file becomes trackable the first time someone adds a path and forgets it; under an allowlist it is invisible until a human names it.

Also untracked on purpose: settings.json (48 KB of UI state, rewritten on every change — personas are extracted from it instead), chats/ and group chats/ (the sessions themselves), and the 85 stock instruct/context/sysprompt templates, which ship with the app and are inert in Chat Completion mode.

⚠ Cards are PNGs with the JSON inside them

A character card is the PNG spec used as a container: v2 stores base64 JSON in a chara tEXt chunk, v3 in ccv3. So the PNG is what SillyTavern loads, and _text/cards/*.json is what a human reviews. st-export.py regenerates the latter with sort_keys, so re-exporting an unchanged card is a no-op diff.

The export is one-way. Editing _text/ does not change what SillyTavern sees. Re-embedding JSON into a PNG is a real operation but one where a mistake corrupts authored content, so it is deliberately not done implicitly. Edit in the UI, run the script, commit.

The SillyTavern deployment itself — compose.yaml, network posture, the heretic-27b constraints — lives in /home/mikkeli/docker/sillytavern/ and is documented in the halogen roadmap's Phase 16. ⚠ heretic-27b gets no tools, ever: it is an uncensored model, and combining that with execution is the one configuration that turns a writing tool into an incident.

S
Description
Personas, lorebooks and character cards for the halogen SillyTavern instance — versioned in place in its live data directory.
Readme 1.3 MiB
Languages
Python 100%