Skip to content

Configuration

Every setting Keryx reads, by the name you set it under. The usual way to set them is keryx setup (a wizard that asks only for what is missing) or keryx config set KEY VALUE; keryx config list shows each one with where its value came from.

Where they live. KERYX_HOME (default $XDG_CONFIG_HOME/keryx, which is ~/.config/keryx, on Linux and macOS alike) holds config.toml for the plain settings and secrets.toml for the credentials, both readable by you alone. A secret is never put on a command line: keryx config set KEY --stdin reads it from standard input, and --from-env VAR from a variable. The PIN is not in either file: it lives beside them in KERYX_HOME/pin, written once (see SECURITY.md). Keep that directory out of a dotfiles repository. What Keryx keeps is in DATA_DIR, STATE_DIR and CACHE_DIR, the XDG data, state and cache directories below.

Which value wins, first to last: the process environment (a systemd Environment=, say), secrets.toml, config.toml, and the default below. Nothing is read from the working directory, so a .env there is ignored.

What the running service may change. Keryx can change some of its own settings — the voice model's set_config tool, or a subagent running keryx config set in a task — and only those marked yes below, until you say otherwise with keryx config unlock KEY or keryx config lock KEY. The ones marked never are protected: every credential, and every setting that is a line of defence (who may call, the approval allowlist, filing issues in public, spending caps, deletion, the network, the debug switches). Nothing unlocks them.

Voice

Setting Default Service may change What it is
OPENAI_API_KEY required (secret) never OpenAI API key with Realtime access. The only setting Keryx cannot start without.
OPENAI_REALTIME_MODEL gpt-realtime-2.1 no The realtime speech-to-speech model a call runs on.
ASSISTANT_NAME Lyra yes What the assistant on the phone is called: the name it answers to and introduces itself by. Lyra and Jarvis each bring a voice of their own; any other name — letters, spaces, apostrophes and hyphens, up to 32 — speaks in OPENAI_VOICE, or marin.
OPENAI_VOICE `` yes The Realtime voice the assistant speaks in. Empty is the assistant's own (ASSISTANT_NAME); a voice the key's organization may not use makes every call fail to open.
OPENAI_TRANSCRIPTION_MODEL gpt-4o-mini-transcribe no Transcribes what the caller says, for the call log.
TRANSCRIPTION_LANGUAGE `` yes The language you speak on a call, as an ISO-639-1 code (en, de, fr), for the call log's transcription — which recall and the memory read. Empty lets the transcriber guess each turn. The voice model itself hears the audio either way.
CLOCK_FORMAT 24h yes How the voice prompt writes the time of day (14:05 or 2:05 PM), and so how the assistant tends to say it.
OPENAI_WEB_SEARCH_MODEL gpt-6-luna no Answers the voice model's own web_search tool, through the Responses API (the Realtime API has no hosted search tool).
VAD_MODE semantic yes How the assistant decides you have finished: semantic waits on whether the sentence sounds finished, so a pause to think does not cut you off; server is a plain silence timer of VAD_SILENCE_MS.
VAD_EAGERNESS medium yes Semantic mode only: low waits longest, high jumps in soonest, auto is medium.
VAD_SILENCE_MS 1200 yes Server mode only: the silence, in milliseconds, that ends a turn.
VAD_THRESHOLD 0.5 yes Server mode only: how loud counts as speech (0 to 1).
VAD_PREFIX_MS 300 yes Server mode only: audio kept from before speech was detected, so the first syllable is not clipped.
NOISE_REDUCTION auto yes Background-noise suppression on what the model hears; auto picks near_field for a phone and far_field for a microphone across the room. Mostly about barge-in: noise mistaken for speech is noise that cuts the assistant off.

Coding agents

Setting Default Service may change What it is
AGENT_BACKEND claude yes The coding agent a task runs on when nobody names one out loud. The running service may switch it only among AGENTS_ENABLED.
AGENTS_ENABLED no Every agent a task may be sent to, comma-separated; blank is AGENT_BACKEND alone. keryx serve refuses a default that is not in here.
SUBAGENT_TIMEOUT_S 10800 yes The longest one subagent run may take, on any agent, in seconds; 0 is no limit.
ANTHROPIC_API_KEY (secret) never Claude, paid per token. Set, it wins over the subscription.
CLAUDE_CODE_OAUTH_TOKEN (secret) never Claude on your subscription, for a machine with no browser: the token claude setup-token prints. Blank uses the stored claude login.
SUBAGENT_MODEL claude-opus-5-5 yes The model a Claude task runs on when none is named.
SUBAGENT_MAX_TURNS 200 no Agent turns one Claude task may take.
SUBAGENT_MAX_BUDGET_USD 10 never Dollars one Claude task may spend: a runaway cap. On a subscription it is the SDK's estimate of what the task would have cost, not a charge.
CODEX_API_KEY (secret) never Codex, paid per token. Keryx logs in with it once, into its own CODEX_HOME; OPENAI_API_KEY is never used for Codex.
CODEX_ACCESS_TOKEN (secret) never Codex on your ChatGPT plan, for a machine with no browser. Blank uses the stored codex login.
CODEX_MODEL yes The model a Codex task runs on; blank is Codex's own default.

