arizuko › reference › Environment variables
environment variables
The environment variables of the arizuko daemons: the file and line that read each one, the type, the default and the effect. The last section names the variables that the code reads and that this page does not describe. Test-only variables are left out.
chanlib.EnvOr(k, d) returns d when the variable is empty or unset. chanlib.MustEnv(k) calls os.Exit(1) when the variable is unset. chanlib.EnvDur(k, d) reads a Go duration such as 30s or 5m, and returns d when the value is empty or does not parse. The code default of an adapter LISTEN_ADDR differs for each daemon, but every service fragment in template/services/ sets :8080. The numbers 9001 to 9010 below are the source defaults for local development with several daemons.core — consumed by every Go daemon via core.LoadConfig
These are read in core/config.go through chanlib.EnvOr / EnvInt / EnvDur and the resolveTimezone() helper. Every daemon that calls core.LoadConfig() (routd, onbod, webd, …) inherits this surface.
API_PORT
Listening port for routd's HTTP API.
| Read by | core / core/config.go:256 → routd |
|---|---|
| Type | int |
| Default | 8080 |
| Example | 8080 |
ARIZUKO_DEV
Dev mode — shares ant/src into every turn's guest for live-reload of the in-guest runner.
| Read by | runed / runed/mounts.go:94 |
|---|---|
| Type | bool ("true" or "1") |
| Default | unset = production mode |
| Example | 1 |
ASSISTANT_NAME
Bot display name and mention trigger; also baked into the data-dir basename and compose container_name. Must contain no whitespace or shell metachars.
| Read by | core / core/config.go:218, webd / webd/main.go:46, teled / teled/main.go:97, discd / discd/main.go:88, whapd / whapd/src/main.ts:45 |
|---|---|
| Type | string |
| Default | arizuko in core and routd; assistant in webd; empty in teled, discd and slakd |
| Example | arizuko |
AUTH_BASE_URL
Public base URL used to build OAuth redirect callbacks and onboarding invite links. HTTPS prefix toggles secure-cookie flag.
| Read by | core / core/config.go:231 |
|---|---|
| Type | URL |
| Default | "" |
| Example | https://demo.example.com |
AUTH_SECRET
Keys the OAuth CSRF state of authd and dashd, and switches the auth of proxyd on. It signs no cookie and seals no stored value: the refresh cookie of authd is an opaque token stored as a sha256 digest, access tokens are ES256, and SECRETS_KEY seals the secrets. arizuko create writes a random value.
| Read by | core / core/config.go:229 |
|---|---|
| Type | string (hex/random) |
| Default | "" |
| Example | a3f1...c0de |
CONTAINER_TIMEOUT
Per-turn guest ceiling, in milliseconds. It is an input to the one deadline, not a timer of its own: runed/manager.go turnTTL takes the lower of RUNED_RUN_TIMEOUT and this value, and a group's own container_config timeout overrides it for that folder. The name kept its Docker-era spelling; what it bounds is a KVM guest.
| Read by | core / core/config.go:225 → runed |
|---|---|
| Type | int (milliseconds, via EnvDur) |
| Default | 3600000 (60 min) |
| Example | 1800000 |
DATA_DIR
Root of this instance's data directory (store/, groups/, ipc/, web/). When set in compose, also passes through to dashd and several adapters as their working/storage root.
| Read by | core / core/config.go:161, dashd / dashd/main.go:167, teled / teled/main.go:89, whapd / whapd/src/main.ts:46 |
|---|---|
| Type | path |
| Default | cwd (core); /srv/data/<daemon> per adapter |
| Example | /srv/data/arizuko_demo |
DEX_ISSUER
OIDC issuer URL of a Dex instance. With DEX_CLIENT_ID it mounts /auth/dex on authd: Dex authenticates the person against its connectors, authd reads federated_claims off the ID token and mints the session for <connector_id>:<user_id> — the principal the native callback for that provider would mint. The folder claim and scope snapshot stay authd's. Unset leaves the login page as it is.
| Read by | authd / core/config.go |
|---|---|
| Type | URL |
| Default | "" |
| Example | https://auth.example.com/dex |
DEX_CLIENT_ID
The static client id authd presents to Dex; the ID token's audience must equal it. Pair with DEX_CLIENT_SECRET for a confidential client; a public client with PKCE needs no secret.
| Read by | authd / core/config.go |
|---|---|
| Type | string |
| Default | "" |
| Example | arizuko |
DEX_CLIENT_SECRET
Secret for DEX_CLIENT_ID, sent on the code exchange. Optional.
| Read by | authd / core/config.go |
|---|---|
| Type | string |
| Default | "" |
| Example | a-long-random-secret |
DISCORD_CLIENT_ID
OAuth client id for the Discord login provider in proxyd's auth flow (distinct from the bot token used by discd).
| Read by | core / core/config.go:234 |
|---|---|
| Type | string |
| Default | "" |
| Example | 1234567890 |
DISCORD_CLIENT_SECRET
OAuth client secret for the Discord login provider.
| Read by | core / core/config.go:235 |
|---|---|
| Type | string |
| Default | "" |
| Example | abc...def |
GITHUB_ALLOWED_ORG
If set, restricts GitHub OAuth sign-in to members of this org.
| Read by | core / core/config.go:239 |
|---|---|
| Type | string |
| Default | "" |
| Example | kronael |
GITHUB_CLIENT_ID
OAuth client id for the GitHub login provider.
| Read by | core / core/config.go:232 |
|---|---|
| Type | string |
| Default | "" |
| Example | Iv1.abc123 |
GITHUB_CLIENT_SECRET
OAuth client secret for the GitHub login provider.
| Read by | core / core/config.go:233 |
|---|---|
| Type | string |
| Default | "" |
| Example | ghs_xxxxx |
GOOGLE_ALLOWED_EMAILS
CSV allowlist of email addresses permitted to complete Google OAuth.
| Read by | core / core/config.go:238 |
|---|---|
| Type | CSV string |
| Default | "" |
| Example | alice@example.com,bob@example.com |
GOOGLE_CLIENT_ID
OAuth client id for the Google login provider.
| Read by | core / core/config.go:236 |
|---|---|
| Type | string |
| Default | "" |
| Example | abc.apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET
OAuth client secret for the Google login provider.
| Read by | core / core/config.go:237 |
|---|---|
| Type | string |
| Default | "" |
| Example | GOCSPX-xxx |
HOST_APP_DIR
Path on the host that contains the arizuko binaries and ant/src; used when bind-mounting the dev runner.
| Read by | core / core/config.go:46, compose / compose/compose.go:66 |
|---|---|
| Type | path |
| Default | directory of the running executable |
| Example | /home/operator/app/arizuko |
HOST_CODEX_DIR
The operator's own codex login state, so the oracle skill can reuse it. Two files are overmounted read-only into the guest's /home/node/.codex/ — auth.json and config.toml — not the whole directory. The overmount carries the host file's owner and mode, so a credential at 0600 owned by the operator is unreadable to the agent's uid and codex reports "not logged in"; runed names the offending file in its log. compose also gives runed its own read-only mount of this path, at the same absolute path, because runed can share in only what runed can open.
| Read by | core / core/config.go:253 → runed/mounts.go buildMounts |
|---|---|
| Type | path |
| Default | "" (mount disabled; falls back to the CODEX_API_KEY/OPENAI_API_KEY env-profile keys) |
| Example | /home/operator/.codex |
HOST_DATA_DIR
The data dir as the host sees it, when that differs from the path the daemon sees. It does not reach a turn: runed does not read it, because virtiofsd is runed's own child in runed's own mount namespace, so a mount source is the path runed sees and is never translated (runed/mounts.go buildMounts). compose emits it to every daemon, and the one production reader is proxyd, which uses it as the directory for its audit files (proxyd/main.go → audit.LoadConfig).
| Read by | core / core/config.go:215 |
|---|---|
| Type | path |
| Default | DATA_DIR |
| Example | /srv/data/arizuko_demo |
IDLE_TIMEOUT
The floor under the guest ceiling, in milliseconds. Nothing is kept around to recycle: a guest is powered off at the end of its turn. What this does is stop CONTAINER_TIMEOUT cutting in before the idle timer has given up — turnTTL raises the guest ceiling to this value plus a 30s grace whenever it sits below (runed/manager.go turnTTL, runed/runtime.go containerGracePeriod).
| Read by | core / core/config.go:226 → runed |
|---|---|
| Type | int (milliseconds) |
| Default | 3600000 (60 min) |
| Example | 600000 |
MAX_CONCURRENT_CONTAINERS
How many guests may run at once before runed queues the next spawn. It is also the size of everything a guest draws from, so runed refuses to start when the numbers do not fit. This many guests at RUNED_GUEST_MEM must fit the host's MemTotal; RUNED_TAP_SUBNET must hold this many /30s; the CID window starting at RUNED_VSOCK_CID_BASE is this wide (runed/qemuhost.go qemuPreconditions, runed/vmrecord.go allocateCID). routd reads the same value as its own dispatch cap, so the two cannot drift.
| Read by | core / core/config.go:227; routd / routd/cmd/routd/main.go:178 |
|---|---|
| Type | int |
| Default | 5 |
| Example | 10 |
MAX_TURN_RETRY
How many times routd runs a turn again when its guest stopped without any bot message: SIGKILL, out of memory, or the RUNED_RUN_TIMEOUT limit. routd does not advance the failed batch, so the next poll gives it to a new guest with a <system-note> that names the retry number and asks the agent to use fewer resources. A turn that already replied is never retried, also not in part. -1 turns retries off. 0 and an unset value both mean 3. The setting applies to the whole instance, with no override for a folder. See retries.
| Read by | routd / routd/cmd/routd/main.go:214 |
|---|---|
| Type | int |
| Default | 3 |
| Example | 5 |
MEDIA_ENABLED
Enables inbound media download and forwarding to the agent. Adapters fall back to URL-only references when off.
| Read by | core / core/config.go:258 |
|---|---|
| Type | bool ("true") |
| Default | false |
| Example | true |
MEDIA_MAX_FILE_BYTES
Per-attachment byte cap on inbound media; adapter ProxyFile streams up to this size before truncation.
| Read by | core / core/config.go:259, teled / teled/main.go:100, discd / discd/main.go:89, emaid / emaid/main.go:81, slakd / slakd/main.go:107 |
|---|---|
| Type | int (bytes) |
| Default | 20971520 (20 MiB) |
| Example | 52428800 |
ONBOARDING_ENABLED
Master switch for the onboarding flow. Affects both routd behaviour and whether compose generates the onbod service.
| Read by | routd / routd/cmd/routd/main.go:203, onbod / onbod/main.go:60, compose / compose/compose.go:747 |
|---|---|
| Type | bool (core: "true"; onbod: "0" disables) |
| Default | false |
| Example | true |
ONBOARDING_PLATFORMS
CSV of platforms (e.g. telegram,discord) for which onboarding admissions are enabled.
| Read by | routd / routd/cmd/routd/main.go:204 |
|---|---|
| Type | CSV string |
| Default | "" |
| Example | telegram,whatsapp |
OBSERVE_WINDOW_MESSAGES
How many trailing #observe-mode messages routd folds into context when a trigger turn fires. Per-route override via routes.observe_window_messages.
| Read by | routd / routd/cmd/routd/main.go:187 |
|---|---|
| Type | int |
| Default | 10 |
| Example | 25 |
OBSERVE_WINDOW_CHARS
Character cap on the #observe context window for a single trigger turn. Per-route override via routes.observe_window_chars.
| Read by | routd / routd/cmd/routd/main.go:188 |
|---|---|
| Type | int (chars) |
| Default | 4000 |
| Example | 8000 |
ENGAGEMENT_TTL
How long a (chat, topic) stays “engaged” after the bot’s last reply. Engaged pairs fire a turn on every message from a person without needing a fresh @mention, and override #observe routing. Reactions, edits and CI events follow their route. Each bot reply pushes the deadline out again; user messages do not. Explicit 0 disables automatic opening and renewal of windows. Existing windows expire or end with disengage. See engagement.
| Read by | routd / routd/cmd/routd/main.go:234 |
|---|---|
| Type | duration (e.g. 10m, 30m) |
| Default | 30m |
| Example | 30m |
SECRETS_KEY
AES-256-GCM key for the secrets table — secrets are encrypted at rest. Required: routd refuses to start without it, and there is no plaintext mode and no AUTH_SECRET fallback. The value is a comma-separated keyring: the first key seals new writes, the rest are decrypt-only, so a value sealed under an old key still reads. To rotate, put the new key first and keep the old one after it; re-write each secret so the new key seals it; remove the old key only after every row is re-written. A v2: value that no key opens is an error, never plaintext. arizuko create generates a random key into the new instance’s .env; the arizuko secret CLI uses the same keyring. Stored values carry a v2: prefix; a row without it is read back as stored, and arizuko secret seal-legacy seals such rows in place.
| Read by | core / core/config.go:264 → routd, arizuko secret CLI |
|---|---|
| Type | string (comma-separated keyring; first = active seal key) |
| Default | none — routd exits if unset; arizuko create generates one |
| Example | 9d4f...new,2a17...retired |
SEND_DISABLED_CHANNELS
CSV of channel names where outbound sends are dropped (read-only mode).
| Read by | routd / routd/cmd/routd/main.go:157 |
|---|---|
| Type | CSV string |
| Default | "" |
| Example | slack,discord |
SEND_DISABLED_GROUPS
CSV of group folders where outbound sends are dropped (read-only mode).
| Read by | routd / routd/cmd/routd/main.go:254 |
|---|---|
| Type | CSV string |
| Default | "" |
| Example | solo/inbox,corp/eng |
TELEGRAM_BOT_TOKEN
Bot token for the Telegram channel adapter.
| Read by | teled / teled/main.go:93 (MustEnv) |
|---|---|
| Type | string |
| Default | "" (teled refuses to start) |
| Example | 12345:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 |
TTS_BASE_URL
OpenAI-compatible /v1/audio/speech base URL the agent's send_voice path hits.
| Read by | routd / routd/cmd/routd/main.go:259 |
|---|---|
| Type | URL |
| Default | http://ttsd:8880 |
| Example | http://ttsd:8880 |
TTS_ENABLED
Enables TTS rendering of agent replies as voice messages.
| Read by | routd / routd/cmd/routd/main.go:258 |
|---|---|
| Type | bool ("true") |
| Default | false |
| Example | true |
TTS_MODEL
Default model name passed in the TTS request body.
| Read by | routd / routd/cmd/routd/main.go:261 |
|---|---|
| Type | string |
| Default | kokoro |
| Example | kokoro |
TTS_TIMEOUT
Timeout for one request to the TTS backend, as a Go duration such as 15s. A bare number does not parse, and routd then uses the default.
| Read by | routd / routd/cmd/routd/main.go:262 |
|---|---|
| Type | duration, Go syntax |
| Default | 15s |
| Example | 30s |
TTS_VOICE
Instance-default voice id; agent may override per-call via send_voice args or per-group via PERSONA.md frontmatter.
| Read by | routd / routd/cmd/routd/main.go:260 |
|---|---|
| Type | string |
| Default | af_bella |
| Example | af_sky |
TZ
IANA timezone for log timestamps, cron evaluation in timed, and Config.Timezone consumers.
| Read by | core / core/cron.go:24, runed / runed/render.go:103, compose / compose/compose.go:65 |
|---|---|
| Type | string (IANA name) |
| Default | UTC |
| Example | Europe/Bratislava |
VIDEO_TRANSCRIPTION_ENABLED
Enables Whisper transcription of inbound video.
| Read by | core / core/config.go:261 |
|---|---|
| Type | bool ("true") |
| Default | false |
| Example | true |
VOICE_TRANSCRIPTION_ENABLED
Enables Whisper transcription of inbound voice messages.
| Read by | core / core/config.go:260 |
|---|---|
| Type | bool ("true") |
| Default | false |
| Example | true |
WEB_HOST
Public hostname the instance serves; used to build chat-link, webhook, and onboarding URLs in agent prompts.
| Read by | core / core/config.go:230 |
|---|---|
| Type | string |
| Default | "" |
| Example | demo.example.com |
WHISPER_BASE_URL
Base URL of the Whisper-compatible transcription endpoint.
| Read by | routd / routd/cmd/routd/main.go:224 |
|---|---|
| Type | URL |
| Default | http://localhost:8080 |
| Example | http://whisper:9000 |
WHISPER_MODEL
Model name passed to Whisper.
| Read by | core / core/config.go:262 |
|---|---|
| Type | string |
| Default | turbo |
| Example | large-v3 |
agent spawn — what runed forwards into the turn's guest
ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, OPENAI_API_KEY, and CODEX_API_KEY pick which model a turn talks to, so they are user-scoped credentials, not folder secrets — the store rejects them at folder scope. A user sets their own in a dedicated dashboard section at /dash/me/env, and that value overrides the operator default for their spawns. The host .env value below is the instance-wide default forwarded when a user has set nothing. See secrets. With the litellm package the forwarding stops: the gateway reads the credential from .env and the guest gets MODEL_GATEWAY_KEY instead.ANTHROPIC_API_KEY
Instance default forwarded into every turn's guest so Claude Code can authenticate, unless the triggering user has set their own at /dash/me/env.
| Read by | runed / runed/secrets.go mergeSecrets |
|---|---|
| Type | string |
| Scope | user (env-profile key; rejected at folder scope) |
| Default | not forwarded if unset |
| Example | sk-ant-... |
CLAUDE_CODE_OAUTH_TOKEN
Alternative auth for Claude Code; forwarded into the guest with ANTHROPIC_API_KEY, under the same user-override rule. runed forwards every env-profile model key the same way: also ANTHROPIC_OAUTH_TOKEN, OPENAI_API_KEY and CODEX_API_KEY. When MODEL_GATEWAY_URL is set, runed withholds all of them, and the guest gets MODEL_GATEWAY_KEY.
| Read by | runed / runed/secrets.go mergeSecrets |
|---|---|
| Type | string |
| Scope | user (env-profile key; rejected at folder scope) |
| Default | not forwarded if unset |
| Example | cc-oauth-... |
LOG_LEVEL
Sets the stderr slog level (debug < info < warn < error) for every daemon, applied by obs.Setup at startup. In runed, debug/trace writes the guest's whole envelope (mounts, exit, stderr) beside the group's other logs after every turn, not only the summary a clean turn gets.
| Read by | every daemon via obs.Setup; turn-log verbosity / runed/turnlog.go |
|---|---|
| Type | string (debug/info/warn/error/trace) |
| Default | info |
| Example | debug |
chanreg
CHANNEL_REGISTER_ALLOW_PUBLIC
Dev override that lets channel adapters register with public IPs. By default chanreg rejects non-private adapter URLs as SSRF risk.
| Read by | chanreg / chanreg/chanreg.go:326 |
|---|---|
| Type | bool ("1") |
| Default | unset = enforce private-only |
| Example | 1 |
arizuko CLI (cmd/arizuko)
ARIZUKO_DATA_DIR
Override the base directory used to construct instance data paths. When set, instance dir is $ARIZUKO_DATA_DIR/arizuko_<name>.
| Read by | cli / cmd/arizuko/main.go:220 |
|---|---|
| Type | path |
| Default | "" (falls back to PREFIX) |
| Example | /var/lib/arizuko |
PREFIX
Filesystem prefix for instance data dirs; final path is $PREFIX/data/arizuko_<name>.
| Read by | cli / cmd/arizuko/main.go:222 |
|---|---|
| Type | path |
| Default | /srv |
| Example | /srv |
arizuko run (compose generation)
Read by compose/compose.go at arizuko run time when it generates the docker-compose YAML. These are not consumed by any running daemon — only by the generator that decides which services to emit, which ports to expose, and which proxyd routes to gate.
DAV_PORT
Host port to expose for davd. Empty ⇒ davd not exposed at the docker level.
| Read by | compose / compose/compose.go:1415 |
|---|---|
| Type | string (port) |
| Default | "" |
| Example | 8096 |
COMPOSE_PROFILES
Comma-separated docker compose profiles that decide which optional daemons run. Generated, not hand-set: activeProfiles derives it from the instance's feature flags (WEB_PORT → web, WEBDAV_ENABLED → davd, ONBOARDING_ENABLED → onbod; timed is always on) and writes it into the compose-managed block of .env. Every optional service fragment carries a matching profiles: [<name>], so this list is the single gate docker reads.
| Read by | docker compose, written by compose/compose.go:734 |
|---|---|
| Type | CSV string |
| Default | timed (always on) |
| Example | davd,onbod,timed,web |
WEB_PORT
Host port the proxyd service is published on; the public entry point for the instance.
| Read by | compose / compose/compose.go:738 |
|---|---|
| Type | string (port) |
| Default | 8095 |
| Example | 443 |
WEB_PORT_ALIASES
CSV of additional host ports also published for proxyd (e.g. expose 80 alongside 443).
| Read by | compose / compose/compose.go:1385 |
|---|---|
| Type | CSV string |
| Default | "" |
| Example | 80,8080 |
WEBDAV_ENABLED
Whether to emit the davd service and gate its /dav/ proxyd route.
| Read by | compose / compose/compose.go:742, and as the route gate at compose/compose.go:603 |
|---|---|
| Type | bool ("true") |
| Default | true |
| Example | false |
routd
Apart from HITL_WAIT and the PROACTIVE_* set below, routd takes every value from core.LoadConfig above. Egress isolation is not a setting on routd or anywhere else: runed starts one egress proxy per turn and refuses to start if the binary is missing (RUNED_EGRESS_BIN).
The PROACTIVE_* vars tune proactive interjection — the agent speaking without being addressed. They are read once at startup by routd.LoadProactiveConfig, called from routd/cmd/routd/main.go. All of them do nothing unless PROACTIVE_ENABLED is on. Per-group opt-in is separate: a proactive: block in the group’s CLAUDE.md frontmatter, not an env var.
HITL_WAIT
How long a held tool call waits for a human verdict before it closes as expired and the agent hears NOT RUN. Keep it under RUNED_RUN_TIMEOUT: past that the guest is killed and the wait ends as a dead turn instead of an answer. See holding a call for a human.
| Read by | routd / routd/pending_actions.go:437 |
|---|---|
| Type | duration (Go syntax) |
| Default | 10m |
| Example | 15m |
PROACTIVE_ENABLED
Master switch for proactive interjection. Unset or false and routd builds no scanner at all — the whole feature is absent, not idle. No shipped template sets it, so every instance is off until an operator opts in. Turning it on starts the scan; each group still has to declare mode: lurk before anything fires.
| Read by | routd / routd/proactive.go:36 |
|---|---|
| Type | bool (1, true, yes, on) |
| Default | unset — off |
| Example | true |
PROACTIVE_SCAN_INTERVAL
Minimum gap between proactive sweeps. The sweep is driven by routd’s own loop, not a timer, so a long turn simply delays the next one — missed ticks are dropped, never replayed as a burst.
| Read by | routd / routd/proactive.go:37 |
|---|---|
| Type | duration (Go syntax, e.g. 30s, 2m) |
| Default | 30s |
| Example | 1m |
PROACTIVE_SILENCE_MIN
How long a chat must have been quiet before an interjection is considered. Below this the conversation is still live and cutting in is rude. Measured from the last real inbound — bot replies and prior proactive messages do not reset the clock.
| Read by | routd / routd/proactive.go:38 |
|---|---|
| Type | duration |
| Default | 90s |
| Example | 5m |
PROACTIVE_SILENCE_MAX
Upper edge of the same band. Past this the chat is dormant — nobody is there to read the message — so the scan skips it.
| Read by | routd / routd/proactive.go:39 |
|---|---|
| Type | duration |
| Default | 12h |
| Example | 4h |
PROACTIVE_COOLDOWN
How long a chat is barred from a second interjection after one fires. Mandatory — state-triggered firing without a cooldown loops. Recorded per chat in routd’s chat_proactive table, written in the same transaction that queues the turn, so a crash mid-dispatch costs at most one missed turn and never a double-fire. There is no per-group override.
| Read by | routd / routd/proactive.go:40 |
|---|---|
| Type | duration |
| Default | 24h |
| Example | 48h |
PROACTIVE_BOT_QUIET
How long after its own last message the bot stays out of a chat. Stops a proactive turn from landing on the heels of a reply the agent just made.
| Read by | routd / routd/proactive.go:41 |
|---|---|
| Type | duration |
| Default | 15m |
| Example | 30m |
PROACTIVE_RECENT_ACTIVITY_MIN
How many inbound messages must have arrived in the last hour for the room to count as busy enough to be worth speaking into. Below the threshold the scan skips the chat.
| Read by | routd / routd/proactive.go:42 |
|---|---|
| Type | int (messages per hour) |
| Default | 3 |
| Example | 5 |
runed — the turn substrate
A turn is one ephemeral KVM guest: qemu-system-x86_64, direct kernel boot, two virtio-fs shares, a vsock control channel, and one tap with its own rule set and its own egress proxy on the host side. Every value below is a setting in the generated env, never something runed derives from a runtime path. The kernel and the rootfs are release artifacts whose names carry a version and a config hash; a daemon that went looking for them would find whichever was newest.
runed resolves and checks all of them once, at start, in runed/qemuhost.go qemuPreconditions — and refuses to start when any is unmet, naming every one in a single message. Checking per turn instead would give you a fleet that reports healthy and answers nothing. There is no substrate selector: one substrate, one legal value, no setting.
RUNED_KERNEL_DIR
The make kernel artifact directory, holding bzImage and initrd.gz. The directory name carries the kernel version and a hash of the build's inputs, so it is configuration and never a guess. runed boots the guest from both in place and copies neither. Required: unset, or either file missing, and runed refuses to start.
| Read by | runed / runed/qemuhost.go qemuPreconditions |
|---|---|
| Type | path |
| Default | none — required |
| Example | /srv/data/arizuko_demo/images/kernel-6.1.112-40e1508980d0 |
RUNED_ROOTFS_DIR
The unpacked release rootfs, exported once per release by make images, shared into the guest read-only. The directory name carries the arizuko-ant image id it was exported from, so a guest cannot boot last week's tree against this week's agent. Beside the tree sits .image-env.json — the agent image's own ENV set, which a directory cannot carry and which runed applies in the guest instead. A rootfs with no readable copy of that file is a refusal, not a guess: without the image's PATH the agent's runtime is not on it.
| Read by | runed / runed/qemuhost.go qemuPreconditions, readImageEnv |
|---|---|
| Type | path |
| Default | none — required |
| Example | /srv/data/arizuko_demo/images/rootfs-a35045d89e3e |
RUNED_QEMU
The VMM. runed runs -version against it at start, so a name that is on PATH but cannot execute is caught at deploy rather than on the first turn.
| Read by | runed / runed/qemuhost.go |
|---|---|
| Type | binary name or path |
| Default | qemu-system-x86_64 |
| Example | /usr/bin/qemu-system-x86_64 |
RUNED_VIRTIOFSD
The daemon that exports the guest's two shares. It must be the Rust build. runed greps the binary's --help for --uid-map and --gid-map and refuses QEMU's C build, which advertises neither and needs real root. A root daemon writes host-root-owned files into the group folder on a guest's say-so, measured 2026-09-24. Alpine's virtiofsd package installs the Rust daemon at this default path; on Debian the same second path (/usr/lib/qemu/virtiofsd) holds the C one instead, which is why the check names the flags and not the file.
| Read by | runed / runed/qemuhost.go checkClamp |
|---|---|
| Type | path |
| Default | /usr/libexec/virtiofsd |
| Example | /usr/libexec/virtiofsd |
RUNED_EGRESS_BIN
The forward proxy binary. runed starts one process per turn on the host side of that guest's tap and kills it with the guest — there is no proxy container and no shared proxy. See components/egress.
| Read by | runed / runed/vmnet.go startEgress |
|---|---|
| Type | binary name or path |
| Default | egress |
| Example | /usr/local/bin/egress |
RUNED_FIREWALL_BIN
What writes the tap's rule set: one accept for the guest's own proxy port above drops for everything else. The rules are IPv4 only, which is why a tap carries one address family.
| Read by | runed / runed/vmnet.go |
|---|---|
| Type | binary name or path |
| Default | iptables |
| Example | /sbin/iptables |
RUNED_IP_BIN
What creates and addresses the tap. It must be iproute2: BusyBox ships an ip applet with no tuntap subcommand, so a PATH lookup passes and ip tuntap add then fails once per turn while runed reports healthy. runed therefore runs ip tuntap show at start and refuses the applet by the capability, not the filename.
| Read by | runed / runed/qemuhost.go checkTuntap |
|---|---|
| Type | binary name or path |
| Default | ip |
| Example | /sbin/ip |
RUNED_GUEST_MEM
QEMU's -m for each guest. This times MAX_CONCURRENT_CONTAINERS is what the host must hold, so the pair is checked against MemTotal at start. Five guests at 4G on a 15 GB host is refused there, not discovered when the second turn touches its pages.
| Read by | runed / runed/qemuhost.go memoryFits |
|---|---|
| Type | QEMU size (bare number is MiB; M and G suffixes) |
| Default | 2G |
| Example | 4G |
RUNED_VSOCK_CID_BASE
The first guest CID. runed allocates upward from here, MAX_CONCURRENT_CONTAINERS wide. The CID and vsock port space belongs to the host, not to an instance, so two instances on one box each need their own window. Leave both at the default and both ask for CID 3 and the same derived ports.
| Read by | runed / runed/vmrecord.go allocateCID |
|---|---|
| Type | int (3 is the lowest legal guest CID) |
| Default | 3 |
| Example | 20 |
RUNED_VSOCK_PORT_BASE
Base of the two vsock ports each guest slot takes — the control channel and the MCP channel. Slot n gets base+2n and base+2n+1. Both are bound on the host before QEMU starts, so a squatter is an error at spawn instead of a guest that boots into nothing. Same host-wide space as the CID: one window per instance.
| Read by | runed / runed/vmrecord.go vsockPorts |
|---|---|
| Type | int |
| Default | 5555 |
| Example | 5600 |
RUNED_TAP_SUBNET
The range each guest's link is carved out of: one /30 per slot, the guest on the low address and the host side on the high one. Two usable addresses and no neighbour — a guest has nobody to talk to on its own link, so there is no bridge and no DHCP. runed checks every slot the cap allows at start, not the first: a subnet that holds the first guest and not the last is a fleet that fails on its second concurrent turn.
| Read by | runed / runed/vmnet.go tapNet |
|---|---|
| Type | IPv4 CIDR |
| Default | 10.97.0.0/24 |
| Example | 10.98.0.0/22 |
RUNED_PROXY_PORT
The port this turn's egress proxy listens on, on the host side of the tap. It is the one port the tap's rule set accepts; everything else the guest sends is dropped below the kernel it runs on.
| Read by | runed / runed/qemuhost.go, runed/vmnet.go |
|---|---|
| Type | int |
| Default | 3128 |
| Example | 3128 |
RUNED_VM_DIR
Where each live guest's own state sits: one directory per turn holding the record that names every pid a restarted runed must reap, the QEMU and virtiofsd pid files, the vhost-user sockets, and the turn's staging tree. A directory left behind after a turn means the reap could not finish — it is reported by path for a person, because a recursive delete over a live bind would take the group's own files.
| Read by | runed / runed/qemuhost.go |
|---|---|
| Type | path |
| Default | DATA_DIR/vms |
| Example | /srv/data/arizuko_demo/vms |
RUNED_BOOT_TIMEOUT
How long runed waits for the booted guest to connect back on its control port before giving up on the spawn.
| Read by | runed / runed/qemu.go |
|---|---|
| Type | duration (Go syntax, e.g. 30s) |
| Default | 30s |
| Example | 60s |
RUNED_REAP_GRACE
How long each process gets to exit on its own before SIGKILL at teardown — QEMU first and alone, then the rule set and the tap, then the host processes that served the guest. runed reuses the same value as its short bound elsewhere in a turn: waiting for a virtiofsd socket to appear, and writing a steer into a running guest.
| Read by | runed / runed/vmrecord.go reapVM, runed/qemu.go |
|---|---|
| Type | duration (Go syntax) |
| Default | 5s |
| Example | 10s |
RUNED_RUN_TIMEOUT
The run ceiling. runed kills the guest here; the effective deadline is the lower of this and the guest ceiling CONTAINER_TIMEOUT sets. routd reads the same var and sizes its dispatch deadline at this plus two minutes, so runed's kill always lands first. Equal deadlines let routd time out first, leave the turn marked running, and re-feed the same message into a second guest.
| Read by | runed / runed/cmd/runed/main.go:92; routd / routd/cmd/routd/main.go:110; adapters size their typing TTL under it (chanlib/typing.go) |
|---|---|
| Type | duration (Go syntax) |
| Default | 20m |
| Example | 30m |
RUNED_SHUTDOWN_GRACE
How long a graceful stop waits for runs in progress after runed stops accepting POST /v1/runs. Compose generation writes this key into the env file of runed.
| Read by | runed / runed/cmd/runed/main.go:201 |
|---|---|
| Type | duration (Go syntax) |
| Default | RUNED_RUN_TIMEOUT |
| Example | 5m |
proxyd
proxyd shares no secret with its backends. It proves itself to each one with an authd-minted ES256 bearer pinned to service:proxyd, verified by auth.ProxydTransit in auth/middleware.go; see the auth concept.
PROXYD_LISTEN
Port proxyd binds to. Plain numbers get a : prefix.
| Read by | proxyd / proxyd/main.go:44 |
|---|---|
| Type | string (port or :port) |
| Default | 8080 |
| Example | 8080 |
PUB_REDIRECT_URL
If set, proxyd redirects /pub/* requests to this base URL instead of serving them locally — lets a CDN front the static docs.
| Read by | proxyd / proxyd/main.go:51 |
|---|---|
| Type | URL |
| Default | "" (proxyd serves /pub/ itself) |
| Example | https://docs.arizuko.com |
TRUSTED_PROXIES
CSV of CIDRs (or bare IPs) trusted to set X-Forwarded-For. Empty ⇒ no client trusted, XFF always replaced with the connection peer.
| Read by | proxyd / proxyd/main.go:53 |
|---|---|
| Type | CSV string (CIDRs / IPs) |
| Default | "" |
| Example | 10.0.0.0/8,172.16.0.0/12 |
VITE_ADDR
Backend address proxyd forwards /pub/* (and chat widget) traffic to.
| Read by | proxyd / proxyd/main.go:50 |
|---|---|
| Type | URL |
| Default | http://vited:8080 |
| Example | http://vited:8080 |
webd
ROUTER_URL
Base URL of routd; webd posts inbound web chat and webhook messages to $ROUTER_URL/v1/messages.
| Read by | webd / webd/main.go — see also ROUTER_URL in adapters |
|---|---|
| Type | URL |
| Default | http://routd:8080 |
| Example | http://routd:8080 |
WEBD_LISTEN
Address webd binds to.
| Read by | webd / webd/main.go:41 |
|---|---|
| Type | listen addr (:port or host:port) |
| Default | :8080 |
| Example | :8080 |
WEBD_URL
Public URL webd advertises to routd when registering as a channel.
| Read by | webd / webd/main.go:42 |
|---|---|
| Type | URL |
| Default | http://webd:8080 |
| Example | http://webd:8080 |
onbod
ONBOARD_POLL_INTERVAL
Cadence at which onbod scans for unprompted users / admission queue entries.
| Read by | onbod / onbod/main.go:273 |
|---|---|
| Type | Go duration string (time.ParseDuration) |
| Default | 10s |
| Example | 30s |
ONBOARDING_GREETING
Custom greeting string injected into onboarding prompts in place of the default copy.
| Read by | onbod / onbod/main.go:268 |
|---|---|
| Type | string |
| Default | "" |
| Example | Welcome to the workspace. |
ONBOD_LISTEN_ADDR
Address onbod binds to.
| Read by | onbod / onbod/main.go:270 |
|---|---|
| Type | listen addr |
| Default | :8080 |
| Example | :8080 |
onbod also reads ONBOARDING_ENABLED ("0" → clean exit) and ROUTER_URL via core. It trusts X-User-* headers only behind the proxyd transit bearer (auth.ProxydTransit); on its mixed public/authed invite routes an unproven request is treated as anonymous rather than rejected.
dashd
DASH_PORT
Port dashd binds to. Plain numbers get a : prefix.
| Read by | dashd / dashd/main.go:170, compose / compose/compose.go:206 |
|---|---|
| Type | string |
| Default | :8080 |
| Example | :8080 |
SURROGATE_<NAME>_CLIENT_ID
OAuth app client id for the surrogate “Connect <name>” flow at /dash/me/connections — arizuko authenticating as the user against a third-party API, not a user signing in to arizuko. <NAME> is the provider name upper-cased (-→_); a pair exists for every registered provider, built-in or dropped in at <datadir>/surrogate/*.toml (see how-to: add an OAuth provider). SURROGATE_GITHUB_CLIENT_ID feeds the built-in GitHub connector; it is distinct from GITHUB_CLIENT_ID, the login provider. Unset hides that provider's connection option.
| Read by | routd + dashd / auth/surrogate/engine.go (NewEngine) |
|---|---|
| Type | string |
| Default | "" |
| Example | Iv1.def456 |
SURROGATE_<NAME>_CLIENT_SECRET
OAuth app client secret paired with SURROGATE_<NAME>_CLIENT_ID. One pair per provider.
| Read by | routd + dashd / auth/surrogate/engine.go (NewEngine) |
|---|---|
| Type | string |
| Default | "" |
| Example | ghs_yyyyy |
timed
ROUTER_URL
routd's address. timed opens no database — it reads due tasks and records runs over routd's HTTP API, so scheduled_tasks has exactly one writer path.
| Read by | timed / timed/main.go |
|---|---|
| Type | URL |
| Default | none — required, timed refuses to start without it |
| Example | http://routd:8080 |
timed also reads AUTHD_URL + AUTHD_SERVICE_KEY (exchanged at boot for the service:timed token routd's gate requires) and TZ (for cron evaluation).
ttsd
TTSD_ADDR
Address ttsd binds to.
| Read by | ttsd / ttsd/main.go:35 |
|---|---|
| Type | listen addr |
| Default | :8880 |
| Example | :8880 |
TTS_BACKEND_URL
Upstream Kokoro/OpenAI-compatible TTS server ttsd proxies to.
| Read by | ttsd / ttsd/main.go:36 |
|---|---|
| Type | URL |
| Default | http://kokoro:8880 |
| Example | http://kokoro:8880 |
ttsd also reads LOG_LEVEL.
litellm (model gateway package)
A third-party image, so it has no generated env file: template/services/litellm/litellm.yml names the .env keys docker interpolates into it, and compose/fragments_test.go pins that set. Component page: components/litellm.
MODEL_GATEWAY_KEY
The gateway master key. The litellm package declares it as generated, and arizuko package add and package sync provision it in the instance .env. routd uses it to mint a separate virtual key for a turn; runed passes that virtual key to the guest. The fragment requires the master key with ${MODEL_GATEWAY_KEY:?}.
| Read by | litellm via template/services/litellm/litellm.yml (LITELLM_MASTER_KEY); routd for minting turn keys |
|---|---|
| Type | string |
| Default | none — required once the package is installed |
| Example | $(openssl rand -hex 24) |
MODEL_GATEWAY_URL
Where runed points every spawn’s model client (ANTHROPIC_BASE_URL for claude). Unset means the container keeps the credential and talks to the vendor directly, which is the default.
| Read by | runed for the spawn env |
|---|---|
| Type | URL |
| Default | "" = no gateway |
| Example | http://litellm:4000 |
litellm also reads ANTHROPIC_API_KEY (or, when that is empty, CLAUDE_CODE_OAUTH_TOKEN) as the credential it forwards upstream, TZ, and the three OTLP keys under observability.
Adapter common surface
Every channel adapter shares the same set of plumbing env vars:
CHANNEL_NAME
Name the adapter registers with routd; routing uses this verbatim. Has a per-daemon default.
| Read by | teled :55, discd :64, emaid :55, slakd :56 |
|---|---|
| Type | string |
| Default | matches the platform (telegram, discord, slack, …) |
| Example | telegram-prod |
LISTEN_ADDR
Address the adapter binds for its outbound-send HTTP server (routd → adapter).
| Read by | teled :61 (:9001), discd :68 (:9002), whapd :156 (:9002), emaid :64 (:9003), slakd :61 (:8080) |
|---|---|
| Type | listen addr |
| Default | varies (see column above; :8080 when pinned by the service fragment) |
| Example | :8080 |
LISTEN_URL
URL the adapter advertises to routd on registration so routd can reach it.
| Read by | teled :62, discd :69, whapd :157, emaid :65, slakd :62 |
|---|---|
| Type | URL |
| Default | http://<daemon>:<port> |
| Example | http://teled:9001 |
ROUTER_URL
Base URL of routd where the adapter posts inbound messages.
| Read by | teled :59 (MustEnv), discd :66 (MustEnv), whapd :154, emaid :62 (MustEnv), slakd :59 (MustEnv), webd :38, onbod :143 |
|---|---|
| Type | URL |
| Default | required in most adapters (MustEnv); webd/onbod default to http://routd:8080 |
| Example | http://routd:8080 |
All adapters also read AUTHD_SERVICE_KEY and AUTHD_URL — they exchange that key for a service:<daemon> ES256 token and present it on every routd call. Most also read MEDIA_MAX_FILE_BYTES.
teled (Telegram)
Reads TELEGRAM_BOT_TOKEN (MustEnv), ASSISTANT_NAME, DATA_DIR, plus the adapter common surface. No teled-specific env vars beyond these.
discd (Discord)
DISCORD_BOT_TOKEN
Bot token. Required unless DISCORD_USER_TOKEN is set instead.
| Read by | discd / discd/main.go:71 |
|---|---|
| Type | string |
| Default | "" (refuses to start if both bot/user tokens empty) |
| Example | MTAxNTM4... |
DISCORD_USER_TOKEN
User-account token; selects user-mode operation, bypassing bot intents. Mutually exclusive with the bot token.
| Read by | discd / discd/main.go:72 |
|---|---|
| Type | string |
| Default | "" |
| Example | mfa.xxxxx |
whapd (WhatsApp)
WHATSAPP_AUTH_DIR
Directory where Baileys multi-file auth state (creds, keys) is persisted.
| Read by | whapd / whapd/src/main.ts:49 |
|---|---|
| Type | path |
| Default | $DATA_DIR/store/whatsapp-auth or /srv/data/store/whatsapp-auth |
| Example | /srv/data/arizuko_demo/store/whatsapp-auth |
whapd also reads ASSISTANT_NAME, DATA_DIR, ROUTER_URL, LISTEN_ADDR, LISTEN_URL.
emaid (Email)
EMAIL_ACCOUNT
Email address / IMAP+SMTP login username.
| Read by | emaid / emaid/main.go:73 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | bot@example.com |
EMAIL_IMAP_HOST
IMAP server hostname.
| Read by | emaid / emaid/main.go:71 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | imap.gmail.com |
EMAIL_IMAP_PORT
IMAP server port.
| Read by | emaid / emaid/main.go:76 |
|---|---|
| Type | string (port) |
| Default | 993 |
| Example | 993 |
EMAIL_PASSWORD
Login password / app password for IMAP+SMTP.
| Read by | emaid / emaid/main.go:74 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | xxxx xxxx xxxx xxxx |
EMAIL_SMTP_HOST
SMTP server hostname.
| Read by | emaid / emaid/main.go:72 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | smtp.gmail.com |
EMAIL_SMTP_PORT
SMTP server port.
| Read by | emaid / emaid/main.go:77 |
|---|---|
| Type | string (port) |
| Default | 587 |
| Example | 587 |
EMAIL_STRICT_AUTH
Reject inbound mail outright when SPF/DKIM/DMARC fails (fail-closed). When unset (default), failed-auth messages still reach the agent but flagged untrusted in the envelope.
| Read by | emaid / emaid/auth.go:65 |
|---|---|
| Type | bool (truthy strings: 1, true, yes, on) |
| Default | false |
| Example | true |
EMAIL_TRUSTED_AUTHSERV
Hostname of the upstream MTA whose Authentication-Results header emaid will trust (e.g. mx.google.com for Gmail). When unset, emaid classifies every inbound mail as untrusted. Available since v0.40.5.
| Read by | emaid / emaid/auth.go:64 |
|---|---|
| Type | string (hostname, lowercased) |
| Default | unset |
| Example | mx.google.com |
EMAIL_TRUSTED_DOMAINS
Comma-separated allowlist of sender domains that count as trusted (matched against the From-address domain after DMARC alignment). Empty entries ignored. Both this and EMAIL_TRUSTED_AUTHSERV must agree for a message to be trusted. Added v0.40.5.
| Read by | emaid / emaid/auth.go:69 |
|---|---|
| Type | comma-separated list |
| Default | unset (no domain allowlist) |
| Example | example.com, partner.org |
slakd (Slack)
SLACK_BOT_TOKEN
Slack xoxb- bot token used by the Web API and Events API client.
| Read by | slakd / slakd/main.go:100 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | xoxb-... |
SLACK_SIGNING_SECRET
Shared secret Slack uses to sign incoming event webhooks; slakd verifies X-Slack-Signature against it.
| Read by | slakd / slakd/main.go:101 (MustEnv) |
|---|---|
| Type | string |
| Default | required |
| Example | 8f742231... |
SLAKD_USERS_CACHE_TTL
TTL (seconds) of the Slack user-info cache; lower values cost more API quota.
| Read by | slakd / slakd/main.go:108 |
|---|---|
| Type | int (seconds) |
| Default | 900 (15 min) |
| Example | 3600 |
split daemons (routd / runed / authd)
Three daemons share the work: routd routes messages and runs the message loop, runed starts the turn guests, and authd handles auth and identity. These variables connect them.
AUTHD_URL
In-network base URL of authd. routd, runed, webd, and proxyd reach it for service-token exchange and JWKS. dashd uses it for both of those and as the backend for its /dash/authd/ page — the signing-key and session tables it reads over HTTP rather than by opening auth.db.
| Read by | compose / compose/compose.go:99 → routd, runed, webd, proxyd, dashd |
|---|---|
| Type | URL |
| Default | http://authd:8080 |
| Example | http://authd:8080 |
AUTHD_SERVICE_KEY
Per-daemon shared secret a service daemon presents to authd to authenticate. Provisioned once per daemon and persisted to env/<daemon>.env; authd recognises them via AUTHD_SERVICE_KEYS.
| Read by | compose / compose/compose.go:38 → routd, runed |
|---|---|
| Type | string (secret) |
| Default | auto-provisioned at compose time |
| Example | <random hex> |
AUTHD_SERVICE_KEYS
The principal=secret pairs authd accepts, one per service daemon. Rotating a key here invalidates that daemon's in-flight service tokens.
| Read by | compose / compose/compose.go:104 → authd |
|---|---|
| Type | string (comma-separated principal=secret) |
| Default | auto-built from the provisioned service keys |
| Example | service:routd=...,service:runed=... |
RUNED_URL
In-network base URL of runed (the container-runner plane). routd dispatches runs to it.
| Read by | routd / routd/cmd/routd/main.go:43 |
|---|---|
| Type | URL |
| Default | http://runed:8080 |
| Example | http://runed:8080 |
ROUTD_SERVICE_TOKEN
A static service token that routd sends to runed only when the token exchange at AUTHD_URL with AUTHD_SERVICE_KEY fails.
| Read by | routd / routd/cmd/routd/main.go:148 |
|---|---|
| Type | string (token) |
| Default | empty |
| Example | <jwt> |
the store server
Every owner's tables live on one DynamoDB-protocol server per instance — ExtendDB over PostgreSQL, shipped as the extenddb package. Each daemon reads the five keys below and signs as itself, so the server's policy can bound what each one reaches. A daemon without DYNAMODB_URL refuses to start: there is no file to fall back to. Bring-up is in INSTALL.md § The store server: one extenddb init run prints the admin password, and extenddb manage mints one access key per daemon.
The key is minted by hand, so it can be missing. A daemon that has DYNAMODB_URL and no key refuses to dial (dynstore: endpoint ... takes an access key and a secret) and exits at boot. Because docker compose up runs under systemd, that one exit restart-loops the whole instance (BUGS L406). arizuko generate logs store enrolment is partial at ERROR, names those daemons and then continues (compose/compose.go warnPartialEnrolment, over store.StoreReach). Enabling a package that adds a daemon, gitd for one, without minting its key puts the whole instance in a restart loop.
DYNAMODB_URL
The endpoint every daemon opens its owner’s tables on. Required: unset, each daemon refuses to start with names no endpoint.
| Read by | every daemon / objstore/dynstore/env.go |
|---|---|
| Type | URL (TLS; the server refuses to start without it) |
| Default | none — required; the extenddb package manifest sets https://extenddb:18443 |
| Example | https://extenddb:18443 |
STORE_CA
The endpoint's certificate, base64-encoded, because a daemon has its environment and no file of its own. extenddb init generates it; a value that is not base64 is refused at boot rather than surfacing later as a handshake failure against a name nobody typed.
| Read by | every daemon / objstore/dynstore/env.go |
|---|---|
| Type | string (base64 PEM) |
| Default | "" = the system roots |
| Example | LS0tLS1CRUdJTiBDRVJUSUZJ... |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
One key pair per daemon, written into env/<daemon>.env and carried forward by every later arizuko generate. The pair is what the server's policy binds: routd reaches routd.*, dashd reaches routd's and onbod's, an adapter reaches its own prefix (store.StoreReach is the full table). A shared pair would make that boundary meaningless.
| Read by | every daemon / objstore/dynstore/env.go, carried by compose/compose.go |
|---|---|
| Type | string (secret) |
| Default | none — an endpoint with no credential is refused at boot |
| Example | <minted by extenddb manage> |
AWS_REGION
The region the request is signed for. Any value works against a self-hosted server as long as every daemon uses the same one; it matters only if the endpoint is Amazon's own.
| Read by | every daemon / objstore/dynstore/env.go |
|---|---|
| Type | string |
| Default | us-east-1 |
| Example | us-east-1 |
STORE_DB_PASSWORD / STORE_APP_PASSWORD
The first password belongs to the instance's PostgreSQL role. The second belongs to the role used by the store server. The ExtendDB package declares both in its manifest.
| Read by | the extenddb compose fragment / template/services/extenddb/extenddb.yml |
|---|---|
| Type | string (secret) |
| Default | generated by arizuko package add or package sync when the package is installed |
| Example | <random hex> |
observability — OTLP export
slog to journald is always on. Setting the OTLP endpoint also fans every slog event out to an OpenTelemetry collector, through the library obs/. Unset = zero overhead. The litellm gateway reads the same three keys (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_HEADERS) and, when the endpoint is set, sends one span per model request to <endpoint>/v1/traces with token counts and cost — message content excluded.
OTEL_EXPORTER_OTLP_ENDPOINT
OTLP/HTTP collector endpoint. When set, every daemon's obs.Setup sends slog events to stderr and the collector; records carrying turn_id get a deterministic TraceID so one turn's events group. When empty, only the stderr JSON handler runs.
| Read by | obs / obs/obs.go:36 (every daemon's main) |
|---|---|
| Type | URL |
| Default | "" = OTLP disabled |
| Example | http://collector:4318 |
Some variables live elsewhere
- davd — the daemon binary is upstream
sigoden/dufswrapped in alpine for the healthcheck (davd/Dockerfile). Its config is CLI args and dufs's own env vars, not arizuko's. - vited — static-file server image with no arizuko Go/TS code. Generated at compose time.
- Test-only env vars — e.g.
TEST_*,SMOKE_INSTANCE(Makefile-only). Omitted by scope. - the
egressbinary — each flag has an env default:EGRESS_LISTEN,EGRESS_ALLOW,EGRESS_DENY,EGRESS_ALLOW_ALL,EGRESS_CLIENT,EGRESS_DENY_PRIVATEandEGRESS_ALLOW_LOOPBACK, read inegress/cmd/egress/main.go. See egress. - Variables read by the code and not described on this page — core:
ARIZUKO_BACKEND,MOUNT_ALLOWED_ROOTS,APP_SRC_DIR; all daemons:ARIZUKO_INSTANCE,AUTHD_SERVICE_NAME,GRANTS_URL,METRICS_ENABLED,OTEL_EXPORTER_OTLP_TRACES_ENDPOINT; routd:ARIZUKO_DEFAULT_MODEL,ARIZUKO_DEFAULT_EFFORT,ARIZUKO_DEFAULT_PRODUCT,COST_CAPS_ENABLED,SESSION_IDLE_EXPIRY,MAX_OUTBOUND_RETRY,ONBOD_URL,HOSTING_DOMAIN,WEB_VHOST_ALIASES,CONNECTORS_TOML,CONNECTOR_CALL_TIMEOUT_MS; webd:CHAT_ANON_DOS_RPM,WEBHOOK_RATE_HOOK,WEBHOOK_RATE_WEB,PROXYD_URL; audit:AUDIT_ENABLED,AUDIT_MAX_BYTES,AUDIT_ROTATE_HOURS,AUDIT_WEBHOOK_SECRET,AUDIT_WEBHOOK_URL,AUDIT_WEBHOOK_URL_SYSTEM,AUDIT_WEBHOOK_URL_WEB; dashd:DASH_ACCENT_COLOR; emaid:EMAIL_ACCESS_TOKEN,EMAIL_UNVERIFIED_SUBJECT_PREFIX,EMAIL_VERIFY_DKIM; googd:GDRIVE_API_URL,GDRIVE_IMPERSONATE,GDRIVE_SA_KEY_JSON; gitd:GITHUB_API_URL,GITHUB_APP_ACCEPTS_REPO,GITHUB_TOKEN; slakd:SLAKD_STALE_FAIL_LIMIT,SLAKD_STALE_SECONDS,SLAKD_WATCHDOG_SECONDS; whapd:WHAPD_STATE_DIR,WHAPD_URL,WHATSAPP_PHONE; the CLI:ARIZUKO_CHAT_URL,ARIZUKO_CHAT_TOKEN,ARIZUKO_CHAT_GROUP,XDG_CONFIG_HOME. The package README of each daemon describes them.