Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions items/openclaw/components/openclaw.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@ spec:
kind: SERVICE
source:
type: IMAGE
# Latest stable calendar release (beta tags are not pinned for the catalog).
# PINNED, and the pin is load-bearing rather than merely current: 2026.7.1
# is the last stable release in which
# `gateway.controlUi.dangerouslyDisableDeviceAuth` still works, and this
# item depends on it to be reachable at all (see the block above `command`).
#
# Verified by reading `shouldSkipControlUiPairing` in each published
# bundle: 2026.7.1 ends `return role === "operator" && policy.allowBypass`,
# while 2026.8.1 and 2026.8.2 both end `return null`, having dropped that
# branch. Upstream calls the key "a retired break-glass input, now fully
# inert" and has `doctor --fix` delete it. 2026.7.2 exists only as betas,
# which this catalog does not pin.
#
# So bumping this tag re-breaks the deployment silently. Anything past
# 2026.7.1 has to pair devices, which means either re-adding the
# in-container `devices approve` watcher this item used to carry, or
# `gateway.auth.trustedProxy.deviceAutoApprove` (2026.8.x only), which
# needs `gateway.auth.mode: "trusted-proxy"` and therefore a platform edge
# that injects an identity header. Settle that before changing the tag.
ref: ghcr.io/openclaw/openclaw:2026.7.1
# Converge config through OpenClaw's own `config set` CLI, then exec the
# stock gateway entrypoint (`tini -s --` passes argv through, so `sh -c` works).
Expand All @@ -28,12 +45,50 @@ spec:
# the config lives on a persistent volume, so a guard would pin the very first
# boot's origin forever and leave a re-deployed app permanently unreachable.
# `config set` is declarative, so re-applying is a no-op.
#
# GATEWAY AUTH IS UNTOUCHED, despite the name of the key below.
# `gateway.auth.mode` stays at its default (`token`), so every Control UI
# connection still has to present OPENCLAW_GATEWAY_TOKEN. Verified by
# reading the connect path in the 2026.7.1 bundle
# (dist/message-handler-*.js), where three gates run in this order:
#
# 1. handleMissingDeviceIdentity() — device identity; allowBypass admits
# 2. `if (!authOk) { rejectUnauthorized(authResult); return; }`
# 3. shouldSkipControlUiPairing() — pairing; allowBypass skips
#
# Gate 2 is unconditional — no isControlUi test, no allowBypass exemption —
# and it sits BETWEEN the two gates the key affects. So the key removes
# device identity and pairing while the token check stays fully enforced.
#
# That is the intended posture for this item: anyone holding the token gets
# in from any browser, and no device is enrolled or allowlisted. Device
# pairing is a second gate that a valid token does not satisfy, it can only
# be cleared by an already-paired admin session, and on a fresh deployment
# nothing is ever paired — so left alone the Control UI is permanently
# unreachable, reporting "device pairing required (requestId: ...)" and
# naming a CLI that a catalog user has no host shell to run. Upstream was
# asked to let a valid token bypass pairing and declined (openclaw#29908,
# closed as not planned).
#
# The alternative, carried here previously, was an in-container watcher that
# approved pending requests via `devices approve`. It worked and kept device
# identity, but it made a background process load-bearing for reachability
# and put a cold CLI start in front of every first connect. Traded away
# deliberately for a declarative key and an instant first load.
#
# What it costs: with no device identity there is no second factor and no
# per-device revocation, so a leaked token is sufficient on its own, and
# browsers re-present the token each session instead of holding a stored
# device token. `openclaw security audit` reports
# `gateway.control_ui.device_auth_disabled` as critical — expected here, not
# a regression. The token is the whole boundary; rotate it if it leaks.
command: >-
sh -c 'set -e;
node /app/dist/index.js config set --batch-json
"[{\"path\":\"gateway.mode\",\"value\":\"local\"},
{\"path\":\"gateway.bind\",\"value\":\"lan\"},
{\"path\":\"gateway.controlUi.allowedOrigins\",\"value\":[\"${OPENCLAW_PUBLIC_ORIGIN%/}\"]}]";
{\"path\":\"gateway.controlUi.allowedOrigins\",\"value\":[\"${OPENCLAW_PUBLIC_ORIGIN%/}\"]},
{\"path\":\"gateway.controlUi.dangerouslyDisableDeviceAuth\",\"value\":true}]";
exec node openclaw.mjs gateway'
endpoints:
primary:
Expand Down
17 changes: 17 additions & 0 deletions items/openclaw/listing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ spec:
keep configuration, session history, and the OAuth-token encryption
key across restarts.

## Access and security
Every connection must present the gateway token, so the Control UI is
never open to an unauthenticated visitor. Any browser holding the token
can connect — there is no per-device approval step to complete first.

That differs from a default OpenClaw install, which also requires each
new browser to be approved once by running `openclaw devices approve` on
the Gateway host. A one-click deployment gives you no host shell to run
that from, so this item turns the device check off and leaves the token
as the single gate.

The practical consequence is that the gateway token is the only thing
guarding an admin surface that can run tools and shell commands. There is
no second factor and no per-device revocation, so treat the token as a
secret, don't paste it into shared channels, and rotate it from the
Configuration tab if it leaks.

Messaging-channel onboarding (WhatsApp, Telegram, Discord) requires
the interactive CLI and is not available in this deployment yet.
category: AI_ML
Expand Down
Loading