From 0febabd5ef4a91f938ce6f91f7396c2accf7ea2b Mon Sep 17 00:00:00 2001 From: marinom2 Date: Wed, 29 Jul 2026 16:01:48 +0300 Subject: [PATCH] feat(worker): let operators add settings that survive a reinstall Adding anything to the worker's environment meant editing the container by hand, and the next install from the app silently threw it away - the run command is generated fresh each time, so any -e flag added manually disappeared with the old container. The operator had no way to know until a capability quietly stopped being advertised. The installer now applies `/../worker.local.env` via --env-file when it exists. That covers web search (SEARCH_ENABLED, TAVILY_API_KEY, SEARCH_MAX_RESULTS, SEARCH_TIMEOUT - all read by the worker binary today) and anything similar later. A file rather than a field in the UI, deliberately. A credential typed into the app would travel through the generated script, and that script is streamed to the install log and rendered on screen. A file the operator writes once never passes through either. The flag goes BEFORE the -e flags this script emits, so script-managed values win. A stale local file must not be able to repoint RPC_URL or a registry address and leave the worker talking to the wrong chain. Windows builds the flag as an array instead of a string: splatting an empty string into a docker argument list leaves a stray empty argument, which docker rejects. Verified: tsc clean, 673 tests, eslint clean, `bash -n` parses the 667-line generated installer, and a worker started from such a file reports the SEARCH_* variables with the chain settings still intact. Worth recording for whoever reads this next: the binary reads SEARCH_ENABLED, with a D. `SEARCH_ENABLE` appears in some docs and is read by nothing - the worker starts cleanly, looks healthy, and never advertises search. --- lib/scriptgen.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/scriptgen.ts b/lib/scriptgen.ts index f5b6e47..6d815ce 100644 --- a/lib/scriptgen.ts +++ b/lib/scriptgen.ts @@ -72,8 +72,22 @@ function sortitionRunUnix(net: NetworkConfig): string { // Heartbeat store; ephemeral (no persistence). Shares its netns with the worker. 'docker run -d --restart always --name lightchain-redis --add-host=host.docker.internal:host-gateway redis:7-alpine redis-server --save "" --appendonly no >/dev/null', "for _ in $(seq 1 15); do docker exec lightchain-redis redis-cli ping >/dev/null 2>&1 && break; sleep 1; done", + // Operator-managed extras (web-search keys, tuning), kept in their own file. + // + // Deliberately NOT collected in the UI: a credential typed into the app would + // travel through this generated script, which is streamed to the install log + // and shown on screen. A file the operator writes once never passes through + // either. It also survives reinstalls, which -e flags baked in here would not: + // before this, re-running install silently dropped any added settings. + // + // Placed BEFORE the -e flags so the values this script manages always win. A + // stale local file must never be able to repoint RPC_URL or the registry. + 'LOCAL_ENV="$(dirname "$KD")/worker.local.env"', + 'EXTRA_ENV=""', + '[ -f "$LOCAL_ENV" ] && { EXTRA_ENV="--env-file $LOCAL_ENV"; echo "▶ applying operator settings from $LOCAL_ENV"; }', "docker run -d --restart always --user root --name lightchain-worker \\", " --network container:lightchain-redis \\", + " $EXTRA_ENV \\", ' -v "$KD:/data" \\', ' -e "WORKER_KEYSTORE_PATH=/data/eth-keystore/$KSF" \\', ' -e "WORKER_KEYSTORE_PASSWORD=${WORKER_PASSWORD:-}" \\', @@ -105,8 +119,15 @@ function sortitionRunWin(net: NetworkConfig): string { "docker rm -f lightchain-worker lightchain-redis 2>$null | Out-Null", 'docker run -d --restart always --name lightchain-redis --add-host=host.docker.internal:host-gateway redis:7-alpine redis-server --save "" --appendonly no | Out-Null', "for ($i=0; $i -lt 15; $i++){ docker exec lightchain-redis redis-cli ping 2>$null | Out-Null; if ($LASTEXITCODE -eq 0) { break }; Start-Sleep 1 }", + // Same operator-managed extras file as the unix path - see the comment there. + // Built as an array rather than a string: splatting an empty string into a + // docker argument list leaves a stray empty arg, which docker rejects. + '$localEnv = Join-Path (Split-Path $env:KEYS_DIR -Parent) "worker.local.env"', + "$extraEnv = @()", + 'if (Test-Path $localEnv) { $extraEnv = @("--env-file", $localEnv); Write-Host "> applying operator settings from $localEnv" }', "docker run -d --restart always --user root --name lightchain-worker `", " --network container:lightchain-redis `", + " @extraEnv `", ' -v "$($env:KEYS_DIR):/data" `', ' -e "WORKER_KEYSTORE_PATH=/data/eth-keystore/$ksf" `', ' -e "WORKER_KEYSTORE_PASSWORD=$($env:WORKER_PASSWORD)" `',