Self-hosting
Kiori is one Node.js process and one SQLite file. No other services, no dependencies to install.
Requirements
- Node.js 22.13 or newer (uses the built-in
node:sqlite) - A key for any OpenAI-compatible model endpoint. OpenRouter is the default and gives access to most models.
Configuration (.env in the project folder)
The older PSYCHE_* names are still read, so an existing .env keeps working.
| Variable | Default | |
|---|---|---|
KIORI_LLM_KEY | — | model provider key |
KIORI_LLM_BASE_URL | https://openrouter.ai/api/v1 | any OpenAI-compatible endpoint |
KIORI_ORGANIZER_MODEL | anthropic/claude-haiku-5.5 | writes brains (build, scenes, learning) |
KIORI_AUDITOR_MODEL | anthropic/claude-haiku-5.5 | checks (drafts, sleep) |
KIORI_FALLBACK_MODEL | deepseek/deepseek-v4.1-flash | last attempt when the main model refuses or answers badly |
KIORI_PROVIDER_SORT | throughput | on OpenRouter, pick the fastest provider of a model (price, latency, or empty) |
KIORI_EMBED_MODEL | openai/text-embedding-3-small | recall by meaning; KIORI_EMBED=0 turns it off |
KIORI_PLAYGROUND_MODEL | z-ai/glm-5.3 | the console's playground only |
KIORI_LEARN_EVERY | 20 | events waiting before learning starts by itself |
KIORI_LEARN_HOLD | 4 | newest events that learning on its own leaves for later (they're still being rerolled or edited) |
PORT | 7700 | |
KIORI_HOST | 127.0.0.1 | the address to listen on; 0.0.0.0 in a container or behind a reverse proxy on another machine |
KIORI_PUBLIC_URL | (this machine) | where customers reach this Kiori, e.g. https://api.kiori.io: sign-in links point here |
KIORI_SECRET | (a random key in data/local/secret.key) | seals customers' provider keys in the database; keep it with your backups, a database without it can't open them |
KIORI_PROXY_ALLOW_LOCAL | 0 | 1 lets the drop-in chat endpoint pass requests on to a model on this machine or the local network (otherwise only https addresses on the internet) |
KIORI_DATA | data/local | where the database (kiori.db, or psyche.db on installs from before the rename) and admin.key live |
Commands
node bin/kiori.mjs serve # API + console + docs (bin/psyche.mjs still works too)
node bin/kiori.mjs project create "Studio" # a project and its first key (shown once)
node bin/kiori.mjs key create --project prj_... # another key
node bin/kiori.mjs import ./vault --project prj_... # import a markdown brain vault (character or world)
node bin/kiori.mjs embed --project prj_... # fingerprint imported memories
npm test # unit tests (no model calls)
Data and backups
Everything is in data/local/kiori.db (psyche.db on installs from before the rename) (SQLite, WAL mode). Back it up like any SQLite file. Events are the source of truth: a story's learning can always be rebuilt from them.
What it costs to run
Measured on the default models (Claude Haiku 5.5 through OpenRouter):
| Operation | When | Measured cost | Time |
|---|---|---|---|
| Build a character | once | ~$0.012 | ~1.5 min (background) |
| Place people in scenes | per ~60 events sent without present | ~$0.002 | seconds |
| Learn | per character, per stretch of events they were in | ~$0.0015 | seconds |
| Context (recall) | every turn | one embedding, ~$0.00001 | milliseconds |
| Check a draft | optional, per reply | ~$0.0006–0.002 | a few seconds |
| Sleep | occasionally | ~$0.001 | seconds |
A 100-event story with three characters costs about $0.04 in learning; checking every reply adds about $0.05. Building a cast of ten characters costs about $0.12.
Limits of v1
- One SQLite file: comfortable for thousands of characters and millions of memories on one machine; a hosted multi-tenant deployment would move to Postgres behind the same API.
- Memories are written in English; your chat can be in any language.
- Learning is per story and serialized; many stories learn in parallel.