Configuration

Environment variables, the brains file, run modes, and the YOTHERE_HOME layout.

Beta · access required

yothere is in invite-only beta. The commands below work once you have access.Request access and we'll get you set up.

yothere reads all of its configuration from environment variables through yothere.config.settings. Nothing is required to start: the defaults give a working, single-user, local setup. This page lists the variables that matter, the brains.yaml registry, the two run modes, and how the legacy RELAY_* names still resolve. For the setup flow, see Onboarding; to run it as a service, see Self-hosting.

How configuration resolves

Configuration comes from two layers. A service wrapper first sources $YOTHERE_HOME/relay.env (a mode-600 KEY=value file) into the environment, then the process environment applies on top. settings reads env at import time, and every writable path roots at YOTHERE_HOME (default ~/.yothere, falling back to an existing ~/.relay). Secrets for the voice and brain surface live in a separate mode-600 file, so generated unit files never carry a secret.

Core environment variables

Variable Meaning Default
YOTHERE_HOME writable state root for the whole fleet ~/.yothere (falls back to an existing ~/.relay)
YOTHERE_RUNNER_LABEL the launchd or systemd service label the watchdog keys its heartbeat check on relay-runner
YOTHERE_WORK_DIR the directory the harness runs jobs against (worker file and tool scope); refuses ~/Documents and ~/Desktop current directory
YOTHERE_NOTIFIER needs-eyes ping channel: telegram, command, or none none
YOTHERE_NOTIFY_PY path to the telegram bridge script (used when YOTHERE_NOTIFIER=telegram) unset

The command notifier reads its shell command from YOTHERE_NOTIFIER_CMD (the message is passed as the last argument). The derived roots YOTHERE_DATA_DIR, YOTHERE_STATE_DIR, YOTHERE_LOG_DIR, YOTHERE_THREADS_DIR, and YOTHERE_VAULT_DIR default to subdirectories of YOTHERE_HOME and rarely need overriding.

Voice environment variables

Only the voice surface (the [voice] extra) reads these. GEMINI_API_KEY drives Gemini Live and is shared with the LLM router, so it can live in a shared LLM env file rather than being duplicated. The Twilio credentials live in the voice env file.

Variable Meaning
GEMINI_API_KEY Google AI Studio key for Gemini Live (cockpit voice)
DEEPGRAM_API_KEY legacy STT key, off the call path now, still parsed for back-compat
TWILIO_ACCOUNT_SID Twilio account SID (inbound and outbound phone)
TWILIO_AUTH_TOKEN Twilio auth token
TWILIO_NUMBER the Twilio phone number
YOTHERE_VOICE_PUBLIC_WSS the public wss:// host Twilio dials into (the cloudflared tunnel hostname)
YOTHERE_VOICECALL_BEARER bearer token gating the dangerous surface; when unset, every protected route returns 403 (fails closed)
YOTHERE_TRUST_TAILNET_IDENTITY set to 1 so a browser on your tailnet connects without the bearer in the URL (>= 1.4.0)
YOTHERE_ALLOW_BOB_HARNESS set to 1 to opt in to the bob (headless Claude Code) harness on the voice surface
Heads up The voice server binds 127.0.0.1:8767 by default. If you bind a non-loopback address you MUST set YOTHERE_VOICECALL_BEARER (or run hosted auth), or the server fails closed and doctor reports a hard FAIL. See Self-hosting.

The brains.yaml file

A brain is whatever does the work; yothere drives it over the Brain Protocol. $YOTHERE_HOME/brains.yaml maps names to connection descriptors:

default: local-claude
brains:
  local-claude: { harness: claude }
  remote-brain: { harness: remote, url: "wss://host/brain", token_env: "MY_BRAIN_TOKEN" }

A thread targets a brain by its brain meta key; otherwise YOTHERE_BRAIN, then the file’s default, then local-claude. A remote (non-loopback) endpoint must require a token and should be wss://. Reference the token with token_env: so the secret is not in the file, and treat brains.yaml like an SSH key (do not commit it). yothere init seeds this file only when it does not already exist.

Run modes

YOTHERE_AUTH_MODE selects the deployment mode.

Mode Behavior
off (default) single-user local: no login, a loopback and tailnet carve-out, one YOTHERE_HOME. Byte-identical to the pre-auth cockpit, so local use stays zero-config.
hosted multi-tenant: the cockpit requires a login, each account is scoped to its own per-tenant home, and signup is invite-gated (/signup, then /onboarding).

In hosted mode, mint invite codes from the CLI. Each redemption at /signup creates exactly one account, stamped with the daily cost cap you set:

yothere invite create --uses 1 --budget 5 --email [email protected]

Per-tenant homes currently live at ~/.relay-<tenant> on disk. That path prefix is not covered by the env shim below and is renamed separately from the environment variables, so it stays .relay- for now.

Legacy RELAY_ aliases

Every YOTHERE_* variable also accepts its RELAY_* sibling. An env shim mirrors both prefixes at package import, and again when parsing an env file, so a config keyed either way resolves regardless of which name a reader asks for. When both names are present, the reader’s requested name wins (the shim only fills a name that is unset and never overwrites an explicit value).

# these are equivalent
export YOTHERE_WORK_DIR=~/code/my-project
export RELAY_WORK_DIR=~/code/my-project

New configs should use the YOTHERE_* names. The legacy RELAY_* names stay supported for at least one release.