Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 53 additions & 65 deletions .clawker.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
10 changes: 10 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions .serena/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}
26 changes: 22 additions & 4 deletions components/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface GatewayArgs {
env?: Record<string, string>;
/** Individual secret env vars — each key is a separate Pulumi secret */
envVars?: Record<string, pulumi.Input<string>>;
/** IANA timezone (e.g. "America/Los_Angeles") — sets TZ env var on the container */
timezone?: string;
auth: { mode: "token"; token: pulumi.Input<string> };
initHash: string;
/** Hash of rendered configs (envoy.yaml + Corefile) — triggers container replacement on policy change */
Expand All @@ -30,8 +32,8 @@ export interface GatewayArgs {
imageDigest: pulumi.Input<string>;
}

// Keys that cannot be overridden via gatewayEnv-<profile>-<KEY> (set by the component itself)
const RESERVED_ENV_KEYS = new Set(["OPENCLAW_GATEWAY_TOKEN"]);
// Keys that cannot be overridden via env or gatewayEnv-<profile>-<KEY> (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<string>;
Expand Down Expand Up @@ -63,13 +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<string>[] = [
`HOME=/home/node`,
`TERM=xterm-256color`,
`NODE_EXTRA_CA_CERTS=${ENVOY_CA_CERT_PATH}`,
pulumi.interpolate`OPENCLAW_GATEWAY_TOKEN=${args.auth.token}`,
...Object.entries(args.env ?? {}).map(([k, v]) => `${k}=${v}`),
...(args.timezone ? [`TZ=${args.timezone}`] : []),
...filteredEnv.map(([k, v]) => `${k}=${v}`),
];

// Merge base envs with individual secret env vars, filtering reserved keys
Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
42 changes: 21 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading