Skip to content

Configuration

Elliot Boney edited this page Jun 23, 2026 · 2 revisions

Configuration

shelldon is configured entirely through environment variables — there is no config file to edit beyond your .env. This page documents every variable the code actually reads. For where these get loaded at runtime see Running on the Pi; for the package layout see Architecture and Development.

A starting point ships as .env.example — copy it and fill in the blanks:

cp .env.example .env   # then edit

.env is gitignored (it holds secrets). On the Pi the systemd service reads it via EnvironmentFile; on a plain Linux box you load it into the environment yourself before launching (set -a; . ./.env; set +a).

Surface selection

Two variables pick which chat surface and which display shelldon uses. Both default to the zero-hardware path, so an unset config runs a headless CLI pet.

Variable Purpose Values / Default
SHELLDON_TRANSPORT Which chat surface to run. telegram for the bot; anything else (or unset) → local CLI (stdin/stdout). Read in shelldon/app.py.
SHELLDON_DISPLAY Which display to drive. waveshare for the real E-Ink panel; anything else (or unset) → a headless stub renderer (no hardware touched). Read in shelldon/app.py.

Telegram transport

Used only when SHELLDON_TRANSPORT=telegram. Resolved in shelldon/transport/telegram.py.

Variable Purpose Example / Default
SHELLDON_TELEGRAM_BOT_TOKEN The bot token from @BotFather. shelldon's own variable so it never collides with a v1 bot. Required for the telegram transport. 123456:ABC-DEF...
TELEGRAM_BOT_TOKEN Fallback token name, used only if SHELLDON_TELEGRAM_BOT_TOKEN is unset. (unset)
ALLOWED_USERS Comma-separated Telegram numeric user ids allowed to talk to the pet — the security gate. Non-integer/blank entries are dropped with a warning. 123456789,987654321
ALLOW_ALL_USERS Lets anyone message the bot (disables the allowlist). Not recommended. false (truthy values: 1, true, yes, on)

If neither token variable is set while SHELLDON_TRANSPORT=telegram, startup fails fast with a clear error.

LLM brain — the provider chain

The broker builds an ordered, comma-separated chain of providers from PROVIDER_CHAIN (default glm). Each provider in the chain reads its own credential/model variables. Resolved in shelldon/broker/chain.py. An unknown preset, an empty chain, or a preset missing its required config fails at startup — no silent degradation.

Variable Purpose Example / Default
PROVIDER_CHAIN Ordered, comma-separated list of provider presets. Duplicates and blanks are dropped with a warning. glm (e.g. glm,ollama,groq)

GLM (default — via Z.ai, Anthropic-compatible)

Preset name glm. Uses the anthropic SDK against Z.ai's Anthropic-compatible endpoint.

Variable Purpose Example / Default
GLM_API_KEY Your Z.ai API key. Falls back to ANTHROPIC_API_KEY if unset. (required for the glm preset)
GLM_BASE_URL Endpoint override. Falls back to ANTHROPIC_BASE_URL, then the Z.ai default. https://api.z.ai/api/anthropic
GLM_MODEL Model id. Falls back to ANTHROPIC_MODEL, then glm-4.7. glm-4.7

Native Claude

Preset name claude. Same Anthropic SDK, native endpoint.

Variable Purpose Example / Default
ANTHROPIC_API_KEY Anthropic API key. (required for the claude preset)
CLAUDE_MODEL Model id. Unset → the adapter's Claude default. (unset)

ANTHROPIC_BASE_URL and ANTHROPIC_MODEL are also read as fallbacks for the glm preset (see above).

Ollama (local, free)

Preset name ollama. Uses the openai SDK against a local Ollama server. OLLAMA_API_BASE is required when this preset is in the chain; the /v1 OpenAI-compatible path is appended automatically if missing.

Variable Purpose Example / Default
OLLAMA_API_BASE Base URL of your Ollama server (required for the ollama preset). http://your-lan-box:11434
OLLAMA_MODEL Model id to request. qwen2.5
OLLAMA_API_KEY Sent to satisfy the SDK; Ollama ignores it. ollama (default)

OpenAI-compatible presets

These all use the openai SDK. Each has a default base URL (overridable via {NAME}_BASE_URL), an API-key variable, and a model variable. Preset names: openai, openrouter, groq, cerebras, nvidia, mistral, github, gemini.