Owner, callers and PIN

Setting Default Service may change What it is
OWNER_NAME no What the assistant calls you in the prompts it is handed; blank is "the owner". Only a name: the rest of what it knows about you is its memory.
ALLOWED_CALLERS never Your phone numbers in E.164 (+15551234567), comma-separated. Every other caller is refused. This is the handsets one person picks up, not a guest list: a call Keryx places to any of them reached you.
OWNER_NUMBER never Which of your numbers Keryx rings first; blank is the first of ALLOWED_CALLERS.
KERYX_PIN (secret) never The phone PIN: 6 to 8 digits, asked for before anything is dispatched from a call. Not kept in the configuration files: it lives in KERYX_HOME/pin, written once by keryx setup or by the first call, and this variable, set in the environment, overrides that file. With no PIN anywhere nothing of yours is read out on the phone and every dispatch is refused.
PIN_FAILURE_LIMIT 10 never Wrong PINs, counted across every call, before PIN entry locks for everyone — the right PIN included.
PIN_FAILURE_WINDOW_HOURS 24 never How long a wrong PIN is remembered.
PIN_LOCKOUT_MINUTES 60 never How long PIN entry stays locked once the limit is reached.
BRIEFING_BEFORE_PIN true never Whether a call is handed its standing briefing — unreported results, the memory, project names, briefs, skills — before the PIN. The PIN is the line between reading and acting; off means nothing of yours is said until it is given. Does nothing until a PIN exists.

Phone

Setting Default Service may change What it is
TWILIO_ACCOUNT_SID no Your Twilio account SID (AC…).
TWILIO_AUTH_TOKEN (secret) never Your Twilio auth token.
TWILIO_NUMBER no The Twilio number Keryx answers and calls from, in E.164.
SMS_ENABLED false never Whether Keryx may text at all. Off: many Twilio accounts cannot send SMS in their region, and Slack is the written channel. Outbound calls are unaffected.
PUBLIC_HOST never The public hostname Twilio reaches this machine on, e.g. keryx.example.com: the name routed to your tunnel.
CLOUDFLARE_TUNNEL keryx never The Cloudflare tunnel the service runs beside Keryx on Linux (keryx service install, and scripts/dev.sh in a clone).
HOST 127.0.0.1 never The address the phone server binds.
PORT 8080 never The port the phone server binds.

Google

Setting Default Service may change What it is
GOOGLE_OAUTH_CLIENT_ID no The Google OAuth client id. Blank: read from GOOGLE_CLIENT_SECRETS_FILE.
GOOGLE_OAUTH_CLIENT_SECRET (secret) never The Google OAuth client secret, with the id above.
GOOGLE_CLIENT_SECRETS_FILE no The OAuth client JSON the Google Cloud console downloads (desktop or web shape). Blank: KERYX_HOME/google_client_secret.json, where keryx setup puts it.
USER_GOOGLE_EMAIL no The Google account agents act as; keryx setup fills it in from the sign-in.
GOOGLE_WORKSPACE_MCP false no Give every subagent the workspace-mcp server (send mail, manage the calendar). Needed for Codex, which has no claude.ai connectors; Claude already has them.

Plugin credentials

Setting Default Service may change What it is
SLACK_BOT_TOKEN (secret) never The bot token the send_to_slack plugin posts with (and the PIN-lockout alert).
OPENAI_ADMIN_KEY (secret) never An OpenAI admin key for the check_billing plugin; the ordinary key gets a 401 on the costs endpoint.
ANTHROPIC_ADMIN_KEY (secret) never An sk-ant-admin… key, for what the subagents have cost (check_billing).

Projects and skills

