Usage & Lifecycle
Switching environments
The Relay endpoint is per yoooclaw profile (relay.url / cloud.host in each profile's config.json). Use env to switch the active profile and rebuild the websocket service group in one step:
hermes yoooclaw env # show the active environment + Relay status
hermes yoooclaw env test # switch to the `test` profile
hermes yc env test # equivalent short alias
hermes yoooclaw env default # switch back to the `default` (prod) profile/yoooclaw env <profile> and /yc env <profile> do the same from a chat session. The built-in default, test, and development profiles are initialized on first use with the matching Relay endpoint. Changing the profile changes the lifecycle generation, so the watchdog releases the old profile's storage writer lock, claims the new one, and rebuilds the tunnels — exactly one profile is claimed at any time. An unknown profile name fails without disturbing the current connection.
The Relay apiKey stays account-global (~/.yoooclaw/credentials.json), independent of profile. Switch it with:
yoooclaw auth set-default-api-key <label>CLI profiles use openclaw-service*.yoooclaw.com for the Relay tunnel; APP chat shares the same connection. The installer repairs stale legacy URLs if an old plugin version persisted them into CLI profiles.
Daemonless storage model
In plugin mode there is no CLI daemon. The plugin process is both the tunnel owner and the sole writer of the profile's storage:
| Plugin mode | Standalone CLI mode | |
|---|---|---|
| Trigger | Hermes loads the yoooclaw / yoooclaw_app plugins | CLI-only install, you run daemon start yourself |
| Storage writer | The plugin process (sole) | The daemon (sole) |
| Tunnel owner | The plugin | The daemon's own Relay WS |
| CLI read-only commands | Work normally (read files directly) | Work normally |
yoooclaw daemon start | Refused: YOOOCLAW_DAEMON_DISABLED_BY_PLUGIN | Works normally |
The two modes are mutually exclusive via ~/.yoooclaw/profiles/<profile>/writer.lock — an OS advisory lock, released automatically when the process dies, in the same lock namespace across languages (Python plugin / Go CLI). On startup the plugin stops any leftover daemon and then claims the lock; disabling the plugin releases it, and yoooclaw daemon start returns you to standalone mode with no data migration required (the storage layout is a contract both sides share).
There's also a separate account-level Relay consumer lock (standalone-relay.flock, compatible with the CLI-side lock of the same name), outside the profile directory, that must be acquired before opening any Relay connection — this prevents two profiles, or the plugin and the standalone CLI, from consuming the same account's Relay stream at once and double-writing messages. If the active-profile pointer references a manually-deleted profile directory, it now safely falls back to default instead of erroring.
Inbound frames land directly in the plugin's in-process storage engine: /notifications, recordings.result.write, images.sync, and friends are written following the CLI's directory contract (tmp file + atomic replace), and events like recording.status are forwarded to the tunnel over an in-process event bus rather than a local HTTP callback. lightrules.* and /light/send frames become embedded-CLI subprocess calls — the rule files belong to the CLI, which avoids a second writer.
Explicitly not available in plugin mode: recordings.retranscribe and asr.init return YOOOCLAW_NOT_IMPLEMENTED (in the Hermes setup, ASR is done on the App side).
Lifecycle management
The plugin manages the storage claim and the openclaw-service tunnels as a single transaction boundary. On startup it computes a lifecycle generation (the first 24 hex chars of a SHA-256) from the plugin version, active profile, Relay environment, PHONE_NOTIFICATIONS_ENV, openclaw-service URL, and api-key fingerprint. Any change makes the generation mismatch and triggers a rebuild of the whole group.
hermes yoooclaw lifecycle status
hermes yoooclaw lifecycle restart
hermes yc lifecycle statuslifecycle restart doesn't reconnect on the spot: it sets a restart flag, and the watchdog rebuilds the tunnels (disconnect + connect) on its next poll, by default within 5 seconds.
The lifecycle snapshot schema is now v3, adding pid, instanceId, hermesProfile, profile, heartbeatAt, lastControlRequestId, lastControlStatus, and pendingControlRequestId. When --hermes-profile/HERMES_PROFILE is set, the snapshot is split per Hermes profile (lifecycle/<hermesProfile>.json) instead of a single flat lifecycle.json. There's also a new cross-process control file (control/<profile>.json): a short-lived, one-off CLI invocation can write to it to request a gateway restart, which the long-running gateway process picks up and ACKs in its own snapshot. hermes yoooclaw lifecycle status now reads this persisted snapshot and reports running/stale/stopped/mismatch based on heartbeat freshness (stale past 30 seconds by default), rather than synthesizing "stopped" whenever no local transport happens to be registered. The Relay handshake also now carries clientType/version/capabilities (e.g. agent-commands-v1) and a stable instanceId, which helps when debugging multi-process/multi-instance setups.
The APP adapter runs a watchdog while connected: a generation change rebuilds the group, and so does the Relay websocket staying disconnected past YOOOCLAW_HERMES_WS_RESTART_AFTER (default 30 seconds). Set YOOOCLAW_HERMES_LIFECYCLE_WATCH_INTERVAL to tune the poll interval (default 5 seconds); failed recoveries use exponential backoff capped by YOOOCLAW_HERMES_LIFECYCLE_MAX_BACKOFF (default 60 seconds).
A failed storage claim (the writer lock is held by another process) does not block connecting: chat and queries keep working, ingest just takes the fallback path, and the reason is recorded in the log and in lifecycle status.
Surface area
The tool plugin exposes notification, recording (recording_list also supports from/to filtering by recording time), captured web-page (synced_web_page_list / search / path / storage_path, with from/to time-range filtering on capturedAt — this group of tools used to be named web_*, but since that read as Hermes's built-in live web_search, it was renamed to synced_web_page_*), image, Relay, light-control, light-rule CRUD, status reporting (the tool is still named daemon_status; in daemonless mode it reports the storage claim and tunnel state), and doctor tools. The APP platform adapter connects directly to Relay and translates the existing OpenClaw-compatible chat.send, chat.history, chat.abort, and sessions.* RPC frames into Hermes messages and APP-owned session state.
Common lifecycle-related environment variables
Most setups never need to touch these — they matter when you're adjusting timeouts, disabling an automatic behavior, or doing local development:
| Variable | Default | Effect |
|---|---|---|
YOOOCLAW_HERMES_CLAIM_STORAGE | on | Set to 0 to stop the plugin from claiming local storage at load; ingest then takes the fallback path. When unset it inherits the value of YOOOCLAW_HERMES_AUTO_START_DAEMON (legacy name). |
YOOOCLAW_HERMES_DIRECT_INGEST | on | Set to 0 to send ingest back down the old daemon-HTTP path (kill switch). |
YOOOCLAW_HERMES_WS_RESTART_AFTER | 30 (seconds) | How long the Relay websocket must stay disconnected before the whole group restarts. |
YOOOCLAW_HERMES_LIFECYCLE_WATCH_INTERVAL | 5 (seconds) | Watchdog poll interval. |
YOOOCLAW_HERMES_LIFECYCLE_MAX_BACKOFF | 60 (seconds) | Cap on exponential backoff after failed recoveries. |
YOOOCLAW_HERMES_LIFECYCLE_MISMATCH_STREAK | 2 | How many consecutive mismatch readings are required before a rebuild (avoids over-reacting to probe noise). |
YOOOCLAW_CLI_PATH | unset | Explicitly points at a CLI binary, entirely bypassing the embedded CLI's PATH injection — only meant for local development against a non-embedded CLI build. |
YOOOCLAW_HERMES_INSTALL_CLI | on | Set to 0 to disable PATH injection (shim install) at plugin load. |
YOOOCLAW_HERMES_INSTALL_SKILLS | on | Set to 0 to disable the bundled-skills copy step. |
HERMES_PROFILE | unset | Names the Hermes profile to target when Hermes runs under a named profile — the installer and gateway-restart commands use it to locate config and split the lifecycle snapshot; equivalent to the installer's --hermes-profile. |
Next steps
- Debugging & Troubleshooting — log locations, common failures.
- Standalone CLI — the CLI bundled in the plugin is this same one; read-only commands and the storage layout are fully shared.