Preset API key var Model var Base-URL override Default base URL
openai OPENAI_API_KEY OPENAI_MODEL OPENAI_BASE_URL SDK default (api.openai.com)
openrouter OPENROUTER_API_KEY OPENROUTER_MODEL OPENROUTER_BASE_URL https://openrouter.ai/api/v1
groq GROQ_API_KEY GROQ_MODEL GROQ_BASE_URL https://api.groq.com/openai/v1
cerebras CEREBRAS_API_KEY CEREBRAS_MODEL CEREBRAS_BASE_URL https://api.cerebras.ai/v1
nvidia NVIDIA_API_KEY NVIDIA_MODEL NVIDIA_BASE_URL https://integrate.api.nvidia.com/v1
mistral MISTRAL_API_KEY MISTRAL_MODEL MISTRAL_BASE_URL https://api.mistral.ai/v1
github GITHUB_TOKEN GITHUB_MODEL GITHUB_BASE_URL https://models.github.ai/inference
gemini GEMINI_API_KEY GEMINI_MODEL GEMINI_BASE_URL https://generativelanguage.googleapis.com/v1beta/openai/

Because all of these speak the same wire format, the generic openai preset reaches any OpenAI-compatible endpoint by pointing OPENAI_BASE_URL at it — the named presets above are just convenience defaults. A name/... prefix on a model id (LiteLLM convention) is stripped automatically.

Free-tier quotas are independent per provider, so a common setup stacks several and lets the chain advance when one rate-limits:

PROVIDER_CHAIN="glm,groq,cerebras,openrouter"

Display / face

Read in shelldon/display/waveshare.py (only relevant when SHELLDON_DISPLAY=waveshare).

Variable Purpose Example / Default
SHELLDON_FACE_FONT Path to the font used to render the E-Ink face. Override for a different panel/box or a custom font. /usr/share/fonts/opentype/unifont/unifont.otf

Worker privilege drop

Read in shelldon/app.py. These configure the OS identity the per-turn forked worker drops to, so it's denied access to the locked credential vault. All optional — unset means dev mode (no isolation; logged as a no-op). Real isolation requires Linux + privilege (i.e. the Pi/service deployment).

Variable Purpose Notes
SHELLDON_WORKER_USER Username to drop the worker to (resolved via pwd). Takes precedence over uid/gid. Must not resolve to uid 0 (root) — rejected.
SHELLDON_WORKER_UID Numeric uid to drop to. Must be paired with SHELLDON_WORKER_GID; uid 0 is rejected.
SHELLDON_WORKER_GID Numeric gid to drop to. Must be paired with SHELLDON_WORKER_UID.

Setting a uid without a gid (or vice versa) is a misconfiguration and fails fast at startup.

Paths (not configurable via env)

shelldon's state lives under ~/.shelldon/. These are hardcoded, not environment-driven — listed here so you know where data lands:

  • ~/.shelldon/memory/ — the curated markdown knowledge tree (and the OS-locked vault/ inside it).
  • ~/.shelldon/history.db — the WAL/FTS5 sqlite conversation history.
  • ~/.shelldon/workspace/ — the self-coding workspace, with live/ and staging/ tool directories.

Example .env

# --- LLM brain ---------------------------------------------------------------
# GLM via Z.ai (Anthropic-compatible) is the default.
GLM_API_KEY=your-zai-key-here
GLM_MODEL=glm-4.7
# GLM_BASE_URL=https://api.z.ai/api/anthropic   # default; override for a different endpoint
# PROVIDER_CHAIN=glm                             # ordered fallback, e.g. "glm,ollama,groq"
# OLLAMA_API_BASE=http://your-lan-box:11434      # if using a local model in the chain

# --- Surfaces ----------------------------------------------------------------
SHELLDON_TRANSPORT=telegram                       # or leave unset for the local CLI
# SHELLDON_DISPLAY=waveshare                      # set on a Pi with the E-Ink panel
# SHELLDON_FACE_FONT=/usr/share/fonts/opentype/unifont/unifont.otf

# --- Telegram transport (when SHELLDON_TRANSPORT=telegram) -------------------
SHELLDON_TELEGRAM_BOT_TOKEN=123456:ABC-DEF...     # from @BotFather
ALLOWED_USERS=123456789                           # your Telegram user id (comma-separated for more)
# ALLOW_ALL_USERS=false                           # true lets anyone message it (not recommended)

# --- Worker isolation (Pi/service deployment) --------------------------------
# SHELLDON_WORKER_USER=shelldon-worker            # drop the forked worker to this user

Free-tier example (no GLM key)

Point the generic openai preset at any free OpenAI-compatible endpoint:

PROVIDER_CHAIN=openai
OPENAI_API_KEY=your-free-key
OPENAI_BASE_URL=https://api.groq.com/openai/v1
OPENAI_MODEL=llama-3.3-70b-versatile
SHELLDON_TRANSPORT=telegram
SHELLDON_TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
ALLOWED_USERS=123456789

Clone this wiki locally