From 9640f78dcca9cd9039616cafdfa6f3a70c4a7c6d Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 17:21:58 -0700 Subject: [PATCH 1/6] fix: pass server timezone to gateway container via TZ env var The timezone config was only applied to the VPS host via timedatectl but not propagated to the gateway container, causing it to run in UTC regardless of the configured timezone. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/gateway.ts | 3 +++ index.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/components/gateway.ts b/components/gateway.ts index 5e886ca..01f12d6 100644 --- a/components/gateway.ts +++ b/components/gateway.ts @@ -22,6 +22,8 @@ export interface GatewayArgs { env?: Record; /** Individual secret env vars — each key is a separate Pulumi secret */ envVars?: Record>; + /** IANA timezone (e.g. "America/Los_Angeles") — sets TZ env var on the container */ + timezone?: string; auth: { mode: "token"; token: pulumi.Input }; initHash: string; /** Hash of rendered configs (envoy.yaml + Corefile) — triggers container replacement on policy change */ @@ -69,6 +71,7 @@ export class Gateway extends pulumi.ComponentResource { `TERM=xterm-256color`, `NODE_EXTRA_CA_CERTS=${ENVOY_CA_CERT_PATH}`, pulumi.interpolate`OPENCLAW_GATEWAY_TOKEN=${args.auth.token}`, + ...(args.timezone ? [`TZ=${args.timezone}`] : []), ...Object.entries(args.env ?? {}).map(([k, v]) => `${k}=${v}`), ]; diff --git a/index.ts b/index.ts index 74f9433..c35f5cd 100644 --- a/index.ts +++ b/index.ts @@ -241,6 +241,7 @@ const gatewayInstances = gateways.map((gw, gwIndex) => { corefilePath: envoy.corefilePath, env: gw.env, envVars, + timezone: cfg.get("timezone"), auth: { mode: "token", token }, initHash: init.contentHash, configHash: envoy.configHash, From 535973be05ca1614d28100645bcbbeeff2da864e Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 17:23:18 -0700 Subject: [PATCH 2/6] chore: cleanup --- .serena/project.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.serena/project.yml b/.serena/project.yml index 42237c6..a18c927 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -134,3 +134,17 @@ read_only_memory_patterns: [] # Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) # This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. line_ending: + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} From 9619ae406307299cf672f59415eaabef7ec0eaf4 Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 17:26:29 -0700 Subject: [PATCH 3/6] fix: resolve npm audit vulnerabilities (flatted, picomatch, tar) Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9a30e44..a6c08cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2537,9 +2537,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3238,9 +3238,9 @@ } }, "node_modules/flatted": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", - "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -4454,9 +4454,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", + "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", "license": "MIT", "engines": { "node": ">=10" @@ -5042,9 +5042,9 @@ } }, "node_modules/tar": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz", - "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -5091,9 +5091,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -5369,9 +5369,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -5460,9 +5460,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { From 68d0d7d75778a284f454ce3cb41687da69bb5154 Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 17:31:07 -0700 Subject: [PATCH 4/6] feat: add dependency review SCA check to PR security workflow Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/security.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index c26a000..4aa5125 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -10,6 +10,16 @@ permissions: actions: read jobs: + dependency-review: + name: Dependency Review (SCA) + runs-on: ubuntu-latest + timeout-minutes: 5 + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4 + with: + fail-on-severity: high semgrep: name: Semgrep SAST runs-on: ubuntu-latest From d0d030baad64622368a6a3418e55b19651603874 Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 17:39:31 -0700 Subject: [PATCH 5/6] chore: clawker config update --- .clawker.yaml | 118 +++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 65 deletions(-) diff --git a/.clawker.yaml b/.clawker.yaml index d5c2a0e..c31c5e9 100644 --- a/.clawker.yaml +++ b/.clawker.yaml @@ -1,63 +1,60 @@ -# Clawker configuration for openclaw-docker -# Go CLI that generates OpenClaw Docker deployment artifacts -# Learn more about Clawker at https://clawker.dev; https://docs.clawker.dev; https://github.com/schmitthub/clawker - -build: - image: "node:24" - - packages: - - ca-certificates - - openssh-client - - inject: - after_user_switch: - - ENV NVM_DIR="/home/claude/.nvm" - - ENV NODE_VERSION="24" - - ENV PATH="/home/claude/.pulumi/bin:$PATH" - - instructions: - # Install nvm, Node.js, and pnpm as the claude user - user_run: - - cmd: curl -fsSL https://get.pulumi.com | sh - - cmd: curl -LsSf https://astral.sh/uv/install.sh | sh - - cmd: | - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash - - cmd: | - . "$NVM_DIR/nvm.sh" && \ - nvm install $NODE_VERSION && \ - nvm use $NODE_VERSION && \ - nvm alias default $NODE_VERSION - - cmd: | - . "$NVM_DIR/nvm.sh" && \ - npm install -g pnpm typescript - - cmd: | - echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.bashrc && \ - echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> ~/.bashrc && \ - echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> ~/.bashrc - agent: - from_env: - - GH_TOKEN - - CONTEXT7_API_KEY claude_code: config: strategy: copy use_host_auth: true enable_shared_dir: true + from_env: + - GH_TOKEN + - CONTEXT7_API_KEY post_init: | claude mcp add -s local serena -- uvx --from git+https://github.com/oraios/serena serena start-mcp-server --context claude-code --project "$(pwd)" --enable-web-dashboard false claude mcp add -s user --header "CONTEXT7_API_KEY: $CONTEXT7_API_KEY" --transport http context7 https://mcp.context7.com/mcp claude mcp add -s user -t http deepwiki https://mcp.deepwiki.com/mcp - -workspace: - default_mode: bind - +build: + image: node:24 + inject: + after_user_switch: + - ENV NVM_DIR="/home/claude/.nvm" + - ENV NODE_VERSION="24" + - ENV PATH="/home/claude/.pulumi/bin:$PATH" + instructions: + user_run: + - curl -fsSL https://get.pulumi.com | sh + - curl -LsSf https://astral.sh/uv/install.sh | sh + - | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash + - | + . "$NVM_DIR/nvm.sh" && \ + nvm install $NODE_VERSION && \ + nvm use $NODE_VERSION && \ + nvm alias default $NODE_VERSION + - | + . "$NVM_DIR/nvm.sh" && \ + npm install -g pnpm typescript + - | + echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.bashrc && \ + echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> ~/.bashrc && \ + echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> ~/.bashrc + packages: + - ca-certificates + - openssh-client +loop: + calls_per_hour: 100 + completion_threshold: 2 + loop_delay_seconds: 3 + max_consecutive_test_loops: 3 + max_loops: 50 + output_decline_threshold: 70 + safety_completion_threshold: 5 + same_error_threshold: 5 + session_expiration_hours: 24 + skip_permissions: true + stagnation_threshold: 3 + timeout_minutes: 15 security: + docker_socket: true firewall: - enable: true - ip_range_sources: - - name: github - - name: google # Required for Go modules (proxy.golang.org uses GCS) add_domains: - registry.yarnpkg.com - mcp.deepwiki.com @@ -69,23 +66,14 @@ security: - files.pythonhosted.org - get.pulumi.com - www.pulumi.com - docker_socket: true + enable: true + ip_range_sources: + - name: github + - name: google git_credentials: + copy_git_config: true + forward_gpg: true forward_https: true forward_ssh: true - forward_gpg: true - copy_git_config: true - -loop: - max_loops: 50 # Maximum loops before stopping (default: 50) - stagnation_threshold: 3 # Loops without progress before circuit trips (default: 3) - timeout_minutes: 15 # Per-loop timeout in minutes (default: 15) - calls_per_hour: 100 # Rate limit: max calls per hour, 0 to disable (default: 100) - completion_threshold: 2 # Completion indicators required for strict mode (default: 2) - session_expiration_hours: 24 # Session TTL, auto-reset if older (default: 24) - same_error_threshold: 5 # Same error repetitions before circuit trips (default: 5) - output_decline_threshold: 70 # Output decline percentage that triggers trip (default: 70) - max_consecutive_test_loops: 3 # Test-only loops before circuit trips (default: 3) - loop_delay_seconds: 3 # Seconds to wait between loop iterations (default: 3) - safety_completion_threshold: 5 # Force exit after N loops with completion indicators (default: 5) - skip_permissions: true # Pass --dangerously-skip-permissions to claude (default: false) +workspace: + default_mode: bind From f64bd3694dafdb207aec2203053b745d140dda17 Mon Sep 17 00:00:00 2001 From: Andrew Schmitt Date: Sun, 29 Mar 2026 18:02:18 -0700 Subject: [PATCH 6/6] fix: added TZ to reserved keys. args don't longer bypass reserved keys --- components/gateway.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/components/gateway.ts b/components/gateway.ts index 01f12d6..7f30b1f 100644 --- a/components/gateway.ts +++ b/components/gateway.ts @@ -32,8 +32,8 @@ export interface GatewayArgs { imageDigest: pulumi.Input; } -// Keys that cannot be overridden via gatewayEnv-- (set by the component itself) -const RESERVED_ENV_KEYS = new Set(["OPENCLAW_GATEWAY_TOKEN"]); +// Keys that cannot be overridden via env or gatewayEnv-- (set by the component itself) +const RESERVED_ENV_KEYS = new Set(["OPENCLAW_GATEWAY_TOKEN", "TZ"]); export class Gateway extends pulumi.ComponentResource { public readonly containerId: pulumi.Output; @@ -65,14 +65,29 @@ export class Gateway extends pulumi.ComponentResource { { parent: this, provider: dockerProvider }, ); - // Base env vars + user-defined env + // Filter reserved keys from user-defined env + const userEnvEntries = Object.entries(args.env ?? {}); + const envConflicts = userEnvEntries + .map(([k]) => k) + .filter((k) => RESERVED_ENV_KEYS.has(k)); + if (envConflicts.length > 0) { + pulumi.log.warn( + `Gateway "${args.profile}" env contains reserved key(s) that will be ignored: ${envConflicts.join(", ")}`, + this, + ); + } + const filteredEnv = userEnvEntries.filter( + ([k]) => !RESERVED_ENV_KEYS.has(k), + ); + + // Base env vars + filtered user-defined env const envs: pulumi.Input[] = [ `HOME=/home/node`, `TERM=xterm-256color`, `NODE_EXTRA_CA_CERTS=${ENVOY_CA_CERT_PATH}`, pulumi.interpolate`OPENCLAW_GATEWAY_TOKEN=${args.auth.token}`, ...(args.timezone ? [`TZ=${args.timezone}`] : []), - ...Object.entries(args.env ?? {}).map(([k, v]) => `${k}=${v}`), + ...filteredEnv.map(([k, v]) => `${k}=${v}`), ]; // Merge base envs with individual secret env vars, filtering reserved keys