Merge remote-tracking branch 'upstream/main'
# Conflicts: # openclaw_assistant/CHANGELOG.md # openclaw_assistant/config.yaml # openclaw_assistant/run.sh # openclaw_assistant/translations/bg.yaml # openclaw_assistant/translations/de.yaml # openclaw_assistant/translations/en.yaml # openclaw_assistant/translations/es.yaml # openclaw_assistant/translations/pl.yaml # openclaw_assistant/translations/pt-BR.yaml
This commit is contained in:
@@ -2,10 +2,65 @@
|
||||
|
||||
All notable changes to the OpenClaw Assistant Home Assistant Add-on will be documented in this file.
|
||||
|
||||
## [0.5.50] - 2026-02-23
|
||||
## [0.5.51] - 2026-02-23
|
||||
|
||||
### Fixed
|
||||
- **`web_fetch failed: fetch failed`**: changed `force_ipv4_dns` default to **true**. Node 22 tries IPv6 first; most HAOS VMs lack IPv6 egress, causing outbound `web_fetch` / HTTP tool calls to time out.
|
||||
|
||||
### Added
|
||||
- New add-on option `gateway_env_vars` that accepts a YAML map; values are exported verbatim to the gateway process at startup with limits (50 vars, key ≤255 chars, value ≤10000 chars).
|
||||
- **`nginx_log_level` option** (`minimal` / `full`, default `minimal`): suppresses repetitive Home Assistant health-check and polling requests (`GET /`, `GET /v1/models`, `POST /tools/invoke`) from the nginx access log.
|
||||
- New add-on option `gateway_env_vars` that accepts a YAML map; values are exported verbatim to the gateway process at startup with limits (50 vars, key <=255 chars, value <=10000 chars).
|
||||
|
||||
## [0.5.50] - 2026-02-23
|
||||
|
||||
**[!WARNING!]**
|
||||
This update contains lots of changes. It is adviced to backup before installing!
|
||||
|
||||
### Changed
|
||||
- **Upgraded OpenClaw to v2026.2.22-2** — includes major gateway/auth/pairing fixes and security hardening.
|
||||
- Precreate `$OPENCLAW_CONFIG_DIR/identity` on startup to prevent `EACCES` errors on CLI commands that need device identity.
|
||||
- Gateway token is auto-constructed from detected LAN IP when `lan_https` is active and `gateway_public_url` is empty.
|
||||
- Config helper now receives the effective internal port (gateway_port + 1 in lan_https mode).
|
||||
|
||||
### Notes — v2026.2.22 impact on this add-on
|
||||
- **Pairing fixes (loopback)**: v2026.2.22 auto-approves loopback scope-upgrade pairing requests, includes `operator.read`/`operator.write` in default scope bundles, and treats `operator.admin` as satisfying other scopes. This greatly improves `local_only` mode reliability.
|
||||
- **`dangerouslyDisableDeviceAuth` security warning**: v2026.2.22 now emits a startup warning when this flag is active. The warning is **expected and harmless** for `lan_https` mode — the flag is still required because LAN browser connections through the HTTPS proxy are not considered loopback by the gateway. Token auth remains enforced.
|
||||
- **Gateway lock improvements**: stale-lock detection now uses port reachability, reducing false "already running" errors after unclean restarts.
|
||||
- **Log file size cap**: new `logging.maxFileBytes` default (500 MB) prevents disk exhaustion from log storms.
|
||||
- **`wss://` default for remote onboarding**: validates our HTTPS proxy approach as the correct direction.
|
||||
|
||||
### Added
|
||||
- **Disk-space monitoring on the landing page** — shows total / used / available with colour-coded indicator (🟢 / 🟡 / 🔴).
|
||||
- **Low-disk warning banner** appears automatically when usage exceeds 90 %.
|
||||
- **`oc-cleanup` terminal command** — interactive helper that shows cache sizes (npm, pnpm, OpenClaw, Homebrew, pycache, tmp) and lets users reclaim space with a menu-driven cleanup.
|
||||
- Startup disk-space check with log warnings when the overlay is above 75 % or 90 %.
|
||||
- **`access_mode` preset option** — simplifies secure access configuration with one setting:
|
||||
- `custom` (default, backward-compatible): use individual gateway settings
|
||||
- `local_only`: loopback + token (Ingress/terminal only)
|
||||
- `lan_https`: **built-in HTTPS reverse proxy for LAN access** (recommended for phones/tablets)
|
||||
- `lan_reverse_proxy`: LAN bind + trusted-proxy for external reverse proxy (NPM, Caddy, Traefik)
|
||||
- `tailnet_https`: Tailscale interface bind + token auth
|
||||
- **Built-in TLS certificate generation** (`lan_https` mode):
|
||||
- Auto-generates a local CA + server certificate on first startup
|
||||
- Server cert is regenerated automatically when LAN IP changes
|
||||
- CA certificate downloadable from the landing page for one-tap phone trust
|
||||
- nginx HTTPS server block terminates TLS and proxies to the loopback gateway
|
||||
- **Overhauled landing page** with:
|
||||
- Real-time status cards (gateway health, secure context, access mode)
|
||||
- Access wizard with step-by-step guidance per mode
|
||||
- Error translation — maps raw errors like `1008: requires device identity` to friendly messages with fixes
|
||||
- CA certificate download button (lan_https mode)
|
||||
- Migration banner for users on `custom` mode recommending a preset
|
||||
- Collapsible reverse-proxy recipes (NPM / Caddy / Traefik / Tailscale)
|
||||
- Added `openssl` to Docker image for TLS certificate generation.
|
||||
- Translations for `access_mode` in all 6 languages (EN, BG, DE, ES, PL, PT-BR).
|
||||
|
||||
### Fixed
|
||||
- **`lan_https` — error 1008 "pairing required"**: auto-set `gateway.controlUi.dangerouslyDisableDeviceAuth: true` to skip interactive device pairing (token auth remains enforced). Replaces the invalid `pairingMode` key that caused `Unrecognized key` config errors.
|
||||
- Config helper now removes stale/invalid keys (e.g. `pairingMode`) from `controlUi` on startup.
|
||||
- Landing page error translation now covers "pairing required" and "origin not allowed" errors with correct fix guidance.
|
||||
- Dropdown translations for `access_mode`, `gateway_mode`, `gateway_bind_mode`, and `gateway_auth_mode` now show human-readable labels in all 6 languages.
|
||||
- **`lan_https` — error 1008 "origin not allowed"**: auto-configure `gateway.controlUi.allowedOrigins` with the HTTPS proxy origins (LAN IP, `homeassistant.local`, `homeassistant`) so the Control UI WebSocket is accepted.
|
||||
|
||||
## [0.5.49] - 2026-02-22
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
rsync \
|
||||
bat \
|
||||
less \
|
||||
openssl \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -100,9 +101,9 @@ RUN if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then \
|
||||
fi
|
||||
USER root
|
||||
|
||||
# Install OpenClaw globally (official npm release)
|
||||
# Install OpenClaw globally
|
||||
RUN npm config set fund false && npm config set audit false \
|
||||
&& npm install -g openclaw@2026.2.21-2
|
||||
&& npm install -g openclaw@2026.2.22-2
|
||||
|
||||
# Shell aliases and color options for interactive use
|
||||
RUN tee -a /etc/bash.bashrc <<'EOF'
|
||||
@@ -124,10 +125,12 @@ fi
|
||||
EOF
|
||||
COPY run.sh /run.sh
|
||||
COPY oc_config_helper.py /oc_config_helper.py
|
||||
COPY render_nginx.py /render_nginx.py
|
||||
COPY oc-cleanup.sh /usr/local/bin/oc-cleanup
|
||||
COPY openclaw-proxy-shim.cjs /usr/local/lib/openclaw-proxy-shim.cjs
|
||||
COPY nginx.conf.tpl /etc/nginx/nginx.conf.tpl
|
||||
COPY landing.html.tpl /etc/nginx/landing.html.tpl
|
||||
RUN chmod +x /run.sh /oc_config_helper.py \
|
||||
RUN chmod +x /run.sh /oc_config_helper.py /render_nginx.py /usr/local/bin/oc-cleanup \
|
||||
&& mkdir -p /run/nginx
|
||||
|
||||
CMD [ "/run.sh" ]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: OpenClaw Assistant
|
||||
version: "0.5.50"
|
||||
version: "0.5.51"
|
||||
slug: openclaw_assistant
|
||||
description: Run OpenClaw Assistant (OpenClaw-compatible) as a Home Assistant add-on.
|
||||
url: https://github.com/techartdev/OpenClawHomeAssistant
|
||||
@@ -66,27 +66,43 @@ options:
|
||||
# - loopback: bind to 127.0.0.1 only (local access only, most secure)
|
||||
# - lan: bind to all interfaces (accessible from local network)
|
||||
# - tailnet: bind to Tailscale IP only (accessible only via Tailscale — recommended for remote access)
|
||||
# - auto: prefer loopback; use tailnet if Tailscale is available
|
||||
# Default is loopback for security.
|
||||
gateway_bind_mode: loopback
|
||||
|
||||
# Gateway port to listen on
|
||||
gateway_port: 18789
|
||||
|
||||
# Access mode preset — simplifies secure access configuration.
|
||||
# custom: use individual gateway_bind_mode / auth_mode settings (backward compatible)
|
||||
# local_only: loopback + token auth (Ingress / terminal only, most secure)
|
||||
# lan_https: Built-in HTTPS reverse proxy for LAN access (recommended for phones/tablets)
|
||||
# lan_reverse_proxy: LAN bind + trusted-proxy for an external reverse proxy (NPM, Caddy, …)
|
||||
# tailnet_https: Tailscale interface bind + token auth
|
||||
access_mode: custom
|
||||
|
||||
# Gateway authentication mode:
|
||||
# - token: standard token auth (default)
|
||||
# - trusted-proxy: trust auth headers from configured reverse proxies
|
||||
gateway_auth_mode: token
|
||||
|
||||
# Comma-separated trusted proxy IP/CIDR list for trusted-proxy mode.
|
||||
# Example: "127.0.0.1,192.168.88.0/24"
|
||||
gateway_trusted_proxies: ""
|
||||
|
||||
# Enable OpenAI-compatible Chat Completions API endpoint
|
||||
# When enabled, OpenClaw can be used as a conversation agent in HA Assist pipeline
|
||||
# via Extended OpenAI Conversation (HACS) or any OpenAI-compatible client
|
||||
enable_openai_api: false
|
||||
|
||||
# Allow insecure HTTP authentication (required for HTTP gateway access on LAN)
|
||||
# WARNING: Only enable if you're using HTTP (not HTTPS) for gateway_public_url
|
||||
# Default is false for security.
|
||||
allow_insecure_auth: false
|
||||
|
||||
# Force IPv4-first DNS result ordering for Node fetch/network calls.
|
||||
# Useful on networks where IPv6 resolution exists but IPv6 egress is broken
|
||||
# (can affect Telegram API polling in some HAOS/VM setups).
|
||||
force_ipv4_dns: false
|
||||
# Most HAOS VMs lack IPv6 egress, causing web_fetch / Telegram timeouts.
|
||||
# Default: true (recommended). Set to false only if you need IPv6.
|
||||
force_ipv4_dns: true
|
||||
|
||||
# Nginx access log verbosity:
|
||||
# full: log all requests (useful for debugging)
|
||||
# minimal: suppress repetitive HA health-check and polling requests (default)
|
||||
nginx_log_level: minimal
|
||||
|
||||
# Environment variables to pass to the gateway as a YAML map
|
||||
# These variables are exported to the gateway process at startup
|
||||
@@ -119,9 +135,12 @@ schema:
|
||||
clean_session_locks_on_start: bool?
|
||||
clean_session_locks_on_exit: bool?
|
||||
gateway_mode: list(local|remote)?
|
||||
gateway_bind_mode: list(auto|loopback|lan|tailnet)?
|
||||
gateway_bind_mode: list(loopback|lan|tailnet)?
|
||||
gateway_port: int(1,65535)?
|
||||
access_mode: list(custom|local_only|lan_https|lan_reverse_proxy|tailnet_https)?
|
||||
gateway_auth_mode: list(token|trusted-proxy)?
|
||||
gateway_trusted_proxies: str?
|
||||
enable_openai_api: bool?
|
||||
allow_insecure_auth: bool?
|
||||
force_ipv4_dns: bool?
|
||||
gateway_env_vars: map(str)?
|
||||
nginx_log_level: list(full|minimal)?
|
||||
|
||||
@@ -9,36 +9,327 @@
|
||||
a,button{font:inherit}
|
||||
.card{max-width:1100px;margin:0 auto;background:#111827;border:1px solid #1f2937;border-radius:12px;padding:16px}
|
||||
.row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
|
||||
.btn{background:#2563eb;color:white;border:0;border-radius:10px;padding:10px 14px;cursor:pointer;text-decoration:none;display:inline-block}
|
||||
.btn{background:#2563eb;color:white;border:0;border-radius:10px;padding:10px 14px;cursor:pointer;text-decoration:none;display:inline-block;font-size:14px}
|
||||
.btn.secondary{background:#334155}
|
||||
.btn.green{background:#059669}
|
||||
.btn.amber{background:#d97706}
|
||||
.btn:hover{filter:brightness(1.15)}
|
||||
.muted{color:#9ca3af;font-size:14px}
|
||||
.term{margin-top:14px;height:70vh;min-height:420px;border:1px solid #1f2937;border-radius:10px;overflow:hidden}
|
||||
.term{margin-top:14px;height:60vh;min-height:360px;border:1px solid #1f2937;border-radius:10px;overflow:hidden}
|
||||
iframe{width:100%;height:100%;border:0;background:black}
|
||||
code{background:#0b1220;padding:2px 6px;border-radius:6px}
|
||||
code{background:#0b1220;padding:2px 6px;border-radius:6px;font-size:13px}
|
||||
.status-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin:12px 0}
|
||||
.status-item{padding:10px 14px;border-radius:10px;background:#0d1117;border:1px solid #1f2937;font-size:14px;display:flex;align-items:center;gap:8px}
|
||||
.status-item .icon{font-size:18px;flex-shrink:0}
|
||||
.banner{padding:12px 16px;border-radius:10px;margin:10px 0;font-size:14px;line-height:1.5}
|
||||
.banner.info{background:#1e3a5f;border:1px solid #2563eb}
|
||||
.banner.warn{background:#422006;border:1px solid #d97706}
|
||||
.banner.error{background:#3b0d0d;border:1px solid #dc2626}
|
||||
.banner.success{background:#052e16;border:1px solid #059669}
|
||||
.wizard{background:#0d1117;border:1px solid #1f2937;border-radius:10px;padding:14px;margin:12px 0}
|
||||
.wizard h3{margin:0 0 8px;font-size:15px}
|
||||
.wizard ol{margin:6px 0;padding-left:22px;font-size:14px;line-height:1.8}
|
||||
.wizard code{font-size:12px}
|
||||
details{margin:8px 0}
|
||||
details>summary{cursor:pointer;font-size:14px;color:#60a5fa;font-weight:500}
|
||||
details>summary:hover{text-decoration:underline}
|
||||
.hidden{display:none}
|
||||
.badge{display:inline-block;padding:2px 8px;border-radius:6px;font-size:12px;font-weight:600;vertical-align:middle}
|
||||
.badge.secure{background:#059669;color:#fff}
|
||||
.badge.insecure{background:#dc2626;color:#fff}
|
||||
.badge.mode{background:#2563eb;color:#fff}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h2 style="margin:0 0 8px 0">OpenClaw Assistant</h2>
|
||||
<h2 style="margin:0 0 4px 0">OpenClaw Assistant</h2>
|
||||
<div style="margin-bottom:10px">
|
||||
<span class="badge mode" id="modeBadge">__ACCESS_MODE__</span>
|
||||
<span class="badge" id="secureBadge"></span>
|
||||
</div>
|
||||
|
||||
<!-- ==================== STATUS GRID ==================== -->
|
||||
<div class="status-grid">
|
||||
<div class="status-item" id="statusGateway">
|
||||
<span class="icon">⏳</span>
|
||||
<span>Gateway: checking…</span>
|
||||
</div>
|
||||
<div class="status-item" id="statusSecure">
|
||||
<span class="icon">🔒</span>
|
||||
<span>Secure context: checking…</span>
|
||||
</div>
|
||||
<div class="status-item" id="statusAccess">
|
||||
<span class="icon">📡</span>
|
||||
<span>Access mode: <b>__ACCESS_MODE__</b></span>
|
||||
</div>
|
||||
<div class="status-item" id="statusDisk">
|
||||
<span class="icon" id="diskIcon">💾</span>
|
||||
<span id="diskText">Disk: __DISK_USED__ / __DISK_TOTAL__ (__DISK_PCT__) — __DISK_AVAIL__ free</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== ACTION BUTTONS ==================== -->
|
||||
<div class="row" style="margin-bottom:6px">
|
||||
<a class="btn" id="gwbtn" href="__GATEWAY_PUBLIC_URL____GW_PUBLIC_URL_PATH__?token=__GATEWAY_TOKEN__" target="_blank" rel="noopener noreferrer">Open Gateway Web UI</a>
|
||||
<a class="btn secondary" href="./terminal/" target="_self">Open Terminal (full page)</a>
|
||||
<a class="btn green hidden" id="certBtn" href="" target="_blank" rel="noopener noreferrer">Download CA Certificate</a>
|
||||
</div>
|
||||
|
||||
<div class="muted">
|
||||
Tip: The gateway UI is intentionally opened outside of Ingress to avoid websocket/proxy issues.
|
||||
Set <code>gateway_public_url</code> in the add-on options.
|
||||
<!-- ==================== MIGRATION BANNER ==================== -->
|
||||
<div class="banner warn hidden" id="migrationBanner">
|
||||
<b>⚠️ Migration notice:</b> OpenClaw v2026.2.21+ requires HTTPS or localhost for Control UI.
|
||||
Plain HTTP LAN access no longer works. Switch <code>access_mode</code> to <b>lan_https</b>
|
||||
in add-on Configuration for one-click secure LAN access, then restart.
|
||||
</div>
|
||||
|
||||
<div class="muted" style="margin-top:8px">
|
||||
If the Gateway UI says <b>Unauthorized</b>, you need the token. In the terminal run:
|
||||
<code>openclaw config get gateway.auth.token</code>
|
||||
<!-- ==================== LOW DISK SPACE BANNER ==================== -->
|
||||
<div class="banner warn hidden" id="diskBanner">
|
||||
<b>⚠️ Low disk space:</b> <span id="diskBannerText"></span><br>
|
||||
Add-on updates and Docker builds may fail. Open the terminal and run <code>oc-cleanup</code> to free space.
|
||||
For Docker-level cleanup, open a <strong>host root shell</strong> (Advanced SSH add-on with Protection Mode off, or type <code>login</code> at the HAOS console) and run <code>docker image prune -a</code>.
|
||||
</div>
|
||||
|
||||
<!-- ==================== ERROR BANNER (populated by JS) ==================== -->
|
||||
<div class="banner error hidden" id="errorBanner"></div>
|
||||
|
||||
<!-- ==================== SUCCESS BANNER ==================== -->
|
||||
<div class="banner success hidden" id="successBanner"></div>
|
||||
|
||||
<!-- ==================== ACCESS WIZARD ==================== -->
|
||||
<div class="wizard hidden" id="wizard">
|
||||
<h3>🧭 Quick-Start: Secure LAN Access</h3>
|
||||
<div id="wizardContent"></div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== TIPS ==================== -->
|
||||
<details>
|
||||
<summary>Tips & token help</summary>
|
||||
<div class="muted" style="margin-top:6px">
|
||||
The gateway UI opens in a separate tab to avoid websocket/proxy issues with Ingress.
|
||||
Set <code>gateway_public_url</code> in add-on options if the button URL is wrong.
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">
|
||||
If the Gateway UI says <b>Unauthorized</b>, get your token in the terminal:
|
||||
<code>openclaw config get gateway.auth.token</code>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- ==================== PROXY RECIPES ==================== -->
|
||||
<details>
|
||||
<summary>Reverse-proxy recipes (NPM / Caddy / Traefik / Tailscale)</summary>
|
||||
<div style="margin-top:8px;font-size:13px;color:#9ca3af;line-height:1.7">
|
||||
|
||||
<b>Nginx Proxy Manager (NPM)</b>
|
||||
<pre style="background:#0b1220;padding:8px;border-radius:6px;overflow-x:auto;font-size:12px">Scheme: https
|
||||
Forward: <HA-IP>:18789
|
||||
WS: ON
|
||||
SSL tab: Request a new SSL certificate (Let's Encrypt or custom)</pre>
|
||||
|
||||
<b>Caddy</b>
|
||||
<pre style="background:#0b1220;padding:8px;border-radius:6px;overflow-x:auto;font-size:12px">openclaw.example.com {
|
||||
reverse_proxy <HA-IP>:18789
|
||||
}</pre>
|
||||
|
||||
<b>Traefik (docker labels)</b>
|
||||
<pre style="background:#0b1220;padding:8px;border-radius:6px;overflow-x:auto;font-size:12px">- "traefik.http.routers.openclaw.rule=Host(`openclaw.example.com`)"
|
||||
- "traefik.http.routers.openclaw.tls.certresolver=le"
|
||||
- "traefik.http.services.openclaw.loadbalancer.server.port=18789"</pre>
|
||||
|
||||
<b>Tailscale HTTPS</b>
|
||||
<pre style="background:#0b1220;padding:8px;border-radius:6px;overflow-x:auto;font-size:12px"># 1. Set access_mode to tailnet_https in add-on configuration
|
||||
# 2. Enable Tailscale HTTPS in your Tailnet admin: DNS → HTTPS Certificates
|
||||
# 3. On the HA host: tailscale cert <machine-name>.ts.net
|
||||
# 4. Set gateway_public_url to https://<machine-name>.ts.net:18789</pre>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- ==================== TERMINAL ==================== -->
|
||||
<div class="term">
|
||||
<iframe src="./terminal/" title="Terminal"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== CLIENT-SIDE LOGIC ==================== -->
|
||||
<script>
|
||||
(function() {
|
||||
const ACCESS_MODE = '__ACCESS_MODE__';
|
||||
const HTTPS_PORT = '__HTTPS_PORT__';
|
||||
const GW_PUBLIC_URL = '__GATEWAY_PUBLIC_URL__';
|
||||
const GW_TOKEN = '__GATEWAY_TOKEN__';
|
||||
const DISK_PCT = '__DISK_PCT__';
|
||||
const DISK_AVAIL = '__DISK_AVAIL__';
|
||||
const DISK_USED = '__DISK_USED__';
|
||||
const DISK_TOTAL = '__DISK_TOTAL__';
|
||||
|
||||
const $ = id => document.getElementById(id);
|
||||
|
||||
// ---------- Secure context detection ----------
|
||||
const isSecure = window.isSecureContext;
|
||||
const secureBadge = $('secureBadge');
|
||||
const statusSecure = $('statusSecure');
|
||||
if (isSecure) {
|
||||
secureBadge.textContent = 'secure';
|
||||
secureBadge.className = 'badge secure';
|
||||
statusSecure.innerHTML = '<span class="icon">✅</span><span>Secure context: <b>yes</b></span>';
|
||||
} else {
|
||||
secureBadge.textContent = 'not secure';
|
||||
secureBadge.className = 'badge insecure';
|
||||
statusSecure.innerHTML = '<span class="icon">❌</span><span>Secure context: <b>no</b> — HTTPS required for Control UI</span>';
|
||||
}
|
||||
|
||||
// ---------- Gateway health check ----------
|
||||
(async function checkGateway() {
|
||||
const statusEl = $('statusGateway');
|
||||
try {
|
||||
const url = GW_PUBLIC_URL
|
||||
? GW_PUBLIC_URL.replace(/\/$/, '') + '/api/health'
|
||||
: '/api/health'; // fallback to relative (only works if proxied)
|
||||
const r = await fetch(url, { mode: 'no-cors', cache: 'no-store' }).catch(() => null);
|
||||
if (r && (r.ok || r.type === 'opaque')) {
|
||||
statusEl.innerHTML = '<span class="icon">✅</span><span>Gateway: <b>running</b></span>';
|
||||
} else {
|
||||
statusEl.innerHTML = '<span class="icon">⚠️</span><span>Gateway: <b>unreachable</b> (may still be starting)</span>';
|
||||
}
|
||||
} catch {
|
||||
statusEl.innerHTML = '<span class="icon">❌</span><span>Gateway: <b>unreachable</b></span>';
|
||||
}
|
||||
})();
|
||||
|
||||
// ---------- Error translation ----------
|
||||
const ERROR_MAP = {
|
||||
'control ui requires device identity': {
|
||||
friendly: 'The Gateway UI requires HTTPS or localhost (secure context). Plain HTTP over LAN is blocked since OpenClaw v2026.2.21.',
|
||||
fix: ACCESS_MODE === 'lan_https'
|
||||
? 'Your add-on is configured for lan_https. Open the gateway via the HTTPS URL above and install the CA certificate on your device.'
|
||||
: 'Switch <code>access_mode</code> to <b>lan_https</b> in add-on Configuration, then restart. This enables a built-in HTTPS proxy for LAN access.'
|
||||
},
|
||||
'requires secure context': {
|
||||
friendly: 'The browser is not in a secure context. HTTPS or localhost is required.',
|
||||
fix: 'Use the HTTPS URL provided by the add-on, or set up a reverse proxy with TLS.'
|
||||
},
|
||||
'pairing required': {
|
||||
friendly: 'The Gateway requires device pairing before the Control UI can connect.',
|
||||
fix: ACCESS_MODE === 'lan_https'
|
||||
? 'Restart the add-on — it auto-sets <code>controlUi.dangerouslyDisableDeviceAuth: true</code> to skip pairing (token auth is still enforced). <br><small>Note: v2026.2.22+ shows an <em>expected</em> security warning for this flag in the gateway logs — it is safe to ignore.</small>'
|
||||
: 'Set <code>access_mode</code> to <b>lan_https</b> and restart. Or from the terminal: edit <code>/config/.openclaw/openclaw.json</code> and set <code>gateway.controlUi.dangerouslyDisableDeviceAuth: true</code>, then restart the gateway.'
|
||||
},
|
||||
'origin not allowed': {
|
||||
friendly: 'The Gateway rejected the browser origin. The Control UI URL is not in the allow-list.',
|
||||
fix: ACCESS_MODE === 'lan_https'
|
||||
? 'Restart the add-on — it auto-adds HTTPS origins to <code>controlUi.allowedOrigins</code>. If you changed your LAN IP, a restart regenerates the config.'
|
||||
: 'Manually add your origin: <code>openclaw config set gateway.controlUi.allowedOrigins \'["https://YOUR_IP:18789"]\' </code>'
|
||||
},
|
||||
'1008': {
|
||||
friendly: 'WebSocket disconnected (1008).',
|
||||
fix: 'Ensure you are connecting over HTTPS. Check the add-on logs for the specific sub-error (device identity / origin / pairing).'
|
||||
}
|
||||
};
|
||||
|
||||
// Expose for manual use: translateError('1008')
|
||||
window.translateError = function(rawError) {
|
||||
const lower = (rawError || '').toLowerCase();
|
||||
for (const [pattern, info] of Object.entries(ERROR_MAP)) {
|
||||
if (lower.includes(pattern)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// ---------- Migration banner ----------
|
||||
if (ACCESS_MODE === 'custom') {
|
||||
$('migrationBanner').classList.remove('hidden');
|
||||
}
|
||||
|
||||
// ---------- Disk space monitoring ----------
|
||||
if (DISK_PCT) {
|
||||
const pctNum = parseInt(DISK_PCT, 10);
|
||||
const diskIcon = $('diskIcon');
|
||||
const statusDisk = $('statusDisk');
|
||||
if (pctNum >= 90) {
|
||||
diskIcon.textContent = '🔴';
|
||||
statusDisk.style.borderColor = '#dc2626';
|
||||
$('diskBanner').classList.remove('hidden');
|
||||
$('diskBannerText').textContent =
|
||||
`Disk is ${DISK_PCT} full (${DISK_AVAIL} free of ${DISK_TOTAL}).`;
|
||||
} else if (pctNum >= 75) {
|
||||
diskIcon.textContent = '🟡';
|
||||
statusDisk.style.borderColor = '#d97706';
|
||||
$('diskBanner').classList.remove('hidden');
|
||||
$('diskBannerText').textContent =
|
||||
`Disk is ${DISK_PCT} full (${DISK_AVAIL} free of ${DISK_TOTAL}). Consider cleaning up soon.`;
|
||||
} else {
|
||||
diskIcon.textContent = '🟢';
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- CA certificate download ----------
|
||||
if (ACCESS_MODE === 'lan_https' && HTTPS_PORT) {
|
||||
const certBtn = $('certBtn');
|
||||
// Build cert URL relative to the gateway's HTTPS port
|
||||
const host = window.location.hostname || 'homeassistant.local';
|
||||
certBtn.href = 'https://' + host + ':' + HTTPS_PORT + '/cert/ca.crt';
|
||||
certBtn.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// ---------- Access wizard ----------
|
||||
const wizardEl = $('wizard');
|
||||
const wizardContent = $('wizardContent');
|
||||
|
||||
if (ACCESS_MODE === 'lan_https') {
|
||||
wizardEl.classList.remove('hidden');
|
||||
wizardContent.innerHTML = `
|
||||
<div class="banner success">✅ Built-in HTTPS proxy is active on port <b>${HTTPS_PORT}</b>.</div>
|
||||
<ol>
|
||||
<li>Click <b>Open Gateway Web UI</b> above — it will use HTTPS automatically.</li>
|
||||
<li>Your browser may show a certificate warning the first time. Click <b>Advanced → Proceed</b> to continue.</li>
|
||||
<li><b>For phones/tablets (one-time):</b> Click <b>Download CA Certificate</b>, then install it:
|
||||
<ul style="margin:4px 0;padding-left:18px">
|
||||
<li><b>Android:</b> Settings → Security → Install certificate → CA certificate → select the file</li>
|
||||
<li><b>iOS:</b> Open the .crt file → Install Profile → Settings → General → About → Certificate Trust Settings → enable</li>
|
||||
</ul>
|
||||
After installing the CA, the browser will trust the gateway without warnings.
|
||||
</li>
|
||||
</ol>`;
|
||||
} else if (ACCESS_MODE === 'lan_reverse_proxy') {
|
||||
wizardEl.classList.remove('hidden');
|
||||
wizardContent.innerHTML = `
|
||||
<ol>
|
||||
<li>Configure your reverse proxy (NPM / Caddy / Traefik) to forward HTTPS to <code><HA-IP>:${GW_PUBLIC_URL ? new URL(GW_PUBLIC_URL).port || '18789' : '18789'}</code>.</li>
|
||||
<li>Set <code>gateway_public_url</code> to your HTTPS URL (e.g. <code>https://openclaw.example.com</code>).</li>
|
||||
<li>Set <code>gateway_trusted_proxies</code> to your proxy's IP/CIDR.</li>
|
||||
<li>Restart the add-on. See <b>Reverse-proxy recipes</b> below for copy-paste configs.</li>
|
||||
</ol>`;
|
||||
} else if (ACCESS_MODE === 'tailnet_https') {
|
||||
wizardEl.classList.remove('hidden');
|
||||
wizardContent.innerHTML = `
|
||||
<ol>
|
||||
<li>Ensure Tailscale is installed on the HA host and this device.</li>
|
||||
<li>Enable HTTPS certificates in Tailnet admin: <b>DNS → HTTPS Certificates</b>.</li>
|
||||
<li>On the HA host: <code>tailscale cert <machine-name>.ts.net</code></li>
|
||||
<li>Set <code>gateway_public_url</code> to <code>https://<machine-name>.ts.net:18789</code></li>
|
||||
<li>Restart the add-on.</li>
|
||||
</ol>`;
|
||||
} else if (ACCESS_MODE === 'local_only') {
|
||||
wizardEl.classList.remove('hidden');
|
||||
wizardContent.innerHTML = `
|
||||
<div class="banner info">Gateway is bound to localhost only. Use the embedded terminal or Ingress.</div>
|
||||
<p style="font-size:14px;">To access from phones or other devices, switch <code>access_mode</code> to <b>lan_https</b> in add-on Configuration.</p>`;
|
||||
} else if (ACCESS_MODE === 'custom' && !isSecure) {
|
||||
wizardEl.classList.remove('hidden');
|
||||
wizardContent.innerHTML = `
|
||||
<div class="banner warn">You are using custom settings and this page is not in a secure context.
|
||||
The Gateway Control UI will reject connections over plain HTTP.</div>
|
||||
<p style="font-size:14px"><b>Recommended:</b> Go to <b>Settings → Add-ons → OpenClaw Assistant → Configuration</b>
|
||||
and set <code>access_mode</code> to one of:</p>
|
||||
<ul style="font-size:14px;line-height:1.8;padding-left:22px">
|
||||
<li><b>lan_https</b> — easiest, adds built-in HTTPS proxy (no external setup needed)</li>
|
||||
<li><b>lan_reverse_proxy</b> — if you already have NPM / Caddy / Traefik</li>
|
||||
<li><b>tailnet_https</b> — if you use Tailscale</li>
|
||||
</ul>`;
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,8 +9,8 @@ http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Log to stdout/stderr (container-friendly)
|
||||
access_log /dev/stdout;
|
||||
# Logging (configurable via nginx_log_level option)
|
||||
__NGINX_ACCESS_LOG__
|
||||
error_log /dev/stderr notice;
|
||||
|
||||
sendfile on;
|
||||
@@ -59,4 +59,6 @@ http {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
__HTTPS_GATEWAY_BLOCK__
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# oc-cleanup — Disk space monitor & cleanup helper for OpenClaw
|
||||
# Run from the add-on terminal: oc-cleanup
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
BOLD="\033[1m"
|
||||
RED="\033[91m"
|
||||
GREEN="\033[92m"
|
||||
YELLOW="\033[93m"
|
||||
CYAN="\033[96m"
|
||||
RESET="\033[0m"
|
||||
|
||||
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════${RESET}"
|
||||
echo -e "${BOLD}${CYAN} OpenClaw Disk Space Monitor & Cleanup${RESET}"
|
||||
echo -e "${BOLD}${CYAN}═══════════════════════════════════════════${RESET}"
|
||||
echo ""
|
||||
|
||||
# ── Disk usage ───────────────────────────────────────────────
|
||||
DATA_MOUNT="/config"
|
||||
if df -h "$DATA_MOUNT" >/dev/null 2>&1; then
|
||||
DISK_TOTAL=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $2}')
|
||||
DISK_USED=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $3}')
|
||||
DISK_AVAIL=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $4}')
|
||||
DISK_PCT=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $5}')
|
||||
DISK_PCT_NUM=${DISK_PCT//%/}
|
||||
|
||||
echo -e "${BOLD}Disk usage (data partition):${RESET}"
|
||||
echo -e " Total: ${DISK_TOTAL}"
|
||||
echo -e " Used: ${DISK_USED} (${DISK_PCT})"
|
||||
echo -e " Available: ${DISK_AVAIL}"
|
||||
echo ""
|
||||
|
||||
if [ "$DISK_PCT_NUM" -ge 90 ]; then
|
||||
echo -e "${RED}${BOLD}⚠ CRITICAL: Disk is ${DISK_PCT} full!${RESET}"
|
||||
echo -e "${RED} Add-on updates and Docker builds may fail.${RESET}"
|
||||
echo ""
|
||||
elif [ "$DISK_PCT_NUM" -ge 75 ]; then
|
||||
echo -e "${YELLOW}${BOLD}⚠ WARNING: Disk is ${DISK_PCT} full.${RESET}"
|
||||
echo -e "${YELLOW} Consider cleaning up to avoid future build failures.${RESET}"
|
||||
echo ""
|
||||
else
|
||||
echo -e "${GREEN}✓ Disk usage looks healthy.${RESET}"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Add-on cache sizes ──────────────────────────────────────
|
||||
echo -e "${BOLD}Add-on cache sizes:${RESET}"
|
||||
|
||||
show_size() {
|
||||
local label="$1" path="$2"
|
||||
if [ -d "$path" ]; then
|
||||
local size
|
||||
size=$(du -sh "$path" 2>/dev/null | cut -f1)
|
||||
printf " %-30s %s\n" "$label" "$size"
|
||||
fi
|
||||
}
|
||||
|
||||
show_size "npm cache" "/config/.npm"
|
||||
show_size "npm global packages" "/config/.node_global"
|
||||
show_size "pnpm store" "/config/.node_global/pnpm"
|
||||
show_size "OpenClaw config + skills" "/config/.openclaw"
|
||||
show_size "Homebrew" "/config/.linuxbrew"
|
||||
show_size "Agent workspace" "/config/clawd"
|
||||
show_size "Python __pycache__" "/config/.openclaw/__pycache__"
|
||||
show_size "Temp files (/tmp)" "/tmp"
|
||||
echo ""
|
||||
|
||||
# ── Cleanup menu ────────────────────────────────────────────
|
||||
echo -e "${BOLD}What can be cleaned from inside the add-on:${RESET}"
|
||||
echo " 1) npm cache (safe — rebuilds on demand)"
|
||||
echo " 2) pnpm store cache (safe — rebuilds on demand)"
|
||||
echo " 3) Python __pycache__ (safe — regenerated automatically)"
|
||||
echo " 4) /tmp files (safe — transient data)"
|
||||
echo " 5) All of the above"
|
||||
echo " 6) Show Docker prune commands (must run from HA host SSH)"
|
||||
echo " q) Quit"
|
||||
echo ""
|
||||
read -r -p "Choose [1-6/q]: " choice
|
||||
|
||||
cleanup_npm() {
|
||||
echo -e "${CYAN}Cleaning npm cache...${RESET}"
|
||||
npm cache clean --force 2>/dev/null || true
|
||||
rm -rf /config/.npm/_cacache 2>/dev/null || true
|
||||
echo " Done."
|
||||
}
|
||||
|
||||
cleanup_pnpm() {
|
||||
echo -e "${CYAN}Cleaning pnpm store...${RESET}"
|
||||
pnpm store prune 2>/dev/null || true
|
||||
echo " Done."
|
||||
}
|
||||
|
||||
cleanup_pycache() {
|
||||
echo -e "${CYAN}Cleaning Python __pycache__...${RESET}"
|
||||
find /config -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||
echo " Done."
|
||||
}
|
||||
|
||||
cleanup_tmp() {
|
||||
echo -e "${CYAN}Cleaning /tmp...${RESET}"
|
||||
rm -rf /tmp/* 2>/dev/null || true
|
||||
echo " Done."
|
||||
}
|
||||
|
||||
show_docker_commands() {
|
||||
echo ""
|
||||
echo -e "${BOLD}${YELLOW}Run these from a HOST root shell (not this add-on terminal):${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Option A — Advanced SSH & Web Terminal add-on:${RESET}"
|
||||
echo " Install it, disable Protection Mode, then open its terminal."
|
||||
echo ""
|
||||
echo -e " ${BOLD}Option B — HAOS console (VirtualBox / keyboard):${RESET}"
|
||||
echo " Type 'login' at the HAOS prompt to get a root shell."
|
||||
echo ""
|
||||
echo -e " ${BOLD}# Then run:${RESET}"
|
||||
echo " docker image prune -a # remove unused images"
|
||||
echo " docker builder prune -a # remove build cache"
|
||||
echo " docker system df # check Docker disk usage"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Important: The 'ha docker' CLI does NOT support prune."
|
||||
echo -e "You must use the raw 'docker' command from a host root shell.${RESET}"
|
||||
}
|
||||
|
||||
case "${choice:-q}" in
|
||||
1) cleanup_npm ;;
|
||||
2) cleanup_pnpm ;;
|
||||
3) cleanup_pycache ;;
|
||||
4) cleanup_tmp ;;
|
||||
5) cleanup_npm; cleanup_pnpm; cleanup_pycache; cleanup_tmp ;;
|
||||
6) show_docker_commands ;;
|
||||
q|Q) echo "Bye." ;;
|
||||
*) echo "Unknown option." ;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
# Show result
|
||||
if df -h "$DATA_MOUNT" >/dev/null 2>&1; then
|
||||
DISK_AVAIL_NOW=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $4}')
|
||||
DISK_PCT_NOW=$(df -h "$DATA_MOUNT" | awk 'NR==2{print $5}')
|
||||
echo -e "${BOLD}Disk now: ${DISK_PCT_NOW} used, ${DISK_AVAIL_NOW} available${RESET}"
|
||||
fi
|
||||
@@ -57,16 +57,17 @@ def set_gateway_setting(key, value):
|
||||
return write_config(cfg)
|
||||
|
||||
|
||||
def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_api: bool, allow_insecure_auth: bool):
|
||||
def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_api: bool, auth_mode: str, trusted_proxies_csv: str):
|
||||
"""
|
||||
Apply gateway settings to OpenClaw config.
|
||||
|
||||
Args:
|
||||
mode: "local" or "remote"
|
||||
bind_mode: "auto", "loopback", "lan", or "tailnet"
|
||||
bind_mode: "loopback", "lan", or "tailnet"
|
||||
port: Port number to listen on (must be 1-65535)
|
||||
enable_openai_api: Enable OpenAI-compatible Chat Completions endpoint
|
||||
allow_insecure_auth: Allow insecure HTTP authentication
|
||||
auth_mode: Gateway auth mode (token|trusted-proxy)
|
||||
trusted_proxies_csv: Comma-separated trusted proxy IP/CIDR list
|
||||
"""
|
||||
# Validate gateway mode
|
||||
if mode not in ["local", "remote"]:
|
||||
@@ -74,14 +75,19 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
return False
|
||||
|
||||
# Validate bind mode
|
||||
if bind_mode not in ["auto", "loopback", "lan", "tailnet"]:
|
||||
print(f"ERROR: Invalid bind_mode '{bind_mode}'. Must be 'auto', 'loopback', 'lan', or 'tailnet'")
|
||||
if bind_mode not in ["loopback", "lan", "tailnet"]:
|
||||
print(f"ERROR: Invalid bind_mode '{bind_mode}'. Must be 'loopback', 'lan', or 'tailnet'")
|
||||
return False
|
||||
|
||||
# Validate port range
|
||||
if port < 1 or port > 65535:
|
||||
print(f"ERROR: Invalid port {port}. Must be between 1 and 65535")
|
||||
return False
|
||||
|
||||
# Validate auth mode
|
||||
if auth_mode not in ["token", "trusted-proxy"]:
|
||||
print(f"ERROR: Invalid auth_mode '{auth_mode}'. Must be 'token' or 'trusted-proxy'")
|
||||
return False
|
||||
|
||||
cfg = read_config()
|
||||
if cfg is None:
|
||||
@@ -92,10 +98,10 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
|
||||
gateway = cfg["gateway"]
|
||||
|
||||
# controlUi should be nested inside gateway
|
||||
if "controlUi" not in gateway:
|
||||
gateway["controlUi"] = {}
|
||||
|
||||
# auth should be nested inside gateway
|
||||
if "auth" not in gateway:
|
||||
gateway["auth"] = {}
|
||||
|
||||
# http.endpoints.chatCompletions should be nested inside gateway
|
||||
if "http" not in gateway:
|
||||
gateway["http"] = {}
|
||||
@@ -104,14 +110,22 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
if "chatCompletions" not in gateway["http"]["endpoints"]:
|
||||
gateway["http"]["endpoints"]["chatCompletions"] = {}
|
||||
|
||||
control_ui = gateway["controlUi"]
|
||||
auth = gateway["auth"]
|
||||
chat_completions = gateway["http"]["endpoints"]["chatCompletions"]
|
||||
|
||||
|
||||
trusted_proxies = [p.strip() for p in trusted_proxies_csv.split(",") if p.strip()]
|
||||
|
||||
# OpenClaw trusted-proxy mode requires nested auth.trustedProxy config.
|
||||
# Use a sane default user header expected from reverse proxies.
|
||||
trusted_proxy_cfg_default = {"userHeader": "x-forwarded-user"}
|
||||
|
||||
current_mode = gateway.get("mode", "")
|
||||
current_bind = gateway.get("bind", "")
|
||||
current_port = gateway.get("port", 18789)
|
||||
current_openai_api = chat_completions.get("enabled", False)
|
||||
current_insecure = control_ui.get("allowInsecureAuth", False)
|
||||
current_auth_mode = auth.get("mode", "token")
|
||||
current_trusted_proxies = gateway.get("trustedProxies", [])
|
||||
current_trusted_proxy_cfg = auth.get("trustedProxy")
|
||||
|
||||
changes = []
|
||||
|
||||
@@ -131,9 +145,18 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
chat_completions["enabled"] = enable_openai_api
|
||||
changes.append(f"chatCompletions.enabled: {current_openai_api} -> {enable_openai_api}")
|
||||
|
||||
if current_insecure != allow_insecure_auth:
|
||||
control_ui["allowInsecureAuth"] = allow_insecure_auth
|
||||
changes.append(f"allowInsecureAuth: {current_insecure} -> {allow_insecure_auth}")
|
||||
if current_auth_mode != auth_mode:
|
||||
auth["mode"] = auth_mode
|
||||
changes.append(f"auth.mode: {current_auth_mode} -> {auth_mode}")
|
||||
|
||||
if current_trusted_proxies != trusted_proxies:
|
||||
gateway["trustedProxies"] = trusted_proxies
|
||||
changes.append(f"trustedProxies: {current_trusted_proxies} -> {trusted_proxies}")
|
||||
|
||||
if auth_mode == "trusted-proxy":
|
||||
if current_trusted_proxy_cfg != trusted_proxy_cfg_default:
|
||||
auth["trustedProxy"] = trusted_proxy_cfg_default
|
||||
changes.append("auth.trustedProxy: configured default userHeader=x-forwarded-user")
|
||||
|
||||
if changes:
|
||||
if write_config(cfg):
|
||||
@@ -143,10 +166,74 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
print("ERROR: Failed to write config")
|
||||
return False
|
||||
else:
|
||||
print(f"INFO: Gateway settings already correct (mode={mode}, bind={bind_mode}, port={port}, chatCompletions={enable_openai_api}, allowInsecureAuth={allow_insecure_auth})")
|
||||
print(f"INFO: Gateway settings already correct (mode={mode}, bind={bind_mode}, port={port}, chatCompletions={enable_openai_api}, authMode={auth_mode}, trustedProxies={trusted_proxies})")
|
||||
return True
|
||||
|
||||
|
||||
def set_control_ui_origins(origins_csv: str):
|
||||
"""
|
||||
Configure gateway.controlUi for the built-in HTTPS proxy.
|
||||
|
||||
Sets:
|
||||
- allowedOrigins: the HTTPS proxy origins so the browser WebSocket
|
||||
is accepted (required since v2026.2.21).
|
||||
- dangerouslyDisableDeviceAuth: true — skips the interactive device
|
||||
pairing ceremony. In a self-hosted HA add-on the user already
|
||||
controls the gateway token, so the pairing step adds friction
|
||||
without meaningful security benefit.
|
||||
|
||||
Also removes any stale/invalid keys (e.g. pairingMode) that may have
|
||||
been written by earlier add-on versions.
|
||||
|
||||
Args:
|
||||
origins_csv: Comma-separated list of allowed origins.
|
||||
Pass an empty string to clear / leave unset.
|
||||
"""
|
||||
cfg = read_config()
|
||||
if cfg is None:
|
||||
cfg = {}
|
||||
|
||||
if "gateway" not in cfg:
|
||||
cfg["gateway"] = {}
|
||||
gateway = cfg["gateway"]
|
||||
|
||||
if "controlUi" not in gateway:
|
||||
gateway["controlUi"] = {}
|
||||
|
||||
control_ui = gateway["controlUi"]
|
||||
origins = [o.strip() for o in origins_csv.split(",") if o.strip()]
|
||||
changes = []
|
||||
|
||||
# --- allowedOrigins ---
|
||||
current_origins = control_ui.get("allowedOrigins", [])
|
||||
if current_origins != origins:
|
||||
control_ui["allowedOrigins"] = origins
|
||||
changes.append(f"allowedOrigins: {current_origins} -> {origins}")
|
||||
|
||||
# --- dangerouslyDisableDeviceAuth ---
|
||||
# Skips the interactive pairing handshake (error 1008: pairing required).
|
||||
# Token auth is still enforced; this only disables the per-device approval.
|
||||
if control_ui.get("dangerouslyDisableDeviceAuth") is not True:
|
||||
control_ui["dangerouslyDisableDeviceAuth"] = True
|
||||
changes.append("dangerouslyDisableDeviceAuth: True")
|
||||
|
||||
# --- Remove invalid keys from earlier add-on versions ---
|
||||
for stale_key in ("pairingMode",):
|
||||
if stale_key in control_ui:
|
||||
del control_ui[stale_key]
|
||||
changes.append(f"removed invalid key: {stale_key}")
|
||||
|
||||
if not changes:
|
||||
print(f"INFO: controlUi already correct: origins={origins}, deviceAuth=disabled")
|
||||
return True
|
||||
|
||||
if write_config(cfg):
|
||||
print(f"INFO: Updated controlUi: {', '.join(changes)}")
|
||||
return True
|
||||
print("ERROR: Failed to write config")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point for use by run.sh"""
|
||||
if len(sys.argv) < 2:
|
||||
@@ -156,15 +243,16 @@ def main():
|
||||
cmd = sys.argv[1]
|
||||
|
||||
if cmd == "apply-gateway-settings":
|
||||
if len(sys.argv) != 7:
|
||||
print("Usage: oc_config_helper.py apply-gateway-settings <local|remote> <auto|loopback|lan|tailnet> <port> <enable_openai_api:true|false> <allow_insecure:true|false>")
|
||||
if len(sys.argv) != 8:
|
||||
print("Usage: oc_config_helper.py apply-gateway-settings <local|remote> <loopback|lan|tailnet> <port> <enable_openai_api:true|false> <auth_mode:token|trusted-proxy> <trusted_proxies_csv>")
|
||||
sys.exit(1)
|
||||
mode = sys.argv[2]
|
||||
bind_mode = sys.argv[3]
|
||||
port = int(sys.argv[4])
|
||||
enable_openai_api = sys.argv[5].lower() == "true"
|
||||
allow_insecure_auth = sys.argv[6].lower() == "true"
|
||||
success = apply_gateway_settings(mode, bind_mode, port, enable_openai_api, allow_insecure_auth)
|
||||
auth_mode = sys.argv[6]
|
||||
trusted_proxies_csv = sys.argv[7]
|
||||
success = apply_gateway_settings(mode, bind_mode, port, enable_openai_api, auth_mode, trusted_proxies_csv)
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
elif cmd == "get":
|
||||
@@ -177,6 +265,14 @@ def main():
|
||||
print(value)
|
||||
sys.exit(0)
|
||||
|
||||
elif cmd == "set-control-ui-origins":
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: oc_config_helper.py set-control-ui-origins <origins_csv>")
|
||||
sys.exit(1)
|
||||
origins_csv = sys.argv[2]
|
||||
success = set_control_ui_origins(origins_csv)
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
elif cmd == "set":
|
||||
if len(sys.argv) != 4:
|
||||
print("Usage: oc_config_helper.py set <key> <value>")
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Render nginx.conf and landing page HTML from templates.
|
||||
|
||||
Called by run.sh with the following env vars:
|
||||
GW_PUBLIC_URL, GW_TOKEN, TERMINAL_PORT,
|
||||
ENABLE_HTTPS_PROXY, HTTPS_PROXY_PORT,
|
||||
GATEWAY_INTERNAL_PORT, ACCESS_MODE,
|
||||
DISK_TOTAL, DISK_USED, DISK_AVAIL, DISK_PCT
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
tpl = Path('/etc/nginx/nginx.conf.tpl').read_text()
|
||||
landing_tpl = Path('/etc/nginx/landing.html.tpl').read_text()
|
||||
|
||||
public_url = os.environ.get('GW_PUBLIC_URL', '')
|
||||
terminal_port = os.environ.get('TERMINAL_PORT', '7681')
|
||||
enable_https = os.environ.get('ENABLE_HTTPS_PROXY', 'false') == 'true'
|
||||
https_port = os.environ.get('HTTPS_PROXY_PORT', '')
|
||||
internal_gw_port = os.environ.get('GATEWAY_INTERNAL_PORT', '')
|
||||
access_mode = os.environ.get('ACCESS_MODE', 'custom')
|
||||
|
||||
# Disk usage info (collected by run.sh)
|
||||
disk_total = os.environ.get('DISK_TOTAL', '')
|
||||
disk_used = os.environ.get('DISK_USED', '')
|
||||
disk_avail = os.environ.get('DISK_AVAIL', '')
|
||||
disk_pct = os.environ.get('DISK_PCT', '')
|
||||
nginx_log_level = os.environ.get('NGINX_LOG_LEVEL', 'minimal')
|
||||
|
||||
# Token comes from environment (best-effort CLI query in run.sh)
|
||||
token = os.environ.get('GW_TOKEN', '')
|
||||
|
||||
gw_path = '' if public_url.endswith('/') else '/'
|
||||
|
||||
# ── nginx.conf ──────────────────────────────────────────────
|
||||
# Build access_log directive (minimal suppresses HA health-check / polling noise)
|
||||
if nginx_log_level == 'minimal':
|
||||
access_log_block = (
|
||||
'# Suppress repetitive HA health-check / polling requests\n'
|
||||
' map $http_user_agent $loggable {\n'
|
||||
' ~HomeAssistant 0;\n'
|
||||
' default 1;\n'
|
||||
' }\n'
|
||||
' access_log /dev/stdout combined if=$loggable;'
|
||||
)
|
||||
else:
|
||||
access_log_block = 'access_log /dev/stdout;'
|
||||
|
||||
conf = tpl.replace('__NGINX_ACCESS_LOG__', access_log_block)
|
||||
conf = conf.replace('__TERMINAL_PORT__', terminal_port)
|
||||
|
||||
# Build HTTPS gateway proxy block (only for lan_https mode)
|
||||
https_block = ''
|
||||
if enable_https and https_port and internal_gw_port:
|
||||
https_block = f"""
|
||||
# --- HTTPS Gateway Proxy (lan_https mode) ---
|
||||
server {{
|
||||
listen {https_port} ssl;
|
||||
|
||||
ssl_certificate /config/certs/gateway.crt;
|
||||
ssl_certificate_key /config/certs/gateway.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# Proxy all traffic to the loopback gateway with WebSocket support
|
||||
location / {{
|
||||
proxy_pass http://127.0.0.1:{internal_gw_port};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
}}
|
||||
|
||||
# Download the local CA certificate (install on phone for trusted access)
|
||||
location = /cert/ca.crt {{
|
||||
alias /etc/nginx/html/openclaw-ca.crt;
|
||||
default_type application/x-x509-ca-cert;
|
||||
add_header Content-Disposition 'attachment; filename="openclaw-ca.crt"';
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
conf = conf.replace('__HTTPS_GATEWAY_BLOCK__', https_block)
|
||||
Path('/etc/nginx/nginx.conf').write_text(conf)
|
||||
|
||||
# ── landing page ────────────────────────────────────────────
|
||||
# If lan_https and no explicit public URL, auto-construct one
|
||||
if enable_https and not public_url:
|
||||
try:
|
||||
lan_ip = subprocess.check_output(
|
||||
['hostname', '-I'], text=True, timeout=2
|
||||
).split()[0]
|
||||
except Exception:
|
||||
lan_ip = '127.0.0.1'
|
||||
public_url = f'https://{lan_ip}:{https_port}'
|
||||
gw_path = '/'
|
||||
|
||||
landing = landing_tpl.replace('__GATEWAY_TOKEN__', token)
|
||||
landing = landing.replace('__GATEWAY_PUBLIC_URL__', public_url)
|
||||
landing = landing.replace('__GW_PUBLIC_URL_PATH__', gw_path)
|
||||
landing = landing.replace('__ACCESS_MODE__', access_mode)
|
||||
landing = landing.replace('__HTTPS_PORT__', https_port if enable_https else '')
|
||||
landing = landing.replace('__DISK_TOTAL__', disk_total)
|
||||
landing = landing.replace('__DISK_USED__', disk_used)
|
||||
landing = landing.replace('__DISK_AVAIL__', disk_avail)
|
||||
landing = landing.replace('__DISK_PCT__', disk_pct)
|
||||
|
||||
out_dir = Path('/etc/nginx/html')
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
out_file = out_dir / 'index.html'
|
||||
out_file.write_text(landing)
|
||||
|
||||
# Ensure nginx can read it even if base image uses restrictive umask/permissions.
|
||||
try:
|
||||
out_dir.chmod(0o755)
|
||||
out_file.chmod(0o644)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+152
-36
@@ -50,12 +50,54 @@ GATEWAY_MODE=$(jq -r '.gateway_mode // "local"' "$OPTIONS_FILE")
|
||||
GATEWAY_BIND_MODE=$(jq -r '.gateway_bind_mode // "loopback"' "$OPTIONS_FILE")
|
||||
GATEWAY_PORT=$(jq -r '.gateway_port // 18789' "$OPTIONS_FILE")
|
||||
ENABLE_OPENAI_API=$(jq -r '.enable_openai_api // false' "$OPTIONS_FILE")
|
||||
ALLOW_INSECURE_AUTH=$(jq -r '.allow_insecure_auth // false' "$OPTIONS_FILE")
|
||||
FORCE_IPV4_DNS=$(jq -r '.force_ipv4_dns // false' "$OPTIONS_FILE")
|
||||
GATEWAY_AUTH_MODE=$(jq -r '.gateway_auth_mode // "token"' "$OPTIONS_FILE")
|
||||
GATEWAY_TRUSTED_PROXIES=$(jq -r '.gateway_trusted_proxies // empty' "$OPTIONS_FILE")
|
||||
FORCE_IPV4_DNS=$(jq -r '.force_ipv4_dns // true' "$OPTIONS_FILE")
|
||||
ACCESS_MODE=$(jq -r '.access_mode // "custom"' "$OPTIONS_FILE")
|
||||
NGINX_LOG_LEVEL=$(jq -r '.nginx_log_level // "minimal"' "$OPTIONS_FILE")
|
||||
GW_ENV_VARS=$(jq -c '.gateway_env_vars // {}' "$OPTIONS_FILE")
|
||||
|
||||
export TZ="$TZNAME"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Access mode presets — override individual gateway settings for common scenarios
|
||||
# ------------------------------------------------------------------------------
|
||||
ENABLE_HTTPS_PROXY=false
|
||||
GATEWAY_INTERNAL_PORT="$GATEWAY_PORT"
|
||||
|
||||
case "$ACCESS_MODE" in
|
||||
local_only)
|
||||
GATEWAY_BIND_MODE="loopback"
|
||||
GATEWAY_AUTH_MODE="token"
|
||||
echo "INFO: Access mode: local_only (loopback + token, Ingress/terminal only)"
|
||||
;;
|
||||
lan_https)
|
||||
# Gateway binds loopback on internal port; nginx terminates TLS on the external port.
|
||||
GATEWAY_BIND_MODE="loopback"
|
||||
GATEWAY_AUTH_MODE="token"
|
||||
ENABLE_HTTPS_PROXY=true
|
||||
GATEWAY_INTERNAL_PORT=$((GATEWAY_PORT + 1))
|
||||
echo "INFO: Access mode: lan_https (built-in HTTPS proxy on 0.0.0.0:${GATEWAY_PORT})"
|
||||
;;
|
||||
lan_reverse_proxy)
|
||||
GATEWAY_BIND_MODE="lan"
|
||||
GATEWAY_AUTH_MODE="trusted-proxy"
|
||||
if [ -z "$GATEWAY_TRUSTED_PROXIES" ]; then
|
||||
echo "ERROR: access_mode=lan_reverse_proxy requires gateway_trusted_proxies to be set."
|
||||
echo "ERROR: Set it to your reverse proxy's IP/CIDR (e.g. 127.0.0.1,192.168.88.0/24)."
|
||||
fi
|
||||
echo "INFO: Access mode: lan_reverse_proxy (LAN bind + trusted-proxy auth)"
|
||||
;;
|
||||
tailnet_https)
|
||||
GATEWAY_BIND_MODE="tailnet"
|
||||
GATEWAY_AUTH_MODE="token"
|
||||
echo "INFO: Access mode: tailnet_https (Tailscale bind + token auth)"
|
||||
;;
|
||||
custom|*)
|
||||
echo "INFO: Access mode: custom (using individual gateway_bind_mode/auth_mode settings)"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Reduce risk of secrets ending up in logs
|
||||
set +x
|
||||
|
||||
@@ -99,7 +141,7 @@ export OPENCLAW_CONFIG_DIR=/config/.openclaw
|
||||
export OPENCLAW_WORKSPACE_DIR=/config/clawd
|
||||
export XDG_CONFIG_HOME=/config
|
||||
|
||||
mkdir -p /config/.openclaw /config/clawd /config/keys /config/secrets
|
||||
mkdir -p /config/.openclaw /config/.openclaw/identity /config/clawd /config/keys /config/secrets
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sync built-in OpenClaw skills from image to persistent storage
|
||||
@@ -407,7 +449,9 @@ fi
|
||||
|
||||
if [ -f "$OPENCLAW_CONFIG_PATH" ]; then
|
||||
if [ -f "$HELPER_PATH" ]; then
|
||||
if ! python3 "$HELPER_PATH" apply-gateway-settings "$GATEWAY_MODE" "$GATEWAY_BIND_MODE" "$GATEWAY_PORT" "$ENABLE_OPENAI_API" "$ALLOW_INSECURE_AUTH"; then
|
||||
# In lan_https mode the gateway uses an internal port; nginx owns the external one.
|
||||
EFFECTIVE_GW_PORT="$GATEWAY_INTERNAL_PORT"
|
||||
if ! python3 "$HELPER_PATH" apply-gateway-settings "$GATEWAY_MODE" "$GATEWAY_BIND_MODE" "$EFFECTIVE_GW_PORT" "$ENABLE_OPENAI_API" "$GATEWAY_AUTH_MODE" "$GATEWAY_TRUSTED_PROXIES"; then
|
||||
rc=$?
|
||||
echo "ERROR: Failed to apply gateway settings via oc_config_helper.py (exit code ${rc})."
|
||||
echo "ERROR: Gateway configuration may be incorrect; aborting startup."
|
||||
@@ -422,6 +466,88 @@ else
|
||||
echo "INFO: Run 'openclaw onboard' first, then restart the add-on"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# TLS certificate generation for built-in HTTPS proxy (lan_https mode)
|
||||
# Generates a local CA + server cert so phones/tablets get proper HTTPS.
|
||||
# The CA cert can be installed once on a device for trusted access.
|
||||
# ------------------------------------------------------------------------------
|
||||
if [ "$ENABLE_HTTPS_PROXY" = "true" ]; then
|
||||
CERT_DIR="/config/certs"
|
||||
mkdir -p "$CERT_DIR"
|
||||
|
||||
# Detect primary LAN IP
|
||||
LAN_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
STORED_IP=$(cat "$CERT_DIR/.cert_ip" 2>/dev/null || echo "")
|
||||
|
||||
# --- Local CA (generated once, persists across restarts) ---
|
||||
if [ ! -f "$CERT_DIR/ca.key" ] || [ ! -f "$CERT_DIR/ca.crt" ]; then
|
||||
echo "INFO: Generating local CA certificate (one-time)..."
|
||||
openssl genrsa -out "$CERT_DIR/ca.key" 2048 2>/dev/null
|
||||
openssl req -new -x509 -key "$CERT_DIR/ca.key" -out "$CERT_DIR/ca.crt" \
|
||||
-days 3650 -nodes -subj "/CN=OpenClaw Local CA" 2>/dev/null
|
||||
chmod 600 "$CERT_DIR/ca.key"
|
||||
STORED_IP="" # force server cert regeneration
|
||||
echo "INFO: Local CA created at $CERT_DIR/ca.crt"
|
||||
fi
|
||||
|
||||
# --- Server cert (regenerated when LAN IP changes) ---
|
||||
if [ ! -f "$CERT_DIR/gateway.crt" ] || [ ! -f "$CERT_DIR/gateway.key" ] || [ "$LAN_IP" != "$STORED_IP" ]; then
|
||||
echo "INFO: Generating server TLS certificate for IP: ${LAN_IP:-unknown}..."
|
||||
openssl genrsa -out "$CERT_DIR/gateway.key" 2048 2>/dev/null
|
||||
openssl req -new -key "$CERT_DIR/gateway.key" -out "$CERT_DIR/gateway.csr" \
|
||||
-subj "/CN=OpenClaw Gateway" 2>/dev/null
|
||||
|
||||
# SAN extension — include LAN IP, loopback, and common mDNS names
|
||||
cat > "$CERT_DIR/_san.ext" <<SANEOF
|
||||
subjectAltName=IP:${LAN_IP:-127.0.0.1},IP:127.0.0.1,DNS:localhost,DNS:homeassistant,DNS:homeassistant.local
|
||||
SANEOF
|
||||
|
||||
openssl x509 -req -in "$CERT_DIR/gateway.csr" \
|
||||
-CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" -CAcreateserial \
|
||||
-out "$CERT_DIR/gateway.crt" -days 3650 \
|
||||
-extfile "$CERT_DIR/_san.ext" 2>/dev/null
|
||||
|
||||
rm -f "$CERT_DIR/gateway.csr" "$CERT_DIR/_san.ext" "$CERT_DIR/ca.srl"
|
||||
chmod 600 "$CERT_DIR/gateway.key"
|
||||
printf '%s' "$LAN_IP" > "$CERT_DIR/.cert_ip"
|
||||
echo "INFO: Server TLS certificate generated (SAN includes IP:${LAN_IP:-127.0.0.1})"
|
||||
else
|
||||
echo "INFO: Reusing existing TLS certificate (IP: $STORED_IP)"
|
||||
fi
|
||||
|
||||
# Make CA cert available for download via nginx
|
||||
mkdir -p /etc/nginx/html
|
||||
cp "$CERT_DIR/ca.crt" /etc/nginx/html/openclaw-ca.crt 2>/dev/null || true
|
||||
echo "INFO: CA certificate available for download at /cert/ca.crt on the HTTPS port"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Configure gateway.controlUi for the HTTPS proxy:
|
||||
#
|
||||
# 1. allowedOrigins — the browser's HTTPS origin must be listed,
|
||||
# otherwise v2026.2.21+ rejects with 1008 "origin not allowed".
|
||||
#
|
||||
# 2. dangerouslyDisableDeviceAuth — skips the interactive device
|
||||
# pairing ceremony (1008 "pairing required"). In a self-hosted
|
||||
# add-on the user already controls the token, so per-device
|
||||
# approval adds friction without real security benefit.
|
||||
#
|
||||
# NOTE: v2026.2.22+ emits a startup security warning when this
|
||||
# flag is active. The warning is expected and harmless for this
|
||||
# use case — run `openclaw security audit` for details.
|
||||
#
|
||||
# Also cleans up any stale keys (e.g. pairingMode) from older
|
||||
# add-on versions that would cause "Unrecognized key" errors.
|
||||
# ------------------------------------------------------------------
|
||||
if [ -n "$LAN_IP" ] && [ -f "$HELPER_PATH" ] && [ -f "$OPENCLAW_CONFIG_PATH" ]; then
|
||||
ALLOWED_ORIGINS="https://${LAN_IP}:${GATEWAY_PORT}"
|
||||
# Also permit common mDNS/hostname variants so the cert SAN names work too
|
||||
ALLOWED_ORIGINS="${ALLOWED_ORIGINS},https://homeassistant.local:${GATEWAY_PORT}"
|
||||
ALLOWED_ORIGINS="${ALLOWED_ORIGINS},https://homeassistant:${GATEWAY_PORT}"
|
||||
python3 "$HELPER_PATH" set-control-ui-origins "$ALLOWED_ORIGINS" || \
|
||||
echo "WARN: Could not set controlUi settings — gateway may reject the Control UI"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Proxy shim for undici/OpenClaw startup
|
||||
# Keep official OpenClaw npm release while enabling HTTP(S)_PROXY support.
|
||||
@@ -510,40 +636,30 @@ fi
|
||||
# The gateway token is NOT managed by the add-on; OpenClaw will generate/store it.
|
||||
# Best-effort: query it via CLI (works even if openclaw.json is JSON5). If unknown, we hide the button.
|
||||
GW_TOKEN="$(timeout 2s openclaw config get gateway.auth.token 2>/dev/null | tr -d '\n' || true)"
|
||||
GW_PUBLIC_URL="$GW_PUBLIC_URL" GW_TOKEN="$GW_TOKEN" TERMINAL_PORT="$TERMINAL_PORT" python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
tpl = Path('/etc/nginx/nginx.conf.tpl').read_text()
|
||||
landing_tpl = Path('/etc/nginx/landing.html.tpl').read_text()
|
||||
public_url = os.environ.get('GW_PUBLIC_URL','')
|
||||
terminal_port = os.environ.get('TERMINAL_PORT', '7681')
|
||||
# Collect disk usage for landing page status card
|
||||
DISK_TOTAL="" DISK_USED="" DISK_AVAIL="" DISK_PCT=""
|
||||
if df -h /config >/dev/null 2>&1; then
|
||||
DISK_TOTAL=$(df -h /config | awk 'NR==2{print $2}')
|
||||
DISK_USED=$(df -h /config | awk 'NR==2{print $3}')
|
||||
DISK_AVAIL=$(df -h /config | awk 'NR==2{print $4}')
|
||||
DISK_PCT=$(df -h /config | awk 'NR==2{print $5}')
|
||||
echo "INFO: Disk usage: ${DISK_USED}/${DISK_TOTAL} (${DISK_PCT} used, ${DISK_AVAIL} free)"
|
||||
# Warn early if disk is getting full
|
||||
DISK_PCT_NUM=${DISK_PCT//%/}
|
||||
if [ "$DISK_PCT_NUM" -ge 90 ] 2>/dev/null; then
|
||||
echo "WARNING: Disk is ${DISK_PCT} full! Add-on updates may fail. Run 'oc-cleanup' in the terminal."
|
||||
elif [ "$DISK_PCT_NUM" -ge 75 ] 2>/dev/null; then
|
||||
echo "NOTICE: Disk is ${DISK_PCT} full. Consider running 'oc-cleanup' in the terminal."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Token comes from environment (best-effort CLI query in run.sh)
|
||||
token = os.environ.get('GW_TOKEN','')
|
||||
|
||||
gw_path = '' if public_url.endswith('/') else '/'
|
||||
|
||||
# Replace terminal port placeholder in nginx config
|
||||
conf = tpl.replace('__TERMINAL_PORT__', terminal_port)
|
||||
Path('/etc/nginx/nginx.conf').write_text(conf)
|
||||
|
||||
landing = landing_tpl.replace('__GATEWAY_TOKEN__', token)
|
||||
landing = landing.replace('__GATEWAY_PUBLIC_URL__', public_url)
|
||||
landing = landing.replace('__GW_PUBLIC_URL_PATH__', gw_path)
|
||||
|
||||
out_dir = Path('/etc/nginx/html')
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
out_file = out_dir / 'index.html'
|
||||
out_file.write_text(landing)
|
||||
|
||||
# Ensure nginx can read it even if base image uses restrictive umask/permissions.
|
||||
try:
|
||||
out_dir.chmod(0o755)
|
||||
out_file.chmod(0o644)
|
||||
except Exception:
|
||||
pass
|
||||
PY
|
||||
GW_PUBLIC_URL="$GW_PUBLIC_URL" GW_TOKEN="$GW_TOKEN" TERMINAL_PORT="$TERMINAL_PORT" \
|
||||
ENABLE_HTTPS_PROXY="$ENABLE_HTTPS_PROXY" HTTPS_PROXY_PORT="$GATEWAY_PORT" \
|
||||
GATEWAY_INTERNAL_PORT="$GATEWAY_INTERNAL_PORT" ACCESS_MODE="$ACCESS_MODE" \
|
||||
DISK_TOTAL="$DISK_TOTAL" DISK_USED="$DISK_USED" DISK_AVAIL="$DISK_AVAIL" DISK_PCT="$DISK_PCT" \
|
||||
NGINX_LOG_LEVEL="$NGINX_LOG_LEVEL" \
|
||||
python3 /render_nginx.py
|
||||
|
||||
echo "Starting ingress proxy (nginx) on :48099 ..."
|
||||
nginx -g 'daemon off;' &
|
||||
|
||||
@@ -46,27 +46,55 @@ configuration:
|
||||
gateway_mode:
|
||||
name: Режим на Gateway
|
||||
description: Режим на работа на Gateway - local (локално изпълнение на gateway, препоръчително) или remote (свързване към отдалечен gateway)
|
||||
|
||||
options:
|
||||
local: "Локален (препоръчително)"
|
||||
remote: "Отдалечен"
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Режим на свързване на Gateway
|
||||
description: Режим на мрежово свързване - auto (OpenClaw избира), loopback (само 127.0.0.1, по-сигурно), lan (всички интерфейси) или tailnet (само Tailscale интерфейс).
|
||||
|
||||
description: Режим на мрежово свързване - loopback (само 127.0.0.1, най-сигурен), lan (всички интерфейси) или tailnet (само Tailscale интерфейс). Пренебрегва се от предварителните режими на достъп.
|
||||
options:
|
||||
loopback: "Loopback (само 127.0.0.1, най-сигурен)"
|
||||
lan: "LAN (всички интерфейси)"
|
||||
tailnet: "Tailnet (само Tailscale)"
|
||||
|
||||
gateway_port:
|
||||
name: Порт на Gateway
|
||||
description: Номер на порт, на който OpenClaw gateway да слуша (по подразбиране - 18789)
|
||||
|
||||
|
||||
access_mode:
|
||||
name: Режим на достъп
|
||||
description: "Опростява настройката на сигурен достъп. Пренебрегва gateway_bind_mode и gateway_auth_mode, когато не е зададен на Потребителски."
|
||||
options:
|
||||
custom: "Потребителски (индивидуални настройки)"
|
||||
local_only: "Само локален (loopback + token, само Ingress)"
|
||||
lan_https: "LAN HTTPS (вграден HTTPS прокси — препоръчително за телефони)"
|
||||
lan_reverse_proxy: "LAN Reverse Proxy (външен прокси с trusted-proxy удостоверяване)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale + token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Режим на удостоверяване на Gateway
|
||||
description: Режим на удостоверяване - token (по подразбиране) или trusted-proxy (за HTTPS reverse proxy). Пренебрегва се, когато access_mode не е Потребителски.
|
||||
options:
|
||||
token: "Token (по подразбиране)"
|
||||
trusted-proxy: "Доверен прокси (за reverse proxy)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Доверени прокси на Gateway
|
||||
description: Списък с доверени прокси IP/CIDR, разделени със запетая, за trusted-proxy режим (пример - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: Активиране на OpenAI API
|
||||
description: Активиране на OpenAI-съвместим Chat Completions ендпойнт. Позволява използването на OpenClaw като разговорен агент в HA Assist pipeline чрез Extended OpenAI Conversation (HACS) или всеки OpenAI-съвместим клиент.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Разрешаване на HTTP автентикация
|
||||
description: Разрешаване на HTTP автентикация за достъп до gateway в локалната мрежа. ВНИМАНИЕ - Активирайте само ако използвате HTTP (не HTTPS) за gateway_public_url. Необходимо за достъп от браузър през HTTP.
|
||||
|
||||
force_ipv4_dns:
|
||||
name: Принудителен IPv4 DNS ред
|
||||
description: Принудително задава IPv4-приоритет при DNS резолв за Node мрежови заявки. Полезно е, когато IPv6 DNS се резолвира, но IPv6 интернет маршрутизацията е неработеща (може да влияе на Telegram API polling).
|
||||
|
||||
description: Принудително задава IPv4-приоритет при DNS резолв. Повечето HAOS VM нямат IPv6 изход — предизвиква web_fetch и Telegram грешки. Препоръчително ВКЛЮЧЕНО (по подразбиране).
|
||||
nginx_log_level:
|
||||
name: Nginx ниво на логове
|
||||
description: "Подробност на логовете на nginx проксито. 'minimal' (по подразбиране) потиска повтарящи се HA health-check заявки. 'full' логва всичко."
|
||||
options:
|
||||
minimal: "Минимално (потискане на HA polling шум)"
|
||||
full: "Пълно (логване на всички заявки)"
|
||||
gateway_env_vars:
|
||||
name: Променливи на средата за Gateway
|
||||
description: Променливи на средата, предавани на OpenClaw gateway процеса при стартиране (дефинирани като YAML карта; макс. 50 променливи, ключове до 255 знака, стойности до 10000 знака).
|
||||
|
||||
@@ -46,27 +46,55 @@ configuration:
|
||||
gateway_mode:
|
||||
name: Gateway-Modus
|
||||
description: Gateway-Betriebsmodus - local (Gateway lokal ausführen, empfohlen) oder remote (mit einem entfernten Gateway verbinden)
|
||||
|
||||
options:
|
||||
local: "Lokal (empfohlen)"
|
||||
remote: "Remote"
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Gateway-Bindungsmodus
|
||||
description: Netzwerk-Bindungsmodus - auto (OpenClaw wählt), loopback (nur 127.0.0.1, sicherer), lan (alle Schnittstellen) oder tailnet (nur Tailscale-Schnittstelle).
|
||||
|
||||
description: Netzwerk-Bindungsmodus - loopback (nur 127.0.0.1, am sichersten), lan (alle Schnittstellen) oder tailnet (nur Tailscale-Schnittstelle). Wird durch Zugriffsmodus-Voreinstellungen überschrieben.
|
||||
options:
|
||||
loopback: "Loopback (nur 127.0.0.1, am sichersten)"
|
||||
lan: "LAN (alle Schnittstellen)"
|
||||
tailnet: "Tailnet (nur Tailscale)"
|
||||
|
||||
gateway_port:
|
||||
name: Gateway-Port
|
||||
description: Portnummer, auf der das OpenClaw-Gateway lauscht (Standard - 18789)
|
||||
|
||||
|
||||
access_mode:
|
||||
name: Zugriffsmodus
|
||||
description: "Vereinfacht die sichere Zugriffskonfiguration. Überschreibt gateway_bind_mode und gateway_auth_mode wenn nicht auf Benutzerdefiniert gesetzt."
|
||||
options:
|
||||
custom: "Benutzerdefiniert (individuelle Einstellungen)"
|
||||
local_only: "Nur lokal (Loopback + Token, nur Ingress)"
|
||||
lan_https: "LAN HTTPS (eingebauter HTTPS-Proxy — empfohlen für Smartphones)"
|
||||
lan_reverse_proxy: "LAN Reverse-Proxy (externer Proxy mit Trusted-Proxy-Auth)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale-Bindung + Token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Gateway-Authentifizierungsmodus
|
||||
description: Gateway-Auth-Modus - token (Standard) oder trusted-proxy (für HTTPS-Reverse-Proxys). Wird überschrieben wenn access_mode nicht Benutzerdefiniert ist.
|
||||
options:
|
||||
token: "Token (Standard)"
|
||||
trusted-proxy: "Vertrauenswürdiger Proxy (für Reverse-Proxys)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Vertrauenswürdige Gateway-Proxys
|
||||
description: Komma-getrennte Liste vertrauenswürdiger Proxy-IPs/CIDRs für den Trusted-Proxy-Modus (Beispiel - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: OpenAI API aktivieren
|
||||
description: OpenAI-kompatiblen Chat Completions Endpunkt aktivieren. Ermöglicht die Verwendung von OpenClaw als Gesprächsagent in der HA Assist Pipeline über Extended OpenAI Conversation (HACS) oder jeden OpenAI-kompatiblen Client.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Unsichere HTTP-Authentifizierung erlauben
|
||||
description: HTTP-Authentifizierung für Gateway-Zugriff im LAN erlauben. WARNUNG - Nur aktivieren, wenn HTTP (nicht HTTPS) für gateway_public_url verwendet wird. Erforderlich für Browser-Zugriff über HTTP.
|
||||
|
||||
force_ipv4_dns:
|
||||
name: IPv4-DNS-Reihenfolge erzwingen
|
||||
description: Erzwingt IPv4-vorrangige DNS-Auflösung für Node-Netzwerkaufrufe. Nützlich, wenn IPv6-DNS aufgelöst wird, aber IPv6-Internet-Routing nicht funktioniert (kann Telegram-API-Polling beeinträchtigen).
|
||||
|
||||
description: Erzwingt IPv4-vorrangige DNS-Auflösung. Die meisten HAOS-VMs haben keinen IPv6-Ausgang — verursacht web_fetch- und Telegram-Timeouts. Empfohlen EIN (Standard).
|
||||
nginx_log_level:
|
||||
name: Nginx Log-Level
|
||||
description: "Ausführlichkeit des Nginx-Zugriffslogs. 'minimal' (Standard) unterdrückt wiederholte HA-Healthcheck- und Polling-Anfragen. 'full' protokolliert alles."
|
||||
options:
|
||||
minimal: "Minimal (HA-Polling-Rauschen unterdrücken)"
|
||||
full: "Vollständig (alle Anfragen protokollieren)"
|
||||
gateway_env_vars:
|
||||
name: Gateway-Umgebungsvariablen
|
||||
description: Umgebungsvariablen, die beim Start an den OpenClaw-Gateway-Prozess übergeben werden (als YAML-Map; max. 50 Variablen, Schlüssel bis 255 Zeichen, Werte bis 10.000 Zeichen).
|
||||
|
||||
@@ -46,27 +46,55 @@ configuration:
|
||||
gateway_mode:
|
||||
name: Gateway Mode
|
||||
description: Gateway operation mode - local (run gateway locally, recommended) or remote (connect to a remote gateway)
|
||||
|
||||
options:
|
||||
local: "Local (recommended)"
|
||||
remote: "Remote"
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Gateway Bind Mode
|
||||
description: Network bind mode - auto (OpenClaw selects), loopback (127.0.0.1 only, more secure), lan (all interfaces), or tailnet (Tailscale interface only).
|
||||
|
||||
description: Network bind mode - loopback (127.0.0.1 only, most secure), lan (all interfaces), or tailnet (Tailscale interface only). Overridden by access_mode presets.
|
||||
options:
|
||||
loopback: "Loopback (127.0.0.1 only, most secure)"
|
||||
lan: "LAN (all interfaces)"
|
||||
tailnet: "Tailnet (Tailscale only)"
|
||||
|
||||
gateway_port:
|
||||
name: Gateway Port
|
||||
description: Port number for the OpenClaw gateway to listen on (default - 18789)
|
||||
|
||||
|
||||
access_mode:
|
||||
name: Access Mode
|
||||
description: "Simplifies secure access setup. Overrides gateway_bind_mode and gateway_auth_mode when not set to Custom."
|
||||
options:
|
||||
custom: "Custom (use individual settings)"
|
||||
local_only: "Local Only (loopback + token, Ingress only)"
|
||||
lan_https: "LAN HTTPS (built-in HTTPS proxy — recommended for phones)"
|
||||
lan_reverse_proxy: "LAN Reverse Proxy (external proxy with trusted-proxy auth)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale bind + token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Gateway Auth Mode
|
||||
description: Gateway auth mode - token (default) or trusted-proxy (for HTTPS reverse proxies). Overridden when access_mode is not Custom.
|
||||
options:
|
||||
token: "Token (default)"
|
||||
trusted-proxy: "Trusted Proxy (for reverse proxies)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Gateway Trusted Proxies
|
||||
description: Comma-separated trusted proxy IP/CIDR list for trusted-proxy mode (example - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: Enable OpenAI API
|
||||
description: Enable OpenAI-compatible Chat Completions endpoint. Allows using OpenClaw as a conversation agent in HA Assist pipeline via Extended OpenAI Conversation (HACS) or any OpenAI-compatible client.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Allow Insecure HTTP Auth
|
||||
description: Allow HTTP authentication for gateway access on LAN. WARNING - Only enable if using HTTP (not HTTPS) for gateway_public_url. Required for browser access over HTTP.
|
||||
|
||||
force_ipv4_dns:
|
||||
name: Force IPv4 DNS Order
|
||||
description: Force IPv4-first DNS ordering for Node network calls. Useful when IPv6 DNS resolves but IPv6 internet routing is broken (can affect Telegram API polling).
|
||||
|
||||
description: Force IPv4-first DNS ordering for Node network calls. Most HAOS VMs lack IPv6 egress, causing web_fetch and Telegram timeouts. Recommended ON (default).
|
||||
nginx_log_level:
|
||||
name: Nginx Log Level
|
||||
description: "Access log verbosity for the built-in nginx proxy. 'minimal' (default) suppresses repetitive HA health-check and polling requests. 'full' logs everything."
|
||||
options:
|
||||
minimal: "Minimal (suppress HA polling noise)"
|
||||
full: "Full (log all requests)"
|
||||
gateway_env_vars:
|
||||
name: Gateway Environment Variables
|
||||
description: Environment variables passed to the OpenClaw gateway process at startup (defined as a YAML map; max 50 variables, keys up to 255 characters, values up to 10,000 characters).
|
||||
|
||||
@@ -46,27 +46,55 @@ configuration:
|
||||
gateway_mode:
|
||||
name: Modo del Gateway
|
||||
description: Modo de operación del Gateway - local (ejecutar gateway localmente, recomendado) o remote (conectar a un gateway remoto)
|
||||
|
||||
options:
|
||||
local: "Local (recomendado)"
|
||||
remote: "Remoto"
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Modo de enlace del Gateway
|
||||
description: "Modo de enlace de red: auto (OpenClaw selecciona), loopback (solo 127.0.0.1, más seguro), lan (todas las interfaces) o tailnet (solo interfaz Tailscale)."
|
||||
|
||||
description: "Modo de enlace de red: loopback (solo 127.0.0.1, más seguro), lan (todas las interfaces) o tailnet (solo interfaz Tailscale). Se anula con las preselecciones del modo de acceso."
|
||||
options:
|
||||
loopback: "Loopback (solo 127.0.0.1, más seguro)"
|
||||
lan: "LAN (todas las interfaces)"
|
||||
tailnet: "Tailnet (solo Tailscale)"
|
||||
|
||||
gateway_port:
|
||||
name: Puerto del Gateway
|
||||
description: Número de puerto en el que el gateway de OpenClaw escuchará (predeterminado - 18789)
|
||||
|
||||
|
||||
access_mode:
|
||||
name: Modo de acceso
|
||||
description: "Simplifica la configuración de acceso seguro. Anula gateway_bind_mode y gateway_auth_mode cuando no es Personalizado."
|
||||
options:
|
||||
custom: "Personalizado (configuración individual)"
|
||||
local_only: "Solo local (loopback + token, solo Ingress)"
|
||||
lan_https: "LAN HTTPS (proxy HTTPS integrado — recomendado para teléfonos)"
|
||||
lan_reverse_proxy: "LAN Proxy inverso (proxy externo con auth trusted-proxy)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale + token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Modo de autenticación del Gateway
|
||||
description: Modo de autenticación - token (predeterminado) o trusted-proxy (para proxys inversos HTTPS). Se anula cuando access_mode no es Personalizado.
|
||||
options:
|
||||
token: "Token (predeterminado)"
|
||||
trusted-proxy: "Proxy de confianza (para proxys inversos)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Proxys de confianza del Gateway
|
||||
description: Lista de IPs/CIDR de proxys de confianza separados por comas para el modo trusted-proxy (ejemplo - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: Activar API OpenAI
|
||||
description: Activar endpoint de Chat Completions compatible con OpenAI. Permite usar OpenClaw como agente de conversación en HA Assist pipeline mediante Extended OpenAI Conversation (HACS) o cualquier cliente compatible con OpenAI.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Permitir autenticación HTTP insegura
|
||||
description: Permitir autenticación HTTP para acceso al gateway en LAN. ADVERTENCIA - Solo habilitar si usa HTTP (no HTTPS) para gateway_public_url. Requerido para acceso desde navegador por HTTP.
|
||||
|
||||
force_ipv4_dns:
|
||||
name: Forzar orden DNS IPv4
|
||||
description: Fuerza el orden de DNS con prioridad IPv4 para llamadas de red de Node. Útil cuando el DNS IPv6 resuelve, pero el enrutamiento IPv6 a Internet falla (puede afectar el polling de la API de Telegram).
|
||||
|
||||
description: Fuerza prioridad IPv4 en DNS. La mayoría de VMs HAOS no tienen salida IPv6 — causa errores en web_fetch y Telegram. Recomendado ACTIVADO (por defecto).
|
||||
nginx_log_level:
|
||||
name: Nivel de log Nginx
|
||||
description: "Nivel de detalle del log de acceso de nginx. 'minimal' (por defecto) suprime las solicitudes repetitivas de health-check y polling de HA. 'full' registra todo."
|
||||
options:
|
||||
minimal: "Mínimo (suprimir ruido de polling HA)"
|
||||
full: "Completo (registrar todas las solicitudes)"
|
||||
gateway_env_vars:
|
||||
name: Variables de entorno del Gateway
|
||||
description: Variables de entorno que se pasan al proceso del gateway OpenClaw al iniciarse (definidas como un mapa YAML; máximo 50 variables, claves de hasta 255 caracteres, valores de hasta 10 000 caracteres).
|
||||
description: Variables de entorno que se pasan al proceso del gateway OpenClaw al iniciarse (definidas como un mapa YAML; máximo 50 variables, claves de hasta 255 caracteres, valores de hasta 10 000 caracteres).
|
||||
|
||||
@@ -45,28 +45,56 @@ configuration:
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Tryb bindowania Gateway
|
||||
description: Tryb bindowania sieci - auto (OpenClaw wybiera), loopback (tylko 127.0.0.1, bezpieczniej), lan (wszystkie interfejsy) lub tailnet (tylko interfejs Tailscale).
|
||||
|
||||
description: Tryb bindowania sieci - loopback (tylko 127.0.0.1, najbezpieczniejszy), lan (wszystkie interfejsy) lub tailnet (tylko interfejs Tailscale). Nadpisywane przez ustawienia trybu dostępu.
|
||||
options:
|
||||
loopback: "Loopback (tylko 127.0.0.1, najbezpieczniejszy)"
|
||||
lan: "LAN (wszystkie interfejsy)"
|
||||
tailnet: "Tailnet (tylko Tailscale)"
|
||||
|
||||
gateway_port:
|
||||
name: Port Gateway
|
||||
description: Numer portu na którym gateway OpenClaw będzie nasłuchiwał (domyślnie - 18789)
|
||||
|
||||
enable_openai_api:
|
||||
name: Włącz API OpenAI
|
||||
description: Włącz endpoint Chat Completions kompatybilny z OpenAI. Pozwala używać OpenClaw jako agenta konwersacji w HA Assist pipeline przez Extended OpenAI Conversation (HACS) lub dowolnego klienta kompatybilnego z OpenAI.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Zezwól na niezabezpieczone uwierzytelnianie HTTP
|
||||
description: Zezwól na uwierzytelnianie HTTP dla dostępu do gateway w sieci LAN. UWAGA - Włącz tylko jeśli używasz HTTP (nie HTTPS) dla gateway_public_url. Wymagane dla dostępu przez przeglądarkę przez HTTP.
|
||||
|
||||
|
||||
gateway_mode:
|
||||
name: Tryb Gateway
|
||||
description: Tryb działania gateway - local (uruchom gateway lokalnie, zalecane) lub remote (połącz się ze zdalnym gateway)
|
||||
options:
|
||||
local: "Lokalny (zalecane)"
|
||||
remote: "Zdalny"
|
||||
|
||||
access_mode:
|
||||
name: Tryb dostępu
|
||||
description: "Upraszcza konfigurację bezpiecznego dostępu. Nadpisuje gateway_bind_mode i gateway_auth_mode gdy nie jest ustawiony na Niestandardowy."
|
||||
options:
|
||||
custom: "Niestandardowy (indywidualne ustawienia)"
|
||||
local_only: "Tylko lokalny (loopback + token, tylko Ingress)"
|
||||
lan_https: "LAN HTTPS (wbudowany proxy HTTPS — zalecane dla telefonów)"
|
||||
lan_reverse_proxy: "LAN Reverse Proxy (zewnętrzny proxy z auth trusted-proxy)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale + token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Tryb uwierzytelniania Gateway
|
||||
description: Tryb uwierzytelniania - token (domyślnie) lub trusted-proxy (dla reverse proxy HTTPS). Nadpisywane gdy access_mode nie jest Niestandardowy.
|
||||
options:
|
||||
token: "Token (domyślnie)"
|
||||
trusted-proxy: "Zaufany proxy (dla reverse proxy)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Zaufane proxy Gateway
|
||||
description: Lista zaufanych IP/CIDR proxy rozdzielona przecinkami dla trybu trusted-proxy (przykład - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: Włącz API OpenAI
|
||||
description: Włącz endpoint Chat Completions kompatybilny z OpenAI. Pozwala używać OpenClaw jako agenta konwersacji w HA Assist pipeline przez Extended OpenAI Conversation (HACS) lub dowolnego klienta kompatybilnego z OpenAI.
|
||||
force_ipv4_dns:
|
||||
name: Wymuś kolejność DNS IPv4
|
||||
description: Wymusza preferowanie IPv4 przy rozwiązywaniu DNS dla wywołań sieciowych Node. Przydatne, gdy DNS IPv6 się rozwiązuje, ale routing IPv6 do Internetu nie działa (może wpływać na polling API Telegrama).
|
||||
|
||||
description: Wymusza priorytet IPv4 w DNS. Większość VM HAOS nie ma wyjścia IPv6 — powoduje błędy web_fetch i Telegram. Zalecane WŁĄCZONE (domyślnie).
|
||||
nginx_log_level:
|
||||
name: Poziom logów Nginx
|
||||
description: "Szczegółowość logów dostępu nginx. 'minimal' (domyślnie) pomija powtarzające się żądania health-check i polling HA. 'full' loguje wszystko."
|
||||
options:
|
||||
minimal: "Minimalny (pominięcie szumu polling HA)"
|
||||
full: "Pełny (logowanie wszystkich żądań)"
|
||||
gateway_env_vars:
|
||||
name: Zmienne środowiskowe gateway
|
||||
description: Zmienne środowiskowe przekazywane do procesu gateway OpenClaw podczas uruchamiania (zdefiniowane jako mapa YAML; maks. 50 zmiennych, klucze do 255 znaków, wartości do 10000 znaków).
|
||||
|
||||
@@ -46,27 +46,55 @@ configuration:
|
||||
gateway_mode:
|
||||
name: Modo do Gateway
|
||||
description: Modo de operação do gateway - local (executar gateway localmente, recomendado) ou remote (conectar a um gateway remoto)
|
||||
|
||||
options:
|
||||
local: "Local (recomendado)"
|
||||
remote: "Remoto"
|
||||
|
||||
gateway_bind_mode:
|
||||
name: Modo de Vinculação do Gateway
|
||||
description: Modo de vinculação de rede - auto (OpenClaw escolhe), loopback (somente 127.0.0.1, mais seguro), lan (todas as interfaces) ou tailnet (somente interface Tailscale).
|
||||
|
||||
description: Modo de vinculação de rede - loopback (somente 127.0.0.1, mais seguro), lan (todas as interfaces) ou tailnet (somente interface Tailscale). Substituído pelas predefinições do modo de acesso.
|
||||
options:
|
||||
loopback: "Loopback (somente 127.0.0.1, mais seguro)"
|
||||
lan: "LAN (todas as interfaces)"
|
||||
tailnet: "Tailnet (somente Tailscale)"
|
||||
|
||||
gateway_port:
|
||||
name: Porta do Gateway
|
||||
description: Número da porta para o gateway do OpenClaw escutar (padrão - 18789)
|
||||
|
||||
|
||||
access_mode:
|
||||
name: Modo de Acesso
|
||||
description: "Simplifica a configuração de acesso seguro. Substitui gateway_bind_mode e gateway_auth_mode quando não definido como Personalizado."
|
||||
options:
|
||||
custom: "Personalizado (configurações individuais)"
|
||||
local_only: "Apenas local (loopback + token, apenas Ingress)"
|
||||
lan_https: "LAN HTTPS (proxy HTTPS integrado — recomendado para celulares)"
|
||||
lan_reverse_proxy: "LAN Reverse Proxy (proxy externo com auth trusted-proxy)"
|
||||
tailnet_https: "Tailscale HTTPS (Tailscale + token)"
|
||||
|
||||
gateway_auth_mode:
|
||||
name: Modo de Autenticação do Gateway
|
||||
description: Modo de autenticação - token (padrão) ou trusted-proxy (para reverse proxies HTTPS). Substituído quando access_mode não é Personalizado.
|
||||
options:
|
||||
token: "Token (padrão)"
|
||||
trusted-proxy: "Proxy Confiável (para reverse proxies)"
|
||||
|
||||
gateway_trusted_proxies:
|
||||
name: Proxies Confiáveis do Gateway
|
||||
description: Lista de IPs/CIDR de proxies confiáveis separados por vírgula para o modo trusted-proxy (exemplo - 127.0.0.1,192.168.88.0/24).
|
||||
|
||||
enable_openai_api:
|
||||
name: Habilitar API OpenAI
|
||||
description: Habilitar endpoint de Chat Completions compatível com OpenAI. Permite usar o OpenClaw como agente de conversação no pipeline do HA Assist via Extended OpenAI Conversation (HACS) ou qualquer cliente compatível com OpenAI.
|
||||
|
||||
allow_insecure_auth:
|
||||
name: Permitir Autenticação HTTP Insegura
|
||||
description: Permitir autenticação HTTP para acesso ao gateway na LAN. AVISO - Habilite somente se estiver usando HTTP (não HTTPS) para gateway_public_url. Necessário para acesso pelo navegador via HTTP.
|
||||
|
||||
force_ipv4_dns:
|
||||
name: Forçar ordem DNS IPv4
|
||||
description: Força a ordem de DNS com prioridade para IPv4 nas chamadas de rede do Node. Útil quando o DNS IPv6 resolve, mas o roteamento IPv6 para a internet está quebrado (pode afetar o polling da API do Telegram).
|
||||
|
||||
description: Força prioridade IPv4 no DNS. A maioria das VMs HAOS não tem saída IPv6 — causa erros em web_fetch e Telegram. Recomendado LIGADO (padrão).
|
||||
nginx_log_level:
|
||||
name: Nível de log Nginx
|
||||
description: "Detalhamento do log de acesso do nginx. 'minimal' (padrão) suprime requisições repetitivas de health-check e polling do HA. 'full' registra tudo."
|
||||
options:
|
||||
minimal: "Mínimo (suprimir ruído de polling HA)"
|
||||
full: "Completo (registrar todas as requisições)"
|
||||
gateway_env_vars:
|
||||
name: Variáveis de Ambiente do Gateway
|
||||
description: Variáveis de ambiente passadas para o processo do gateway OpenClaw na inicialização (definidas como um mapa YAML; máximo de 50 variáveis, chaves com até 255 caracteres, valores com até 10.000 caracteres).
|
||||
|
||||
Reference in New Issue
Block a user