From 9908db2f99433209c04e8f870ff53fb8c906de0e Mon Sep 17 00:00:00 2001 From: macm1 Date: Mon, 23 Feb 2026 18:53:09 +0300 Subject: [PATCH] feat: support list based gateway env vars --- DOCS.md | 2 +- openclaw_assistant/CHANGELOG.md | 8 +- openclaw_assistant/config.yaml | 26 ++-- openclaw_assistant/run.sh | 164 ++++++++++++++------- openclaw_assistant/translations/bg.yaml | 2 +- openclaw_assistant/translations/de.yaml | 2 +- openclaw_assistant/translations/en.yaml | 2 +- openclaw_assistant/translations/es.yaml | 2 +- openclaw_assistant/translations/pl.yaml | 2 +- openclaw_assistant/translations/pt-BR.yaml | 2 +- 10 files changed, 132 insertions(+), 80 deletions(-) diff --git a/DOCS.md b/DOCS.md index 4a3281c..e9b4f57 100644 --- a/DOCS.md +++ b/DOCS.md @@ -251,7 +251,7 @@ All options are set via **Settings → Apps/Add-ons → OpenClaw Assistant → C When `gateway_auth_mode: trusted-proxy` is used, the add-on sets `gateway.auth.trustedProxy.userHeader` to `x-forwarded-user` by default. | `force_ipv4_dns` | bool | `false` | Force IPv4-first DNS ordering for Node network calls. Useful if IPv6 DNS resolves but IPv6 egress is broken (can affect Telegram API polling). | -| `gateway_env_vars` | map(string) | `{}` | Environment variables exported to the gateway process at startup. Use YAML map format (for example `SERVICE_API_KEY: "..."`). Limits: max 50 vars, key length 255, value length 10000. Reserved runtime keys are blocked (for example `PATH`, `HOME`, `NODE_OPTIONS`, `NODE_PATH`, `OPENCLAW_*`, proxy vars). | +| `gateway_env_vars` | list of `{name, value}` | `[]` | Environment variables exported to the gateway process at startup. UI format: list entries with `name` and `value` (example: `name=OPENAI_API_KEY`, `value=sk-...`). Limits: max 50 vars, key length 255, value length 10000. Reserved runtime keys are blocked (for example `PATH`, `HOME`, `NODE_OPTIONS`, `NODE_PATH`, `OPENCLAW_*`, proxy vars). Legacy string/object formats are still accepted for backward compatibility. | ### Terminal diff --git a/openclaw_assistant/CHANGELOG.md b/openclaw_assistant/CHANGELOG.md index 63aa31a..2cbbf10 100644 --- a/openclaw_assistant/CHANGELOG.md +++ b/openclaw_assistant/CHANGELOG.md @@ -2,10 +2,12 @@ All notable changes to the OpenClaw Assistant Home Assistant Add-on will be documented in this file. -## [0.5.52] - 2026-02-24 +## [0.5.52] - 2026-02-23 ### Added -- New add-on option `gateway_env_vars` that accepts a YAML map and safely injects values into the gateway process at startup (max 50 vars, key ≤255 chars, value ≤10000 chars). +- New add-on option `gateway_env_vars` that accepts a list of `{name, value}` objects from Home Assistant UI and safely injects values into the gateway process at startup (max 50 vars, key <=255 chars, value <=10000 chars). +- Guard `gateway_env_vars` from overriding reserved runtime/proxy/`OPENCLAW_*` keys. +- Keep legacy string/object input formats for backward compatibility. ## [0.5.51] - 2026-02-23 @@ -14,7 +16,7 @@ All notable changes to the OpenClaw Assistant Home Assistant Add-on will be docu ### Added - **`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). +- New add-on option `gateway_env_vars` and export support for gateway process environment variables with limits (50 vars, key <=255 chars, value <=10000 chars). ## [0.5.50] - 2026-02-23 diff --git a/openclaw_assistant/config.yaml b/openclaw_assistant/config.yaml index ff1fddc..eb873c3 100644 --- a/openclaw_assistant/config.yaml +++ b/openclaw_assistant/config.yaml @@ -104,22 +104,18 @@ options: # 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 - # Examples: - # API keys: - # OPENAI_API_KEY: "sk-abc123" - # ANTHROPIC_API_KEY: "claude-key" - # Service URLs: - # SERVICE_URL: "https://api.example.com" - # LOG_LEVEL: "debug" - # Feature flags: - # FEATURE_FLAG_X: "1" - # FEATURE_FLAG_Y: "0" + # Environment variables to pass to the gateway process at startup. + # Format: list of objects with name/value fields. + # Example: + # gateway_env_vars: + # - name: OPENAI_API_KEY + # value: "sk-abc123" + # - name: LOG_LEVEL + # value: "debug" # Reserved keys are blocked to protect runtime/security # (e.g. PATH, HOME, NODE_OPTIONS, NODE_PATH, OPENCLAW_*, proxy vars). # Limits: max 50 variables, max key length 255 chars, max value length 10000 chars - gateway_env_vars: {} + gateway_env_vars: [] schema: @@ -144,5 +140,7 @@ schema: gateway_trusted_proxies: str? enable_openai_api: bool? force_ipv4_dns: bool? - gateway_env_vars: map(str)? + gateway_env_vars: + - name: "match(^[A-Z_][A-Z0-9_]*$)" + value: str nginx_log_level: list(full|minimal)? diff --git a/openclaw_assistant/run.sh b/openclaw_assistant/run.sh index b58bcf5..21d5b13 100644 --- a/openclaw_assistant/run.sh +++ b/openclaw_assistant/run.sh @@ -55,7 +55,9 @@ GATEWAY_TRUSTED_PROXIES=$(jq -r '.gateway_trusted_proxies // empty' "$OPTIONS_FI 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") +GW_ENV_VARS_TYPE=$(jq -r 'if .gateway_env_vars == null then "null" else (.gateway_env_vars | type) end' "$OPTIONS_FILE") +GW_ENV_VARS_RAW=$(jq -r '.gateway_env_vars // empty' "$OPTIONS_FILE") +GW_ENV_VARS_JSON=$(jq -c '.gateway_env_vars // []' "$OPTIONS_FILE") export TZ="$TZNAME" @@ -211,63 +213,113 @@ is_reserved_gateway_env_var() { esac } -# Export gateway environment variables from add-on config -# These are user-defined variables that should be available to the gateway process -if [ "$GW_ENV_VARS" != "{}" ] && [ -n "$GW_ENV_VARS" ]; then - if printf '%s' "$GW_ENV_VARS" | jq -e 'type == "object"' >/dev/null 2>&1; then - echo "INFO: Setting gateway environment variables from add-on config..." - env_count=0 - max_env_vars=50 - max_var_name_size=255 - max_var_value_size=10000 +try_export_gateway_env_var() { + local key="$1" + local value="$2" - # Use null-delimited key/value pairs to preserve spaces and special chars. - while IFS= read -r -d '' key && IFS= read -r -d '' value; do - if [ -z "$key" ]; then - continue - fi - - # Validate variable name format - if ! [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then - echo "WARN: Invalid environment variable name: '$key' (must start with letter/underscore, skip)" - continue - fi - - # Protect critical runtime variables from accidental override. - if is_reserved_gateway_env_var "$key"; then - echo "WARN: Reserved environment variable '$key' cannot be overridden via gateway_env_vars (skip)" - continue - fi - - # Enforce max variable name length - if [ ${#key} -gt $max_var_name_size ]; then - echo "WARN: Environment variable name too long: '$key' (max $max_var_name_size chars, skip)" - continue - fi - - # Enforce max variable value length - if [ ${#value} -gt $max_var_value_size ]; then - echo "WARN: Environment variable value too long for '$key' (max $max_var_value_size chars, skip)" - continue - fi - - # Enforce limit on number of variables - if [ $env_count -ge $max_env_vars ]; then - echo "WARN: Maximum environment variables limit ($max_env_vars) reached (skip)" - continue - fi - - export "$key=$value" - ((env_count++)) - echo "INFO: Exported gateway env var: $key" - done < <(printf '%s' "$GW_ENV_VARS" | jq -j 'to_entries[] | .key, "\u0000", (.value | tostring), "\u0000"') - - if [ $env_count -gt 0 ]; then - echo "INFO: Successfully exported $env_count gateway environment variable(s)" - fi - else - echo "WARN: Invalid gateway_env_vars format in add-on options (expected JSON object), skipping" + if [ -z "$key" ]; then + return 0 fi + + # Validate variable name format + if ! [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then + echo "WARN: Invalid environment variable name: '$key' (must start with letter/underscore, skip)" + return 0 + fi + + # Protect critical runtime variables from accidental override. + if is_reserved_gateway_env_var "$key"; then + echo "WARN: Reserved environment variable '$key' cannot be overridden via gateway_env_vars (skip)" + return 0 + fi + + # Enforce max variable name length + if [ ${#key} -gt $max_var_name_size ]; then + echo "WARN: Environment variable name too long: '$key' (max $max_var_name_size chars, skip)" + return 0 + fi + + # Enforce max variable value length + if [ ${#value} -gt $max_var_value_size ]; then + echo "WARN: Environment variable value too long for '$key' (max $max_var_value_size chars, skip)" + return 0 + fi + + # Enforce limit on number of variables + if [ $env_count -ge $max_env_vars ]; then + echo "WARN: Maximum environment variables limit ($max_env_vars) reached (skip)" + return 0 + fi + + export "$key=$value" + ((env_count++)) + echo "INFO: Exported gateway env var: $key" +} + +# Export gateway environment variables from add-on config +# These are user-defined variables that should be available to the gateway process. +# Primary format: array of {name, value} objects. +if [ "$GW_ENV_VARS_TYPE" = "array" ] || [ "$GW_ENV_VARS_TYPE" = "object" ] || { [ "$GW_ENV_VARS_TYPE" = "string" ] && [ -n "$GW_ENV_VARS_RAW" ]; }; then + env_count=0 + max_env_vars=50 + max_var_name_size=255 + max_var_value_size=10000 + + if [ "$GW_ENV_VARS_TYPE" = "array" ] && [ "$GW_ENV_VARS_JSON" != "[]" ]; then + echo "INFO: Setting gateway environment variables from list config..." + + invalid_entries_count=$(printf '%s' "$GW_ENV_VARS_JSON" | jq '[.[] | select((type != "object") or ((.name | type) != "string") or (has("value") | not))] | length') + if [ "$invalid_entries_count" -gt 0 ]; then + echo "WARN: Found $invalid_entries_count invalid gateway_env_vars entries; expected objects with 'name' and 'value' keys (skip)" + fi + + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_JSON" | jq -j '.[] | select((type == "object") and ((.name | type) == "string") and (has("value"))) | .name, "\u0000", (.value | tostring), "\u0000"') + elif [ "$GW_ENV_VARS_TYPE" = "object" ] && [ "$GW_ENV_VARS_JSON" != "{}" ]; then + # Backward compatibility for old map/object configuration. + echo "INFO: Setting gateway environment variables from object config (legacy format)..." + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_JSON" | jq -j 'to_entries[] | .key, "\u0000", (.value | tostring), "\u0000"') + elif [ "$GW_ENV_VARS_TYPE" = "string" ] && [ -n "$GW_ENV_VARS_RAW" ]; then + # Preferred for complex values: JSON object string in one line. + if printf '%s' "$GW_ENV_VARS_RAW" | jq -e 'type == "object"' >/dev/null 2>&1; then + echo "INFO: Setting gateway environment variables from JSON string config..." + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_RAW" | jq -j 'to_entries[] | .key, "\u0000", (.value | tostring), "\u0000"') + else + # Supported simple format: KEY=VALUE pairs separated by ';' or newlines. + echo "INFO: Setting gateway environment variables from KEY=VALUE string config..." + while IFS= read -r entry; do + entry="${entry%$'\r'}" + trimmed="$(printf '%s' "$entry" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')" + + # Skip empty lines and comments. + if [ -z "$trimmed" ] || [[ "$trimmed" == \#* ]]; then + continue + fi + + if [[ "$trimmed" != *"="* ]]; then + echo "WARN: Invalid gateway_env_vars entry '$trimmed' (expected KEY=VALUE, skip)" + continue + fi + + key="${trimmed%%=*}" + value="${trimmed#*=}" + key="$(printf '%s' "$key" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')" + + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_RAW" | tr ';' '\n') + fi + fi + + if [ $env_count -gt 0 ]; then + echo "INFO: Successfully exported $env_count gateway environment variable(s)" + fi +elif [ "$GW_ENV_VARS_TYPE" != "null" ]; then + echo "WARN: Invalid gateway_env_vars format in add-on options (expected list, string or object), skipping" fi # ------------------------------------------------------------------------------ diff --git a/openclaw_assistant/translations/bg.yaml b/openclaw_assistant/translations/bg.yaml index 647827e..03ea571 100644 --- a/openclaw_assistant/translations/bg.yaml +++ b/openclaw_assistant/translations/bg.yaml @@ -97,4 +97,4 @@ configuration: full: "Пълно (логване на всички заявки)" gateway_env_vars: name: Променливи на средата за Gateway - description: Променливи на средата, предавани на OpenClaw gateway процеса при стартиране (дефинирани като YAML карта; макс. 50 променливи, ключове до 255 знака, стойности до 10000 знака). + description: Променливи на средата, предавани на OpenClaw gateway процеса при стартиране (списък от записи name/value в Home Assistant UI; макс. 50 променливи, ключове до 255 знака, стойности до 10000 знака). diff --git a/openclaw_assistant/translations/de.yaml b/openclaw_assistant/translations/de.yaml index 825eb30..49448ad 100644 --- a/openclaw_assistant/translations/de.yaml +++ b/openclaw_assistant/translations/de.yaml @@ -97,4 +97,4 @@ configuration: 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). + description: Umgebungsvariablen, die beim Start an den OpenClaw-Gateway-Prozess übergeben werden (Liste mit name/value-Einträgen in der Home Assistant UI; max. 50 Variablen, Schlüssel bis 255 Zeichen, Werte bis 10.000 Zeichen). diff --git a/openclaw_assistant/translations/en.yaml b/openclaw_assistant/translations/en.yaml index 2dbf0d8..cd2cd70 100644 --- a/openclaw_assistant/translations/en.yaml +++ b/openclaw_assistant/translations/en.yaml @@ -97,4 +97,4 @@ configuration: 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). + description: Environment variables passed to the OpenClaw gateway process at startup (list entries with name/value in Home Assistant UI; max 50 variables, keys up to 255 characters, values up to 10,000 characters). diff --git a/openclaw_assistant/translations/es.yaml b/openclaw_assistant/translations/es.yaml index 493a41c..771716f 100644 --- a/openclaw_assistant/translations/es.yaml +++ b/openclaw_assistant/translations/es.yaml @@ -97,4 +97,4 @@ configuration: 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 (lista de entradas name/value en la UI de Home Assistant; maximo 50 variables, claves de hasta 255 caracteres, valores de hasta 10 000 caracteres). diff --git a/openclaw_assistant/translations/pl.yaml b/openclaw_assistant/translations/pl.yaml index bf1bdac..d7dadf0 100644 --- a/openclaw_assistant/translations/pl.yaml +++ b/openclaw_assistant/translations/pl.yaml @@ -97,4 +97,4 @@ configuration: 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). + description: Zmienne środowiskowe przekazywane do procesu gateway OpenClaw podczas uruchamiania (lista wpisow name/value w UI Home Assistant; maks. 50 zmiennych, klucze do 255 znakow, wartosci do 10000 znakow). diff --git a/openclaw_assistant/translations/pt-BR.yaml b/openclaw_assistant/translations/pt-BR.yaml index 9187e60..8207da3 100644 --- a/openclaw_assistant/translations/pt-BR.yaml +++ b/openclaw_assistant/translations/pt-BR.yaml @@ -97,4 +97,4 @@ configuration: 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). + description: Variaveis de ambiente passadas para o processo do gateway OpenClaw na inicializacao (lista de entradas name/value na UI do Home Assistant; maximo de 50 variaveis, chaves com ate 255 caracteres, valores com ate 10.000 caracteres).