Compare commits
95 Commits
vincentkoc
...
feat/volce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f2e2f7bab | ||
|
|
a3b36799a9 | ||
|
|
7bd5c5d5a4 | ||
|
|
892620ddab | ||
|
|
c62a6e7040 | ||
|
|
14b3743228 | ||
|
|
10b8839a82 | ||
|
|
f64d5ddf60 | ||
|
|
f23da067f6 | ||
|
|
92cada2aca | ||
|
|
2706cbd6d7 | ||
|
|
3cfb402bda | ||
|
|
25db01fe08 | ||
|
|
21bb46d304 | ||
|
|
7a27e2648a | ||
|
|
f48698a50b | ||
|
|
cf82614259 | ||
|
|
26eb1f781d | ||
|
|
c2874aead7 | ||
|
|
50a8942c07 | ||
|
|
e217f8c3f7 | ||
|
|
8c1518f0f3 | ||
|
|
b43aadc34c | ||
|
|
c529bafdc3 | ||
|
|
577e5cc74b | ||
|
|
621d8e1312 | ||
|
|
6cb7e16d40 | ||
|
|
24d18d0d72 | ||
|
|
be7f825006 | ||
|
|
8b1fe0d1e2 | ||
|
|
36a0df423d | ||
|
|
1835dec200 | ||
|
|
b2d84528f8 | ||
|
|
f4c89aa66e | ||
|
|
9516ace3c9 | ||
|
|
14b0d2b816 | ||
|
|
4cd7d95746 | ||
|
|
f265d45840 | ||
|
|
d25a106628 | ||
|
|
f202e73077 | ||
|
|
08e020881d | ||
|
|
356d61aacf | ||
|
|
6aa11f3092 | ||
|
|
073651fb57 | ||
|
|
b577228d6b | ||
|
|
084f621025 | ||
|
|
2b76901f35 | ||
|
|
99048dbec2 | ||
|
|
810218756d | ||
|
|
ede496fa1a | ||
|
|
fbb79d4013 | ||
|
|
cb84c537f4 | ||
|
|
e393d7aa5b | ||
|
|
dff61a10e1 | ||
|
|
11f6bea598 | ||
|
|
8db5e77ffa | ||
|
|
da844d6411 | ||
|
|
ac2ef69454 | ||
|
|
635b6298e3 | ||
|
|
283029bdea | ||
|
|
6007941f04 | ||
|
|
5cc631cc9c | ||
|
|
55aaeb5085 | ||
|
|
2cdbadee1f | ||
|
|
6b2f2811dc | ||
|
|
220bd95eff | ||
|
|
c6ee14d60e | ||
|
|
f81522af2e | ||
|
|
75d4f6d51b | ||
|
|
eccff0b6c0 | ||
|
|
9231d7d30f | ||
|
|
677384c519 | ||
|
|
e1cb73cdeb | ||
|
|
3f19259843 | ||
|
|
d2a7293744 | ||
|
|
dcf2c6d7f1 | ||
|
|
e36245bd37 | ||
|
|
ef42fe0094 | ||
|
|
b5a77b9cb2 | ||
|
|
d7891badda | ||
|
|
78caf9ec3d | ||
|
|
e93e67bc8e | ||
|
|
7c593cd333 | ||
|
|
79183852f9 | ||
|
|
4c4147fb0a | ||
|
|
5eca08dab7 | ||
|
|
12d75ff7f5 | ||
|
|
436f79839b | ||
|
|
325992b777 | ||
|
|
c20d519e05 | ||
|
|
9abab6a2c9 | ||
|
|
fe609c0c77 | ||
|
|
0bee3f337a | ||
|
|
f4a59eb5d8 | ||
|
|
187f4ea41f |
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -13,7 +13,7 @@ body:
|
||||
attributes:
|
||||
label: Summary
|
||||
description: One-sentence statement of what is broken.
|
||||
placeholder: After upgrading to 2026.2.13, Telegram thread replies fail with "reply target not found".
|
||||
placeholder: After upgrading to <version>, <channel> behavior regressed from <prior version>.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -48,7 +48,7 @@ body:
|
||||
attributes:
|
||||
label: OpenClaw version
|
||||
description: Exact version/build tested.
|
||||
placeholder: 2026.2.13
|
||||
placeholder: <version such as 2026.2.17>
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@@ -83,7 +83,7 @@ body:
|
||||
- Frequency (always/intermittent/edge case)
|
||||
- Consequence (missed messages, failed onboarding, extra cost, etc.)
|
||||
placeholder: |
|
||||
Affected: Telegram group users on 2026.2.13
|
||||
Affected: Telegram group users on <version>
|
||||
Severity: High (blocks replies)
|
||||
Frequency: 100% repro
|
||||
Consequence: Agents cannot respond in threads
|
||||
@@ -92,4 +92,4 @@ body:
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any context that helps triage but does not fit above.
|
||||
placeholder: Regression started after upgrade from 2026.2.12; temporary workaround is restarting gateway every 30m.
|
||||
placeholder: Regression started after upgrade from <previous-version>; temporary workaround is ...
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -2,7 +2,7 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Onboarding
|
||||
url: https://discord.gg/clawd
|
||||
about: New to OpenClaw? Join Discord for setup guidance from Krill in \#help.
|
||||
about: "New to OpenClaw? Join Discord for setup guidance in #help."
|
||||
- name: Support
|
||||
url: https://discord.gg/clawd
|
||||
about: Get help from Krill and the community on Discord in \#help.
|
||||
about: "Get help from the OpenClaw community on Discord in #help."
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -21,7 +21,7 @@ body:
|
||||
attributes:
|
||||
label: Problem to solve
|
||||
description: What user pain this solves and why current behavior is insufficient.
|
||||
placeholder: Teams cannot distinguish agent personas in mixed channels, causing misrouted follow-ups.
|
||||
placeholder: Agents cannot distinguish persona context in mixed channels, causing misrouted follow-ups.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
147
CHANGELOG.md
@@ -2,77 +2,49 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## 2026.2.20 (Unreleased)
|
||||
## 2026.2.21 (Unreleased)
|
||||
|
||||
### Changes
|
||||
|
||||
- Docs: fix FAQ typos and add documentation spellcheck automation with a custom codespell dictionary/ignore list, including CI coverage. (#22457) Thanks @vincentkoc.
|
||||
- Dev tooling: add dead-code scans to CI via Knip/ts-prune/ts-unused-exports and report unused dependencies/exports in non-blocking checks. (#22468) Thanks @vincentkoc.
|
||||
- Dev tooling: move `@larksuiteoapi/node-sdk` out of root `package.json` and keep it scoped to `extensions/feishu` where it is used. (#22471) Thanks @vincentkoc.
|
||||
- Dev tooling: remove unused root dependency `signal-utils` from core manifest after confirming it was only used by extension-only paths. (#22471) Thanks @vincentkoc.
|
||||
- Dev tooling: remove unused root devDependency `ollama` now that native Ollama support uses local HTTP transport code paths only. (#22471) Thanks @vincentkoc.
|
||||
- Dev tooling: remove unused root devDependencies `@lit/context` and `@lit-labs/signals` flagged as unused by Knip dead-code reports. (#22471) Thanks @vincentkoc.
|
||||
- Dev tooling: remove unused root dependency `lit` that is now scoped to `ui/` package dependencies. (#22471) Thanks @vincentkoc.
|
||||
- Dev tooling: remove unused root dependencies `long` and `rolldown`; keep A2UI bundling functional by falling back to `pnpm dlx rolldown` when the binary is not locally installed. (#22481) Thanks @vincentkoc.
|
||||
- Dev tooling: fix A2UI bundle resolution for removed root `lit` deps by resolving `lit`, `@lit/context`, `@lit-labs/signals`, and `signal-utils` from UI workspace dependencies in `rolldown.config.mjs` during bundling. (#22481) Thanks @vincentkoc.
|
||||
- Dev tooling: simplify `canvas-a2ui` bundling script by removing temporary vendored `node_modules` symlink logic now that `ui` workspace dependencies are explicit. (#22481) Thanks @vincentkoc.
|
||||
- Telegram: dedupe sent-message cache storage by removing redundant per-chat Set tracking and using the timestamp map as the single source of truth. (#22127) thanks @TaKO8Ki.
|
||||
- Agents/Subagents: default subagent spawn depth now uses shared `maxSpawnDepth=2`, enabling depth-1 orchestrator spawning by default while keeping depth policy checks consistent across spawn and prompt paths. (#22223) Thanks @tyler6204.
|
||||
- Providers/Onboarding: add Volcano Engine (Doubao) and BytePlus providers/models (including coding variants), wire onboarding auth choices for interactive + non-interactive flows, and align docs to `volcengine-api-key`. (#7967) Thanks @funmore123.
|
||||
- Channels/CLI: add per-account/channel `defaultTo` outbound routing fallback so `openclaw agent --deliver` can send without explicit `--reply-to` when a default target is configured. (#16985) Thanks @KirillShchetinin.
|
||||
- iOS/Chat: clean chat UI noise by stripping inbound untrusted metadata/timestamp prefixes, formatting tool outputs into concise summaries/errors, compacting the composer while typing, and supporting tap-to-dismiss keyboard in chat view. (#22122) thanks @mbelinky.
|
||||
- iOS/Watch: bridge mirrored watch prompt notification actions into iOS quick-reply handling, including queued action handoff until app model initialization. (#22123) thanks @mbelinky.
|
||||
- iOS/Tests: cover IPv4-mapped IPv6 loopback in manual TLS policy tests for connect validation paths. (#22045) Thanks @mbelinky.
|
||||
- iOS/Gateway: stabilize background wake and reconnect behavior with background reconnect suppression/lease windows, BGAppRefresh wake fallback, location wake hook throttling, and APNs wake retry+nudge instrumentation. (#21226) thanks @mbelinky.
|
||||
- Auto-reply/UI: add model fallback lifecycle visibility in verbose logs, /status active-model context with fallback reason, and cohesive WebUI fallback indicators. (#20704) Thanks @joshavant.
|
||||
- Channels: allow per-channel model overrides via `channels.modelByChannel` and note them in /status. Thanks @thewilloftheshadow.
|
||||
- Telegram/Streaming: simplify preview streaming config to `channels.telegram.streaming` (boolean), auto-map legacy `streamMode` values, and remove block-vs-partial preview branching. (#22012) thanks @obviyus.
|
||||
- Discord/Streaming: add stream preview mode for live draft replies with partial/block options and configurable chunking. Thanks @thewilloftheshadow. Inspiration @neoagentic-ship-it.
|
||||
- Discord: add configurable ephemeral defaults for slash-command responses. (#16563) Thanks @wei.
|
||||
- Discord/Telegram: add configurable lifecycle status reactions for queued/thinking/tool/done/error phases with a shared controller and emoji/timing overrides. Thanks @wolly-tundracube and @thewilloftheshadow.
|
||||
- Discord/Voice: add voice channel join/leave/status via `/vc`, plus auto-join configuration for realtime voice conversations. Thanks @thewilloftheshadow.
|
||||
- Discord: add configurable ephemeral defaults for slash-command responses. (#16563) Thanks @wei.
|
||||
- Discord: support updating forum `available_tags` via channel edit actions for forum tag management. (#12070) Thanks @xiaoyaner0201.
|
||||
- Channels: allow per-channel model overrides via `channels.modelByChannel` and note them in /status. Thanks @thewilloftheshadow.
|
||||
- Discord: include channel topics in trusted inbound metadata on new sessions. Thanks @thewilloftheshadow.
|
||||
- Docs/Discord: document forum channel thread creation flows and component limits. Thanks @thewilloftheshadow.
|
||||
- iOS/Chat: clean chat UI noise by stripping inbound untrusted metadata/timestamp prefixes, formatting tool outputs into concise summaries/errors, compacting the composer while typing, and supporting tap-to-dismiss keyboard in chat view. (#22122) thanks @mbelinky.
|
||||
- iOS/Watch: bridge mirrored watch prompt notification actions into iOS quick-reply handling, including queued action handoff until app model initialization. (#22123) thanks @mbelinky.
|
||||
- iOS/Gateway: stabilize background wake and reconnect behavior with background reconnect suppression/lease windows, BGAppRefresh wake fallback, location wake hook throttling, and APNs wake retry+nudge instrumentation. (#21226) thanks @mbelinky.
|
||||
- Auto-reply/UI: add model fallback lifecycle visibility in verbose logs, /status active-model context with fallback reason, and cohesive WebUI fallback indicators. (#20704) Thanks @joshavant.
|
||||
- MSTeams: dedupe sent-message cache storage by removing duplicate per-message Set storage and using timestamps Map keys as the single membership source. (#22514) Thanks @TaKO8Ki.
|
||||
- Agents/Subagents: default subagent spawn depth now uses shared `maxSpawnDepth=2`, enabling depth-1 orchestrator spawning by default while keeping depth policy checks consistent across spawn and prompt paths. (#22223) Thanks @tyler6204.
|
||||
- Security/Agents: make owner-ID obfuscation use a dedicated HMAC secret from configuration (`ownerDisplaySecret`) and update hashing behavior so obfuscation is decoupled from gateway token handling for improved control. (#7343) Thanks @vincentkoc.
|
||||
- Security/Infra: switch gateway lock and tool-call synthetic IDs from SHA-1 to SHA-256 with unchanged truncation length to strengthen hash basis while keeping deterministic behavior and lock key format. (#7343) Thanks @vincentkoc.
|
||||
- Dependencies/Tooling: add non-blocking dead-code scans in CI via Knip/ts-prune/ts-unused-exports to surface unused dependencies and exports earlier. (#22468) Thanks @vincentkoc.
|
||||
- Dependencies/Unused Dependencies: remove or scope unused root and extension deps (`@larksuiteoapi/node-sdk`, `signal-utils`, `ollama`, `lit`, `@lit/context`, `@lit-labs/signals`, `@microsoft/agents-hosting-express`, `@microsoft/agents-hosting-extensions-teams`, and plugin-local `openclaw` devDeps in `extensions/open-prose`, `extensions/lobster`, and `extensions/llm-task`). (#22471, #22495) Thanks @vincentkoc.
|
||||
- Dependencies/A2UI: harden dependency resolution after root cleanup (resolve `lit`, `@lit/context`, `@lit-labs/signals`, and `signal-utils` from workspace/root) and simplify bundling fallback behavior, including `pnpm dlx rolldown` compatibility. (#22481, #22507) Thanks @vincentkoc.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
|
||||
- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
|
||||
- Agents/Tool images: include source filenames in `agents/tool-images` resize logs so compression events can be traced back to specific files.
|
||||
- Providers/OAuth: harden Qwen and Chutes refresh handling by validating refresh response expiry values and preserving prior refresh tokens when providers return empty refresh token fields, with regression coverage for empty-token responses.
|
||||
- Models/Kimi-Coding: add missing implicit provider template for `kimi-coding` with correct `anthropic-messages` API type and base URL, fixing 403 errors when using Kimi for Coding. (#22409)
|
||||
- Auto-reply/Tools: forward `senderIsOwner` through embedded queued/followup runner params so owner-only tools remain available for authorized senders. (#22296) thanks @hcoj.
|
||||
- Agents/Subagents: restore announce-chain delivery to agent injection, defer nested announce output until descendant follow-up content is ready, and prevent descendant deferrals from consuming announce retry budget so deep chains do not drop final completions. (#22223) Thanks @tyler6204.
|
||||
- Gateway/Auth: require `gateway.trustedProxies` to include a loopback proxy address when `auth.mode="trusted-proxy"` and `bind="loopback"`, preventing same-host proxy misconfiguration from silently blocking auth. (#22082, follow-up to #20097) thanks @mbelinky.
|
||||
- Security/OpenClawKit/UI: prevent injected inbound user context metadata blocks from leaking into chat history in TUI, webchat, and macOS surfaces by stripping all untrusted metadata prefixes at display boundaries. (#22142) Thanks @Mellowambience, @vincentkoc.
|
||||
- Security/OpenClawKit/UI: strip inbound metadata blocks from user messages in TUI rendering while preserving user-authored content. (#22345) Thanks @kansodata, @vincentkoc.
|
||||
- Security/OpenClawKit/UI: prevent inbound metadata leaks and reply-tag streaming artifacts in TUI rendering by stripping untrusted metadata prefixes at display boundaries. (#22346) Thanks @akramcodez, @vincentkoc.
|
||||
- Agents/System Prompt: label allowlisted senders as authorized senders to avoid implying ownership. Thanks @thewilloftheshadow.
|
||||
- Agents/Tool display: fix exec cwd suffix inference so `pushd ... && popd ... && <command>` does not keep stale `(in <dir>)` context in summaries. (#21925) Thanks @Lukavyi.
|
||||
- Discord: restore model picker back navigation when a provider is missing and document the Discord picker flow. (#21458) Thanks @pejmanjohn and @thewilloftheshadow.
|
||||
- Gateway/Auth: allow trusted-proxy mode with loopback bind for same-host reverse-proxy deployments, while still requiring configured `gateway.trustedProxies`. (#20097) thanks @xinhuagu.
|
||||
- Gateway/Auth: allow authenticated clients across roles/scopes to call `health` while preserving role and scope enforcement for non-health methods. (#19699) thanks @Nachx639.
|
||||
- Gateway/Security: remove shared-IP fallback for canvas endpoints and require token or session capability for canvas access. Thanks @thewilloftheshadow.
|
||||
- Gateway/Hooks: include transform export name in hook-transform cache keys so distinct exports from the same module do not reuse the wrong cached transform function. (#13855) thanks @mcaxtr.
|
||||
- Gateway/Control UI: return 404 for missing static-asset paths instead of serving SPA fallback HTML, while preserving client-route fallback behavior for extensionless and non-asset dotted paths. (#12060) thanks @mcaxtr.
|
||||
- Gateway/Pairing: prevent device-token rotate scope escalation by enforcing an approved-scope baseline, preserving approved scopes across metadata updates, and rejecting rotate requests that exceed approved role scope implications. (#20703) thanks @coygeek.
|
||||
- Gateway/Security: require secure context and paired-device checks for Control UI auth even when `gateway.controlUi.allowInsecureAuth` is set, and align audit messaging with the hardened behavior. (#20684) thanks @coygeek.
|
||||
- Security/Agents: restrict local MEDIA tool attachments to core tools and the OpenClaw temp root to prevent untrusted MCP tool file exfiltration. Thanks @NucleiAv and @thewilloftheshadow.
|
||||
- macOS/Build: default release packaging to `BUNDLE_ID=ai.openclaw.mac` in `scripts/package-mac-dist.sh`, so Sparkle feed URL is retained and auto-update no longer fails with an empty appcast feed. (#19750) thanks @loganprit.
|
||||
- Gateway/Pairing: clear persisted paired-device state when the gateway client closes with `device token mismatch` (`1008`) so reconnect flows can cleanly re-enter pairing. (#22071) Thanks @mbelinky.
|
||||
- Memory/QMD: respect per-agent `memorySearch.enabled=false` during gateway QMD startup initialization, split multi-collection QMD searches into per-collection queries (`search`/`vsearch`/`query`) to avoid sparse-term drops, prefer collection-hinted doc resolution to avoid stale-hash collisions, retry boot updates on transient lock/timeout failures, skip `qmd embed` in BM25-only `search` mode (including `memory index --force`), and serialize embed runs globally with failure backoff to prevent CPU storms on multi-agent hosts. (#20581, #21590, #20513, #20001, #21266, #21583, #20346, #19493) Thanks @danielrevivo, @zanderkrause, @sunyan034-cmd, @tilleulenspiegel, @dae-oss, @adamlongcreativellc, @jonathanadams96, and @kiliansitel.
|
||||
- Memory/Builtin: prevent automatic sync races with manager shutdown by skipping post-close sync starts and waiting for in-flight sync before closing SQLite, so `onSearch`/`onSessionStart` no longer fail with `database is not open` in ephemeral CLI flows. (#20556, #7464) Thanks @FuzzyTG and @henrybottter.
|
||||
- Hooks/Session memory: trigger bundled `session-memory` persistence on both `/new` and `/reset` so reset flows no longer skip markdown transcript capture before archival. (#21382) Thanks @mofesolapaul.
|
||||
- Signal/Outbound: preserve case for Base64 group IDs during outbound target normalization so cross-context routing and policy checks no longer break when group IDs include uppercase characters. (#5578) Thanks @heyhudson.
|
||||
- Providers/Copilot: drop persisted assistant `thinking` blocks for Claude models (while preserving turn structure/tool blocks) so follow-up requests no longer fail on invalid `thinkingSignature` payloads. (#19459) Thanks @jackheuberger.
|
||||
- Providers/Copilot: add `claude-sonnet-4.6` and `claude-sonnet-4.5` to the default GitHub Copilot model catalog and add coverage for model-list/definition helpers. (#20270, fixes #20091) Thanks @Clawborn.
|
||||
- Dependencies/Agents: bump embedded Pi SDK packages (`@mariozechner/pi-agent-core`, `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, `@mariozechner/pi-tui`) to `0.54.0`. (#21578) Thanks @Takhoffman.
|
||||
- Gateway/Config: allow `gateway.customBindHost` in strict config validation when `gateway.bind="custom"` so valid custom bind-host configurations no longer fail startup. (#20318, fixes #20289) Thanks @MisterGuy420.
|
||||
- Config/Agents: expose Pi compaction tuning values `agents.defaults.compaction.reserveTokens` and `agents.defaults.compaction.keepRecentTokens` in config schema/types and apply them in embedded Pi runner settings overrides with floor enforcement via `reserveTokensFloor`. (#21568) Thanks @Takhoffman.
|
||||
- Auto-reply/WebChat: avoid defaulting inbound runtime channel labels to unrelated providers (for example `whatsapp`) for webchat sessions so channel-specific formatting guidance stays accurate. (#21534) Thanks @lbo728.
|
||||
- Status: include persisted `cacheRead`/`cacheWrite` in session summaries so compact `/status` output consistently shows cache hit percentages from real session data.
|
||||
- Heartbeat/Cron: restore interval heartbeat behavior so missing `HEARTBEAT.md` no longer suppresses runs (only effectively empty files skip), preserving prompt-driven and tagged-cron execution paths.
|
||||
- WhatsApp/Cron/Heartbeat: enforce allowlisted routing for implicit scheduled/system delivery by merging pairing-store + configured `allowFrom` recipients, selecting authorized recipients when last-route context points to a non-allowlisted chat, and preventing heartbeat fan-out to recent unauthorized chats.
|
||||
- Heartbeat/Active hours: constrain active-hours `24` sentinel parsing to `24:00` in time validation so invalid values like `24:30` are rejected early. (#21410) thanks @adhitShet.
|
||||
- Heartbeat: treat `activeHours` windows with identical `start`/`end` times as zero-width (always outside the window) instead of always-active. (#21408) thanks @adhitShet.
|
||||
- Gateway/Pairing: tolerate legacy paired devices missing `roles`/`scopes` metadata in websocket upgrade checks and backfill metadata on reconnect. (#21447, fixes #21236) Thanks @joshavant.
|
||||
- Gateway/Pairing/CLI: align read-scope compatibility in pairing/device-token checks and add local `openclaw devices` fallback recovery for loopback `pairing required` deadlocks, with explicit fallback notice to unblock approval bootstrap flows. (#21616) Thanks @shakkernerd.
|
||||
- CLI/Pairing: default `pairing list` and `pairing approve` to the sole available pairing channel when omitted, so TUI-only setups can recover from `pairing required` without guessing channel arguments. (#21527) Thanks @losts1.
|
||||
- TUI/Pairing: show explicit pairing-required recovery guidance after gateway disconnects that return `pairing required`, including approval steps to unblock quickstart TUI hatching on fresh installs. (#21841) Thanks @nicolinux.
|
||||
- TUI/Input: suppress duplicate backspace events arriving in the same input burst window so SSH sessions no longer delete two characters per backspace press in the composer. (#19318) Thanks @eheimer.
|
||||
@@ -82,47 +54,89 @@ Docs: https://docs.openclaw.ai
|
||||
- Memory/Tools: return explicit `unavailable` warnings/actions from `memory_search` when embedding/provider failures occur (including quota exhaustion), so disabled memory does not look like an empty recall result. (#21894) Thanks @XBS9.
|
||||
- Session/Startup: require the `/new` and `/reset` greeting path to run Session Startup file-reading instructions before responding, so daily memory startup context is not skipped on fresh-session greetings. (#22338) Thanks @armstrong-pv.
|
||||
- Auth/Onboarding: align OAuth profile-id config mapping with stored credential IDs for OpenAI Codex and Chutes flows, preventing `provider:default` mismatches when OAuth returns email-scoped credentials. (#12692) thanks @mudrii.
|
||||
- Docker: pin base images to SHA256 digests in Docker builds to prevent mutable tag drift. (#7734) Thanks @coygeek.
|
||||
- Docker/Security: run E2E and install-sh test images as non-root by adding appuser directives. Thanks @thewilloftheshadow.
|
||||
- Docker: run build steps as the `node` user and use `COPY --chown` to avoid recursive ownership changes, trimming image size and layer churn. Thanks @huntharo.
|
||||
- Provider/HTTP: treat HTTP 503 as failover-eligible for LLM provider errors. (#21086) Thanks @Protocol-zero-0.
|
||||
- Anthropic/Agents: preserve required pi-ai default OAuth beta headers when `context1m` injects `anthropic-beta`, preventing 401 auth failures for `sk-ant-oat-*` tokens. (#19789, fixes #19769) Thanks @minupla.
|
||||
- Slack: pass `recipient_team_id` / `recipient_user_id` through Slack native streaming calls so `chat.startStream`/`appendStream`/`stopStream` work reliably across DMs and Slack Connect setups, and disable block streaming when native streaming is active. (#20988) Thanks @Dithilli. Earlier recipient-ID groundwork was contributed in #20377 by @AsserAl1012.
|
||||
- CLI/Config: add canonical `--strict-json` parsing for `config set` and keep `--json` as a legacy alias to reduce help/behavior drift. (#21332) thanks @adhitShet.
|
||||
- CLI: keep `openclaw -v` as a root-only version alias so subcommand `-v, --verbose` flags (for example ACP/hooks/skills) are no longer intercepted globally. (#21303) thanks @adhitShet.
|
||||
- Config/Memory: restore schema help/label metadata for hybrid `mmr` and `temporalDecay` settings so configuration surfaces show correct names and guidance. (#18786) Thanks @rodrigouroz.
|
||||
- Memory: return empty snippets when `memory_get`/QMD read files that have not been created yet, and harden memory indexing/session helpers against ENOENT races so missing Markdown no longer crashes tools. (#20680) Thanks @pahdo.
|
||||
- Tools/web_search: handle xAI Responses API payloads that emit top-level `output_text` blocks (without a `message` wrapper) so Grok web_search no longer returns `No response` for those results. (#20508) Thanks @echoVic.
|
||||
- Telegram/Streaming: always clean up draft previews even when dispatch throws before fallback handling, preventing orphaned preview messages during failed runs. (#19041) thanks @mudrii.
|
||||
- Telegram/Streaming: split reasoning and answer draft preview lanes to prevent cross-lane overwrites, and ignore literal `<think>` tags inside inline/fenced code snippets so sample markup is not misrouted as reasoning. (#20774) Thanks @obviyus.
|
||||
- Telegram/Streaming: restore 30-char first-preview debounce and scope `NO_REPLY` prefix suppression to partial sentinel fragments so normal `No...` text is not filtered. (#22613) thanks @obviyus.
|
||||
- Telegram/Status reactions: refresh stall timers on repeated phase updates and honor ack-reaction scope when lifecycle reactions are enabled, preventing false stall emojis and unwanted group reactions. Thanks @wolly-tundracube and @thewilloftheshadow.
|
||||
- Telegram/Status reactions: keep lifecycle reactions active when available-reactions lookup fails by falling back to unrestricted variant selection instead of suppressing reaction updates. (#22380) thanks @obviyus.
|
||||
- Discord/Streaming: apply `replyToMode: first` only to the first Discord chunk so block-streamed replies do not spam mention pings. (#20726) Thanks @thewilloftheshadow for the report.
|
||||
- Discord/Components: map DM channel targets back to user-scoped component sessions so button/select interactions stay in the main DM session. Thanks @thewilloftheshadow.
|
||||
- Discord/Allowlist: lazy-load guild lists when resolving Discord user allowlists so ID-only entries resolve even if guild fetch fails. (#20208) Thanks @zhangjunmengyang.
|
||||
|
||||
- Discord/Gateway: handle close code 4014 (missing privileged gateway intents) without crashing the gateway. Thanks @thewilloftheshadow.
|
||||
- Discord: ingest inbound stickers as media so sticker-only messages and forwarded stickers are visible to agents. Thanks @thewilloftheshadow.
|
||||
- Security/Net: strip sensitive headers (`Authorization`, `Proxy-Authorization`, `Cookie`, `Cookie2`) on cross-origin redirects in `fetchWithSsrFGuard` to prevent credential forwarding across origin boundaries. (#20313) Thanks @afurm.
|
||||
- Security/Systemd: reject CR/LF in systemd unit environment values and fix argument escaping so generated units cannot be injected with extra directives. Thanks @thewilloftheshadow.
|
||||
- Security/Tools: add per-wrapper random IDs to untrusted-content markers from `wrapExternalContent`/`wrapWebContent`, preventing marker spoofing from escaping content boundaries. (#19009) Thanks @Whoaa512.
|
||||
- Skills/Security: sanitize skill env overrides to block unsafe runtime injection variables and only allow sensitive keys when declared in skill metadata, with warnings for suspicious values. Thanks @thewilloftheshadow.
|
||||
- Skills/SonosCLI: add troubleshooting guidance for `sonos discover` failures on macOS direct mode (`sendto: no route to host`) and sandbox network restrictions (`bind: operation not permitted`). (#21316) Thanks @huntharo.
|
||||
- Auto-reply/Runner: emit `onAgentRunStart` only after agent lifecycle or tool activity begins (and only once per run), so fallback preflight errors no longer mark runs as started. (#21165) Thanks @shakkernerd.
|
||||
- Agents/Failover: treat non-default override runs as direct fallback-to-configured-primary (skip configured fallback chain), normalize default-model detection for provider casing/whitespace, and add regression coverage for override/auth error paths. (#18820) Thanks @Glucksberg.
|
||||
- Auto-reply/Tool results: serialize tool-result delivery and keep the delivery chain progressing after individual failures so concurrent tool outputs preserve user-visible ordering. (#21231) thanks @ahdernasr.
|
||||
- Auto-reply/Prompt caching: restore prefix-cache stability by keeping inbound system metadata session-stable and moving per-message IDs (`message_id`, `message_id_full`, `reply_to_id`, `sender_id`) into untrusted conversation context. (#20597) Thanks @anisoptera.
|
||||
- iOS/Security: force `https://` for non-loopback manual gateway hosts during iOS onboarding to block insecure remote transport URLs. (#21969) Thanks @mbelinky.
|
||||
- Shared/Security: reject insecure deep links that use `ws://` non-loopback gateway URLs to prevent plaintext remote websocket configuration. (#21970) Thanks @mbelinky.
|
||||
- macOS/Security: reject non-loopback `ws://` remote gateway URLs in macOS remote config to block insecure plaintext websocket endpoints. (#21971) Thanks @mbelinky.
|
||||
- Browser/Security: block upload path symlink escapes so browser upload sources cannot traverse outside the allowed workspace via symlinked paths. (#21972) Thanks @mbelinky.
|
||||
- iOS/Watch: add actionable watch approval/reject controls and quick-reply actions so watch-originated approvals and responses can be sent directly from notification flows. (#21996) Thanks @mbelinky.
|
||||
- iOS/Watch: refresh iOS and watch app icon assets with the lobster icon set to keep phone/watch branding aligned. (#21997) Thanks @mbelinky.
|
||||
- CLI/Onboarding: fix Anthropic-compatible custom provider verification by normalizing base URLs to avoid duplicate `/v1` paths during setup checks. (#21336) Thanks @17jmumford.
|
||||
- iOS/Gateway/Tools: prefer uniquely connected node matches when duplicate display names exist, surface actionable `nodes invoke` pairing-required guidance with request IDs, and refresh active iOS gateway registration after location-capability setting changes so capability updates apply immediately. (#22120) thanks @mbelinky.
|
||||
- Gateway/Auth: require `gateway.trustedProxies` to include a loopback proxy address when `auth.mode="trusted-proxy"` and `bind="loopback"`, preventing same-host proxy misconfiguration from silently blocking auth. (#22082, follow-up to #20097) thanks @mbelinky.
|
||||
- Gateway/Auth: allow trusted-proxy mode with loopback bind for same-host reverse-proxy deployments, while still requiring configured `gateway.trustedProxies`. (#20097) thanks @xinhuagu.
|
||||
- Gateway/Auth: allow authenticated clients across roles/scopes to call `health` while preserving role and scope enforcement for non-health methods. (#19699) thanks @Nachx639.
|
||||
- Gateway/Hooks: include transform export name in hook-transform cache keys so distinct exports from the same module do not reuse the wrong cached transform function. (#13855) thanks @mcaxtr.
|
||||
- Gateway/Control UI: return 404 for missing static-asset paths instead of serving SPA fallback HTML, while preserving client-route fallback behavior for extensionless and non-asset dotted paths. (#12060) thanks @mcaxtr.
|
||||
- Gateway/Pairing: prevent device-token rotate scope escalation by enforcing an approved-scope baseline, preserving approved scopes across metadata updates, and rejecting rotate requests that exceed approved role scope implications. (#20703) thanks @coygeek.
|
||||
- Gateway/Pairing: clear persisted paired-device state when the gateway client closes with `device token mismatch` (`1008`) so reconnect flows can cleanly re-enter pairing. (#22071) Thanks @mbelinky.
|
||||
- Gateway/Config: allow `gateway.customBindHost` in strict config validation when `gateway.bind="custom"` so valid custom bind-host configurations no longer fail startup. (#20318, fixes #20289) Thanks @MisterGuy420.
|
||||
- Gateway/Pairing: tolerate legacy paired devices missing `roles`/`scopes` metadata in websocket upgrade checks and backfill metadata on reconnect. (#21447, fixes #21236) Thanks @joshavant.
|
||||
- Gateway/Pairing/CLI: align read-scope compatibility in pairing/device-token checks and add local `openclaw devices` fallback recovery for loopback `pairing required` deadlocks, with explicit fallback notice to unblock approval bootstrap flows. (#21616) Thanks @shakkernerd.
|
||||
- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
|
||||
- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
|
||||
- Agents/Subagents: restore announce-chain delivery to agent injection, defer nested announce output until descendant follow-up content is ready, and prevent descendant deferrals from consuming announce retry budget so deep chains do not drop final completions. (#22223) Thanks @tyler6204.
|
||||
- Agents/System Prompt: label allowlisted senders as authorized senders to avoid implying ownership. Thanks @thewilloftheshadow.
|
||||
- Agents/Tool display: fix exec cwd suffix inference so `pushd ... && popd ... && <command>` does not keep stale `(in <dir>)` context in summaries. (#21925) Thanks @Lukavyi.
|
||||
- Tools/web_search: handle xAI Responses API payloads that emit top-level `output_text` blocks (without a `message` wrapper) so Grok web_search no longer returns `No response` for those results. (#20508) Thanks @echoVic.
|
||||
- Agents/Failover: treat non-default override runs as direct fallback-to-configured-primary (skip configured fallback chain), normalize default-model detection for provider casing/whitespace, and add regression coverage for override/auth error paths. (#18820) Thanks @Glucksberg.
|
||||
- Docker/Build: include `ownerDisplay` in `CommandsSchema` object-level defaults so Docker `pnpm build` no longer fails with `TS2769` during plugin SDK d.ts generation. (#22558) Thanks @obviyus.
|
||||
- Docker/Browser: install Playwright Chromium into `/home/node/.cache/ms-playwright` and set `node:node` ownership so browser binaries are available to the runtime user in browser-enabled images. (#22585) thanks @obviyus.
|
||||
- Hooks/Session memory: trigger bundled `session-memory` persistence on both `/new` and `/reset` so reset flows no longer skip markdown transcript capture before archival. (#21382) Thanks @mofesolapaul.
|
||||
- Dependencies/Agents: bump embedded Pi SDK packages (`@mariozechner/pi-agent-core`, `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, `@mariozechner/pi-tui`) to `0.54.0`. (#21578) Thanks @Takhoffman.
|
||||
- Config/Agents: expose Pi compaction tuning values `agents.defaults.compaction.reserveTokens` and `agents.defaults.compaction.keepRecentTokens` in config schema/types and apply them in embedded Pi runner settings overrides with floor enforcement via `reserveTokensFloor`. (#21568) Thanks @Takhoffman.
|
||||
- Docker: pin base images to SHA256 digests in Docker builds to prevent mutable tag drift. (#7734) Thanks @coygeek.
|
||||
- Docker: run build steps as the `node` user and use `COPY --chown` to avoid recursive ownership changes, trimming image size and layer churn. Thanks @huntharo.
|
||||
- Config/Memory: restore schema help/label metadata for hybrid `mmr` and `temporalDecay` settings so configuration surfaces show correct names and guidance. (#18786) Thanks @rodrigouroz.
|
||||
- Skills/SonosCLI: add troubleshooting guidance for `sonos discover` failures on macOS direct mode (`sendto: no route to host`) and sandbox network restrictions (`bind: operation not permitted`). (#21316) Thanks @huntharo.
|
||||
- macOS/Build: default release packaging to `BUNDLE_ID=ai.openclaw.mac` in `scripts/package-mac-dist.sh`, so Sparkle feed URL is retained and auto-update no longer fails with an empty appcast feed. (#19750) thanks @loganprit.
|
||||
- Signal/Outbound: preserve case for Base64 group IDs during outbound target normalization so cross-context routing and policy checks no longer break when group IDs include uppercase characters. (#5578) Thanks @heyhudson.
|
||||
- Anthropic/Agents: preserve required pi-ai default OAuth beta headers when `context1m` injects `anthropic-beta`, preventing 401 auth failures for `sk-ant-oat-*` tokens. (#19789, fixes #19769) Thanks @minupla.
|
||||
- Security/Exec: block unquoted heredoc body expansion tokens in shell allowlist analysis, reject unterminated heredocs, and require explicit approval for allowlisted heredoc execution on gateway hosts to prevent heredoc substitution allowlist bypass. This ships in the next npm release. Thanks @torturado for reporting.
|
||||
- WhatsApp/Security: enforce allowlist JID authorization for reaction actions so authenticated callers cannot target non-allowlisted chats by forging `chatJid` + valid `messageId` pairs. This ships in the next npm release. Thanks @aether-ai-agent for reporting.
|
||||
- ACP/Security: escape control and delimiter characters in ACP `resource_link` title/URI metadata before prompt interpolation to prevent metadata-driven prompt injection through resource links. This ships in the next npm release. Thanks @aether-ai-agent for reporting.
|
||||
- TTS/Security: make model-driven provider switching opt-in by default (`messages.tts.modelOverrides.allowProvider=false` unless explicitly enabled), while keeping voice/style overrides available, to reduce prompt-injection-driven provider hops and unexpected TTS cost escalation. This ships in the next npm release. Thanks @aether-ai-agent for reporting.
|
||||
- Security/Agents: keep overflow compaction retry budgeting global across tool-result truncation recovery so successful truncation cannot reset the overflow retry counter and amplify retry/cost cycles. This ships in the next npm release. Thanks @aether-ai-agent for reporting.
|
||||
- BlueBubbles/Security (optional beta iMessage plugin): require webhook token authentication for all BlueBubbles webhook requests (including loopback/proxied setups), removing passwordless webhook fallback behavior. Thanks @zpbrent.
|
||||
- iOS/Security: force `https://` for non-loopback manual gateway hosts during iOS onboarding to block insecure remote transport URLs. (#21969) Thanks @mbelinky.
|
||||
- Gateway/Security: remove shared-IP fallback for canvas endpoints and require token or session capability for canvas access. Thanks @thewilloftheshadow.
|
||||
- Gateway/Security: require secure context and paired-device checks for Control UI auth even when `gateway.controlUi.allowInsecureAuth` is set, and align audit messaging with the hardened behavior. This ships in the next npm release. (#20684) Thanks @coygeek and @Vasco0x4 for reporting.
|
||||
- Gateway/Security: scope tokenless Tailscale forwarded-header auth to Control UI websocket auth only, so HTTP gateway routes still require token/password even on trusted hosts. This ships in the next npm release. Thanks @zpbrent for reporting.
|
||||
- Docker/Security: run E2E and install-sh test images as non-root by adding appuser directives. Thanks @thewilloftheshadow.
|
||||
- Skills/Security: sanitize skill env overrides to block unsafe runtime injection variables and only allow sensitive keys when declared in skill metadata, with warnings for suspicious values. Thanks @thewilloftheshadow.
|
||||
- Security/Commands: block prototype-key injection in runtime `/debug` overrides and require own-property checks for gated command flags (`bash`, `config`, `debug`) so inherited prototype values cannot enable privileged commands. Thanks @tdjackey for reporting.
|
||||
- Security/Browser: block non-network browser navigation protocols (including `file:`, `data:`, and `javascript:`) while preserving `about:blank`, preventing local file reads via browser tool navigation. This ships in the next npm release. Thanks @q1uf3ng for reporting.
|
||||
- Security/Exec: block shell startup-file env injection (`BASH_ENV`, `ENV`, `BASH_FUNC_*`, `LD_*`, `DYLD_*`) across config env ingestion, node-host inherited environment sanitization, and macOS exec host runtime to prevent pre-command execution from attacker-controlled environment variables. This ships in the next npm release. Thanks @tdjackey.
|
||||
- Security/Exec (Windows): canonicalize `cmd.exe /c` command text across validation, approval binding, and audit/event rendering to prevent trailing-argument approval mismatches in `system.run`. This ships in the next npm release. Thanks @tdjackey for reporting.
|
||||
- Security/Gateway/Hooks: block `__proto__`, `constructor`, and `prototype` traversal in webhook template path resolution to prevent prototype-chain payload data leakage in `messageTemplate` rendering. (#22213) Thanks @SleuthCo.
|
||||
- Security/OpenClawKit/UI: prevent injected inbound user context metadata blocks from leaking into chat history in TUI, webchat, and macOS surfaces by stripping all untrusted metadata prefixes at display boundaries. (#22142) Thanks @Mellowambience, @vincentkoc.
|
||||
- Security/OpenClawKit/UI: strip inbound metadata blocks from user messages in TUI rendering while preserving user-authored content. (#22345) Thanks @kansodata, @vincentkoc.
|
||||
- Security/OpenClawKit/UI: prevent inbound metadata leaks and reply-tag streaming artifacts in TUI rendering by stripping untrusted metadata prefixes at display boundaries. (#22346) Thanks @akramcodez, @vincentkoc.
|
||||
- Security/Agents: restrict local MEDIA tool attachments to core tools and the OpenClaw temp root to prevent untrusted MCP tool file exfiltration. Thanks @NucleiAv and @thewilloftheshadow.
|
||||
- Security/Net: strip sensitive headers (`Authorization`, `Proxy-Authorization`, `Cookie`, `Cookie2`) on cross-origin redirects in `fetchWithSsrFGuard` to prevent credential forwarding across origin boundaries. (#20313) Thanks @afurm.
|
||||
- Security/Systemd: reject CR/LF in systemd unit environment values and fix argument escaping so generated units cannot be injected with extra directives. Thanks @thewilloftheshadow.
|
||||
- Security/Tools: add per-wrapper random IDs to untrusted-content markers from `wrapExternalContent`/`wrapWebContent`, preventing marker spoofing from escaping content boundaries. (#19009) Thanks @Whoaa512.
|
||||
- Shared/Security: reject insecure deep links that use `ws://` non-loopback gateway URLs to prevent plaintext remote websocket configuration. (#21970) Thanks @mbelinky.
|
||||
- macOS/Security: reject non-loopback `ws://` remote gateway URLs in macOS remote config to block insecure plaintext websocket endpoints. (#21971) Thanks @mbelinky.
|
||||
- Browser/Security: block upload path symlink escapes so browser upload sources cannot traverse outside the allowed workspace via symlinked paths. (#21972) Thanks @mbelinky.
|
||||
- Security/Dependencies: bump transitive `hono` usage to `4.11.10` to incorporate timing-safe authentication comparison hardening for `basicAuth`/`bearerAuth` (`GHSA-gq3j-xvxp-8hrf`). Thanks @vincentkoc.
|
||||
- Security/Gateway: parse `X-Forwarded-For` with trust-preserving semantics when requests come from configured trusted proxies, preventing proxy-chain spoofing from influencing client IP classification and rate-limit identity. Thanks @AnthonyDiSanti and @vincentkoc.
|
||||
- iOS/Gateway/Tools: prefer uniquely connected node matches when duplicate display names exist, surface actionable `nodes invoke` pairing-required guidance with request IDs, and refresh active iOS gateway registration after location-capability setting changes so capability updates apply immediately. (#22120) thanks @mbelinky.
|
||||
- Security/Sandbox: remove default `--no-sandbox` for the browser container entrypoint, add explicit opt-in via `OPENCLAW_BROWSER_NO_SANDBOX` / `CLAWDBOT_BROWSER_NO_SANDBOX`, and harden the default container security posture (`GHSA-43x4-g22p-3hrq`). Thanks @TerminalsandCoffee and @vincentkoc.
|
||||
- Security/Sandbox: remove default `--no-sandbox` for the browser container entrypoint, add explicit opt-in via `OPENCLAW_BROWSER_NO_SANDBOX` / `CLAWDBOT_BROWSER_NO_SANDBOX`, and add security-audit checks for stale/missing sandbox browser Docker hash labels. This ships in the next npm release. Thanks @TerminalsandCoffee and @vincentkoc.
|
||||
- Security/Sandbox Browser: require VNC password auth for noVNC observer sessions in the sandbox browser entrypoint, plumb per-container noVNC passwords from runtime, and emit short-lived noVNC observer token URLs while keeping loopback-only host port publishing. This ships in the next npm release. Thanks @TerminalsandCoffee for reporting.
|
||||
- Security/Sandbox Browser: default browser sandbox containers to a dedicated Docker network (`openclaw-sandbox-browser`), add optional CDP ingress source-range restrictions, auto-create missing dedicated networks, and warn in `openclaw security --audit` when browser sandboxing runs on bridge without source-range limits. This ships in the next npm release. Thanks @TerminalsandCoffee for reporting.
|
||||
|
||||
## 2026.2.19
|
||||
|
||||
@@ -206,6 +220,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/Exec: remove file-existence oracle behavior from `tools.exec.safeBins` by using deterministic argv-only stdin-safe validation and blocking file-oriented flags (for example `sort -o`, `jq -f`, `grep -f`) so allow/deny results no longer disclose host file presence. This ships in the next npm release. Thanks @nedlir for reporting.
|
||||
- Security/Browser: route browser URL navigation through one SSRF-guarded validation path for tab-open/CDP-target/Playwright navigation flows and block private/metadata destinations by default (configurable via `browser.ssrfPolicy`). This ships in the next npm release. Thanks @dorjoos for reporting.
|
||||
- Security/Exec: for the next npm release, harden safe-bin stdin-only enforcement by blocking output/recursive flags (`sort -o/--output`, grep recursion) and tightening default safe bins to remove `sort`/`grep`, preventing safe-bin allowlist bypass for file writes/recursive reads. Thanks @nedlir for reporting.
|
||||
- Security/Exec: block grep safe-bin positional operand bypass by setting grep positional budget to zero, so `-e/--regexp` cannot smuggle bare filename reads (for example `.env`) via ambiguous positionals; safe-bin grep patterns must come from `-e/--regexp`. This ships in the next npm release. Thanks @athuljayaram for reporting.
|
||||
- Security/Gateway/Agents: remove implicit admin scopes from agent tool gateway calls by classifying methods to least-privilege operator scopes, and enforce owner-only tooling (`cron`, `gateway`, `whatsapp_login`) through centralized tool-policy wrappers plus tool metadata to prevent non-owner DM privilege escalation. Ships in the next npm release. Thanks @Adam55A-code for reporting.
|
||||
- Security/Gateway: centralize gateway method-scope authorization and default non-CLI gateway callers to least-privilege method scopes, with explicit CLI scope handling, full core-handler scope classification coverage, and regression guards to prevent scope drift.
|
||||
- Security/Net: block SSRF bypass via NAT64 (`64:ff9b::/96`, `64:ff9b:1::/48`), 6to4 (`2002::/16`), and Teredo (`2001:0000::/32`) IPv6 transition addresses, and fail closed on IPv6 parse errors. Thanks @jackhax.
|
||||
|
||||
@@ -34,7 +34,10 @@ ARG OPENCLAW_INSTALL_BROWSER=""
|
||||
RUN if [ -n "$OPENCLAW_INSTALL_BROWSER" ]; then \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \
|
||||
mkdir -p /home/node/.cache/ms-playwright && \
|
||||
PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright \
|
||||
node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \
|
||||
chown -R node:node /home/node/.cache/ms-playwright && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
|
||||
fi
|
||||
|
||||
@@ -30,6 +30,12 @@ The wizard guides you step by step through setting up the gateway, workspace, ch
|
||||
Works with npm, pnpm, or bun.
|
||||
New install? Start here: [Getting started](https://docs.openclaw.ai/start/getting-started)
|
||||
|
||||
## Sponsors
|
||||
|
||||
| OpenAI | Blacksmith |
|
||||
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| [](https://openai.com/) | [](https://blacksmith.sh/) |
|
||||
|
||||
**Subscriptions (OAuth):**
|
||||
|
||||
- **[Anthropic](https://www.anthropic.com/)** (Claude Pro/Max)
|
||||
|
||||
@@ -47,8 +47,17 @@ When patching a GHSA via `gh api`, include `X-GitHub-Api-Version: 2022-11-28` (o
|
||||
|
||||
- Public Internet Exposure
|
||||
- Using OpenClaw in ways that the docs recommend not to
|
||||
- Deployments where mutually untrusted/adversarial operators share one gateway host and config
|
||||
- Prompt injection attacks
|
||||
|
||||
## Deployment Assumptions
|
||||
|
||||
OpenClaw security guidance assumes:
|
||||
|
||||
- The host where OpenClaw runs is within a trusted OS/admin boundary.
|
||||
- Anyone who can modify `~/.openclaw` state/config (including `openclaw.json`) is effectively a trusted operator.
|
||||
- A single Gateway shared by mutually untrusted people is **not a recommended setup**. Use separate gateways (or at minimum separate OS users/hosts) per trust boundary.
|
||||
|
||||
## Plugin Trust Boundary
|
||||
|
||||
Plugins/extensions are loaded **in-process** with the Gateway and are treated as trusted code.
|
||||
|
||||
@@ -21,8 +21,8 @@ android {
|
||||
applicationId = "ai.openclaw.android"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 202602200
|
||||
versionName = "2026.2.20"
|
||||
versionCode = 202602210
|
||||
versionName = "2026.2.21"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20260220</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/100.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/102.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/1024.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/108.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/114.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/120.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/172.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/180.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/196.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/216.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/234.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/258.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/29.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/40.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/48.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/55.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/57.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/58.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/60.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/66.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/80.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/87.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/88.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/92.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
@@ -1,31 +1 @@
|
||||
{
|
||||
"images" : [
|
||||
{ "filename" : "icon-20@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" },
|
||||
{ "filename" : "icon-20@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" },
|
||||
{ "filename" : "icon-20@2x.png", "idiom" : "iphone","scale" : "2x", "size" : "20x20" },
|
||||
{ "filename" : "icon-20@3x.png", "idiom" : "iphone","scale" : "3x", "size" : "20x20" },
|
||||
|
||||
{ "filename" : "icon-29@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" },
|
||||
{ "filename" : "icon-29@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" },
|
||||
{ "filename" : "icon-29@2x.png", "idiom" : "iphone","scale" : "2x", "size" : "29x29" },
|
||||
{ "filename" : "icon-29@3x.png", "idiom" : "iphone","scale" : "3x", "size" : "29x29" },
|
||||
|
||||
{ "filename" : "icon-40@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" },
|
||||
{ "filename" : "icon-40@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" },
|
||||
{ "filename" : "icon-40@2x.png", "idiom" : "iphone","scale" : "2x", "size" : "40x40" },
|
||||
{ "filename" : "icon-40@3x.png", "idiom" : "iphone","scale" : "3x", "size" : "40x40" },
|
||||
|
||||
{ "filename" : "icon-60@2x.png", "idiom" : "iphone","scale" : "2x", "size" : "60x60" },
|
||||
{ "filename" : "icon-60@3x.png", "idiom" : "iphone","scale" : "3x", "size" : "60x60" },
|
||||
|
||||
{ "filename" : "icon-76@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" },
|
||||
|
||||
{ "filename" : "icon-83.5@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" },
|
||||
|
||||
{ "filename" : "icon-1024.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" }
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"}]}
|
||||
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 878 B |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -1904,6 +1904,7 @@ private extension NodeAppModel {
|
||||
}
|
||||
GatewayDiagnostics.log(
|
||||
"operator gateway connected host=\(url.host ?? "?") scheme=\(url.scheme ?? "?")")
|
||||
await self.talkMode.reloadConfig()
|
||||
await self.refreshBrandingFromGateway()
|
||||
await self.refreshAgentsFromGateway()
|
||||
await self.refreshShareRouteFromGateway()
|
||||
|
||||
@@ -306,6 +306,26 @@ struct SettingsTab: View {
|
||||
help: "Keeps the screen awake while OpenClaw is open.")
|
||||
|
||||
DisclosureGroup("Advanced") {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Talk Voice (Gateway)")
|
||||
.font(.footnote.weight(.semibold))
|
||||
.foregroundStyle(.secondary)
|
||||
LabeledContent("Provider", value: "ElevenLabs")
|
||||
LabeledContent(
|
||||
"API Key",
|
||||
value: self.appModel.talkMode.gatewayTalkConfigLoaded
|
||||
? (self.appModel.talkMode.gatewayTalkApiKeyConfigured ? "Configured" : "Not configured")
|
||||
: "Not loaded")
|
||||
LabeledContent(
|
||||
"Default Model",
|
||||
value: self.appModel.talkMode.gatewayTalkDefaultModelId ?? "eleven_v3 (fallback)")
|
||||
LabeledContent(
|
||||
"Default Voice",
|
||||
value: self.appModel.talkMode.gatewayTalkDefaultVoiceId ?? "auto (first available)")
|
||||
Text("Configured on gateway via talk.apiKey, talk.modelId, and talk.voiceId.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
self.featureToggle(
|
||||
"Voice Directive Hint",
|
||||
isOn: self.$talkVoiceDirectiveHintEnabled,
|
||||
@@ -399,6 +419,9 @@ struct SettingsTab: View {
|
||||
// Keep setup front-and-center when disconnected; keep things compact once connected.
|
||||
self.gatewayExpanded = !self.isGatewayConnected
|
||||
self.selectedAgentPickerId = self.appModel.selectedAgentId ?? ""
|
||||
if self.isGatewayConnected {
|
||||
self.appModel.reloadTalkConfig()
|
||||
}
|
||||
}
|
||||
.onChange(of: self.selectedAgentPickerId) { _, newValue in
|
||||
let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
@@ -24,6 +24,10 @@ final class TalkModeManager: NSObject {
|
||||
var statusText: String = "Off"
|
||||
/// 0..1-ish (not calibrated). Intended for UI feedback only.
|
||||
var micLevel: Double = 0
|
||||
var gatewayTalkConfigLoaded: Bool = false
|
||||
var gatewayTalkApiKeyConfigured: Bool = false
|
||||
var gatewayTalkDefaultModelId: String?
|
||||
var gatewayTalkDefaultVoiceId: String?
|
||||
|
||||
private enum CaptureMode {
|
||||
case idle
|
||||
@@ -1733,6 +1737,10 @@ extension TalkModeManager {
|
||||
} else {
|
||||
self.apiKey = (localApiKey?.isEmpty == false) ? localApiKey : configApiKey
|
||||
}
|
||||
self.gatewayTalkDefaultVoiceId = self.defaultVoiceId
|
||||
self.gatewayTalkDefaultModelId = self.defaultModelId
|
||||
self.gatewayTalkApiKeyConfigured = (self.apiKey?.isEmpty == false)
|
||||
self.gatewayTalkConfigLoaded = true
|
||||
if let interrupt = talk?["interruptOnSpeech"] as? Bool {
|
||||
self.interruptOnSpeech = interrupt
|
||||
}
|
||||
@@ -1741,6 +1749,10 @@ extension TalkModeManager {
|
||||
if !self.modelOverrideActive {
|
||||
self.currentModelId = self.defaultModelId
|
||||
}
|
||||
self.gatewayTalkDefaultVoiceId = nil
|
||||
self.gatewayTalkDefaultModelId = nil
|
||||
self.gatewayTalkApiKeyConfigured = false
|
||||
self.gatewayTalkConfigLoaded = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20260220</string>
|
||||
</dict>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20260220</string>
|
||||
<key>WKCompanionAppBundleIdentifier</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20260220</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -92,7 +92,7 @@ targets:
|
||||
- CFBundleURLName: ai.openclaw.ios
|
||||
CFBundleURLSchemes:
|
||||
- openclaw
|
||||
CFBundleShortVersionString: "2026.2.20"
|
||||
CFBundleShortVersionString: "2026.2.21"
|
||||
CFBundleVersion: "20260220"
|
||||
UILaunchScreen: {}
|
||||
UIApplicationSceneManifest:
|
||||
@@ -146,7 +146,7 @@ targets:
|
||||
path: ShareExtension/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: OpenClaw Share
|
||||
CFBundleShortVersionString: "2026.2.20"
|
||||
CFBundleShortVersionString: "2026.2.21"
|
||||
CFBundleVersion: "20260220"
|
||||
NSExtension:
|
||||
NSExtensionPointIdentifier: com.apple.share-services
|
||||
@@ -176,7 +176,7 @@ targets:
|
||||
path: WatchApp/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: OpenClaw
|
||||
CFBundleShortVersionString: "2026.2.20"
|
||||
CFBundleShortVersionString: "2026.2.21"
|
||||
CFBundleVersion: "20260220"
|
||||
WKCompanionAppBundleIdentifier: "$(OPENCLAW_APP_BUNDLE_ID)"
|
||||
WKWatchKitApp: true
|
||||
@@ -200,7 +200,7 @@ targets:
|
||||
path: WatchExtension/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: OpenClaw
|
||||
CFBundleShortVersionString: "2026.2.20"
|
||||
CFBundleShortVersionString: "2026.2.21"
|
||||
CFBundleVersion: "20260220"
|
||||
NSExtension:
|
||||
NSExtensionAttributes:
|
||||
@@ -228,5 +228,5 @@ targets:
|
||||
path: Tests/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: OpenClawTests
|
||||
CFBundleShortVersionString: "2026.2.20"
|
||||
CFBundleShortVersionString: "2026.2.21"
|
||||
CFBundleVersion: "20260220"
|
||||
|
||||
@@ -364,21 +364,6 @@ private enum ExecHostExecutor {
|
||||
let skillAllow: Bool
|
||||
}
|
||||
|
||||
private static let blockedEnvKeys: Set<String> = [
|
||||
"PATH",
|
||||
"NODE_OPTIONS",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYOPT",
|
||||
]
|
||||
|
||||
private static let blockedEnvPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
]
|
||||
|
||||
static func handle(_ request: ExecHostRequest) async -> ExecHostResponse {
|
||||
let command = request.command.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
guard !command.isEmpty else {
|
||||
@@ -580,18 +565,8 @@ private enum ExecHostExecutor {
|
||||
error: nil)
|
||||
}
|
||||
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String]? {
|
||||
guard let overrides else { return nil }
|
||||
var merged = ProcessInfo.processInfo.environment
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.blockedEnvKeys.contains(upper) { continue }
|
||||
if self.blockedEnvPrefixes.contains(where: { upper.hasPrefix($0) }) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String] {
|
||||
HostEnvSanitizer.sanitize(overrides: overrides)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
56
apps/macos/Sources/OpenClaw/HostEnvSanitizer.swift
Normal file
@@ -0,0 +1,56 @@
|
||||
import Foundation
|
||||
|
||||
enum HostEnvSanitizer {
|
||||
// Keep in sync with src/infra/host-env-security-policy.json.
|
||||
// Parity is validated by src/infra/host-env-security.policy-parity.test.ts.
|
||||
private static let blockedKeys: Set<String> = [
|
||||
"NODE_OPTIONS",
|
||||
"NODE_PATH",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYLIB",
|
||||
"RUBYOPT",
|
||||
"BASH_ENV",
|
||||
"ENV",
|
||||
"GCONV_PATH",
|
||||
"IFS",
|
||||
"SSLKEYLOGFILE",
|
||||
]
|
||||
|
||||
private static let blockedPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
"BASH_FUNC_",
|
||||
]
|
||||
|
||||
private static func isBlocked(_ upperKey: String) -> Bool {
|
||||
if self.blockedKeys.contains(upperKey) { return true }
|
||||
return self.blockedPrefixes.contains(where: { upperKey.hasPrefix($0) })
|
||||
}
|
||||
|
||||
static func sanitize(overrides: [String: String]?) -> [String: String] {
|
||||
var merged: [String: String] = [:]
|
||||
for (rawKey, value) in ProcessInfo.processInfo.environment {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.isBlocked(upper) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
|
||||
guard let overrides else { return merged }
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
// PATH is part of the security boundary (command resolution + safe-bin checks). Never
|
||||
// allow request-scoped PATH overrides from agents/gateways.
|
||||
if upper == "PATH" { continue }
|
||||
if self.isBlocked(upper) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
}
|
||||
}
|
||||
@@ -862,33 +862,8 @@ extension MacNodeRuntime {
|
||||
UserDefaults.standard.object(forKey: cameraEnabledKey) as? Bool ?? false
|
||||
}
|
||||
|
||||
private static let blockedEnvKeys: Set<String> = [
|
||||
"PATH",
|
||||
"NODE_OPTIONS",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYOPT",
|
||||
]
|
||||
|
||||
private static let blockedEnvPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
]
|
||||
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String]? {
|
||||
guard let overrides else { return nil }
|
||||
var merged = ProcessInfo.processInfo.environment
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.blockedEnvKeys.contains(upper) { continue }
|
||||
if self.blockedEnvPrefixes.contains(where: { upper.hasPrefix($0) }) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String] {
|
||||
HostEnvSanitizer.sanitize(overrides: overrides)
|
||||
}
|
||||
|
||||
private nonisolated static func locationMode() -> OpenClawLocationMode {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.20</string>
|
||||
<string>2026.2.21</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>202602200</string>
|
||||
<string>202602210</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import path from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "rolldown";
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(here, "../../../../..");
|
||||
const uiRoot = path.resolve(repoRoot, "ui");
|
||||
const fromHere = (p) => path.resolve(here, p);
|
||||
const outputFile = path.resolve(
|
||||
here,
|
||||
@@ -17,19 +17,28 @@ const outputFile = path.resolve(
|
||||
|
||||
const a2uiLitDist = path.resolve(repoRoot, "vendor/a2ui/renderers/lit/dist/src");
|
||||
const a2uiThemeContext = path.resolve(a2uiLitDist, "0.8/ui/context/theme.js");
|
||||
const a2uiNodeModules = path.resolve(repoRoot, "ui/node_modules");
|
||||
const rootNodeModules = path.resolve(repoRoot, "node_modules");
|
||||
const uiNodeModules = path.resolve(uiRoot, "node_modules");
|
||||
const repoNodeModules = path.resolve(repoRoot, "node_modules");
|
||||
|
||||
const resolveA2uiDep = (pkg, rel = "") => {
|
||||
const uiPath = path.resolve(a2uiNodeModules, pkg, rel);
|
||||
if (existsSync(uiPath)) {
|
||||
return uiPath;
|
||||
function resolveUiDependency(moduleId) {
|
||||
const candidates = [
|
||||
path.resolve(uiNodeModules, moduleId),
|
||||
path.resolve(repoNodeModules, moduleId),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return path.resolve(rootNodeModules, pkg, rel);
|
||||
};
|
||||
const fallbackCandidates = candidates.join(", ");
|
||||
throw new Error(
|
||||
`A2UI bundle config cannot resolve ${moduleId}. Checked: ${fallbackCandidates}. ` +
|
||||
"Keep dependency installed in ui workspace or repo root before bundling.",
|
||||
);
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
export default {
|
||||
input: fromHere("bootstrap.js"),
|
||||
experimental: {
|
||||
attachDebugInfo: "none",
|
||||
@@ -40,13 +49,13 @@ export default defineConfig({
|
||||
"@a2ui/lit": path.resolve(a2uiLitDist, "index.js"),
|
||||
"@a2ui/lit/ui": path.resolve(a2uiLitDist, "0.8/ui/ui.js"),
|
||||
"@openclaw/a2ui-theme-context": a2uiThemeContext,
|
||||
"@lit/context": resolveA2uiDep("@lit/context", "index.js"),
|
||||
"@lit/context/": resolveA2uiDep("@lit/context"),
|
||||
"@lit-labs/signals": resolveA2uiDep("@lit-labs/signals", "index.js"),
|
||||
"@lit-labs/signals/": resolveA2uiDep("@lit-labs/signals"),
|
||||
lit: resolveA2uiDep("lit", "index.js"),
|
||||
"lit/": resolveA2uiDep("lit"),
|
||||
"signal-utils/": resolveA2uiDep("signal-utils"),
|
||||
"@lit/context": resolveUiDependency("@lit/context"),
|
||||
"@lit/context/": resolveUiDependency("@lit/context/"),
|
||||
"@lit-labs/signals": resolveUiDependency("@lit-labs/signals"),
|
||||
"@lit-labs/signals/": resolveUiDependency("@lit-labs/signals/"),
|
||||
lit: resolveUiDependency("lit"),
|
||||
"lit/": resolveUiDependency("lit/"),
|
||||
"signal-utils/": resolveUiDependency("signal-utils/"),
|
||||
},
|
||||
},
|
||||
output: {
|
||||
@@ -55,4 +64,4 @@ export default defineConfig({
|
||||
codeSplitting: false,
|
||||
sourcemap: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
14
docs/assets/sponsors/blacksmith.svg
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
3
docs/assets/sponsors/openai.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -46,7 +46,8 @@ Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **R
|
||||
|
||||
Security note:
|
||||
|
||||
- Always set a webhook password. If you expose the gateway through a reverse proxy (Tailscale Serve/Funnel, nginx, Cloudflare Tunnel, ngrok), the proxy may connect to the gateway over loopback. The BlueBubbles webhook handler treats requests with forwarding headers as proxied and will not accept passwordless webhooks.
|
||||
- Always set a webhook password.
|
||||
- Webhook authentication is always required. OpenClaw rejects BlueBubbles webhook requests unless they include a password/guid that matches `channels.bluebubbles.password` (for example `?password=<password>` or `x-password`), regardless of loopback/proxy topology.
|
||||
|
||||
## Keeping Messages.app alive (VM / headless setups)
|
||||
|
||||
@@ -284,7 +285,7 @@ Control whether responses are sent as a single message or streamed in blocks:
|
||||
- Media cap via `channels.bluebubbles.mediaMaxMb` (default: 8 MB).
|
||||
- Outbound text is chunked to `channels.bluebubbles.textChunkLimit` (default: 4000 chars).
|
||||
|
||||
## Configuration reference
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Status: ready for DMs and guild channels via the official Discord gateway.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
You will need to create a new application with a bot, add the bot to your server, and pair it to OpenClaw. We recommend adding your bot to your own private server. If you don't have one yet, [create one first](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server) (choose **Create My Own > For me and my friends**).
|
||||
|
||||
@@ -963,7 +963,7 @@ openclaw logs --follow
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Configuration reference pointers
|
||||
## Configuration
|
||||
|
||||
Primary reference:
|
||||
|
||||
|
||||
@@ -523,7 +523,7 @@ See [Get group/user IDs](#get-groupuser-ids) for lookup tips.
|
||||
|
||||
---
|
||||
|
||||
## Configuration reference
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Gateway configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ title: "Google Chat"
|
||||
|
||||
Status: ready for DMs + spaces via Google Chat API webhooks (HTTP only).
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Create a Google Cloud project and enable the **Google Chat API**.
|
||||
- Go to: [Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)
|
||||
|
||||
@@ -21,7 +21,7 @@ title: grammY
|
||||
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` are set (otherwise it long-polls).
|
||||
- **Sessions:** direct chats collapse into the agent main session (`agent:<agentId>:<mainKey>`); groups use `agent:<agentId>:telegram:group:<chatId>`; replies route back to the same channel.
|
||||
- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`, `channels.telegram.webhookHost`.
|
||||
- **Live stream preview:** optional `channels.telegram.streamMode` sends a temporary message and updates it with `editMessageText`. This is separate from channel block streaming.
|
||||
- **Live stream preview:** optional `channels.telegram.streaming` sends a temporary message and updates it with `editMessageText`. This is separate from channel block streaming.
|
||||
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
|
||||
|
||||
Open questions
|
||||
|
||||
@@ -28,7 +28,7 @@ Status: legacy external CLI integration. Gateway spawns `imsg rpc` and communica
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Local Mac (fast path)">
|
||||
@@ -358,7 +358,7 @@ imsg send <handle> "test"
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Configuration reference pointers
|
||||
## Configuration
|
||||
|
||||
- [Configuration reference - iMessage](/gateway/configuration-reference#imessage)
|
||||
- [Gateway configuration](/gateway/configuration)
|
||||
|
||||
@@ -31,7 +31,7 @@ Local checkout (when running from a git repo):
|
||||
openclaw plugins install ./extensions/line
|
||||
```
|
||||
|
||||
## Setup
|
||||
## Onboarding
|
||||
|
||||
1. Create a LINE Developers account and open the Console:
|
||||
[https://developers.line.biz/console/](https://developers.line.biz/console/)
|
||||
@@ -48,7 +48,7 @@ The gateway responds to LINE’s webhook verification (GET) and inbound events (
|
||||
If you need a custom path, set `channels.line.webhookPath` or
|
||||
`channels.line.accounts.<id>.webhookPath` and update the URL accordingly.
|
||||
|
||||
## Configure
|
||||
## Configuration
|
||||
|
||||
Minimal config:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ OpenClaw will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Setup
|
||||
## Onboarding
|
||||
|
||||
1. Install the Matrix plugin:
|
||||
- From npm: `openclaw plugins install @openclaw/matrix`
|
||||
@@ -270,7 +270,7 @@ Common failures:
|
||||
|
||||
For triage flow: [/channels/troubleshooting](/channels/troubleshooting).
|
||||
|
||||
## Configuration reference (Matrix)
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ OpenClaw will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
1. Install the Mattermost plugin.
|
||||
2. Create a Mattermost bot account and copy the **bot token**.
|
||||
|
||||
@@ -38,7 +38,7 @@ OpenClaw will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Install the Microsoft Teams plugin.
|
||||
2. Create an **Azure Bot** (App ID + client secret + tenant ID).
|
||||
@@ -236,7 +236,7 @@ This is often easier than hand-editing JSON manifests.
|
||||
2. Find the bot in Teams and send a DM
|
||||
3. Check gateway logs for incoming activity
|
||||
|
||||
## Setup (minimal text-only)
|
||||
## Onboarding (minimal)
|
||||
|
||||
1. **Install the Microsoft Teams plugin**
|
||||
- From npm: `openclaw plugins install @openclaw/msteams`
|
||||
|
||||
@@ -30,7 +30,7 @@ OpenClaw will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Install the Nextcloud Talk plugin.
|
||||
2. On your Nextcloud server, create a bot:
|
||||
@@ -106,7 +106,7 @@ Minimal config:
|
||||
| Reactions | Supported |
|
||||
| Native commands | Not supported |
|
||||
|
||||
## Configuration reference (Nextcloud Talk)
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ openclaw plugins install --link <path-to-openclaw>/extensions/nostr
|
||||
|
||||
Restart the Gateway after installing or enabling plugins.
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
1. Generate a Nostr keypair (if needed):
|
||||
|
||||
@@ -69,7 +69,7 @@ export NOSTR_PRIVATE_KEY="nsec1..."
|
||||
|
||||
4. Restart the Gateway.
|
||||
|
||||
## Configuration reference
|
||||
## Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------ | -------- | ------------------------------------------- | ----------------------------------- |
|
||||
|
||||
@@ -17,7 +17,7 @@ Status: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-R
|
||||
- A phone number that can receive one verification SMS (for SMS registration path).
|
||||
- Browser access for Signal captcha (`signalcaptchas.org`) during registration.
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Use a **separate Signal number** for the bot (recommended).
|
||||
2. Install `signal-cli` (Java required if you use the JVM build).
|
||||
@@ -76,7 +76,7 @@ Disable with:
|
||||
- If you run the bot on **your personal Signal account**, it will ignore your own messages (loop protection).
|
||||
- For "I text the bot and it replies," use a **separate bot number**.
|
||||
|
||||
## Setup path A: link existing Signal account (QR)
|
||||
## Onboarding (option A): link existing Signal account (QR)
|
||||
|
||||
1. Install `signal-cli` (JVM or native build).
|
||||
2. Link a bot account:
|
||||
@@ -101,7 +101,7 @@ Example:
|
||||
|
||||
Multi-account support: use `channels.signal.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
|
||||
|
||||
## Setup path B: register dedicated bot number (SMS, Linux)
|
||||
## Onboarding (option B): register dedicated bot number (SMS, Linux)
|
||||
|
||||
Use this when you want a dedicated bot number instead of linking an existing Signal app account.
|
||||
|
||||
@@ -290,7 +290,7 @@ For triage flow: [/channels/troubleshooting](/channels/troubleshooting).
|
||||
- Keep `channels.signal.dmPolicy: "pairing"` unless you explicitly want broader DM access.
|
||||
- SMS verification is only needed for registration or recovery flows, but losing control of the number/account can complicate re-registration.
|
||||
|
||||
## Configuration reference (Signal)
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Status: production-ready for DMs + channels via Slack app integrations. Default
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Socket Mode (default)">
|
||||
@@ -487,7 +487,7 @@ channels:
|
||||
- Media and non-text payloads fall back to normal delivery.
|
||||
- If streaming fails mid-reply, OpenClaw falls back to normal delivery for remaining payloads.
|
||||
|
||||
## Configuration reference pointers
|
||||
## Configuration
|
||||
|
||||
Primary reference:
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Status: production-ready for bot DMs + groups via grammY. Long polling is the de
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the bot token in BotFather">
|
||||
@@ -226,21 +226,8 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
|
||||
Requirement:
|
||||
|
||||
- `channels.telegram.streamMode` is not `"off"` (default: `"partial"`)
|
||||
|
||||
Modes:
|
||||
|
||||
- `off`: no live preview
|
||||
- `partial`: frequent preview updates from partial text
|
||||
- `block`: chunked preview updates using `channels.telegram.draftChunk`
|
||||
|
||||
`draftChunk` defaults for `streamMode: "block"`:
|
||||
|
||||
- `minChars: 200`
|
||||
- `maxChars: 800`
|
||||
- `breakPreference: "paragraph"`
|
||||
|
||||
`maxChars` is clamped by `channels.telegram.textChunkLimit`.
|
||||
- `channels.telegram.streaming` is `true` (default)
|
||||
- legacy `channels.telegram.streamMode` values are auto-mapped to `streaming`
|
||||
|
||||
This works in direct chats and groups/topics.
|
||||
|
||||
@@ -248,7 +235,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
|
||||
For complex replies (for example media payloads), OpenClaw falls back to normal final delivery and then cleans up the preview message.
|
||||
|
||||
`streamMode` is separate from block streaming. When block streaming is explicitly enabled for Telegram, OpenClaw skips the preview stream to avoid double-streaming.
|
||||
Preview streaming is separate from block streaming. When block streaming is explicitly enabled for Telegram, OpenClaw skips the preview stream to avoid double-streaming.
|
||||
|
||||
Telegram-only reasoning stream:
|
||||
|
||||
@@ -721,7 +708,7 @@ Primary reference:
|
||||
- `channels.telegram.textChunkLimit`: outbound chunk size (chars).
|
||||
- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true).
|
||||
- `channels.telegram.streamMode`: `off | partial | block` (live stream preview).
|
||||
- `channels.telegram.streaming`: `true | false` (live stream preview; default: true).
|
||||
- `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
||||
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts.
|
||||
@@ -745,7 +732,7 @@ Telegram-specific high-signal fields:
|
||||
- access control: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups`, `groups.*.topics.*`
|
||||
- command/menu: `commands.native`, `customCommands`
|
||||
- threading/replies: `replyToMode`
|
||||
- streaming: `streamMode` (preview), `draftChunk`, `blockStreaming`
|
||||
- streaming: `streaming` (preview), `blockStreaming`
|
||||
- formatting/delivery: `textChunkLimit`, `chunkMode`, `linkPreview`, `responsePrefix`
|
||||
- media/network: `mediaMaxMb`, `timeoutSeconds`, `retry`, `network.autoSelectFamily`, `proxy`
|
||||
- webhook: `webhookUrl`, `webhookSecret`, `webhookPath`, `webhookHost`
|
||||
|
||||
@@ -32,7 +32,7 @@ openclaw plugins install ./extensions/tlon
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Setup
|
||||
## Onboarding
|
||||
|
||||
1. Install the Tlon plugin.
|
||||
2. Gather your ship URL and login code.
|
||||
|
||||
@@ -27,7 +27,7 @@ openclaw plugins install ./extensions/twitch
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Create a dedicated Twitch account for the bot (or use an existing account).
|
||||
2. Generate credentials: [Twitch Token Generator](https://twitchtokengenerator.com/)
|
||||
@@ -67,7 +67,7 @@ Minimal config:
|
||||
- Each account maps to an isolated session key `agent:<agentId>:twitch:<accountName>`.
|
||||
- `username` is the bot's account (who authenticates), `channel` is which chat room to join.
|
||||
|
||||
## Setup (detailed)
|
||||
## Onboarding (detailed, recommended)
|
||||
|
||||
### Generate credentials
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Status: production-ready via WhatsApp Web (Baileys). Gateway owns linked session
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quick setup
|
||||
## Onboarding
|
||||
|
||||
<Steps>
|
||||
<Step title="Configure WhatsApp access policy">
|
||||
@@ -422,7 +422,7 @@ Behavior notes:
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Configuration reference pointers
|
||||
## Configuration
|
||||
|
||||
Primary reference:
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Zalo ships as a plugin and is not bundled with the core install.
|
||||
- Or select **Zalo** during onboarding and confirm the install prompt
|
||||
- Details: [Plugins](/tools/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Install the Zalo plugin:
|
||||
- From a source checkout: `openclaw plugins install ./extensions/zalo`
|
||||
@@ -53,7 +53,7 @@ It is a good fit for support or notifications where you want deterministic routi
|
||||
- DMs share the agent's main session.
|
||||
- Groups are not yet supported (Zalo docs state "coming soon").
|
||||
|
||||
## Setup (fast path)
|
||||
## Onboarding (quick path)
|
||||
|
||||
### 1) Create a bot token (Zalo Bot Platform)
|
||||
|
||||
@@ -161,7 +161,7 @@ Multi-account support: use `channels.zalo.accounts` with per-account tokens and
|
||||
- Confirm the gateway HTTP endpoint is reachable on the configured path
|
||||
- Check that getUpdates polling is not running (they're mutually exclusive)
|
||||
|
||||
## Configuration reference (Zalo)
|
||||
## Configuration
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ The Gateway machine must have the `zca` binary available in `PATH`.
|
||||
- Verify: `zca --version`
|
||||
- If missing, install zca-cli (see `extensions/zalouser/README.md` or the upstream zca-cli docs).
|
||||
|
||||
## Quick setup (beginner)
|
||||
## Onboarding
|
||||
|
||||
1. Install the plugin (see above).
|
||||
2. Login (QR, on the Gateway machine):
|
||||
|
||||
@@ -24,9 +24,12 @@ openclaw security audit --json
|
||||
```
|
||||
|
||||
The audit warns when multiple DM senders share the main session and recommends **secure DM mode**: `session.dmScope="per-channel-peer"` (or `per-account-channel-peer` for multi-account channels) for shared inboxes.
|
||||
This is for cooperative/shared inbox hardening. A single Gateway shared by mutually untrusted/adversarial operators is not a recommended setup; split trust boundaries with separate gateways (or separate OS users/hosts).
|
||||
It also warns when small models (`<=300B`) are used without sandboxing and with web/browser tools enabled.
|
||||
For webhook ingress, it warns when `hooks.defaultSessionKey` is unset, when request `sessionKey` overrides are enabled, and when overrides are enabled without `hooks.allowedSessionKeyPrefixes`.
|
||||
It also warns when sandbox Docker settings are configured while sandbox mode is off, when `gateway.nodes.denyCommands` uses ineffective pattern-like/unknown entries, when global `tools.profile="minimal"` is overridden by agent tool profiles, and when installed extension plugin tools may be reachable under permissive tool policy.
|
||||
It also warns when sandbox browser uses Docker `bridge` network without `sandbox.browser.cdpSourceRange`.
|
||||
It also warns when existing sandbox browser Docker containers have missing/stale hash labels (for example pre-migration containers missing `openclaw.browserConfigEpoch`) and recommends `openclaw sandbox recreate --browser --all`.
|
||||
It also warns when npm-based plugin/hook install records are unpinned, missing integrity metadata, or drift from currently installed package versions.
|
||||
It warns when `gateway.auth.mode="none"` leaves Gateway HTTP APIs reachable without a shared secret (`/tools/invoke` plus any enabled `/v1/*` endpoint).
|
||||
|
||||
|
||||
@@ -216,6 +216,70 @@ Model refs:
|
||||
|
||||
See [/providers/qwen](/providers/qwen) for setup details and notes.
|
||||
|
||||
### Volcano Engine (Doubao)
|
||||
|
||||
Volcano Engine (火山引擎) provides access to Doubao and other models in China.
|
||||
|
||||
- Provider: `volcengine` (coding: `volcengine-plan`)
|
||||
- Auth: `VOLCANO_ENGINE_API_KEY`
|
||||
- Example model: `volcengine/doubao-seed-1-8-251228`
|
||||
- CLI: `openclaw onboard --auth-choice volcengine-api-key`
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: { model: { primary: "volcengine/doubao-seed-1-8-251228" } },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Available models:
|
||||
|
||||
- `volcengine/doubao-seed-1-8-251228` (Doubao Seed 1.8)
|
||||
- `volcengine/doubao-seed-code-preview-251028`
|
||||
- `volcengine/kimi-k2-5-260127` (Kimi K2.5)
|
||||
- `volcengine/glm-4-7-251222` (GLM 4.7)
|
||||
- `volcengine/deepseek-v3-2-251201` (DeepSeek V3.2 128K)
|
||||
|
||||
Coding models (`volcengine-plan`):
|
||||
|
||||
- `volcengine-plan/ark-code-latest`
|
||||
- `volcengine-plan/doubao-seed-code`
|
||||
- `volcengine-plan/kimi-k2.5`
|
||||
- `volcengine-plan/kimi-k2-thinking`
|
||||
- `volcengine-plan/glm-4.7`
|
||||
|
||||
### BytePlus (International)
|
||||
|
||||
BytePlus ARK provides access to the same models as Volcano Engine for international users.
|
||||
|
||||
- Provider: `byteplus` (coding: `byteplus-plan`)
|
||||
- Auth: `BYTEPLUS_API_KEY`
|
||||
- Example model: `byteplus/seed-1-8-251228`
|
||||
- CLI: `openclaw onboard --auth-choice byteplus-api-key`
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: { model: { primary: "byteplus/seed-1-8-251228" } },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Available models:
|
||||
|
||||
- `byteplus/seed-1-8-251228` (Seed 1.8)
|
||||
- `byteplus/kimi-k2-5-260127` (Kimi K2.5)
|
||||
- `byteplus/glm-4-7-251222` (GLM 4.7)
|
||||
|
||||
Coding models (`byteplus-plan`):
|
||||
|
||||
- `byteplus-plan/ark-code-latest`
|
||||
- `byteplus-plan/doubao-seed-code`
|
||||
- `byteplus-plan/kimi-k2.5`
|
||||
- `byteplus-plan/kimi-k2-thinking`
|
||||
- `byteplus-plan/glm-4.7`
|
||||
|
||||
### Synthetic
|
||||
|
||||
Synthetic provides Anthropic-compatible models behind the `synthetic` provider:
|
||||
|
||||
@@ -100,7 +100,7 @@ This maps to:
|
||||
|
||||
**Channel note:** For non-Telegram channels, block streaming is **off unless**
|
||||
`*.blockStreaming` is explicitly set to `true`. Telegram can stream a live preview
|
||||
(`channels.telegram.streamMode`) without block replies.
|
||||
(`channels.telegram.streaming`) without block replies.
|
||||
|
||||
Config location reminder: the `blockStreaming*` defaults live under
|
||||
`agents.defaults`, not the root config.
|
||||
@@ -110,11 +110,7 @@ Config location reminder: the `blockStreaming*` defaults live under
|
||||
Telegram is the only channel with live preview streaming:
|
||||
|
||||
- Uses Bot API `sendMessage` (first update) + `editMessageText` (subsequent updates).
|
||||
- `channels.telegram.streamMode: "partial" | "block" | "off"`.
|
||||
- `partial`: preview updates with latest stream text.
|
||||
- `block`: preview updates in chunked blocks (same chunker rules).
|
||||
- `off`: no preview streaming.
|
||||
- Preview chunk config (only for `streamMode: "block"`): `channels.telegram.draftChunk` (defaults: `minChars: 200`, `maxChars: 800`).
|
||||
- `channels.telegram.streaming: true | false` (default: `true`).
|
||||
- Preview streaming is separate from block streaming.
|
||||
- When Telegram block streaming is explicitly enabled, preview streaming is skipped to avoid double-streaming.
|
||||
- Text-only finals are applied by editing the preview message in place.
|
||||
@@ -124,8 +120,7 @@ Telegram is the only channel with live preview streaming:
|
||||
```
|
||||
Telegram
|
||||
└─ sendMessage (temporary preview message)
|
||||
├─ streamMode=partial → edit latest text
|
||||
└─ streamMode=block → chunker + edit updates
|
||||
└─ streaming=true → edit latest text
|
||||
└─ final text-only reply → final edit on same message
|
||||
└─ fallback: cleanup preview + normal final delivery (media/complex)
|
||||
```
|
||||
|
||||
@@ -151,12 +151,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
historyLimit: 50,
|
||||
replyToMode: "first", // off | first | all
|
||||
linkPreview: true,
|
||||
streamMode: "partial", // off | partial | block
|
||||
draftChunk: {
|
||||
minChars: 200,
|
||||
maxChars: 800,
|
||||
breakPreference: "paragraph", // paragraph | newline | sentence
|
||||
},
|
||||
streaming: true, // live preview on/off (default true)
|
||||
actions: { reactions: true, sendMessage: true },
|
||||
reactionNotifications: "own", // off | own | all
|
||||
mediaMaxMb: 5,
|
||||
@@ -935,7 +930,9 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway
|
||||
browser: {
|
||||
enabled: false,
|
||||
image: "openclaw-sandbox-browser:bookworm-slim",
|
||||
network: "openclaw-sandbox-browser",
|
||||
cdpPort: 9222,
|
||||
cdpSourceRange: "172.21.0.1/32",
|
||||
vncPort: 5900,
|
||||
noVncPort: 6080,
|
||||
headless: false,
|
||||
@@ -997,8 +994,11 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway
|
||||
**`docker.binds`** mounts additional host directories; global and per-agent binds are merged.
|
||||
|
||||
**Sandboxed browser** (`sandbox.browser.enabled`): Chromium + CDP in a container. noVNC URL injected into system prompt. Does not require `browser.enabled` in main config.
|
||||
noVNC observer access uses VNC auth by default and OpenClaw emits a short-lived token URL (instead of exposing the password in the shared URL).
|
||||
|
||||
- `allowHostControl: false` (default) blocks sandboxed sessions from targeting the host browser.
|
||||
- `network` defaults to `openclaw-sandbox-browser` (dedicated bridge network). Set to `bridge` only when you explicitly want global bridge connectivity.
|
||||
- `cdpSourceRange` optionally restricts CDP ingress at the container edge to a CIDR range (for example `172.21.0.1/32`).
|
||||
- `sandbox.browser.binds` mounts additional host directories into the sandbox browser container only. When set (including `[]`), it replaces `docker.binds` for the browser container.
|
||||
|
||||
</Accordion>
|
||||
@@ -1347,6 +1347,7 @@ Batches rapid text-only messages from the same sender into a single agent turn.
|
||||
|
||||
- `auto` controls auto-TTS. `/tts off|always|inbound|tagged` overrides per session.
|
||||
- `summaryModel` overrides `agents.defaults.model.primary` for auto-summary.
|
||||
- `modelOverrides` is enabled by default; `modelOverrides.allowProvider` defaults to `false` (opt-in).
|
||||
- API keys fall back to `ELEVENLABS_API_KEY`/`XI_API_KEY` and `OPENAI_API_KEY`.
|
||||
|
||||
---
|
||||
@@ -2047,6 +2048,8 @@ See [Plugins](/tools/plugin).
|
||||
// password: "your-password",
|
||||
},
|
||||
trustedProxies: ["10.0.0.1"],
|
||||
// Optional. Default false.
|
||||
allowRealIpFallback: false,
|
||||
tools: {
|
||||
// Additional /tools/invoke HTTP denies
|
||||
deny: ["browser"],
|
||||
@@ -2065,13 +2068,14 @@ See [Plugins](/tools/plugin).
|
||||
- **Auth**: required by default. Non-loopback binds require a shared token/password. Onboarding wizard generates a token by default.
|
||||
- `auth.mode: "none"`: explicit no-auth mode. Use only for trusted local loopback setups; this is intentionally not offered by onboarding prompts.
|
||||
- `auth.mode: "trusted-proxy"`: delegate auth to an identity-aware reverse proxy and trust identity headers from `gateway.trustedProxies` (see [Trusted Proxy Auth](/gateway/trusted-proxy-auth)).
|
||||
- `auth.allowTailscale`: when `true`, Tailscale Serve identity headers satisfy auth (verified via `tailscale whois`). Defaults to `true` when `tailscale.mode = "serve"`.
|
||||
- `auth.allowTailscale`: when `true`, Tailscale Serve identity headers can satisfy Control UI/WebSocket auth (verified via `tailscale whois`); HTTP API endpoints still require token/password auth. This tokenless flow assumes the gateway host is trusted. Defaults to `true` when `tailscale.mode = "serve"`.
|
||||
- `auth.rateLimit`: optional failed-auth limiter. Applies per client IP and per auth scope (shared-secret and device-token are tracked independently). Blocked attempts return `429` + `Retry-After`.
|
||||
- `auth.rateLimit.exemptLoopback` defaults to `true`; set `false` when you intentionally want localhost traffic rate-limited too (for test setups or strict proxy deployments).
|
||||
- `tailscale.mode`: `serve` (tailnet only, loopback bind) or `funnel` (public, requires auth).
|
||||
- `remote.transport`: `ssh` (default) or `direct` (ws/wss). For `direct`, `remote.url` must be `ws://` or `wss://`.
|
||||
- `gateway.remote.token` is for remote CLI calls only; does not enable local gateway auth.
|
||||
- `trustedProxies`: reverse proxy IPs that terminate TLS. Only list proxies you control.
|
||||
- `allowRealIpFallback`: when `true`, the gateway accepts `X-Real-IP` if `X-Forwarded-For` is missing. Default `false` for fail-closed behavior.
|
||||
- `gateway.tools.deny`: extra tool names blocked for HTTP `POST /tools/invoke` (extends default deny list).
|
||||
- `gateway.tools.allow`: remove tool names from the default HTTP deny list.
|
||||
|
||||
|
||||
@@ -204,8 +204,8 @@ The Gateway treats these as **claims** and enforces server-side allowlists.
|
||||
- **Local** connects include loopback and the gateway host’s own tailnet address
|
||||
(so same‑host tailnet binds can still auto‑approve).
|
||||
- All WS clients must include `device` identity during `connect` (operator + node).
|
||||
Control UI can omit it **only** when `gateway.controlUi.allowInsecureAuth` is enabled
|
||||
(or `gateway.controlUi.dangerouslyDisableDeviceAuth` for break-glass use).
|
||||
Control UI can omit it **only** when `gateway.controlUi.dangerouslyDisableDeviceAuth`
|
||||
is enabled for break-glass use.
|
||||
- Non-local connections must sign the server-provided `connect.challenge` nonce.
|
||||
|
||||
## TLS + pinning
|
||||
|
||||
@@ -122,8 +122,10 @@ Short version: **keep the Gateway loopback-only** unless you’re sure you need
|
||||
- **Non-loopback binds** (`lan`/`tailnet`/`custom`, or `auto` when loopback is unavailable) must use auth tokens/passwords.
|
||||
- `gateway.remote.token` is **only** for remote CLI calls — it does **not** enable local auth.
|
||||
- `gateway.remote.tlsFingerprint` pins the remote TLS cert when using `wss://`.
|
||||
- **Tailscale Serve** can authenticate via identity headers when `gateway.auth.allowTailscale: true`.
|
||||
Set it to `false` if you want tokens/passwords instead.
|
||||
- **Tailscale Serve** can authenticate Control UI/WebSocket traffic via identity
|
||||
headers when `gateway.auth.allowTailscale: true`; HTTP API endpoints still
|
||||
require token/password auth. This tokenless flow assumes the gateway host is
|
||||
trusted. Set it to `false` if you want tokens/passwords everywhere.
|
||||
- Treat browser control like operator access: tailnet-only + deliberate node pairing.
|
||||
|
||||
Deep dive: [Security](/gateway/security).
|
||||
|
||||
@@ -22,6 +22,10 @@ and process access when the model does something dumb.
|
||||
- Optional sandboxed browser (`agents.defaults.sandbox.browser`).
|
||||
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
|
||||
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
|
||||
- By default, sandbox browser containers use a dedicated Docker network (`openclaw-sandbox-browser`) instead of the global `bridge` network.
|
||||
Configure with `agents.defaults.sandbox.browser.network`.
|
||||
- Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts container-edge CDP ingress with a CIDR allowlist (for example `172.21.0.1/32`).
|
||||
- noVNC observer access is password-protected by default; OpenClaw emits a short-lived token URL that resolves to the observer session.
|
||||
- `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions target the host browser explicitly.
|
||||
- Optional allowlists gate `target: "custom"`: `allowedControlUrls`, `allowedControlHosts`, `allowedControlPorts`.
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@ OpenClaw is both a product and an experiment: you’re wiring frontier-model beh
|
||||
|
||||
Start with the smallest access that still works, then widen it as you gain confidence.
|
||||
|
||||
## Deployment assumption (important)
|
||||
|
||||
OpenClaw assumes the host and config boundary are trusted:
|
||||
|
||||
- If someone can modify Gateway host state/config (`~/.openclaw`, including `openclaw.json`), treat them as a trusted operator.
|
||||
- Running one Gateway for multiple mutually untrusted/adversarial operators is **not a recommended setup**.
|
||||
- For mixed-trust teams, split trust boundaries with separate gateways (or at minimum separate OS users/hosts).
|
||||
|
||||
## Hardened baseline in 60 seconds
|
||||
|
||||
Use this baseline first, then selectively re-enable tools per trusted agent:
|
||||
@@ -66,6 +74,7 @@ If more than one person can DM your bot:
|
||||
- Set `session.dmScope: "per-channel-peer"` (or `"per-account-channel-peer"` for multi-account channels).
|
||||
- Keep `dmPolicy: "pairing"` or strict allowlists.
|
||||
- Never combine shared DMs with broad tool access.
|
||||
- This hardens cooperative/shared inboxes, but is not designed as hostile co-tenant isolation when users share host/config write access.
|
||||
|
||||
### What the audit checks (high level)
|
||||
|
||||
@@ -118,8 +127,9 @@ High-signal `checkId` values you will most likely see in real deployments (not e
|
||||
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no |
|
||||
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
|
||||
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
|
||||
| `gateway.control_ui.insecure_auth` | critical | Token-only over HTTP, no device identity | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
|
||||
| `config.insecure_or_dangerous_flags` | warn | Any insecure/dangerous debug flags enabled | multiple keys (see finding detail) | no |
|
||||
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
|
||||
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
|
||||
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
|
||||
@@ -134,9 +144,9 @@ High-signal `checkId` values you will most likely see in real deployments (not e
|
||||
## Control UI over HTTP
|
||||
|
||||
The Control UI needs a **secure context** (HTTPS or localhost) to generate device
|
||||
identity. If you enable `gateway.controlUi.allowInsecureAuth`, the UI falls back
|
||||
to **token-only auth** and skips device pairing when device identity is omitted. This is a security
|
||||
downgrade—prefer HTTPS (Tailscale Serve) or open the UI on `127.0.0.1`.
|
||||
identity. `gateway.controlUi.allowInsecureAuth` does **not** bypass secure-context,
|
||||
device-identity, or device-pairing checks. Prefer HTTPS (Tailscale Serve) or open
|
||||
the UI on `127.0.0.1`.
|
||||
|
||||
For break-glass scenarios only, `gateway.controlUi.dangerouslyDisableDeviceAuth`
|
||||
disables device identity checks entirely. This is a severe security downgrade;
|
||||
@@ -144,22 +154,48 @@ keep it off unless you are actively debugging and can revert quickly.
|
||||
|
||||
`openclaw security audit` warns when this setting is enabled.
|
||||
|
||||
## Insecure or dangerous flags summary
|
||||
|
||||
`openclaw security audit` includes `config.insecure_or_dangerous_flags` when any
|
||||
insecure/dangerous debug switches are enabled. This warning aggregates the exact
|
||||
keys so you can review them in one place (for example
|
||||
`gateway.controlUi.allowInsecureAuth=true`,
|
||||
`gateway.controlUi.dangerouslyDisableDeviceAuth=true`,
|
||||
`hooks.gmail.allowUnsafeExternalContent=true`, or
|
||||
`tools.exec.applyPatch.workspaceOnly=false`).
|
||||
|
||||
## Reverse Proxy Configuration
|
||||
|
||||
If you run the Gateway behind a reverse proxy (nginx, Caddy, Traefik, etc.), you should configure `gateway.trustedProxies` for proper client IP detection.
|
||||
|
||||
When the Gateway detects proxy headers (`X-Forwarded-For` or `X-Real-IP`) from an address that is **not** in `trustedProxies`, it will **not** treat connections as local clients. If gateway auth is disabled, those connections are rejected. This prevents authentication bypass where proxied connections would otherwise appear to come from localhost and receive automatic trust.
|
||||
When the Gateway detects proxy headers from an address that is **not** in `trustedProxies`, it will **not** treat connections as local clients. If gateway auth is disabled, those connections are rejected. This prevents authentication bypass where proxied connections would otherwise appear to come from localhost and receive automatic trust.
|
||||
|
||||
```yaml
|
||||
gateway:
|
||||
trustedProxies:
|
||||
- "127.0.0.1" # if your proxy runs on localhost
|
||||
# Optional. Default false.
|
||||
# Only enable if your proxy cannot provide X-Forwarded-For.
|
||||
allowRealIpFallback: false
|
||||
auth:
|
||||
mode: password
|
||||
password: ${OPENCLAW_GATEWAY_PASSWORD}
|
||||
```
|
||||
|
||||
When `trustedProxies` is configured, the Gateway will use `X-Forwarded-For` headers to determine the real client IP for local client detection. Make sure your proxy overwrites (not appends to) incoming `X-Forwarded-For` headers to prevent spoofing.
|
||||
When `trustedProxies` is configured, the Gateway uses `X-Forwarded-For` to determine the client IP. `X-Real-IP` is ignored by default unless `gateway.allowRealIpFallback: true` is explicitly set.
|
||||
|
||||
Good reverse proxy behavior (overwrite incoming forwarding headers):
|
||||
|
||||
```nginx
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
```
|
||||
|
||||
Bad reverse proxy behavior (append/preserve untrusted forwarding headers):
|
||||
|
||||
```nginx
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
```
|
||||
|
||||
## Local session logs live on disk
|
||||
|
||||
@@ -285,6 +321,8 @@ By default, OpenClaw routes **all DMs into the main session** so your assistant
|
||||
|
||||
This prevents cross-user context leakage while keeping group chats isolated.
|
||||
|
||||
This is a messaging-context boundary, not a host-admin boundary. If users are mutually adversarial and share the same Gateway host/config, run separate gateways per trust boundary instead.
|
||||
|
||||
### Secure DM mode (recommended)
|
||||
|
||||
Treat the snippet above as **secure DM mode**:
|
||||
@@ -521,12 +559,19 @@ Rotation checklist (token/password):
|
||||
### 0.6) Tailscale Serve identity headers
|
||||
|
||||
When `gateway.auth.allowTailscale` is `true` (default for Serve), OpenClaw
|
||||
accepts Tailscale Serve identity headers (`tailscale-user-login`) as
|
||||
authentication. OpenClaw verifies the identity by resolving the
|
||||
accepts Tailscale Serve identity headers (`tailscale-user-login`) for Control
|
||||
UI/WebSocket authentication. OpenClaw verifies the identity by resolving the
|
||||
`x-forwarded-for` address through the local Tailscale daemon (`tailscale whois`)
|
||||
and matching it to the header. This only triggers for requests that hit loopback
|
||||
and include `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` as
|
||||
injected by Tailscale.
|
||||
HTTP API endpoints (for example `/v1/*`, `/tools/invoke`, and `/api/channels/*`)
|
||||
still require token/password auth.
|
||||
|
||||
**Trust assumption:** tokenless Serve auth assumes the gateway host is trusted.
|
||||
Do not treat this as protection against hostile same-host processes. If untrusted
|
||||
local code may run on the gateway host, disable `gateway.auth.allowTailscale`
|
||||
and require token/password auth.
|
||||
|
||||
**Security rule:** do not forward these headers from your own reverse proxy. If
|
||||
you terminate TLS or proxy in front of the gateway, disable
|
||||
|
||||
@@ -26,13 +26,18 @@ Set `gateway.auth.mode` to control the handshake:
|
||||
- `password` (shared secret via `OPENCLAW_GATEWAY_PASSWORD` or config)
|
||||
|
||||
When `tailscale.mode = "serve"` and `gateway.auth.allowTailscale` is `true`,
|
||||
valid Serve proxy requests can authenticate via Tailscale identity headers
|
||||
Control UI/WebSocket auth can use Tailscale identity headers
|
||||
(`tailscale-user-login`) without supplying a token/password. OpenClaw verifies
|
||||
the identity by resolving the `x-forwarded-for` address via the local Tailscale
|
||||
daemon (`tailscale whois`) and matching it to the header before accepting it.
|
||||
OpenClaw only treats a request as Serve when it arrives from loopback with
|
||||
Tailscale’s `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`
|
||||
headers.
|
||||
HTTP API endpoints (for example `/v1/*`, `/tools/invoke`, and `/api/channels/*`)
|
||||
still require token/password auth.
|
||||
This tokenless flow assumes the gateway host is trusted. If untrusted local code
|
||||
may run on the same host, disable `gateway.auth.allowTailscale` and require
|
||||
token/password auth instead.
|
||||
To require explicit credentials, set `gateway.auth.allowTailscale: false` or
|
||||
force `gateway.auth.mode: "password"`.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ The wizard opens your browser with a clean (non-tokenized) dashboard URL right a
|
||||
|
||||
**Not on localhost:**
|
||||
|
||||
- **Tailscale Serve** (recommended): keep bind loopback, run `openclaw gateway --tailscale serve`, open `https://<magicdns>/`. If `gateway.auth.allowTailscale` is `true`, identity headers satisfy auth (no token).
|
||||
- **Tailscale Serve** (recommended): keep bind loopback, run `openclaw gateway --tailscale serve`, open `https://<magicdns>/`. If `gateway.auth.allowTailscale` is `true`, identity headers satisfy Control UI/WebSocket auth (no token, assumes trusted gateway host); HTTP APIs still require token/password.
|
||||
- **Tailnet bind**: run `openclaw gateway --bind tailnet --token "<token>"`, open `http://<tailscale-ip>:18789/`, paste token in dashboard settings.
|
||||
- **SSH tunnel**: `ssh -N -L 18789:127.0.0.1:18789 user@host` then open `http://127.0.0.1:18789/` and paste the token in Control UI settings.
|
||||
|
||||
|
||||
@@ -495,6 +495,9 @@ Notes:
|
||||
- Headful (Xvfb) reduces bot blocking vs headless.
|
||||
- Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true`.
|
||||
- No full desktop environment (GNOME) is needed; Xvfb provides the display.
|
||||
- Browser containers default to a dedicated Docker network (`openclaw-sandbox-browser`) instead of global `bridge`.
|
||||
- Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts container-edge CDP ingress by CIDR (for example `172.21.0.1/32`).
|
||||
- noVNC observer access is password-protected by default; OpenClaw provides a short-lived observer token URL instead of sharing the raw password in the URL.
|
||||
|
||||
Use config:
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ Open: `https://<magicdns>/`
|
||||
|
||||
Notes:
|
||||
|
||||
- Serve keeps the Gateway loopback-only and authenticates via Tailscale identity headers.
|
||||
- Serve keeps the Gateway loopback-only and authenticates Control UI/WebSocket traffic via Tailscale identity headers (tokenless auth assumes trusted gateway host; HTTP APIs still require token/password).
|
||||
- To require token/password instead, set `gateway.auth.allowTailscale: false` or use `gateway.auth.mode: "password"`.
|
||||
|
||||
**Option C: Tailnet bind (no Serve)**
|
||||
|
||||
@@ -34,17 +34,17 @@ Notes:
|
||||
# From repo root; set release IDs so Sparkle feed is enabled.
|
||||
# APP_BUILD must be numeric + monotonic for Sparkle compare.
|
||||
BUNDLE_ID=bot.molt.mac \
|
||||
APP_VERSION=2026.2.20 \
|
||||
APP_VERSION=2026.2.21 \
|
||||
APP_BUILD="$(git rev-list --count HEAD)" \
|
||||
BUILD_CONFIG=release \
|
||||
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
|
||||
scripts/package-mac-app.sh
|
||||
|
||||
# Zip for distribution (includes resource forks for Sparkle delta support)
|
||||
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.2.20.zip
|
||||
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.2.21.zip
|
||||
|
||||
# Optional: also build a styled DMG for humans (drag to /Applications)
|
||||
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.20.dmg
|
||||
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.21.dmg
|
||||
|
||||
# Recommended: build + notarize/staple zip + DMG
|
||||
# First, create a keychain profile once:
|
||||
@@ -52,14 +52,14 @@ scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.20.dmg
|
||||
# --apple-id "<apple-id>" --team-id "<team-id>" --password "<app-specific-password>"
|
||||
NOTARIZE=1 NOTARYTOOL_PROFILE=openclaw-notary \
|
||||
BUNDLE_ID=bot.molt.mac \
|
||||
APP_VERSION=2026.2.20 \
|
||||
APP_VERSION=2026.2.21 \
|
||||
APP_BUILD="$(git rev-list --count HEAD)" \
|
||||
BUILD_CONFIG=release \
|
||||
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
|
||||
scripts/package-mac-dist.sh
|
||||
|
||||
# Optional: ship dSYM alongside the release
|
||||
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.2.20.dSYM.zip
|
||||
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.2.21.dSYM.zip
|
||||
```
|
||||
|
||||
## Appcast entry
|
||||
@@ -67,7 +67,7 @@ ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenCl
|
||||
Use the release note generator so Sparkle renders formatted HTML notes:
|
||||
|
||||
```bash
|
||||
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.2.20.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
|
||||
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.2.21.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
|
||||
```
|
||||
|
||||
Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry.
|
||||
@@ -75,7 +75,7 @@ Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when
|
||||
|
||||
## Publish & verify
|
||||
|
||||
- Upload `OpenClaw-2026.2.20.zip` (and `OpenClaw-2026.2.20.dSYM.zip`) to the GitHub release for tag `v2026.2.20`.
|
||||
- Upload `OpenClaw-2026.2.21.zip` (and `OpenClaw-2026.2.21.dSYM.zip`) to the GitHub release for tag `v2026.2.21`.
|
||||
- Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`.
|
||||
- Sanity checks:
|
||||
- `curl -I https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml` returns 200.
|
||||
|
||||
@@ -51,7 +51,7 @@ Notes:
|
||||
- `defaultContextWindow` (default: `32000`) and `defaultMaxTokens` (default: `4096`)
|
||||
are used for discovered models (override if you know your model limits).
|
||||
|
||||
## Setup (manual)
|
||||
## Onboarding
|
||||
|
||||
1. Ensure AWS credentials are available on the **gateway host**:
|
||||
|
||||
@@ -122,7 +122,7 @@ export AWS_REGION=us-east-1
|
||||
|
||||
Or attach the managed policy `AmazonBedrockFullAccess`.
|
||||
|
||||
**Quick setup:**
|
||||
## Quick setup (AWS path)
|
||||
|
||||
```bash
|
||||
# 1. Create IAM role and instance profile
|
||||
|
||||
@@ -146,6 +146,8 @@ Default safe bins: `jq`, `cut`, `uniq`, `head`, `tail`, `tr`, `wc`.
|
||||
|
||||
`grep` and `sort` are not in the default list. If you opt in, keep explicit allowlist entries for
|
||||
their non-stdin workflows.
|
||||
For `grep` in safe-bin mode, provide the pattern with `-e`/`--regexp`; positional pattern form is
|
||||
rejected so file operands cannot be smuggled as ambiguous positionals.
|
||||
|
||||
## Control UI editing
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ title: "Thinking Levels"
|
||||
- When verbose is on, agents that emit structured tool results (Pi, other JSON agents) send each tool call back as its own metadata-only message, prefixed with `<emoji> <tool-name>: <arg>` when available (path/command). These tool summaries are sent as soon as each tool starts (separate bubbles), not as streaming deltas.
|
||||
- When verbose is `full`, tool outputs are also forwarded after completion (separate bubble, truncated to a safe length). If you toggle `/verbose on|full|off` while a run is in-flight, subsequent tool bubbles honor the new setting.
|
||||
|
||||
## Reasoning visibility (/reasoning)
|
||||
## Reasoning visibility (/tools/thinking#reasoning-visibility-reasoning)
|
||||
|
||||
- Levels: `on|off|stream`.
|
||||
- Directive-only message toggles whether thinking blocks are shown in replies.
|
||||
@@ -61,6 +61,7 @@ title: "Thinking Levels"
|
||||
## Related
|
||||
|
||||
- Elevated mode docs live in [Elevated mode](/tools/elevated).
|
||||
- Reasoning visibility behavior is documented in [Reasoning visibility](/tools/thinking#reasoning-visibility-reasoning).
|
||||
|
||||
## Heartbeats
|
||||
|
||||
|
||||