Security8 of 10
What leaves your machine
On this page
- There is no telemetry
- The services she talks to
- The model provider — always
- ElevenLabs — when voice is on
- Web search — when she searches
- Page reading — when she opens a page
- MCP servers — when you install one
- The Live2D sample model — only if you install it
- The browser — when she uses it
- What stays
- Reading the audit trail yourself
Kotoba runs locally and talks outward. Nothing reaches in unless you deliberately configure it to (the ElevenLabs agent voice path is the one such case, and it is not the default).
There is no telemetry
There is no analytics, no crash reporting, no usage ping, and no version check. This was verified by
searching the whole tree — Python, TypeScript and the frontend — for every common name in that
category. The only match is the packaged build explicitly setting NEXT_TELEMETRY_DISABLED=1 to turn
off the web framework's own default.
There is no route that returns a saved key, and nothing writes a key to a log.
The services she talks to
Every one of these is a service you configured or a tool you asked her to use.
The model provider — always
The conversation goes to whichever provider is active. Two are built in:
| Provider | Endpoint | Key |
|---|---|---|
openai (default) | the OpenAI SDK default endpoint | OPENAI_API_KEY |
xai | https://api.x.ai/v1 | XAI_API_KEY |
KOTOBA_LLM_BASE_URL can point either at an OpenAI-compatible gateway of your own.
What goes with a turn is what an LLM assistant sends: the conversation, her system prompt and soul file, the facts in her long-term memory that are selected for the prompt, tool definitions, and the results of the tools she ran — which includes the contents of any file she read and the text of any page she fetched. If she reads a file, that file's contents reach the provider.
By default, reasoning state is stateless: encrypted reasoning is re-fed each turn rather than
retained by the provider. KOTOBA_LLM_STORE=true changes that, and is only needed for models that
reject encrypted reasoning.
ElevenLabs — when voice is on
Required for voice in the web app. Two modes:
local(default) — everything is outbound. Your backend sends captured audio to ElevenLabs for transcription and sends text for synthesis. No public URL, no tunnel.agent— you delegate turn-taking to their Conversational-AI platform, which means their cloud calls into your/v1/chat/completions. That needs a publicly reachable URL and a bearer. This is the one inbound path in the product and it is opt-in.
The terminal does not need ElevenLabs.
Web search — when she searches
Web search is the model provider's own server-side tool. The search query goes to the provider, which runs the search; there is no separate search-engine key and no separate service. On the default provider, searching means the query reaches OpenAI.
Page reading — when she opens a page
web_extract fetches the URL directly from your machine first, so the site sees your address. If
the direct fetch fails or the site serves a block page, it falls back to a public reader service
(r.jina.ai), which means that service sees the URL she opened.
Installing the optional extractor (pip install kotoba-companion[web]) makes the direct path succeed
far more often; without it, the fallback is reached routinely. kotoba doctor says so when the extra
is missing.
MCP servers — when you install one
Finding a server queries public registries (registry.modelcontextprotocol.io, the npm registry,
PyPI). Connecting a remote MCP server sends your requests to whoever runs it. Connecting a
stdio server runs a process on your machine.
Whatever you hand a server, that server has. A token you give it is a token it holds.
The Live2D sample model — only if you install it
No model ships with Kotoba; they cannot be redistributed. If you install the default sample, the
archive is downloaded from the Live2D sample-data host. Nothing else downloads a model, and
KOTOBA_DEFAULT_MODEL_URL can point elsewhere.
The browser — when she uses it
She drives a real Chromium-family browser on a dedicated profile. Pages she opens are ordinary
browsing traffic from your machine. If you point KOTOBA_BROWSER_CDP at a browser you are already
logged into, she is browsing as you, in your sessions.
What stays
- Conversations and the audit trail live in a local SQLite database. They are never uploaded anywhere; they reach the model provider only as the prompt for a turn you are having.
- What she remembers about you is markdown you can open —
~/.kotoba/memory/USER.mdand the topic files beside it — not a table. - Files she works with stay in
~/.kotoba/files(orKOTOBA_WORKSPACE_DIR). Nothing syncs. - Saved keys stay encrypted in that database. No route returns one.
- Visual keepsakes stay in
~/.kotoba/visual-memory. The screenshots she takes while working are ordinary files, under~/.kotoba/files/screenshots/. - Logs stay in
~/.kotoba/, with the query-string token redacted.
Reading the audit trail yourself
Every tool call that changes something — risk write, exec or network — is recorded locally
with what was asked and what happened. Read it through the audit_log_read view rather than the raw
table, because the view answers the question you actually have:
sqlite3 ~/.kotoba/kotoba.db \
"select created_at, action, risk_kind, approved, authority, detail
from audit_log_read order by created_at desc limit 20"
Two kinds of row exist and the view tells them apart. detail = 'decision' is the approval gate
recording an answer. detail = 'executed' (or executed:failed) is the loop recording that a tool
ran.
The authority column names who permitted it, in words rather than codes:
authority | Meaning |
|---|---|
user | you decided — yes or no |
saved-family / saved-exact-command | a standing grant you gave earlier |
auto-safe / allowlist | it never needed asking |
nobody — the card was shown and expired unanswered, denied | you never saw it in time |
nobody — the card never reached the user (channel failed), denied | no screen could draw it |
none — the loop ran it, nobody was asked | a write or a network call that carries no card |
That last one is the honest label on write_file, patch and the tools that reach a service — they
are recorded, not gated.
Be clear about the other gap: read-risk tools are not recorded at all. Searching, fetching a page, opening a link and reading a file leave no row. What she read is in the conversation, not in the log.
