Commit Graph

484 Commits

Author SHA1 Message Date
Pavan Kumar Gondhi 6e026c2fe3 fix(gateway): reject unattributable loopback proxy traffic [AI] (#119950)
* fix(gateway): bind auth limits to ingress attribution

* fix(gateway): close remaining ingress auth gaps

* fix(gateway): carry attribution into new ingress paths

* fix(gateway): close ingress ownership gaps

* fix(gateway): complete proxy ingress hardening

* fix(gateway): stabilize managed Tailscale ingress

* fix(gateway): make Tailscale cleanup ownership-safe

Refuse reset-on-exit publication until Tailscale exposes an atomic owner-bound cleanup operation, and migrate legacy configs with Doctor.

* fix(gateway): finish ingress ownership repair

* fix(gateway): own managed Tailscale route lifetime

Run managed Serve and Funnel routes as foreground claims tied to the Gateway lifecycle. Retire named Service config through Doctor because Tailscale Services cannot run in foreground mode.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* fix(gateway): align Tailscale consumers and build guards

Remove the retired named-service config from Telegram Mini App URL resolution and register the lifecycle worker as an explicit production entry.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* fix(gateway): preserve retired Tailscale inputs

Keep Funnel enabled when removing an ignored named-Service setting and accept the legacy positive reset flag as a no-op now that managed routes always follow Gateway lifetime.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* fix(gateway): preserve Tailscale route diagnostics

Prefer the actionable foreground CLI failure captured during timeout cleanup, and cover the original delayed-failure ordering.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* fix(gateway): reconcile Tailscale ingress with main

Preserve current ingress ownership contracts after the rebase, retire the obsolete device-auth migration check, validate route-owner IPC, and move Tailscale auth coverage onto the managed listener.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* fix(gateway): finish ingress rebase coverage

Unify the rebased net imports and let module-reset WebSocket tests prepare attribution through the same fresh module instance as the handler.

Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>

* test(gateway): align run-loop server fixture

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-08-16 21:01:20 -07:00
Peter Steinberger 086f5916fa fix(gateway): local devices stall on scope upgrade pending approval despite autoApproveLocal (#124589)
* fix(cli): announce when nodes list degrades to paired-only data

tryReadNodeList swallowed every enrichment failure, so the unfiltered nodes list silently rendered a table without connected/commands state. The fallback is now announced on stderr, keeping --json stdout parseable.

* fix(gateway): silently widen local pairing scopes as documented

`autoApproveLocal` has documented "silently approves pairing, role upgrades,
and scope upgrades from trusted local connections" since the loopback
auto-approval landed, but a later hardening pass forced every scope-upgrade
pairing request non-silent. That block protected nothing locally — silent
initial pairing grants a fresh identity arbitrary requested scopes, so any
local process could mint a new keypair instead of upgrading — while it
stranded every row-authorized client (CLI, native apps, node hosts) on a
manual approval no local surface could perform.

Scope upgrades now ride the same silent-local rule as initial pairing, with
one new restriction that encodes the real boundary: the connect must itself
prove local-grade credentials (auth mode none, or the shared token/password).
Identity-proxy connects (tailscale, trusted-proxy) and bearer device tokens
never did, so their pairing rows remain a durable scope cap, and
`autoApproveLocal: false` still forces manual approval for everything.

The silent self-grant also approves the union of requested plus already-held
scopes: approval merges the existing row back in, so a client requesting only
its missing scope no longer fails the caller-authority check.

The decision surface shrinks with the behavior change: the scope-upgrade veto
contradicted shouldAllowSilentLocalPairing's answer, the CLI-container
locality was a duplicate of the shared-secret-loopback predicate, and three
classifiers re-derived the same shared-secret auth check.

Live-verified on an isolated auth-none loopback gateway: a CLI identity
paired at operator.pairing silently widens to operator.read on the next wider
command, with the scope-upgrade security audit line still emitted.

* test(gateway): rewrite veto-era pairing locks for silent local widening

Five control-ui pairing suite cases and the silent-scope-upgrade poc locked
the removed non-silent veto. The suite cases now assert the new invariant
(local shared-auth upgrades widen silently, malformed and legacy-shaped rows
are repaired by the fresh approval, node-then-operator grants complete
without a stranded prompt), and the poc case now exercises the surviving
manual-approval gate by disabling autoApproveLocal after its watcher
connects, keeping the pairing-request broadcast and remediation-hint
assertions alive on a real remaining path. The voice-node bootstrap failure
was leakage from the aborted sibling tests, and passes again once they
complete their flows.
2026-08-16 07:02:24 -07:00
Peter Steinberger b9c6789560 feat(secrets): authenticated egress substitution proxy with destination binding (#123216)
* feat(secrets): add authenticated egress substitution proxy

* feat(secrets): bind egress substitution to hosts

* ci(codeql): classify egress proxy bypass tunnel in network boundary query

* refactor(proxy-capture): use the canonical IP parser instead of node:net

* fix(secrets): compare proxy tokens with a process-keyed MAC
2026-08-13 20:49:31 -07:00
Peter Steinberger e45a9460ce docs: repair spellcheck and anchor drift (#122960)
* docs: repair spellcheck and anchor drift

* docs: satisfy markdown anchor lint
2026-08-12 20:50:56 -07:00
Kimi Yu e1b8150a06 fix(codex): let owners install plugins from discovered marketplaces (#122389)
* fix(codex): allow owner-approved marketplace plugins

* fix(codex): preserve marketplace validation and remove unused exports

* docs(codex): clarify already-installed plugin authorization
2026-08-12 20:29:40 +00:00
Peter Steinberger 0c824f09d5 feat(gateway): observe paired node desktops (#122724)
* feat(gateway): stream paired node desktops

* chore(protocol): refresh desktop observe model

* fix(gateway): preserve desktop stream boundaries

* fix(gateway): keep desktop streams lifetime-bound

* fix(gateway): harden node desktop stream lifecycle

* fix(gateway): stabilize node desktop lifecycle setup

* chore(plugin-sdk): refresh API baselines
2026-08-12 13:17:45 -07:00
Peter Steinberger 508dd471b0 feat(slack): live session cards as the default progress mode (#122552)
* feat(slack): render live session cards as the default Slack progress mode

Slack streaming.mode default flips partial->progress.
Progress mode renders one live Block Kit session card with a status header, narration, plan, activity, diff stat, and elapsed time; it is edited in place and finalized to success or error with an Open in OpenClaw button when gateway.publicOrigin is set.
Final assistant text always delivers separately.
The shared progress compositor gains a success-only additive per-turn diffStat mirroring the task ledger fold.
resolveGatewayPublicOrigin is exported through the plugin SDK.
The diffs viewer URL falls back to publicOrigin.
The old rich/text progress render fork is deleted.
Native task cards remain unchanged and opt-in.

* chore(config): regenerate bundled channel config metadata

* refactor(slack): keep session card state type internal

* refactor(slack): split session-card and diff-stat owners under lint ceilings

* refactor(channels): reuse diff-stat type from its owner module

Import ChannelProgressDraftDiffStat from progress-draft-diffstat instead of
redeclaring it in the compositor, resolving the all-exports deadcode scan.

* chore(plugin-sdk): regenerate api baselines for channel barrels

Baselines drifted after the rebase reconciled them against main; regenerate to
match the branch's actual channel-message/channel-outbound surface.

* fix(slack): drop a session card that cannot terminalize after final delivery

If the final reply is delivered but the terminal card edit fails, the caller
now clears the stale card instead of leaving it stuck in its Working state
(mirrors the pre-card preview cleanup). Adds a transport-failure regression and
corrects three tests that asserted the prior ignore-the-result behavior.

Documents resolveGatewayPublicOrigin as a dependency-light runtime helper on the
config-contracts SDK subpath, which previously described a type-only surface.

Addresses ClawSweeper P2 (unfinalized card) and P1 (runtime SDK contract).

* fix(slack): suppress default tool messages under the default progress card

resolveChannelStreamingSuppressDefaultToolProgressMessages re-derived the stream
mode from config with an "off" default, unlike its sibling resolvers which take
a caller-resolved mode override. After this branch made progress the Slack
default, a default-config channel turn saw mode "off" and left a stray
"Using tool: X" plain message posting alongside the session card. Thread the
caller-resolved mode through (compositor passes params.mode; Slack dispatch passes
slackStreaming.mode), matching resolveChannelStreamingPreviewToolProgress.

Retarget the progress-session-card delivery-trace golden at an EMPTY Slack config
so it proves the real default path; the regenerated golden is byte-identical,
confirming defaults now yield the clean card sequence (one card post, separate
final text, one terminal update with the Open in OpenClaw button, no stray tool
message). Switch the dispatch delivery-mode mock to the real resolver so the card
tests exercise the true channel default (automatic), not a hand-rolled one.

* chore(plugin-sdk): regenerate api baselines for the streaming mode param

resolveChannelStreamingSuppressDefaultToolProgressMessages gained an optional
mode override; the changed signature reflows the surface hash of every barrel
that re-exports it, so regenerate the affected baselines.

* chore(config): regenerate config baselines
2026-08-12 10:38:31 -07:00
Peter Steinberger 28a5889e9b feat: supervise headless Linux host desktop (#122677)
* feat(gateway): manage headless Linux host desktop

* chore(plugin-sdk): refresh API baselines

* fix(gateway): tighten managed desktop status proof

* style(gateway): satisfy managed desktop lint

* fix(gateway): preserve explicit port probe result

* fix(doctor): avoid inferring managed desktop runtime state
2026-08-12 08:56:34 -07:00
Peter Steinberger df707a9670 feat: view this machine in the Desktop panel (#122545)
* feat(gateway): add gateway-host desktop source behind desktop.host lab

Introduce the host as a first-class desktop source so operators can view
the machine OpenClaw runs on, not just cloud-worker environments:

- protocol: desktop.observe / desktop.launch with a discriminated
  DesktopSource union (host | environment) plus an additive auth hint;
  EnvironmentSummary gains a top-level desktop flag
- config: desktop.host { enabled, port?, passwordFile? }, Labs-gated
- rfb-probe: pure RFB version/security-type parser used to detect an
  already-running loopback VNC server and classify its auth
- host-source: attaches to 127.0.0.1:<port>, refuses unauthenticated
  (None) and unsupported (VeNCrypt) servers, and refuses ARD with the
  supported alternative until the macOS milestone
- host-guidance: per-OS enablement text so no path dead-ends
- doctor + status report host desktop availability and auth type only

worker.desktop.observe/launch stay as delegating aliases with identical
behavior. Also drops the now-unused WorkerDesktopTunnels type export.

Live-verified against macOS Screen Sharing: probe reads RFB 003.889,
returns security types [30,33,36,35], classifies ard-account.

* test(gateway): probe RFB handshakes through the socket boundary

The probe's banner and security-offer parsers were exported solely so
unit tests could call them, which the dead-export gate rejects and which
tests internals rather than behavior. Keep them module-local and drive
the probe through a scripted loopback server instead.

The boundary tests also cover what pure-function vectors could not:
handshakes split across packets, legacy RFB 3.3 single-word security,
server-rejected handshakes, early hangups, and connect timeouts.

* feat(ui): let the Desktop panel view this machine, not just cloud workers

The Desktop panel was gated on a cloud-worker session placement, so an
operator running OpenClaw locally had no way to see the machine hosting
their main session even with a VNC server running on it.

Availability now follows the advertised desktop.observe method plus
operator.admin instead of session placement, and the picker lists every
environment whose summary reports a desktop, with the gateway row shown
as "This machine". Sources are passed to the generic desktop.observe /
desktop.launch RPCs; the app launcher stays worker-only. When a host
attach needs a password the gateway did not supply, the panel prompts and
keeps the value in memory for that connection only.

Adds the hostDesktop Labs toggle for desktop.host.enabled.

* fix(scripts): keep the env-var ratchet usable in shallow checkouts

The env-var budget check resolved its base ref, then hard-failed when
`git merge-base` found no shared ancestor. Shallow clones and grafted
agent checkouts resolve origin/main but truncate the history behind it,
so an advisory growth ratchet took down the whole check:changed gate
with "Could not resolve env-var count merge base for: origin/main".

Only the growth comparison needs a baseline, and the script already has
a no-baseline path. Treat git's exit 1 with empty output (no shared
ancestor) as that case and say so on stderr; a genuine failure still
exits 128 and still throws, and the absolute count-vs-budget check runs
either way.

* test(ui): measure the inline-code chip against its line box

The inline-code spacing test compared the chip's height to a prose text
rect, so it silently measured the monospace font's default line spacing.
That is ~17px on macOS and several px shorter on Linux, so the assertion
passed on CI and failed locally at 4.5 against a 3.75 bound -- after the
bound had already been widened once to chase browser font metrics.

Compare the chip to the paragraph's CSS line box instead, which is what
"the chip must not disrupt the line" actually means and is platform
independent. The horizontal gap stays as-is: it is em-derived padding
plus border, and it is the assertion that catches detached punctuation.

Verified both directions on macOS: the file is fully green, and
restoring the pre-fix 0.15em/0.35em padding still fails the gap
assertion at 5.41.

* feat(gateway): view macOS Screen Sharing from the Desktop panel

Modern macOS only offers ARD account authentication for Screen Sharing,
so the host desktop source refused every Mac. The Gateway now performs
the ARD handshake itself against the loopback server and hands the
browser a plain RFB 003.008 no-auth handshake, so the operator's macOS
account password authenticates the desktop without ever reaching the
browser, the observe result, a URL, or a log.

- rfb-preauth: ARD (type 30) Diffie-Hellman with MD5-derived AES-128-ECB
  credentials, and VncAuth (type 2) bit-reversed DES, both under a single
  10s negotiation deadline; Apple's RFB 003.889 maps to 3.8
- observe-bridge: runs pre-auth before splicing and starts the view-only
  filter at clientInit, since the browser handshake is consumed here;
  worker tokens keep the original version start phase
- host-source: attaches ARD, requiring per-observation credentials that
  live only in the one-shot observer token and are dropped after use
- doctor: offers an explicitly confirmed sudo launchctl repair when
  Screen Sharing is off, and prints the System Settings path otherwise

Live-verified against this Mac's Screen Sharing: the DH exchange and
credential framing are accepted and the server returns SecurityResult.
The VncAuth DES vector is confirmed against OpenSSL independently.
2026-08-12 06:58:30 -07:00
Ayaan Zaidi 6a4a546593 feat(mcp): per-requester OAuth for MCP servers (#122166)
Per-requester OAuth for HTTP MCP servers: in shared channels each trusted sender connects their own account. New config: mcp.servers.<name>.oauth.identity ("shared" default, byte-identical behavior) and gateway.publicOrigin (HTTPS except loopback) for the new GET /oauth/mcp/callback served on the Gateway HTTP server. Requester tokens are isolated per (channel, account, sender) in mcp_oauth_stores rows (no schema bump); callbacks correlate through a durable state-keyed one-time index with a 10-minute TTL; per-requester servers are fail-closed out of static/scheduled runtimes; unauthenticated senders get a sign-in link with a portable URL button; MCP config mutations clear requester credentials at the canonical owner. mcp status --json keeps the legacy authStatus fields additively. Proven by a two-user self-hosted-Executor E2E through the real gateway callback. Part of #122034.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-11 20:58:51 +00:00
Peter Steinberger a0ad38e71a feat(docs-ci): validate docs config examples against the schema (#121336)
* feat(docs-ci): validate config examples against schema

* docs: teach canonical agent roster configuration

* docs: nest inline environment variables under vars

* docs: update canonical media model keys

* docs: remove retired config keys from examples

* fix(docs-ci): keep audit internals private

* feat(docs-ci): validate plugin-owned config examples

* fix(docs-ci): validate indented config fences

* fix(ci): route manifest changes through docs audit

* chore(docs): refresh generated config baselines
2026-08-10 07:57:56 -07:00
Peter Steinberger f4bac99a81 feat(secrets): add SQLite-backed secret store (#121559) 2026-08-10 07:08:40 -07:00
Peter Steinberger c6edd3e80f feat(gateway): grant scopes by verified identity (#121531)
* feat(gateway): grant scopes by verified identity

* chore(plugin-sdk): refresh API baseline

* test(agents): isolate transcript repair mocks
2026-08-10 03:19:45 -07:00
Peter Steinberger 914f73ac99 docs: replace retired config keys with canonical schema keys (#121330) 2026-08-09 20:30:43 -07:00
Peter Steinberger 05cd389cac fix(cloud-workers): make Crabbox provisioning idempotent (#121019)
* fix(cloud-workers): make Crabbox provisioning idempotent

* fix(crabbox): preserve desktop fixed-id warmups
2026-08-09 11:51:34 -07:00
Peter Steinberger 9a96375e60 feat(gateway): session-catalog terminal start plans behind cliAgents gate (#121020)
* feat(gateway): add session-catalog terminal start plans

* refactor(gateway): split catalog terminal start handler

* fix(gateway): enforce catalog terminal start eligibility

* test(gateway): split session catalog snapshot coverage
2026-08-09 08:13:39 -07:00
Peter Steinberger 8b0735e89f refactor(memory)!: remove the QMD backend; builtin is the only memory engine (#120936)
* refactor(memory): remove qmd backend

Make builtin the sole memory-core engine, rename the retained session helper barrel, retire QMD config with doctor migrations, and remove QMD runtime/UI/policy surfaces.

* docs(memory): remove qmd backend guidance

Delete the QMD concept page, rewrite memory documentation for builtin retrieval, and remove QMD from navigation and taxonomy source.

* refactor(memory): remove qmd-only leftovers

* refactor(memory): finish qmd integration cleanup

* build(deps): align root string-width types

* build(deps): model root string-width tooling

* refactor(memory): align qmd removal ui and docs

* fix(memory): preserve qmd external paths in doctor

* test(memory): remove obsolete backend probe case

* test(plugin-sdk): refresh private type baseline
2026-08-09 03:05:47 -07:00
Peter Steinberger f67da420f6 feat(ui): labs-gated CLI agent targets in new-session model picker (#120949)
* feat(config): gate CLI agent discovery

* feat(ui): add CLI agents to model picker
2026-08-09 00:00:00 -07:00
Peter Steinberger c2d3d2ce34 fix(cloud-workers): preserve cleanup across fallback disconnects (#120715)
* fix(cloud-workers): close lifecycle ownership gaps

Own bootstrap cleanup at the operation boundary and make fallback workspace sync converge across retries. Re-establish tunnel readiness per connection, retire placements before destructive session mutation, and keep operator diagnostics lightweight and redacted. Cover destructive lifecycle paths in their original execution order.

* fix(cloud-workers): drain local claims before retirement

delete/reset drain admitted local work, re-read exact identity, retire before destructive cleanup; active-claim/race tests.

* fix(cloud-workers): bind retry cleanup to workspace owner

Attest canonical HOME and the exact managed path.
Revalidate ownership before recursive fallback cleanup.
Cover malicious paths and ownership drift with tests.

* fix(cloud-workers): fence fallback workspace receivers
2026-08-08 22:18:54 -07:00
Peter Steinberger cf69593afe fix(cloud-workers): start sessions when primary SSH port is unavailable (#120546)
* fix(cloud-workers): honor Crabbox SSH fallback ports

Select a currently reachable advertised SSH endpoint before returning a Crabbox worker lease, while preserving fail-closed teardown. Allow deletion of failed cloud sessions only after the environment record proves the provider lease is gone, and align the Cloud Workers docs with the shipped configuration and Control UI flow.

* fix(cloud-workers): retry advertised SSH endpoints

Persist provider-advertised SSH fallback ports and retry them through the shared pinned transport used by bootstrap, tunnels, reconnects, and workspace transfers. Keep candidate selection provider-neutral, preserve identity and host-key fencing, and add the same-version SQLite column through the existing additive migration path.

* fix(crabbox): recheck SSH readiness after setup

Treat provider setup as a mutating lifecycle phase: refresh authoritative inspect state and wait for SSH readiness again before returning the lease. This prevents bootstrap from racing setup-triggered SSH restarts on fresh and replayed workers while preserving security attestation and fail-closed teardown.

* fix(cloud-workers): retry bootstrap transfers across ports

Use the shared advertised-port candidate runner for bundle transfer, install, and best-effort upload cleanup as well as preflight. This keeps fresh bootstrap connections on pinned identity and host-key semantics when a previously selected SSH port disappears between phases.

Proof passed: 517 focused tests (1 skipped), final autoreview/TruffleHog clean, targeted lint/format. Full check-changed reached the unrelated max-lines baseline drift on current origin/main: this branch's merge base contains ui/src/pages/chat/components/chat-model-controls.ts while current origin/main removed that baseline entry; do not touch the unrelated baseline.

* fix(cloud-workers): persist fallback ports compatibly

Create the fallback-port companion table lazily as an additive surface, preserving downgrade tolerance for binaries that still expect the older shared-state schema.

* fix(cloud-workers): preserve bundle artifact narrowing

Bind the immutable discriminated artifact before the SSH retry callbacks so bundle-only fields remain narrowed across fresh candidate invocations. Use the shared temp-directory owner in the tunnel fallback test for deterministic cleanup.

* fix(cloud-workers): retire deleted placements

Terminal placement rows retire by exact CAS after session deletion, and managed AWS docs now pin the Gateway SSH CIDR.

* fix(crabbox): await pending AWS attestation

missing authoritative metadata is pending only while the lease is non-ready; ready leases still require explicit false.

* fix(cloud-workers): fence remote command retries

require explicit idempotent/never classification; remote worker launch never retries after ambiguous SSH loss.

* fix(cloud-workers): bound SSH fallback operations
2026-08-08 14:03:36 -07:00
Peter Steinberger e3de98a451 feat(update): scheduled update campaigns with countdown, dev-channel auto-update, and Updates settings page (#120506)
* feat(update): scheduled update campaigns with countdown, dev-channel auto-update, and Updates settings page

* fix(ui): satisfy update campaign deadcode checks

* feat(update): extend scheduled update campaigns

* test(ui): include Updates page in settings sidebar search expectations

* fix(update): end campaigns when apply fails

* fix(update): pin dev campaign applies to the announced commit

* test(ui): align update e2e fixtures

* fix(update): pin adopted dev updates to the announced commit

* docs(update): document automatic dev update campaigns

* fix(ui): gate automatic dev updates on git installs

* fix(gateway): scope detailed update metadata to read-capable clients

* fix(gateway): break post-attach madge cycle with a leaf client-lookup type

* chore(protocol): regenerate clients after rebase

* test(gateway): update method tail after rebase

* fix(update): preserve campaign target and hold state

* test(update): align adoption mocks and split handoff suite

* test(update): route split handoff suites on Windows
2026-08-08 11:19:53 -07:00
Peter Steinberger 83900e4683 feat(browser): add relay authentication v2 (#120526)
* feat(browser): add relay authentication v2

* fix(browser): cap relay test WebSocket payload

* fix(browser): keep relay E2E inside extension boundary

* fix(browser): isolate relay admission and cleanup auth

* fix(browser): finish relay auth migration hardening

* fix(browser): keep preauth transport bounded through teardown
2026-08-08 05:48:24 -07:00
zengLingbiao ae21eb7b93 fix(cron): treat zero sessionRetention as disabled instead of pruning all run sessions (#120213) 2026-08-07 17:18:20 -07:00
Shakker 54e2ab545f test: cover read-only preference sync 2026-08-06 21:44:44 +01:00
Vincent Koc 92b4757227 fix(diagnostics): honor signal-specific OTLP protocols (#119708)
* fix(diagnostics): honor signal-specific OTLP protocols

* fix(docker): forward signal-specific OTLP protocols
2026-08-06 05:39:26 +08:00
Vincent Koc fd1662f49c fix(cli): retire invalid secret flags and prove doctor recovery (#118926)
* test(qa): add doctor CLI recovery coverage

* test(qa): secure doctor exec SecretRef proof

* test(qa): gate doctor systemd recovery proof

* test(qa): normalize doctor terminal output

* test(qa): close doctor probe sockets

* test(qa): classify doctor probe as foreign

* test(qa): track doctor probe sockets

* test(qa): retain doctor instance narrowing

* test(qa): preserve observed doctor recovery proof

* test(qa): keep doctor recovery on stable dist

* test(qa): honor Windows exec ACL blocking

* test(qa): use canonical home for systemd recovery

* test(qa): follow bounded gateway recovery

* test(qa): accept lifecycle service label

* test(qa): align doctor recovery contract

Punchcard-Session: crisp-lantern-orchard-nv

* docs(secrets): remove retired provider bypasses

Punchcard-Session: crisp-lantern-orchard-nv

* test(qa): isolate doctor recovery target

Punchcard-Session: crisp-lantern-orchard-nv

* fix(cli): retire invalid secret provider flags

Punchcard-Session: crisp-lantern-orchard-nv

* test(qa): isolate doctor supervisor mode

Punchcard-Session: crisp-lantern-orchard-nv

* fix(plugins): remove dead secret path bypass

Punchcard-Session: crisp-lantern-orchard-nv

* chore: drop release-owned changelog entry

Punchcard-Session: crisp-lantern-orchard-nv

* test(qa): isolate doctor sudo scope

Punchcard-Session: crisp-lantern-orchard-nv

* fix(secrets): remove dead path bypass

Punchcard-Session: crisp-lantern-orchard-nv

* test(qa): isolate systemd user bus

Punchcard-Session: crisp-lantern-orchard-nv
2026-08-05 11:20:23 +08:00
Josh Avant 618fba92bf feat(audit): add execution identity inspection (#117034)
* feat(audit): add opt-in execution identity inspection

* fix(audit): gate recovery identity retention

* fix(audit): keep recovery identity type private

* test(audit): type internal recovery fixture

* test(audit): split recovery identity coverage

* docs(audit): define operator read trust boundary

* test(qa): register identity scenario child

* fix(audit): enforce shared identity retention bounds

* fix(audit): seal public ingress identity boundary

* fix(audit): keep ingress guard lint-clean

* fix(gateway): preserve advertised method order

* chore(protocol): sync advertised method order

* fix(protocol): encode audit selector invariants

* test(audit): prove exact execution guard

* fix(audit): keep identity storage lazy
2026-08-04 20:05:15 -05:00
kiranmagic7 682f60ce56 fix(config): reject unsupported diagnostics otel grpc (#93087) 2026-08-03 16:28:08 -04:00
Peter Steinberger e9d88696b5 feat(cron): add webhook SSRF policy (#118237) (#118286) 2026-08-02 17:02:20 -07:00
Vincent Koc 89aadef6ef feat(plugins): externalize iMessage channel (#117101) 2026-08-01 08:01:18 +08:00
Koduri Mahesh Bhushan Chowdary 9878bfc8e5 docs: drop retired gateway.reload keys and modes from config docs (#116975)
The gateway.reload documentation advertised four settings the shipped
build rejects. gateway.reload is a strictObject accepting only `mode`,
and `mode` is a union of "off" | "hybrid", so a config written from the
docs fails validation and the gateway fails closed on the unknown keys.

Retired but still documented:
- `debounceMs` and `deferralTimeoutMs` (RETIRED_TUNING_PATHS; stripped by
  `openclaw doctor --fix` via stripRetiredTuningKnobs)
- `mode: "restart"` and `mode: "hot"` (mapped to "hybrid" by
  `openclaw doctor --fix`)

Correct all four docs pages that still describe them, and say what
replaced them: debounce and active-work deferral now run behind built-in
defaults rather than config keys.

Refs #116973
2026-07-31 15:35:16 -07:00
Josh Avant ce67ffb70e feat(agents): add tool-free isolated completion (#114343)
* feat: add isolated pure-inference completion

* fix(google): block ambient system prompt writes

* docs: refresh generated map
2026-07-30 13:24:45 -05:00
Omar Shahine 944cf08f94 docs(automation): rename scheduled-tasks feature wording to Automations (#114855)
* docs(automation): rename scheduled-tasks feature wording to Automations

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhJ8EiMXue6ADLmHfb7FL6

* docs: regenerate docs map and add Automations glossary entries

* docs(templates): follow renamed automations-vs-heartbeat anchor

* docs(automation): fix markdown formatting drift

* docs(automation): teach the canonical automations tool and sync the copied heartbeat default

Review follow-ups: normal instructions use the automations tool with cron as
an explicit compatibility alias; every verbatim copy of the default heartbeat
prompt matches the new shipped text from the strings PR.

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 07:21:56 -07:00
joshavant 1237c0cf81 docs(hooks): document validation responses 2026-07-30 02:59:06 -05:00
joshavant 54a6dba0d3 fix(gateway): validate hook delivery accounts 2026-07-30 02:59:06 -05:00
Vincent Koc 9e041cd386 fix(hooks): preserve multi-account agent delivery (#116095)
* fix(hooks): preserve account routing for agent delivery

Refs #43866

Co-authored-by: Paul Desmond Parker <paul.parker@dcconnect.cn>

* test(gateway): prove hook account delivery handoff

---------

Co-authored-by: Paul Desmond Parker <paul.parker@dcconnect.cn>
2026-07-30 07:10:21 +08:00
Peter Steinberger ca07207649 docs(security): add safe Gmail reader setup (#116041)
* docs(security): add safe Gmail reader setup

* docs: leave release notes to release flow

* docs: refresh documentation map
2026-07-29 15:07:35 -04:00
Ryan Hughes 9560e17185 feat(hooks): add persistent hook session mode (#75918)
* feat(hooks): add explicit persistent sessions

* fix(hooks): validate persistent mapping session keys

* chore: leave persistent hooks note to release process

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 02:57:28 +08:00
Sascha Kuhlmann 814b9f6e36 fix(hooks): bound agent runner admission (#104712)
* fix(hooks): gate agent admission on runner entry

Co-authored-by: Sascha Kuhlmann <117685070+coolmanns@users.noreply.github.com>

* fix(hooks): bound agent runner admission

Co-authored-by: Sascha Kuhlmann <117685070+coolmanns@users.noreply.github.com>

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 01:01:46 +08:00
Peter Steinberger ea967be0df feat(gateway): add loopback locality controls (#115959)
* feat(gateway): add loopback locality controls

* fix(gateway): keep loopback auth delays enforced under concurrency

The pending-timer cap let an attacker park cheap failures in every slot and then guess without penalty. Delays now key off a per-key deadline, so parallel guesses wait out the same escalating penalty and are still bounded by the max delay.

* fix(gateway): share one loopback penalty timer per key

Concurrent failures on a key now share a single timer and deadline instead of allocating one per in-flight request. Also corrects the security doc: the delay raises the cost of repeated guessing from one source, but credentials are compared before the failure response is delayed, so it is not a defense against parallel fan-out.

* docs(gateway): record why loopback delay stays post-verification

* docs: refresh generated docs map

* docs: refresh plugin SDK API baseline

* test: update loopback locality CI expectations
2026-07-29 12:25:57 -04:00
Sascha Kuhlmann 819961a292 fix(hooks): avoid implicit hook delivery targets (#100486)
* fix(gateway): bind hook delivery before scheduling

Co-authored-by: Sascha Kuhlmann <117685070+coolmanns@users.noreply.github.com>

* test(cli): make stderr tail fixture deterministic

* test(cli): allow loaded runners to start

* fix(gateway): narrow hook delivery binding

Co-authored-by: Sascha Kuhlmann <117685070+coolmanns@users.noreply.github.com>

* fix(gateway): keep hook delivery normalization internal

* style(gateway): format hook delivery helper

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-29 23:14:46 +08:00
Peter Steinberger bfb7ff189e docs(codex): replace stale app/list wording with app/installed (#115867) 2026-07-29 08:09:44 -04:00
Pavan Kumar Gondhi 36984fe1af fix(diagnostics): keep internal thinking out of telemetry [AI] (#115261)
* fix(diagnostics): exclude internal thinking from telemetry

* fix(diagnostics): redact replay reasoning fields

* fix(diagnostics): omit replay signatures from telemetry

* chore: sync removed test helper
2026-07-29 14:05:45 +05:30
Vincent Koc bf91a5786c fix(plugins): enforce hook security invariants (#115701) 2026-07-29 16:10:34 +08:00
Peter Steinberger 8fd49c84aa feat: make self-learning automatic by default (#115576)
* feat: make self-learning automatic by default

* fix: preserve capture authorization boundaries

* test: split autonomous capture coverage
2026-07-29 02:04:57 -04:00
metaforismo 430de2299c fix(ios): use configured speech language for system voice (#113372)
* fix(ios): honor speech locale for system voice

* chore(ios): refresh talk locale i18n inventory

* fix(ios): fall through unavailable system voices

* chore(ios): refresh talk locale i18n inventory

---------

Co-authored-by: Colin Johnson <colin@solvely.net>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-28 14:10:23 -04:00
Peter Steinberger 31ad055259 fix(ui): avoid restart prompt when toggling advanced settings (#114937)
* fix(ui): keep advanced disclosure browser-local

* chore: leave changelog to release flow
2026-07-28 01:17:40 -04:00
Peter Steinberger 938749180e refactor(nodes)!: retire arming leases for durable capability enablement (#114892)
* refactor(nodes): retire phone control leases

* fix(doctor): clean retired phone control state

* docs(nodes): document durable capability enablement

* docs(changelog): defer node enablement note

* fix(doctor): harden retired phone control cleanup

* fix(doctor): fail closed on unreadable lease state

* fix(doctor): validate retired lease journals

* fix(doctor): preserve deny-shadowed consent

* docs: refresh node authorization map

* fix(doctor): keep retirement helpers private
2026-07-27 23:51:30 -04:00
Peter Steinberger 5347285d6b improve(models): source pricing from hosted catalog (#114060)
* feat(model-catalog): serve hosted fallback pricing

* refactor(config): retire client pricing bootstrap settings

* refactor(gateway): delete client pricing refresh runtime

* docs(models): explain hosted catalog pricing

* fix(model-catalog): preserve pricing privacy and aliases

* fix(model-catalog): fingerprint pricing eligibility

* fix(model-catalog): harden pricing endpoint checks

* fix(model-catalog): materialize source-safe pricing aliases

* fix(model-catalog): keep unknown pricing fallbacks safe

* fix(model-catalog): reject zero-only hosted prices

* fix(model-catalog): fail closed without pricing policy metadata

* refactor(utils): extract usage pricing normalization

* fix(model-catalog): rebuild policy-owned pricing namespaces

* test(model-catalog): type publisher cost fixtures

* chore(config): regenerate schema baselines

* fix(utils): keep raw pricing tiers private
2026-07-26 03:48:25 -04:00
Peter Steinberger 2de58d408a Control UI: gateway-owned sidebar section order (#113930)
* feat(ui): gateway-owned sidebar section order with hover-only drag grips

* fix(ui): repair sidebar section order CI gates

* fix(protocol): keep sectionOrder optional for older gateways

Native Swift clients ship separately from gateways, so new clients must decode older gateway responses that omit sectionOrder.

* fix(ui): reconcile gateway section ordering with main

* fix(state): allow lazy tables in v5 maintenance

* refactor(ui): retire prefs session section order in favor of gateway-owned order

Supersedes #113948 by deleting the unreleased ui.prefs.sessionSectionOrder key and its browser plumbing. Gateway SQLite sidebar_sections remains the single canonical store.
2026-07-26 01:15:19 -04:00