Configuration#
Every setting is an environment variable. There is no configuration file and no
precedence to learn: a variable that is set wins over the default, and that is
the whole of it. One flag overrides a setting for a single run: add --voice,
which shadows SOMNIA_VOICE and is written on the queue row rather than on the
process, so it survives whichever renderer gets there. serve --host/--port has
no variable behind it at all — the defaults are 127.0.0.1 and 8721, and they
live in the command.
~/somnia.env is the conventional place to keep them, and what the installer
writes and the how-to guides assume. Your shell reads it with source; a
systemd unit reads it with EnvironmentFile=. systemd does no variable
expansion, so write paths out in full: /home/you/library and not
$HOME/library or ~/library.
Read from the environment#
Variable |
Default |
What it is |
|---|---|---|
|
— |
The agent’s model calls. Required by |
|
|
Holds |
|
|
Where rendered chapters are written, and the boundary outside which the server refuses to serve one |
|
|
The Kokoro voice a render uses when the request did not name one. See the voices below |
|
|
The model behind the conversation. |
|
|
How hard that model may think before answering: |
|
|
The sentence embedding model. Must be 384-dimensional |
ANTHROPIC_API_KEY is the Anthropic SDK’s own variable rather than one of ours,
so a key already exported for other tools is picked up without being named
twice.
~ is expanded in the two path settings, so ~/library/audiobooks works when a
shell sets it. Under systemd nothing expands it, which is the trap: see above.
The two that fail quietly#
SOMNIA_LIBRARY_DIR is load-bearing since the page became the player. Get
it wrong and everything starts, the agent answers questions, and every chapter
404s. Chapters are never served by path — the request names a book and a chapter
number and the file comes from the database row — but a row resolving outside
this directory is refused all the same, because a database carried from another
machine can point anywhere. What reaches the phone is that chapter didn’t
arrive; the reason is a warning in the journal. somnia-doctor.sh checks it
directly.
SOMNIA_EMBED_MODEL cannot be changed once anything is indexed. The vector
table is created as float[384], so a model of another width fails outright —
better than the alternative, which is what happens with a different
384-dimensional model: it loads, it indexes, and every distance it produces is
measured against embeddings from the old model. Searching then quietly returns
the wrong passages. Changing it means re-rendering every book.
The voices#
A book is read in one voice and cannot be re-read in another — every timestamp
somnia holds was measured against the audio that was actually produced. So the
voice is chosen per book, when the book is asked for, and written on the
queue row: the page offers a picker on the add press, somnia add --voice names
one, and SOMNIA_VOICE is what a request that named nothing falls back to. The
agent never names one, because nobody chooses a narrator out loud at 2am.
Six are on offer, and the page will accept no others:
Voice |
Sounds like |
|---|---|
|
American, warm and unhurried — the default, and Kokoro’s own highest-graded voice |
|
American, lower and slower |
|
American, a man, even and plain |
|
American, a man, brighter and quicker |
|
British, measured |
|
British, a man, low |
SOMNIA_VOICE and --voice will take any name Kokoro knows, not only these
six — a terminal is a deliberate act and trying af_nicole for a night should
not need a release. A name Kokoro does not know fails when the model loads, with
Kokoro’s own list in the message.
The language is derived from the first letter of the name and must not be set
beside it: b is British English and a is American, and they select different
phonemisers. Until this was fixed every voice was read with an American
phonemiser, which made the two British ones sound like an American impression of
them.
Samples of each live in src/somnia/web/voice/ and are what the picker plays.
They are committed rather than generated at runtime, because the box that serves
the page has no Kokoro on it; scripts/somnia-voices.py, run on the render
host, is what makes them.
Not settable from the environment#
These are fields of Config with no variable behind them. They are code-only
on purpose: each one changes what a render or an index means, so a book
rendered under one value and searched under another is not comparable with
itself, and that is not a thing to leave to a stray line in a unit file.
Setting |
Value |
What it decides |
|---|---|---|
|
120 |
The gap joined between sentences |
|
500 |
The gap between paragraphs |
|
3 |
How many sentences make up one indexed passage |
|
2 |
How far the window moves between passages |
|
|
The encode |
|
4096 |
The ceiling on one model reply |
The first four are the ones that would silently invalidate an index: change the window shape and new passages no longer line up with the old ones, though both sit in the same table answering the same questions.
What each command needs#
Everything opens the database, so SOMNIA_DATA_DIR matters everywhere. Beyond
that, ask and serve need the key; add needs SOMNIA_LIBRARY_DIR and
SOMNIA_VOICE; and serve needs SOMNIA_LIBRARY_DIR to be the same one add
used. The per-command detail is in the
command line reference.
Checking what is actually set#
somnia-doctor.sh loads ~/somnia.env the way a systemd unit would and reports
what somnia’s own configuration loader then sees — which is the question worth
asking, since a variable exported in the shell you are typing in is not
necessarily one the service has.