Hand an in-progress roleplay between two SillyTavern installs — a PC one and a phone one — without either needing to reach the other.
Both installs are local, so when the PC is asleep the phone can't talk to it. This extension parks the character card and the open chat on a small always-on endpoint in between, and picks them up again on the other device.
PC, heading out /stash card + chat go up
phone /stash-pull they land in this install
phone, chat away
phone, heading home /stash updated chat goes up
PC /stash-pull the same chat file is updated in place
The real files, not reconstructions:
- the character's
.png— so the description,first_mes, every alternate greeting and the embedded lorebook come with it, because they live inside the card - the chat's
.jsonl, exactly as it is on disk
A proxied LLM request can't provide this. Artwork is never sent to an API at all, and only the one greeting you picked is sent — the alternates never leave the disk. That's why this runs inside SillyTavern, which has the files.
In SillyTavern: Extensions → Install extension, paste this repo's URL.
Or drop the folder into data/default-user/extensions/stash-sync/.
Then open Extensions → Stash Sync and set:
- Stash URL — where the stash lives. Not shipped with a default on purpose: this repo is public, and the endpoint is ungated, so publishing the address here would publish everything stashed through it.
- This device is called —
pc,phone, whatever. It's the label shown next to an entry so you can tell which install last touched it.
Do this on both installs, same Stash URL, different device names.
/stash |
Push the open character + the chat you're in |
/stash-pull |
Pull the most recent entry into this install |
/stash-pull <name> |
Pull a specific character (or an index) |
/stash-pull <name> tag=AU |
Pick among same-named cards by tag |
/stash-pull <name> [AU] |
Same thing, written next to the name |
/stash-pull <name> force |
Overwrite a local chat that changed since it was stashed |
Both are slash commands, so SillyTavern runs them instead of sending a message:
nothing is added to the chat, no generation fires, no tokens are spent, and the
prompt cache is untouched. Bind /stash to a Quick Reply for a one-tap button.
- Same
Anika.png, different def. If the stash card has a tag the local file does not (fempovvs none), pull imports a new character (Anika1.png) with that def, those tags, and the stashed chat. The Anika already on this install is left alone. - Tags travel with the card and also split stash slots, so two Anika.png
defs can sit in the stash at once.
/stash-pullwith no name still takes the most recent entry./stash-pull Anikawith two Anikas asks fortag=fempov(orAnika [fempov]). - Same file, same tags updates in place — that is the same character on both installs.
- Chat identity is the chat filename — stamped once at creation and never rewritten as messages accumulate. The same conversation keeps one identity; a new or branched chat gets its own.
So /stash twice on one chat overwrites in place and doesn't even re-upload the
card. /stash on a branch adds one chat to that character's existing entry and
does not add a second card.
The stash keeps the 5 most recent characters and 5 most recent chats each.
/stashflushes the chat to disk before reading it, so firing it right after a reply still captures that reply./stash-pullwrites over the same chat file instead of importing a copy, so you don't accumulate… imported.jsonlduplicates. If the local copy changed since it was stashed, SillyTavern refuses rather than overwriting your messages — addforceto override deliberately.- Pulling the chat you currently have open reloads it, so the on-screen copy can't be written back over what was just pulled.
- The target is always taken from the stashed entry, never from whatever you have open — pulling while sitting in a different character's chat cannot touch it.