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

Vision5 of 5

Privacy and deletion

About 6 minutes to read

On this page

An image she remembers is a file on your disk. This page says exactly where, who can read it, what leaves the machine, and how to get rid of one.

Where the bytes are

WhatPathOverride
Keepsakes~/.kotoba/visual-memory/images/KOTOBA_VISUAL_MEMORY_DIR
The keepsake catalog~/.kotoba/visual-memory/index.jsonsame
Screenshots she took~/.kotoba/files/screenshots/<source>/KOTOBA_FILES_DIR
Images you shared at the terminal~/.kotoba/files/shared/same
Clipboard pastes, scratch copy~/.kotoba/tmp/clips/KOTOBA_TMP_DIR

~/.kotoba moves as a whole with KOTOBA_HOME.

A ctrl-v at the terminal writes twice: the raw clipboard bytes into the scratch directory, then a copy into files/shared/. The scratch copy is collectable after 24 hours by a background sweep — but only if the scratch directory was empty when Kotoba was first handed it. Point KOTOBA_TMP_DIR at a folder that already had files in it and nothing there is ever swept, on purpose, so it cannot eat somebody's work.

Images you attach in the browser or on Discord are never written to disk. They sit in the backend's memory for the session and vanish when the process stops — unless she saves one with remember_image, at which point it becomes a keepsake like any other.

Encryption, and who can read them

None of it is encrypted. Kotoba encrypts your API keys in a keystore; it does not encrypt images, their notes, or your files.

Measured on a Linux install with the usual umask:

FileMode
index.json0600 — owner only
images/<id>.<ext>0644 — world-readable
The directories0755 — world-traversable

So the catalog is yours alone, and the pictures are readable by any account on the machine that can reach the path. Any process running as you can read everything. If that matters to you, put ~/.kotoba on an encrypted volume or tighten the mode yourself.

Over HTTP, a keepsake is reachable at GET /api/visual-memory/{id} and a capture at GET /api/files/open?path=…. Both sit behind the /api/* token gate, which is only on when you set a password (KOTOBA_WEB_PASSWORD, or its alias KOTOBA_GATE_PASSWORD). With no password set the gate is open, and what protects those routes is that kotoba serve binds the backend to 127.0.0.1. Expose the port and you have exposed the pictures. The security section covers the gate in full.

The keepsake route takes an id and never a path, so a caller cannot ask it for an arbitrary file — an unknown id and a traversal attempt both come back 404, and the filename read from the index is re-checked against the store before anything opens. A stored SVG is served as application/octet-stream with nosniff, so it cannot run as a document on your origin.

What is in the database

The database holds no images and no keepsake rows. Visual memory is the JSON catalog above, not a table.

What the database does keep about a picture:

  • the turn text. Content is rendered to plain text before it is stored, with an image part collapsed to the marker [image] — never base64. A picture sent with no caption is stored as the sentinel __image_only__. Her own words about what she saw are stored in full, because they are her reply.
  • one audit row per remember_image call: the tool name plus the first 180 characters of its arguments, which is about, note, kind and source. If the source was a data: URL, the first characters of that base64 are in the row as well. view_capture and recall_image are reads and write no audit row.

What leaves your machine

Everything she looks at goes to your model provider. There is no local vision model.

Three requests carry an image out:

  1. The turn. Attachments and tool-result images ride the request as input_image parts.
  2. The recall. recall_image and view_capture read bytes off your disk and put them back into the next request, so a keepsake you saved months ago is uploaded again each time she looks at it.
  3. The caption. A newly filed screenshot is sent once more, at low detail, to be described in one line for the capture log.

The destination is whatever provider you configured — OpenAI or xAI by default, or any endpoint you point base_url at. When the answering model is a reasoning model and reasoning_effort is not off, the request carries store=false, asking the provider not to retain it; setting KOTOBA_LLM_STORE=1 opts back into stored state. With a non-reasoning model no store field is sent at all and the provider's own default decides. Retention beyond that is the provider's policy and nothing in this codebase can enforce it.

Nothing else sends an image anywhere. The events channel that tells your browser a keepsake was recalled carries the id only, never the bytes.

Who can ask her to look

On Discord the answer depends on who is speaking. The guest surface is an allow-list, and the memory family is not on it, so a stranger in a server cannot reach remember_image or recall_image at all. view_capture is owner-only twice over: withheld from the schema list, and refused in words by the tool itself if it is called anyway. The refusal is the tool's, not the model's.

In the browser and the terminal, whoever is at the keyboard is you.

To switch the feature off entirely, turn the tool family off under Settings → Capabilities. Disabling memory removes remember_image and recall_image from every turn; disabling core removes view_capture, along with much else. The prompt is built from the tools the turn is actually offered, so she stops claiming she can do it rather than promising and failing.

Deleting things

A screenshot or a shared image

They are ordinary files in her library.

  • In the browser: the Files panel, delete button, two clicks (there is no undo).
  • Over HTTP: DELETE /api/files/{path} — jailed, no traversal, and idempotent.
  • On disk: delete the file.

The in-memory capture log is not updated by any of these. For the rest of that session she may still see the name, try to open it, and honestly report that she could not.

A keepsake

There is no button and no endpoint for this. The store has a delete function and nothing in the running product calls it. Deleting a keepsake means editing the store by hand:

  1. Stop the backend and the CLI. Writes to the catalog take a cross-process lock; editing it while she is running risks losing the edit or losing other entries.
  2. Delete the image from ~/.kotoba/visual-memory/images/.
  3. Delete its object from ~/.kotoba/visual-memory/index.json.

Deleting the whole ~/.kotoba/visual-memory directory empties the store; it is recreated on the next save.

If you delete the image but leave the row, the result is not a crash and not silence: recall finds the entry, cannot attach the picture, and hands her the note under an instruction to say plainly that she cannot bring it up — and never to describe it as if she were looking. Ask her to save that image again and the orphan row is replaced rather than blocking the save.

Note that the catalog rows are readable text. The about and note you had her write are in plain JSON, capped at 120 and 300 characters, and a summary of the entity names goes into her prompt on every turn while the store is non-empty.