* feat(voice): add a Voice settings page with provider choice and a local model catalog Each host gets a Voice page under host settings. Dictation and voice mode each pick a provider (Local or OpenAI) and a model. Provider settings sit in one group of collapsible rows: local speech-to-text and text-to-speech list every catalog model with its size, languages and description before download, and OpenAI takes a write-only API key and an optional base URL. The local catalog grows from Parakeet/Kokoro to Whisper, SenseVoice, Dolphin, GigaAM, Parakeet Japanese, multilingual Kokoro, Kitten and Piper voices. Each entry declares its sherpa engine family, so the recognizer and TTS build the matching model config and the downloader verifies the files that family needs. Kitten v0.8 is left out: it crashes the pinned sherpa-onnx 1.12.28. Voice mode is now opt-in. The default lived only in code (true), never in config.json, so hosts that never toggled it find it off after upgrading. OpenAI speech-to-text resolved one model for both features, with voice mode's choice taking precedence, so dictation silently used voice mode's model. The resolved config now carries a model per feature; STT_MODEL still overrides both. Rows revealed under a collapsible settings row are a nested density level set by the container: tighter padding, smaller text, and xs controls, with controls taking their size from the row instead of each caller. * test(app): pre-bundle the JSX dev runtime for browser tests Compiling test JSX with the automatic runtime imports react/jsx-dev-runtime. Vite discovered it mid-run on a cold cache and reloaded the page, which failed the first browser test file to import it. * refactor(voice): keep speech provider settings at each use site Store credentials and endpoint options beside the speech feature that consumes them so saved page values cannot be shadowed by a separate provider block. Normalize existing config on load and mirror the inline shape on config reads and patches. This is the config, resolver and wire phase; the provider sheet follows separately. * fix(voice): preserve saved keys when resaving provider settings Forms send the selected provider on each save but cannot read back its API key. Merge patches for the existing provider so these saves retain credentials; replace the site only when the provider changes. * feat(voice): configure each speech use site from a provider sheet The Voice page now lists Speech-to-text, Listening and Speaking as one-line summaries. Tapping one opens a sheet with the provider choice and only that provider's settings: the local catalog (size, languages and description before download, download state per model) or the OpenAI key, base URL, model and language or voice. The sheet's form model sends only the fields the user changed, so re-saving never clears a saved key. Fields an env var decides are read-only with a "Set by" hint; the API key and base URL stay editable because a saved value wins over the env fallback. Save failures show a toast and keep the sheet open with the input. E2E daemons pin speech providers through env vars to avoid model downloads, so the browser spec covers the locked-provider path, the key fallback and the failed save; provider switching is covered by the form model tests. * refactor(voice): press settings and catalog rows through PressHighlight Settings rows that lead somewhere and local model rows now use the standard row press primitive, so native shows the finger-down flash the rest of the app's rows have. Web hover and press styling are unchanged. * fix(voice): preserve STT language for local providers Language belongs to every STT use site. Preserve legacy local language settings through config loading and saving, resolve them without provider gating, and expose them in the provider form.
13 KiB
title, description, nav, order, category
| title | description | nav | order | category |
|---|---|---|---|---|
| Configuration | Configure managed instances and foreground deployments. | Configuration | 40 | Configuration |
Configuration
Managed instances load their configuration from the home’s config.json and defaults. Foreground deployments can override that file with environment variables.
Where config lives
By default, Paseo uses ~/.paseo as its home directory. The configuration file is:
~/.paseo/config.json
You can change the home directory by setting PASEO_HOME or selecting --home on a CLI command.
Precedence
Managed start and Desktop use defaults, then config.json. Inherited daemon-setting environment variables are removed from their launches.
Foreground paseo daemon run, Docker, and direct supervisor launches apply environment variables after the file. Existing legacy supervisor flags retain their precedence across worker restart. Changing deployment overrides requires stopping and relaunching that deployment. Lists append across sources, including hostnames and CORS origins.
Example
Minimal example that configures listening address, hostnames, and MCP:
{
"$schema": "https://paseo.sh/schemas/paseo.config.v1.json",
"version": 1,
"daemon": {
"listen": "127.0.0.1:6767",
"hostnames": ["localhost", ".localhost"],
"mcp": { "enabled": true }
}
}
daemon.hostnames is the primary field. The old daemon.allowedHosts name still works as a deprecated alias for backward compatibility.
Apply changes
Edit the selected file through the CLI:
paseo daemon config get daemon.listen --home ~/paseo-test
paseo daemon config set daemon.listen 127.0.0.1:6800 --home ~/paseo-test
paseo daemon config unset features.webUi.enabled --home ~/paseo-test
get [path] reports configured values and labels missing fields unset. It does not create a home. set parses JSON, otherwise treats the input as a string; --string forces a literal string. --json selects output format. Use whole-object JSON for dynamic keys containing dots. Unknown paths, invalid values, and invalid existing files are rejected without writing. Passwords use set-password.
A successful edit saves the validated file, then reloads once if the instance is reachable. Output distinguishes applied changes, restart requirements, and deployment overrides. A stopped/unbound/unreachable instance reports saved; not applied. A failed reload reports saved; reload failed with a nonzero exit; the file remains saved. Edits never implicitly restart.
After editing config.json directly, reload it:
paseo reload
The daemon validates the complete file before applying anything. It applies runtime-safe changes and lists any settings that still need a restart. If it reports restart-required paths, run:
paseo daemon restart
Runtime-safe settings include relay enablement, MCP settings, browser tools, hostnames, CORS origins, trusted proxies, Git process limits, agent and terminal profiles, provider definitions, metadata generation, the app base URL, provider catalog timeout, and the global plugin switch. Removing one of these settings applies its omitted-field behavior; removing a provider removes it from future launches.
New homes keep relay disabled when you remove daemon.relay.enabled. A daemon whose config already omitted this field when it started keeps the legacy relay-enabled behavior for compatibility. Set daemon.relay.enabled explicitly when editing an older config.
Idle runtime cleanup, listen addresses, authentication, relay endpoints and TLS, worktree allocation, service-proxy addresses, the bundled web UI, logging, speech, voice, credentials, and local model settings require a restart. Reload applies other valid edits in the same file before reporting those paths.
Deployment environment variables and legacy supervisor flags remain authoritative. Reload reports a changed file setting under overrideControlledPaths when a launch override prevents it from taking effect. This includes startup settings such as listen addresses, passwords, relay endpoints and TLS, service-proxy and web UI settings, logging, speech, and voice configuration. List settings such as hostnames and CORS origins still append across sources, so values from config.json continue to apply. Stop and relaunch the deployment without the override to make the file value authoritative.
Agent providers
Agent providers, both the first-class ones Paseo ships with and custom entries you add under agents.providers, are documented on their own page.
See Providers for the mental model and Supported providers for the full list of agents Paseo can launch. For pointing Claude at Anthropic-compatible endpoints (Z.AI, Alibaba/Qwen), multiple profiles, custom binaries, ACP agents, and the additionalModels merge behavior, see Custom providers. The full field reference lives on GitHub at docs/custom-providers.md.
Idle runtimes
Paseo releases resumable idle agent processes after one hour when its daemon baseline allows cleanup and any implemented provider check agrees. Your next prompt resumes the same conversation. Disable this for the entire host by turning off Release idle agents in Settings → your host → Agents, or with:
{ "agents": { "idleRuntimeCleanup": false } }
This setting defaults to true. Changes in Settings apply immediately; config-file edits apply
on config reload. Re-enabling keeps existing idle timestamps. There is no per-agent setting. Providers without a runtime safety check use the same daemon baseline and one-hour idle
period. Active work, descendants and pending finish notifications prevent cleanup. ACP adapters
also retain runtimes with running ACP terminals, including silent ones; exited terminals do not
prevent cleanup. This check does not inspect processes launched internally by the provider.
Worktrees
New worktrees are created under $PASEO_HOME/worktrees by default. To place new worktrees somewhere else, set worktrees.root:
{
"worktrees": {
"root": "/mnt/fast/paseo-worktrees"
}
}
Relative paths are resolved against PASEO_HOME. Existing worktrees remain where they are; changing this setting only changes where Paseo creates and discovers Paseo-managed worktrees going forward.
Voice
Voice is configured through features.dictation and features.voiceMode. Provider credentials and options live inline at features.dictation.stt, features.voiceMode.stt, and features.voiceMode.tts.
For voice philosophy, architecture, and complete local/OpenAI setup examples, see Voice docs.
Bundled web UI
The daemon can serve the browser web client from the same HTTP server. This is enabled in the official Docker image and disabled by default for normal CLI and desktop-managed daemons.
Enable it from the CLI:
paseo daemon config set features.webUi.enabled true
paseo daemon start
Or set the environment variable:
PASEO_WEB_UI_ENABLED=true paseo daemon run
Or persist it in config.json:
{
"features": {
"webUi": {
"enabled": true
}
}
}
When enabled, open the daemon HTTP origin, for example http://localhost:6767/, to load the web app. Static UI files load without daemon auth; API and WebSocket requests still require the configured password.
Logging
Daemon logging uses separate console and file sinks by default:
- Console:
infoand above - File (
$PASEO_HOME/daemon.log):traceand above - File rotation:
10mmax file size,3rotated files kept alongside the active file
{
"log": {
"console": {
"level": "info",
"format": "pretty"
},
"file": {
"level": "trace",
"path": "daemon.log",
"rotate": {
"maxSize": "10m",
"maxFiles": 3
}
}
}
}
Legacy fields log.level and log.format are still supported and map to the new destination settings.
Password authentication
You can require a password to connect to the daemon. When set, all HTTP and WebSocket clients must authenticate. Only the /api/health liveness endpoint is exempt, so that process supervisors and load balancers can probe without credentials.
The easiest way to set a password is with the CLI:
paseo daemon set-password
This prompts for a password, writes the bcrypt hash to config.json, and tells you to restart the daemon. Authentication is a startup setting, so paseo reload will also report it as restart-required.
Alternatively, set the PASEO_PASSWORD environment variable (plaintext, hashed automatically at startup):
PASEO_PASSWORD=my-secret paseo daemon start
Or write the hash directly in config.json:
{
"daemon": {
"auth": {
"password": "$2b$12$..."
}
}
}
After setting a password, restart the daemon for the change to take effect.
Connecting with a password
The CLI picks up a password from, in order:
-
The
passwordquery parameter on atcp://host URI:paseo --host "tcp://192.168.1.10:6767?password=my-secret" ls -
The
PASEO_PASSWORDenvironment variable, used as a fallback when the host carries no embedded password (works forlocalhost:6767, barehost:port, ortcp://hosts without apassword=query):PASEO_PASSWORD=my-secret paseo ls PASEO_PASSWORD=my-secret paseo --host 192.168.1.10:6767 ls
A password= in the URI always wins over the env var, so you can keep PASEO_PASSWORD set globally and still target a different daemon by spelling its password into the URI.
In the mobile app, enter the password in the direct connection setup screen.
Relay
New homes write daemon.relay.enabled: false. Paseo asks before enabling relay when you pair a device; existing homes keep their saved value. See Connectivity to choose and configure a connection method, and Security for the relay encryption model.
Set the persisted value in config.json:
{
"daemon": {
"relay": {
"enabled": true
}
}
}
PASEO_RELAY_ENABLED=true|false overrides the file for a foreground deployment. Managed start uses the file. End and relaunch a deployment to remove its override before changing relay from the app or paseo daemon pair --relay.
Common env vars
PASEO_HOME, set Paseo home directoryPASEO_HOST, set the daemon target for CLI commandsPASEO_PASSWORD, on the daemon, the password to require (plaintext, hashed at startup); on the CLI, the password used to connect when the host URI doesn't include onePASEO_LISTEN, overridedaemon.listenPASEO_RELAY_ENABLED, enable or disable the outbound relay for this daemon launchPASEO_HOSTNAMES, override/extenddaemon.hostnamesPASEO_ALLOWED_HOSTS, deprecated alias forPASEO_HOSTNAMESPASEO_WEB_UI_ENABLED, enable or disable the daemon-served web UIPASEO_WEB_UI_DIST_DIR, override the daemon web UI build directoryPASEO_TRUSTED_PROXIES, configure trusted reverse proxy ranges forX-Forwarded-*headersPASEO_LOG_CONSOLE_LEVEL, overridelog.console.levelPASEO_LOG_FILE_LEVEL, overridelog.file.levelPASEO_LOG_FILE_PATH, overridelog.file.pathPASEO_LOG_FILE_ROTATE_SIZE, overridelog.file.rotate.maxSizePASEO_LOG_FILE_ROTATE_COUNT, overridelog.file.rotate.maxFilesPASEO_LOG,PASEO_LOG_FORMAT, legacy log overrides (still supported)OPENAI_API_KEY,OPENAI_BASE_URL, fallback speech credentials and base URL when neither a site value nor an endpoint env value is setOPENAI_STT_API_KEY,OPENAI_STT_BASE_URL, speech-to-text key and base URL fallbacks when no value is saved at the dictation or voice-mode STT siteOPENAI_TTS_API_KEY,OPENAI_TTS_BASE_URL, text-to-speech key and base URL fallbacks when no value is saved at the voice-mode TTS sitePASEO_VOICE_LLM_PROVIDER, override voice LLM provider (claude,codex,opencode)PASEO_DICTATION_STT_PROVIDER,PASEO_VOICE_STT_PROVIDER,PASEO_VOICE_TTS_PROVIDER, override voice provider selection (localoropenai)PASEO_LOCAL_MODELS_DIR, control local model directoryPASEO_DICTATION_LOCAL_STT_MODEL, override local dictation STT modelPASEO_VOICE_LOCAL_STT_MODEL,PASEO_VOICE_LOCAL_TTS_MODEL, override local voice STT/TTS modelsPASEO_DICTATION_LANGUAGE,PASEO_VOICE_LANGUAGE, override dictation and voice STT languagePASEO_VOICE_LOCAL_TTS_SPEAKER_ID,PASEO_VOICE_LOCAL_TTS_SPEED, optional local voice TTS tuning
Schema
For editor autocomplete/validation, set $schema to:
https://paseo.sh/schemas/paseo.config.v1.json