Every setting has code behind it. A stored value that nothing reads is a promise
the app does not keep, so the struct in src/config.rs and the key list in
Config::set are the whole surface — anything not below is not a setting.
| Key | Effect | Type / accepted values | Default |
|---|---|---|---|
apps |
Bundle IDs to tap. Empty taps everything the Mac plays. | Comma-separated list; blanks trimmed and dropped | empty |
input_device |
Record the room with a named device rather than the system default. | Device name as ambient config lists it; default or the empty string clears it |
system default |
diarize |
Separate voices once the transcript exists. | true/yes/on/1, or false/no/off/0 |
true |
threshold |
How readily two utterances are called different people. Governs record’s automatic diarization only — a standalone ambient diarize ignores it and takes --threshold. |
Float | 0.5 |
sessions_dir |
Where sessions are written. | Path; default or the empty string clears it |
~/Documents/Ambient |
ask_before_recording |
Wait to be told before recording a call the app noticed. | true/yes/on/1, or false/no/off/0 |
true |
audio_retention_days |
How long the track wavs are kept. 0 deletes them as soon as the transcript exists. |
Unsigned integer, or forever / never to keep them indefinitely |
7 |
assistant |
The live assistant’s on/off switch, also in the status menu as Live Assistant. | Boolean, as above | false |
assistant.*, voice.* |
The assistant’s name, limits on speaking, and voice. | See the live assistant | |
talk.firstmate_home |
The firstmate home that ambient talk sends notes to, through its bin/fm-inbox.sh. Unset, talking is refused. |
Path; default or the empty string clears it |
unset |
Booleans accept nothing outside those eight words: ambient config diarize maybe
is refused and leaves the setting as it was, rather than reading an unrecognised
word as a no. An unknown key is likewise refused by name, and the error lists the keys
that do exist.
Precedence
Environment → CLI flag → config file → default, for every setting read
through Config::load(). So AMBIENT_HOME beats sessions_dir, a --app on the
command line beats the stored apps list, and an existing harness that sets
neither is unaffected.
threshold is the exception, and it is worth knowing before you tune it: the
standalone ambient diarize <dir> never loads the config file at all. It starts
from the built-in 0.5 and only --threshold moves it. ambient config with no
arguments prints the resolved values and says outright when AMBIENT_HOME is
overriding sessions_dir.
sessions_dir is the one setting the GUI will refuse. Changing it while a
recording is running splits one conversation across two folders for nothing —
the capture itself is safe either way, because it writes into the directory it
claimed at the start rather than into whatever sessions_dir says now — so the
settings page says no, in an alert naming the reason, rather than accepting a
click and appearing to have done nothing. ambient config sessions_dir <path>
refuses the same change for the same reason, naming the directory being
recorded into, so the CLI cannot walk past the rule the window enforces.
The file is ~/Library/Application Support/Ambient/config.json — deliberately
not under the sessions folder, since that folder is itself a setting, and a
config that lives inside the thing it configures cannot be found before it is
read. AMBIENT_CONFIG moves the file for tests.
Unknown fields in the file are ignored rather than discarding the known ones, so a config written by a newer build still loads in an older one; a partial file keeps the defaults for everything it omits.
The roster
roster.json sits beside the config — same reasoning, and AMBIENT_ROSTER
overrides its path. It is a plain JSON list of names and nothing else: adding a
name already present is a no-op rather than an error, and the list is kept
sorted case-insensitively.
It stores no voiceprints. An embedding kept to recognise someone later is biometric data under Article 9, a different compliance regime from a text file of names, so the roster removes the retyping and not the choosing — see what is kept.
Behaviour on bad input
A missing config file is the ordinary first run: defaults, silently. A file
that cannot be read, or that is not valid JSON, prints a WARNING naming the
path and the parse error and falls back to defaults rather than failing a
recording. The roster behaves the same way and treats an unreadable list as
empty.
A named input_device that has gone away warns and names the alternatives
before falling back to the system default:
WARNING: no input device named "Iriun Webcam Audio" — recording with the system default instead. Available: MacBook Pro Microphone, …Silent fallback is this project’s recurring failure and the one thing a settings layer must not reintroduce.