Setting Default Service may change What it is
PROJECTS no Spoken project names for repositories outside PROJECTS_ROOT, as {"name": "/path"}.
PROJECTS_ROOT ~/projects no Where a task with no project starts; each subdirectory is a project you can name. Never created: without it, such a task starts in DATA_DIR/workspace.
SKILLS_DIR $CLAUDE_CONFIG_DIR/skills (~/.claude/skills) never Where the Claude CLI keeps its skills; listed in the voice prompt so the assistant knows what the subagents are good at.
KERYX_CHECKOUT no The Keryx repository on this machine, which a subagent reads when it reports a problem with Keryx. Unset: the checkout Keryx runs from, when it runs from one.
ISSUE_REPORTING false never Whether a bug or a feature request for Keryx, said on a call, may be filed as a GitHub issue by a subagent, with gh. keryx setup asks; only you can turn it on.
ISSUE_REPO wak31415/keryx never The GitHub repository (owner/name) those issues are filed on.

The approval bridge

Setting Default Service may change What it is
APPROVALS_ENABLED true never Whether a Claude Code prompt left unanswered on your screen may become a call. The hook is installed separately (keryx approvals --install-hook).
APPROVAL_ESCALATE_SECONDS 300 no How long a prompt waits on screen before Keryx rings about it.
APPROVAL_CALL_WINDOW_SECONDS 240 no How long the hook keeps waiting after that. The two together are the longest the hook blocks; its timeout in ~/.claude/settings.json must exceed their sum.
APPROVAL_MAX_PER_HOUR 4 no Approval calls per hour, however many prompts pile up.
APPROVAL_QUIET_HOURS yes HH:MM-HH:MM in which it never rings (may cross midnight), in the host's time.
APPROVAL_BASH_ALLOW git push,git commit never The only shell commands a keypad digit may ever run, matched word for word; only git push and git commit may carry arguments (keryx/approvals/policy.py). This allowlist is the primary control: widening it widens exactly what a phone keypad can execute.
APPROVAL_ROOTS never Where a file may be written by phone approval; blank is PROJECTS_ROOT plus every project in PROJECTS.

Limits and retention

Setting Default Service may change What it is
MAX_CONCURRENT_TASKS 3 yes Subagents running at once; the rest queue.
DISPATCH_WAIT_MAX_SECONDS 25 no How long a call waits for a short task to answer inline.
LOCAL_SILENCE_TIMEOUT 30 yes Seconds of silence that end a local session; 0 never does.
MAX_CALL_SECONDS 1800 yes The longest a phone call may run; Keryx wraps up 30 s before. 0 is no limit.
MAX_PHONE_SESSIONS 2 no Phone calls open at once, each one a realtime session being paid for; another hears that the line is busy.
DAILY_TASK_CAP 50 never Tasks dispatched per day.
TRANSCRIPT_RETENTION_DAYS 0 never Delete call transcripts older than this many days; 0 keeps everything.
TASK_RETENTION_DAYS 0 never Delete finished tasks older than this; 0 keeps everything. A result you have not been told about is never deleted.

Service, storage and logging

Setting Default Service may change What it is
DATA_DIR $XDG_DATA_HOME/keryx (~/.local/share/keryx) never Where tasks, transcripts, memory and sign-in tokens are kept, owner-only (0700, files 0600).
STATE_DIR $XDG_STATE_HOME/keryx (~/.local/state/keryx) never Where the logs, the restart record and the approval bridge's socket are kept, owner-only.
CACHE_DIR $XDG_CACHE_HOME/keryx (~/.cache/keryx) never Where what can be downloaded again is kept.
SERVICE_MANAGER auto never What keryx restart asks to restart this process: auto is systemd or launchd, but only for a process actually running under it; none refuses.
SERVICE_UNIT never The systemd unit or launchd label; blank is the installed default (keryx.service, dev.keryx.agent).
REPORT_SECRET (secret) never Signs the /reports/{id} links texted to you; blank generates one into DATA_DIR/report_secret.
LOG_LEVEL INFO yes How much STATE_DIR/logs/keryx.log says.

Development switches

Setting Default Service may change What it is
DEBUG_SKIP_TWILIO_VALIDATION false never Skip Twilio's request signatures, for a machine nothing outside can reach. keryx serve refuses the phone with it on behind a PUBLIC_HOST.
DEMO_MODE false never Try the phone before a coding agent is set up: calls, the PIN and call-backs are real, and every task comes back after a moment with a sample answer that says it was a demo. No agent runs and no agent tokens are spent. keryx serve --demo turns it on for one run.