Configuration and operations7 of 9
Backups, and moving to another machine
On this page
What holds what
| What you would miss | Where it is |
|---|---|
| Every conversation, and its full-text index | kotoba.db — tables sessions, turns, turns_fts |
| Her name, language, voice and chosen avatar | kotoba.db — table soul_config |
| What she calls you | kotoba.db — table user_profile |
| Saved API keys and MCP credentials, encrypted | kotoba.db — table saved_keys |
| Commands you chose to always allow | kotoba.db — table approved_commands |
| Reminders | kotoba.db — table cronjobs |
| The audit trail | kotoba.db — table audit_log |
| The people she has met on Discord, and her notes on them | kotoba.db — tables discord_people, discord_person_facts |
| The key that decrypts the saved keys | ~/.kotoba/.keystore_key — or KOTOBA_MASTER_KEY |
| What she remembers about you | ~/.kotoba/memory/USER.md and ~/.kotoba/memory/topics/*.md |
| Her files | ~/.kotoba/files/ (including .index.json) |
| Image keepsakes | ~/.kotoba/visual-memory/ |
| Your settings, and which toolsets are off | ~/.kotoba/settings.yaml |
| Connected MCP servers | ~/.kotoba/mcp.yaml (their secrets are in the database) |
| Her face | ~/.kotoba/models/ |
| Custom tools | ~/.kotoba/plugins/ |
There is also a memory_facts table in the database. It is legacy: at every startup its rows are
folded into the markdown store, non-destructively and once, and nothing reads it afterwards. Back up
the markdown.
Three columns exist and hold nothing. turns.emotion and turns.tools_used are NULL in every row —
all six insert_turn call sites pass session, role and content only — and sessions.ended_at and
sessions.turn_count are never written either. They are documented rather than dropped, since removing
them would be a migration over live data for no change in behaviour. Do not build a backup check on
them.
The trap
The database and the master key must travel together.
saved_keys.value is not plaintext. It is v1:<base64(nonce + AES-256-GCM ciphertext)>, and the key
that opens it is either KOTOBA_MASTER_KEY or the 32 random bytes in ~/.kotoba/.keystore_key. Copy
the database without the key and every provider key, the ElevenLabs key and every MCP credential become
permanently unreadable. Measured:
encrypt("sk-secret-value") → "v1:…" (63 characters)
decrypt(…) → "sk-secret-value"
# remove ~/.kotoba/.keystore_key
decrypt(…) → None
They are re-entered, never recovered. Nothing anywhere can reverse it.
Two properties make this quieter than it should be, so it is worth naming them:
- A read never creates a key. Generating one while trying to decrypt is how a lost key file turned into a silent, permanent loss: the fresh key made new saves work, so the panel went on listing the old secret as saved while nothing could ever read it again.
- A lost key orphans more than the obvious one. The model key gets its own error, but the same event takes the ElevenLabs key, the other provider's key and every MCP credential with it — and each of those fails somewhere else entirely, as a voice that went quiet or a server that stopped signing in.
kotoba doctor has a saved keys row that names every orphan at once, precisely so you do not fix
one and meet the next by surprise.
If your filesystem is ephemeral
On anything that discards its disk between deploys, set KOTOBA_MASTER_KEY from a secret manager. With
it set, no key file is created at all (measured), so there is nothing to lose. Any string works — it
is base64-decoded when it can be and otherwise hashed to a stable 32 bytes — but treat it as a secret
of the same weight as everything it protects.
Making a backup
Stop Kotoba first. The database runs in WAL mode, so a live copy of kotoba.db alone can be missing the
most recent turns — take -wal and -shm with it, or stop the process and let it checkpoint.
Then copy two things:
~/.kotobain its entirety. That is settings, keystore key, memory, files, models, MCP config, visual memory, plugins — and the database too, on a normal install.api/kotoba.db(and-wal,-shm), if you are on an old clone where the database still sits beside the package. Where everything lives explains how to tell;kotoba doctorprints the path it actually opened.
~/.kotoba/tmp, ~/.kotoba/browser-profile, ~/.kotoba/playwright-output and ~/.kotoba/cli.log are
disposable.
Restore file modes if your archiver dropped them: .keystore_key, kotoba.db and cli.log are 0600.
The database is re-chmoded on every connect, so that one repairs itself.
Moving to another machine
- Stop Kotoba on both machines.
- Install the same way you installed the first time.
- Copy
~/.kotobaacross, master key included. - If the old install kept its database beside the package, now is when you move it into
~/.kotoba/kotoba.db. Kotoba will not relocate it for you: another process may hold it open, and a move that half succeeds costs the history it was meant to protect. - Run
kotoba doctor. It opens the database (which runs the migrations), reports the schema version, and reads every saved secret to check the master key came with it.
Two things do not travel and do not need to:
- Anything in your
.env. Environment variables are configuration of the machine, not of Kotoba. Remember that a setting saved insettings.yamloverrides the environment, so a value you thought came from.envmay in fact be in the file you just copied. - A path you hard-coded. An absolute
DATABASE_URL,KOTOBA_FILES_DIRorKOTOBA_WORKSPACE_DIRfrom the old machine will point at nothing on the new one.
Renaming her
Her name and her language are seeded from soul/default.md on the very first boot and then owned by
the database. Editing the file afterwards does nothing, on any machine. Change them in
Settings → Personality, or from the terminal.
voice_id and avatar_model are in between: the file fills them only while the stored value is empty,
so once you have chosen a voice or a model the file stops overruling you — otherwise every restart would
quietly undo the choice.
Everything else in that file — her personality, how she addresses you, her emotional rules, her tool voice patterns, her quirks — is re-read on every start. Edit those and restart the backend; a running process will not pick them up mid-conversation.
