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.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# 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 |
|
||||
|
||||
```bash
|
||||
./st-export.py && git add -A && git commit # after editing anything in the UI
|
||||
```
|
||||
|
||||
## ⚠ Why in place, and not symlinks into a separate directory
|
||||
|
||||
`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.**
|
||||
|
||||
## Related
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user