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:
macm1
2026-02-23 18:08:09 +03:00
16 changed files with 1468 additions and 208 deletions
+57 -2
View File
@@ -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
+6 -3
View File
@@ -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" ]
+31 -12
View File
@@ -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)?
+301 -10
View File
@@ -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&hellip;</span>
</div>
<div class="status-item" id="statusSecure">
<span class="icon">🔒</span>
<span>Secure context: checking&hellip;</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 &amp; 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: &lt;HA-IP&gt;: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 &lt;HA-IP&gt;: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 &lt;machine-name&gt;.ts.net
# 4. Set gateway_public_url to https://&lt;machine-name&gt;.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>&lt;HA-IP&gt;:${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 &lt;machine-name&gt;.ts.net</code></li>
<li>Set <code>gateway_public_url</code> to <code>https://&lt;machine-name&gt;.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>
+4 -2
View File
@@ -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__
}
+144
View File
@@ -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
+116 -20
View File
@@ -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>")
+133
View File
@@ -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
View File
@@ -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;' &
+39 -11
View File
@@ -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 знака).
+39 -11
View File
@@ -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).
+39 -11
View File
@@ -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).
+40 -12
View File
@@ -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 10000 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).
+41 -13
View File
@@ -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).
+39 -11
View File
@@ -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).