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:
| Page | What you change |
|---|---|
| LLM | Base URL, API key and model of the brain; bring your own provider |
| Agent Runtime | OpenClaw, Hermes, Claude Code, Codex, OpenCode or PicoClaw |
| TTS | Voice provider (ElevenLabs, OpenAI, Gemini, offline Piper), voice, speed |
| STT & Language | Speech-to-text provider and conversation language |
| Listening & Wake Word | VAD sensitivity, turn-taking, wake word, button, audio devices, volume |
| Realtime Voice | Speech-to-speech mode: Gemini Live, OpenAI Realtime, GPT-Live |
Where settings live
| File (on device) | Owned by | Holds | Applied |
|---|---|---|---|
/root/config/config.json | os-server | LLM, agent runtime, TTS/STT, realtime, wake word | Through the API/UI: live or with an automatic restart. Hand edits: sudo systemctl restart os-server |
/opt/hal/.env | HAL | Low-level HAL_* tuning — audio devices, VAD, echo, realtime overrides | Read 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.
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_model | Live — the runtime's primary model is swapped |
llm_api_key, llm_base_url, llm_disable_thinking | Runtime config is regenerated and the gateway restarts; HAL restarts for key/URL changes |
tts_provider, tts_voice, tts_speed, tts_api_key, tts_base_url | Live — pushed to HAL without a restart |
stt_language, stt_*, deepgram_api_key, wakeword, realtime | HAL restarts automatically (a few seconds of no voice) |
| Agent runtime switch | Runs the switch procedure, then restarts os-server |
Anything in /opt/hal/.env | Needs 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:
| Item | Path on device | After an OTA |
|---|---|---|
| os-server config | /root/config/config.json | Kept |
| HAL env | /opt/hal/.env | Kept 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.