Skip to main content

Customize Your Intern

The Developer Edition lets you change which LLM thinks, which agent runtime runs the conversation, how the Intern sounds and what it hears. This section covers each piece:

PageWhat you change
LLMBase URL, API key and model of the brain; bring your own provider
Agent RuntimeOpenClaw, Hermes, Claude Code, Codex, OpenCode or PicoClaw
TTSVoice provider (ElevenLabs, OpenAI, Gemini, offline Piper), voice, speed
STT & LanguageSpeech-to-text provider and conversation language
Listening & Wake WordVAD sensitivity, turn-taking, wake word, button, audio devices, volume
Realtime VoiceSpeech-to-speech mode: Gemini Live, OpenAI Realtime, GPT-Live

Where settings live​

File (on device)Owned byHoldsApplied
/root/config/config.jsonos-serverLLM, agent runtime, TTS/STT, realtime, wake wordThrough the API/UI: live or with an automatic restart. Hand edits: sudo systemctl restart os-server
/opt/hal/.envHALLow-level HAL_* tuning — audio devices, VAD, echo, realtime overridesRead once at start: sudo systemctl restart hal

HAL reads config.json too — TTS, STT, wake word and the realtime block are stored there by os-server and read by HAL at start. When a HAL_* variable in /opt/hal/.env and a config.json value cover the same knob, the env var wins.

Edit config.json through the API, not by hand

The API validates input, normalises URLs, applies most changes live and restarts the right service for you. Hand-editing works, but some changes (such as the agent runtime switch) only take effect through the API — see each page.

Three ways to change a setting​

1. Admin Web UI​

Open http://<device-ip>/setting and sign in with the admin password (the XXXX from the Intern-XXXX sticker — see Device Access).

The public menu shows General, Wi-Fi, My Voice, MCP Tools, Plugins, Timezone, Scheduled and Connectors. The developer tabs — AI Brain, Runtime, Language, Voice, Realtime (plus Channels and MQTT) — only appear in debug mode. Add ?debug=true before the #:

http://<device-ip>/setting?debug=true#llm AI Brain
http://<device-ip>/setting?debug=true#runtime Runtime
http://<device-ip>/setting?debug=true#stt Language (STT)
http://<device-ip>/setting?debug=true#tts Voice (TTS)
http://<device-ip>/setting?debug=true#realtime Realtime
http://<device-ip>/setting#general General (wake word, admin password)

Without ?debug=true, a deep link to a developer tab silently falls back to General.

2. HTTP API​

The os-server API is served by nginx at http://<device-ip>/api/…. Every settings endpoint is admin-gated. Log in once with the admin password and reuse the session token as a Bearer token:

DEVICE=http://<device-ip>
TOKEN=$(curl -s -X POST $DEVICE/api/login \
-H 'Content-Type: application/json' \
-d '{"password":"XXXX"}' | jq -r .data.token)

# Read the current config (secrets are redacted to has_* flags)
curl -s -H "Authorization: Bearer $TOKEN" $DEVICE/api/device/config | jq .data

os-server also accepts the device's current llm_api_key as a Bearer token. That means changing llm_api_key also changes that credential — prefer the login token in scripts.

PUT /api/device/config has PATCH semantics: send only the fields you want to change; an empty string means "leave unchanged".

curl -s -X PUT $DEVICE/api/device/config \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"tts_voice":"Brian","tts_speed":1.1}'

os-server responses use the envelope {"status": 1, "data": …} on success and {"status": 0, "message": "…"} on failure.

The HAL API is also reachable from your laptop through os-server at http://<device-ip>/api/hardware/<hal-path> with the same Bearer token (on the device itself, call HAL directly at http://127.0.0.1:5001).

3. Edit files over SSH​

sudo nano /root/config/config.json && sudo systemctl restart os-server
sudo nano /opt/hal/.env && sudo systemctl restart hal

config.json is plain JSON — a syntax error stops os-server from starting. Check with sudo jq . /root/config/config.json before restarting.

What applies live and what restarts​

Change (via API/UI)Effect
llm_modelLive — the runtime's primary model is swapped
llm_api_key, llm_base_url, llm_disable_thinkingRuntime config is regenerated and the gateway restarts; HAL restarts for key/URL changes
tts_provider, tts_voice, tts_speed, tts_api_key, tts_base_urlLive — pushed to HAL without a restart
stt_language, stt_*, deepgram_api_key, wakeword, realtimeHAL restarts automatically (a few seconds of no voice)
Agent runtime switchRuns the switch procedure, then restarts os-server
Anything in /opt/hal/.envNeeds sudo systemctl restart hal

You can also restart services from the API: POST /api/system/restart/hal, POST /api/system/restart/os-server, or POST /api/agent/restart for the active agent runtime.

Going back to the Autonomous defaults​

The device ships pointed at the Autonomous AI proxy. The first time you change the LLM, voice or realtime credentials, os-server snapshots the shipped values into autonomous_defaults in config.json. To go back:

curl -s -X POST $DEVICE/api/device/restore-defaults \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"section":"llm"}' # or "voice" / "realtime"

A factory reset (hold the button 10 s, or soft-reset from the Admin panel) wipes config.json entirely.

What survives an OTA update​

The bootstrap service installs official updates automatically (Controlling the OTA Bootstrap). Know where to put your changes so they aren't overwritten:

ItemPath on deviceAfter an OTA
os-server config/root/config/config.jsonKept
HAL env/opt/hal/.envKept by HAL updates, replaced by device-profile updates
Web UI/usr/share/nginx/html/setup/Replaced

If you customise anything in the "Replaced" rows, keep a copy and re-apply it after updates — or disable the bootstrap service while you develop.