Mikkeli 00f5e2fa72 SillyTavern world artifacts, versioned in place
Tracked inside SillyTavern's live data directory rather than symlinked out of
it. data/ is a bind mount into the container, so a symlink to a host path
outside that mount resolves inside the container where the target does not
exist -- broken to SillyTavern while looking correct on the host.

The .gitignore is an allowlist, denying everything and re-including named paths,
because this is live application data rather than a curated export. secrets.json
is the reason: 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. A denylist protects only the paths someone remembered.

Two of the four artifact types are not diffable as stored. Character cards are
PNGs with the card JSON in a tEXt chunk, and personas live inside a 48 KB
settings.json that SillyTavern rewrites on any change. st-export.py extracts
both into _text/ so the reviewable artifact is text, one-way on purpose --
re-embedding JSON into a PNG is where a mistake corrupts authored content.

Stock content is excluded: Seraphina's expression pack is 3.6 MiB of PNG the app
regenerates on startup. The stock lorebook Eldoria.json stays -- 8 KB of
readable JSON is a useful worked example of the format.
2026-08-12 20:59:32 +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%