Files
paseo/public-docs/configuration.md
T
Mohamed Boudra 74a7e5803b Choose voice providers and local speech models from a Voice settings page (#7295)
* 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.
2026-10-26 14:19:02 +07:00

13 KiB
Raw Blame History

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: info and above
  • File ($PASEO_HOME/daemon.log): trace and above
  • File rotation: 10m max file size, 3 rotated 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:

  1. The password query parameter on a tcp:// host URI:

    paseo --host "tcp://192.168.1.10:6767?password=my-secret" ls
    
  2. The PASEO_PASSWORD environment variable, used as a fallback when the host carries no embedded password (works for localhost:6767, bare host:port, or tcp:// hosts without a password= 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 directory
  • PASEO_HOST, set the daemon target for CLI commands
  • PASEO_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 one
  • PASEO_LISTEN, override daemon.listen
  • PASEO_RELAY_ENABLED, enable or disable the outbound relay for this daemon launch
  • PASEO_HOSTNAMES, override/extend daemon.hostnames
  • PASEO_ALLOWED_HOSTS, deprecated alias for PASEO_HOSTNAMES
  • PASEO_WEB_UI_ENABLED, enable or disable the daemon-served web UI
  • PASEO_WEB_UI_DIST_DIR, override the daemon web UI build directory
  • PASEO_TRUSTED_PROXIES, configure trusted reverse proxy ranges for X-Forwarded-* headers
  • PASEO_LOG_CONSOLE_LEVEL, override log.console.level
  • PASEO_LOG_FILE_LEVEL, override log.file.level
  • PASEO_LOG_FILE_PATH, override log.file.path
  • PASEO_LOG_FILE_ROTATE_SIZE, override log.file.rotate.maxSize
  • PASEO_LOG_FILE_ROTATE_COUNT, override log.file.rotate.maxFiles
  • PASEO_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 set
  • OPENAI_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 site
  • OPENAI_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 site
  • PASEO_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 (local or openai)
  • PASEO_LOCAL_MODELS_DIR, control local model directory
  • PASEO_DICTATION_LOCAL_STT_MODEL, override local dictation STT model
  • PASEO_VOICE_LOCAL_STT_MODEL, PASEO_VOICE_LOCAL_TTS_MODEL, override local voice STT/TTS models
  • PASEO_DICTATION_LANGUAGE, PASEO_VOICE_LANGUAGE, override dictation and voice STT language
  • PASEO_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