Configuration

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

Beta · signup open

yothere is in open beta. Create your workspace at app.yothere.ai/signup, your first 7 days are free, no credit card. Then the Founding plan is $20/mo.

yothere reads all of its configuration from environment variables through yothere.config.settings. Nothing is required to start: the defaults give a working 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.

How configuration resolves

Configuration comes from two layers. A service wrapper first sources $YOTHERE_HOME/yothere.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).

Core environment variables

Variable Meaning Default
YOTHERE_HOME writable state root for the whole fleet ~/.yothere (falls back to an existing ~/.relay)
YOTHERE_WORK_DIR the directory the agent runs jobs against (worker file and tool scope); refuses ~/Documents and ~/Desktop current directory
YOTHERE_RUNNER_LABEL the launchd or systemd service label the watchdog keys its heartbeat check on yothere-runner

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.

Agent backends

A worker turn runs on the coding-agent subscription you already have. Two local harnesses ship: claude (the Claude Code CLI) and codex (the OpenAI Codex CLI). They are peers, not a main mode and a lesser one: same containment, same approve-send grant, same resume, same caps. A remote harness reaches anything else that speaks the Brain Protocol.

Variable Meaning Default
YOTHERE_BRAIN the default brain name to resolve from brains.yaml unset
YOTHERE_BRAINS_FILE the named-brain registry $YOTHERE_HOME/brains.yaml
YOTHERE_THREAD_HARNESS per-thread harness when no brain is named: claude, codex, or remote claude
YOTHERE_WORKER_PERMISSION_MODE the machine’s LOCAL permission cap for how much an unattended worker turn may do without asking; the most restrictive of this cap and the cockpit’s per-machine tier wins, and the server can never raise a machine above it. default caps at the read-only (observe) tier, acceptEdits at workspace, bypassPermissions allows full; plan and dontAsk are also accepted, and an unknown value falls back to default. Lower it to scope a machine that holds sensitive data. bypassPermissions
YOTHERE_WORKER_SEND_POLICY outward-send containment for an unattended worker turn. contained stops the worker at a blocked status before any outward action (email, push, message), the text-mode equivalent of showing a draft first. open is the explicit operator opt-out that runs it ungated. contained
Note Even with YOTHERE_WORKER_SEND_POLICY=open, git push and gh stay ungrantable through the approval path: no human approval can mint a grant for them. See the Trust page for the boundary the send-gate does and does not enforce.

Code workspaces

The git repos yothere may work in are registered with yothere workspace add <name> <path>, not configured by env. The registry is workspaces.json next to your threads dir, and it holds paths only: yothere never clones a repo and holds no git credential.

Variable Meaning Default
YOTHERE_WORKSPACES_FILE override the workspace registry path workspaces.json beside YOTHERE_THREADS_DIR

A thread bound to a workspace ignores YOTHERE_WORK_DIR and runs in its own git worktree under $YOTHERE_HOME/worktrees/, on its own branch. Unbound threads keep running in YOTHERE_WORK_DIR exactly as before. See the CLI reference.

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 }
  local-codex:  { harness: codex }
  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.

Codex harness

Codex auth is a one-time login into the CODEX_HOME that yothere owns, not an API key and not your personal ~/.codex (yothere never reads or writes that):

CODEX_HOME=~/.yothere/state/codex-home codex login

yothere doctor warns when that home has no auth.json and prints the exact command for your paths.

