Kotoba

Where to start

What Kotoba isWho she is and where she runs, in one page
InstallingOne package, two commands
First runA key, a model, her first words
The approval gateHow she asks before she acts
The two voice modesLocal voice, or the agent tunnel
The soul fileChange who she is
RoadmapWhat grows next, and what was cut on purpose

Or a section

↑↓ move openesc close124 pages
All pages

Configuration and operations7 of 9

Backups, and moving to another machine

About 5 minutes to read

On this page

What holds what

What you would missWhere it is
Every conversation, and its full-text indexkotoba.db — tables sessions, turns, turns_fts
Her name, language, voice and chosen avatarkotoba.db — table soul_config
What she calls youkotoba.db — table user_profile
Saved API keys and MCP credentials, encryptedkotoba.db — table saved_keys
Commands you chose to always allowkotoba.db — table approved_commands
Reminderskotoba.db — table cronjobs
The audit trailkotoba.db — table audit_log
The people she has met on Discord, and her notes on themkotoba.db — tables discord_people, discord_person_facts
The key that decrypts the saved keys~/.kotoba/.keystore_keyor 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:

  1. ~/.kotoba in its entirety. That is settings, keystore key, memory, files, models, MCP config, visual memory, plugins — and the database too, on a normal install.
  2. 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 doctor prints 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

  1. Stop Kotoba on both machines.
  2. Install the same way you installed the first time.
  3. Copy ~/.kotoba across, master key included.
  4. 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.
  5. 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 in settings.yaml overrides the environment, so a value you thought came from .env may in fact be in the file you just copied.
  • A path you hard-coded. An absolute DATABASE_URL, KOTOBA_FILES_DIR or KOTOBA_WORKSPACE_DIR from 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.