fix(daemon): keep lifecycle operations on the selected instance

Restart reconstructed launch inputs and address fallback could redirect a home-selected shutdown to another daemon. Let supervisors publish bound endpoints, retain their worker restart contract, and share captured local lifecycle ownership between CLI and Desktop.
This commit is contained in:
Mohamed Boudra
2026-09-09 14:09:53 +02:00
parent fdf3b4b47f
commit 264276880e
166 changed files with 3824 additions and 4467 deletions
+47 -10
View File
@@ -276,18 +276,55 @@ Detaching is an explicit lifecycle action, not a creation flag. The agent keeps
## Daemon management
Define an instance once, then start its saved configuration:
```bash
paseo daemon start # Start the daemon
paseo daemon start --web-ui # Start and serve the bundled web UI
paseo daemon status # Check status
paseo reload # Reload config.json (top-level alias)
paseo daemon reload # Reload config.json
paseo daemon stop # Stop the daemon
paseo daemon config set daemon.listen 127.0.0.1:6799 --home ~/paseo-test
paseo daemon config set daemon.relay.enabled false --home ~/paseo-test
paseo daemon start --home ~/paseo-test
paseo project ls --home ~/paseo-test
paseo daemon restart --home ~/paseo-test
paseo daemon stop --home ~/paseo-test
```
Reload validates the whole file, applies runtime-safe changes, and reports `appliedPaths`, `restartRequiredPaths`, and `overrideControlledPaths`. Human output prints `paseo daemon restart` only when a changed setting needs it. Use `--json` or `--format yaml` for the structured result. Run `paseo --host <target> reload` to reload a remote daemon's own configuration file. An older host that does not support reload returns an update-host error.
`start` runs in the background and reports the actual listening address and supervisor PID. It accepts only home selection and `--timeout <seconds>` (default 600). If waiting times out, the supervisor remains running: use the printed status, log, and stop instructions. A worker that exits before becoming ready makes startup fail.
Use `PASEO_HOME` to run multiple isolated daemon instances.
`restart` requests a replacement worker from the existing supervisor. It rereads the file and retains the supervisor's original environment and arguments. Success confirms a different ready worker, following a changed address for a home target. It never starts a stopped daemon or refreshes the supervisor binary. A timeout reports whether the request was acknowledged; it does not prove why reconnection failed.
`stop --home` waits for that local supervisor to exit. On POSIX it signals the supervisor without contacting a TCP endpoint. On Windows it uses the ready daemon's shutdown RPC; an unbound instance requires explicit `--force`. `--force` permits forced process-tree cleanup after the graceful timeout (default 15 seconds). `stop --host` only reports **shutdown requested**; remote process exit is not verified. A service manager may start another instance after the captured supervisor exits.
`status` separates local supervisor state, its published endpoint, the configured address, and RPC reachability. A stopped home is never probed at its configured address. An unbound live supervisor is **not ready**.
`reload` validates the file, applies runtime-safe changes, and reports `appliedPaths`, `restartRequiredPaths`, and `overrideControlledPaths`. It never implicitly restarts. Use `--json` or `--format yaml` for structured results. An older host lacking the capability reports that it needs an update.
The root aliases `start`, `status`, `restart`, `reload`, and `pair` use the same commands as `daemon`. Root `run` and `stop` remain agent operations.
### Foreground deployments and migration
Use environment overrides with the foreground deployment command:
```bash
PASEO_LISTEN=127.0.0.1:6799 PASEO_RELAY_ENABLED=false paseo daemon run --home ~/paseo-test
```
It stays attached until the supervisor exits or you cancel, without a readiness timeout. Worker restart retains these launch inputs. Stop and relaunch the deployment to change them. If the home already has a live supervisor, `run` returns `already_running` without owning or launching a foreground process.
Managed `start` ignores inherited daemon-setting overrides, including `PORT`, `PASEO_LISTEN`, relay, voice, and web UI settings. It preserves provider credentials and executable/runtime controls. `start --foreground` is removed; use `daemon run`. Former start/restart configuration flags such as `--port`, `--no-relay`, and `--web-ui` fail before side effects, with the corresponding `config set` migration. See [configuration edits](/docs/configuration#apply-changes).
### Select one daemon
Every daemon-connected CLI command accepts global `--home` or `--host`, before or after the command. A home selects a local supervisor's published endpoint; a host selects an explicit endpoint. There is no configured-address or default-port fallback.
| Selectors | Result |
| ------------------------------------------ | ------------------------------------------------------ |
| `--home` | That local home, overriding both environment selectors |
| `--host` | That endpoint, overriding both environment selectors |
| Both flags, or conflicting duplicate flags | `TARGET_AMBIGUOUS` |
| Only `PASEO_HOME` or only `PASEO_HOST` | The corresponding target |
| Both environment selectors, without a flag | `TARGET_AMBIGUOUS` |
| Neither | Default local home, `~/.paseo` |
Local-only `start`, `daemon run`, `config`, `onboard`, and `set-password` reject explicit `--host` and ignore `PASEO_HOST`. Endpoint operations retain TCP, Unix socket, Windows pipe, SSH, and pairing-offer transports. A host-side CLI controlling a container needs `--host` or `PASEO_HOST`.
## Hub
@@ -329,12 +366,12 @@ The global `--host` option accepts either a local target (`host:port`, a unix so
Get an offer URL from the daemon you want to control:
```bash
paseo daemon pair # asks before enabling relay, then prints the QR and link
paseo daemon pair # prints the QR and link when relay is enabled
paseo daemon pair --relay # enables relay without prompting
paseo daemon pair --json # structured output; never prompts
```
Relay is off for new installations. In non-interactive or JSON mode, a disabled relay returns a `RELAY_DISABLED` error; pass `--relay` to provide explicit consent. Relay pairing is end-to-end encrypted. See [Security](/docs/security).
Relay is off for new installations. A disabled relay returns a `RELAY_DISABLED` error; pass `--relay` to provide explicit consent. For a stopped home, pairing is labelled offline; `--relay` saves relay enablement and the offer includes a start instruction. A live but unreachable home never falls back to an offline identity. Relay pairing is end-to-end encrypted. See [Security](/docs/security).
Use it from anywhere:
+23 -15
View File
@@ -1,6 +1,6 @@
---
title: Configuration
description: Configure Paseo via config.json, environment variables, and CLI overrides.
description: Configure managed instances and foreground deployments.
nav: Configuration
order: 40
category: Configuration
@@ -8,7 +8,7 @@ category: Configuration
# Configuration
Paseo loads configuration from a single JSON file in your Paseo home directory, with optional environment variable and CLI overrides.
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
@@ -18,18 +18,13 @@ By default, Paseo uses `~/.paseo` as its home directory. The configuration file
~/.paseo/config.json
```
You can change the home directory by setting `PASEO_HOME` or passing `--home` to `paseo daemon start`.
You can change the home directory by setting `PASEO_HOME` or selecting `--home` on a CLI command.
## Precedence
Paseo merges configuration in this order:
Managed `start` and Desktop use defaults, then `config.json`. Inherited daemon-setting environment variables are removed from their launches.
1. Defaults
2. `config.json`
3. Environment variables
4. CLI flags
Lists append across sources (for example, `hostnames` and `cors.allowedOrigins`).
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
@@ -51,7 +46,19 @@ Minimal example that configures listening address, hostnames, and MCP:
## Apply changes
After saving `config.json`, reload it:
Edit the selected file through the CLI:
```bash
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:
```bash
paseo reload
@@ -69,7 +76,7 @@ New homes keep relay disabled when you remove `daemon.relay.enabled`. A daemon w
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.
Environment variables and daemon start 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. Remove the override and restart the daemon if you want the file value to become authoritative.
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
@@ -104,13 +111,14 @@ The daemon can serve the browser web client from the same HTTP server. This is e
Enable it from the CLI:
```bash
paseo daemon start --web-ui
paseo daemon config set features.webUi.enabled true
paseo daemon start
```
Or set the environment variable:
```bash
PASEO_WEB_UI_ENABLED=true paseo daemon start
PASEO_WEB_UI_ENABLED=true paseo daemon run
```
Or persist it in `config.json`:
@@ -225,7 +233,7 @@ Set the persisted value in `config.json`:
}
```
`PASEO_RELAY_ENABLED=true|false` overrides the persisted value for that daemon launch. The matching `paseo daemon start --relay` and `--no-relay` flags have the same authority. Remove the launch override before changing relay from Paseo Desktop or `paseo daemon pair --relay`.
`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
+2
View File
@@ -41,6 +41,8 @@ The image:
The image does not bundle agent CLIs such as Claude Code, Codex, OpenCode, Copilot, or Pi. Add the agents you use with a small child image.
Host-side CLI commands select the container explicitly, for example `paseo project ls --host 127.0.0.1:6767`. Without an endpoint selector the CLI looks for a local home’s supervisor. Container environment settings are deployment overrides; worker restart preserves them. Your container manager owns full supervisor replacement.
## Docker Compose
```yaml
+10 -8
View File
@@ -20,16 +20,17 @@ The web app ships inside the daemon package, so the UI you serve always matches
## Enable it
The bundled web UI is off by default. Turn it on when you start the daemon:
The bundled web UI is off by default. Save the setting, then start the daemon:
```bash
paseo daemon start --web-ui
paseo daemon config set features.webUi.enabled true
paseo daemon start
```
Or with an environment variable:
Or run a foreground deployment with an environment variable:
```bash
PASEO_WEB_UI_ENABLED=true paseo daemon start
PASEO_WEB_UI_ENABLED=true paseo daemon run
```
Or persist it in `config.json` so it survives restarts:
@@ -73,7 +74,8 @@ The rest of this page builds from local to public. **Verify a direct connection
By default the daemon listens on `127.0.0.1:6767`, reachable only from the same machine. To reach it from other devices, bind it to a network interface:
```bash
paseo daemon start --web-ui --listen 0.0.0.0:6767
paseo daemon config set daemon.listen 0.0.0.0:6767
paseo daemon start
```
> **Anyone who can reach the listening address can use your agents.** Before you bind beyond localhost, set a password and review your host allowlist. The relay pairing path avoids this entirely by keeping the daemon bound to localhost, see [Security](/docs/security).
@@ -83,7 +85,7 @@ Two things to configure when you expose the daemon directly:
1. **Set a password** so only authorized clients can connect:
```bash
PASEO_PASSWORD=my-secret paseo daemon start --web-ui --listen 0.0.0.0:6767
paseo daemon set-password
```
See [password authentication](/docs/configuration#password-authentication) for the persistent setup. Password auth controls access; it does not encrypt traffic, put TLS in front of it (below) on any untrusted network.
@@ -91,7 +93,7 @@ Two things to configure when you expose the daemon directly:
2. **Allow your hostname** so the daemon's DNS-rebinding protection accepts requests for your domain:
```bash
paseo daemon start --web-ui --listen 0.0.0.0:6767 --hostnames ".example.com"
paseo daemon config set daemon.hostnames '[".example.com"]'
```
See [DNS rebinding protection](/docs/security#dns-rebinding-protection) for how the host allowlist works.
@@ -179,7 +181,7 @@ If your proxy reaches the daemon from another address, as in some Docker, LAN, o
`PASEO_TRUSTED_PROXIES` accepts the same comma-separated values:
```bash
PASEO_TRUSTED_PROXIES=loopback,172.16.0.0/12 paseo daemon start --web-ui
PASEO_TRUSTED_PROXIES=loopback,172.16.0.0/12 PASEO_WEB_UI_ENABLED=true paseo daemon run
```
Only use `trustedProxies: true` when your final trusted proxy overwrites client-supplied `X-Forwarded-*` headers. Otherwise a client could spoof forwarded header values.