Variable Meaning Default
YOTHERE_CODEX_HOME the CODEX_HOME yothere owns: its config.toml (which registers the send-deny gate), its auth, and the session rollouts a resume reads $YOTHERE_STATE_DIR/codex-home
YOTHERE_CODEX_MODEL the model for a codex turn. A ChatGPT account rejects any -codex-suffixed id; valid values are gpt-5.5, gpt-5.4, gpt-5.4-mini gpt-5.5
YOTHERE_CODEX_BIN path to the codex binary. The desktop app keeps it inside its bundle (/Applications/ChatGPT.app/Contents/Resources/codex since ChatGPT.app absorbed Codex.app in mid-2026; /Applications/Codex.app/Contents/Resources/codex on older installs) and does not put it on PATH, so yothere falls back to those locations, newest first codex on PATH, else the desktop app bundle
YOTHERE_CODEX_PRICE_TABLE JSON override for the token-to-USD price table unset
Note A codex turn's cost is an ESTIMATE. Codex reports token counts and no dollar figure, so yothere prices the tokens from a built-in table, and an unknown model bills at the most expensive row. On a ChatGPT subscription the true marginal cost of a turn is zero until the plan's usage limit, so read the figure as a runaway-loop stopper rather than an invoice.

Parallelism

Running several agent turns at once is the point of the product, and both engines ship with the same shape: a pool of work turns, plus a separate pool for typed questions, so a reply never queues behind long work.

Variable Meaning Default
YOTHERE_LEASER_WORKERS concurrent work turns for the BYO leaser (yothere service). Same as --workers. 5
YOTHERE_LEASER_CHAT_WORKERS concurrent chat turns for the leaser, on top of the work pool. Same as --chat-workers; 0 collapses it to a single pool. 2
YOTHERE_MAX_CONCURRENCY concurrent work turns for the local advance engine (python -m yothere.runner loop). 5
YOTHERE_CHAT_CONCURRENCY concurrent chat turns for the local runner, on top of the work pool. 3
YOTHERE_MAX_TURN_CONTINUATIONS consecutive turn-budget overruns before a thread stops and asks for help instead of grinding. A clean turn resets the counter. 20

Cost caps

The runner and the hosted planner both stop advancing turns once a cap is reached. A capped thread comes back to the Inbox as an answerable card, so you can continue it, raise its cap, or stop it.

Variable Meaning Default
YOTHERE_DAILY_COST_CAP_USD daily fleet spend ceiling in USD. A non-numeric value is ignored and the default stands. 10.0
YOTHERE_PER_THREAD_CAP_USD per-thread spend ceiling in USD. A per-thread override lives in the thread’s own meta, and that is what “raise the cap” writes. 10.0

Voice

Voice is a hosted service. You configure nothing and you bring no key: click Connect in the cockpit and the control plane mints a short-lived per-call token for the session. There is no key to set, no phone number, and nothing to install.

The one exception is the loopback dev path, yothere voice --local, which boots a media host on 127.0.0.1:8767.

Variable Meaning
YOTHERE_VOICECALL_BEARER bearer token gating the local media host’s protected routes; when unset, every protected route returns 403 (fails closed)
YOTHERE_ALLOW_BOB_HARNESS set to 1 to opt in to the headless Claude Code harness on the local voice surface. Off by default.
Heads up The local media host 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 yothere doctor reports a hard FAIL.

Output language

The agent’s output language is a per-workspace setting, not an environment variable. Choose English or Español (Latinoamérica) in the cockpit at /settings, and the agent plans, asks, reports, and speaks in it. English is the default for every account, and /settings is the only place the choice is made: there is no auto-detection and no Accept-Language sniffing.

Prose follows your language; the machine contract underneath (thread states, the ask kinds a daemon reads) stays English by design, so a Spanish thread still rings you exactly like an English one.

Run modes

YOTHERE_AUTH_MODE selects the deployment mode.

Mode Behavior
off (default) single-user local: no login, a loopback carve-out, one YOTHERE_HOME. This is the dev and quickstart path.
hosted multi-tenant: the cockpit requires a login, each account is scoped to its own per-tenant home, and new users sign up at /signup, then /onboarding. This is how app.yothere.ai runs.

Signup is open self-serve. In hosted mode an operator can also mint invite codes from the CLI, for example to pre-provision a specific account with a set budget. 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 live at ~/.yothere-<tenant> under YOTHERE_TENANTS_ROOT (default: the parent of YOTHERE_HOME). This is an implementation detail of hosted mode, not something you set on your own machine.

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.