fea: Add OpenClaw management workflows#1
Conversation
Add three GitHub Actions workflows for managing per-user moltworker deployments via Cloudflare Workers: - create-new-openclaw: provisions a new openclaw-<username> worker with dedicated R2 bucket, sets AI Gateway model (workers-ai/openai/bedrock), Telegram bot token, and auto-generated gateway token - update-openclaw: re-deploys an existing moltworker and updates its model, Telegram token, and shared secrets - delete-moltworker: tears down the worker and optionally its R2 bucket Also adds .env.sample documenting all GitHub Secrets required to run the workflows, and updates .gitignore to allow tracking .env.sample. https://claude.ai/code/session_01B9eCAsVDZeiY4XNvcNuoyH
CF_ACCOUNT_ID is now reused for both the wrangler CLI, R2 endpoint, and AI Gateway account ID — eliminating the duplicate CF_AI_GATEWAY_ACCOUNT_ID repo secret. Reduces required GitHub Secrets from 8 to 7. https://claude.ai/code/session_01B9eCAsVDZeiY4XNvcNuoyH
Move GitHub Actions workflow secrets documentation into the existing .dev.vars.example file as a new section, and delete .env.sample. Also adds CF_ACCOUNT_ID and R2 credentials to the local dev section that were previously missing. https://claude.ai/code/session_01B9eCAsVDZeiY4XNvcNuoyH
Restore .dev.vars.example to match upstream to avoid merge conflicts. Move workflow documentation to dedicated README.md. Rename delete-moltworker to delete-openclaw. Make CF Access secrets required. Add configurable container sleep timeout (default 30m). Convert all Korean text to English.
Auto-generate CDP_SECRET and derive WORKER_URL from username during create workflow. Re-apply WORKER_URL on update. This enables browser automation via the /cdp endpoint without manual configuration.
WalkthroughOpenClaw 인스턴스 관리를 위한 세 개의 새로운 GitHub Actions 워크플로우와 문서를 추가합니다. 워크플로우는 사용자명, 모델, Telegram 봇 토큰 등의 입력을 받아 검증한 후 Cloudflare Workers와 R2 버킷을 프로비저닝, 업데이트, 삭제합니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c1f734f5f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| run: | | ||
| R2_BUCKET="openclaw-${{ inputs.username }}-data" | ||
| echo "Deleting R2 bucket: ${R2_BUCKET}" | ||
| npx wrangler r2 bucket delete "$R2_BUCKET" 2>&1 || echo "::warning::Could not delete bucket (may not be empty or may not exist)" |
There was a problem hiding this comment.
Empty bucket objects before attempting R2 deletion
When delete_r2_bucket is true, this step calls wrangler r2 bucket delete once and then suppresses any failure, but existing data buckets are commonly non-empty, so the delete path silently leaves storage behind while the summary still reports Deleted. The repo’s own teardown flow (test/e2e/fixture/server/terraform-destroy, lines 25-37) explicitly empties objects first for this reason, so this workflow should do the same (or fail hard) to avoid retaining user data unexpectedly.
Useful? React with 👍 / 👎.
| echo "openclaw-${{ inputs.username }}-data" | npx wrangler secret put R2_BUCKET_NAME --name "$WORKER_NAME" | ||
|
|
||
| # --- CDP (browser automation, re-apply WORKER_URL) --- | ||
| echo "https://openclaw-${{ inputs.username }}.notifly.workers.dev" | npx wrangler secret put WORKER_URL --name "$WORKER_NAME" |
There was a problem hiding this comment.
Avoid hardcoding a single workers.dev subdomain
This overwrites WORKER_URL with openclaw-<username>.notifly.workers.dev, which only works for one specific account subdomain and will break CDP/browser automation on any other account (or if the worker is accessed via a different host). Because the update workflow re-applies this value every run, even previously correct WORKER_URL secrets get replaced with an invalid URL in those environments.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In @.github/workflows/create-new-openclaw.yml:
- Around line 136-148: The deployment summary step currently appends a literal
"m" to the sandbox sleep value, which produces "neverm" when
inputs.sandbox_sleep_after is "never"; update the Deployment summary generation
(the step that writes to GITHUB_STEP_SUMMARY and uses
inputs.sandbox_sleep_after) to normalize the value: if
inputs.sandbox_sleep_after equals "never" output "never" as-is, otherwise append
"m" to the numeric minute value (i.e., conditionalize the `m` suffix when
rendering inputs.sandbox_sleep_after).
- Around line 32-45: Add validation for the sandbox_sleep_after input similar to
USERNAME and MODEL: assign SANDBOX_SLEEP_AFTER="${{ inputs.sandbox_sleep_after
}}" and add an if that tests it matches the allowed pattern (either "never" or a
non-negative integer) e.g. using grep -qE '^(never|[0-9]+)$'; if it fails, emit
an ::error:: message referencing the invalid value and exit 1. Update the
workflow block that currently defines USERNAME and MODEL to include
SANDBOX_SLEEP_AFTER and the new validation check.
- Around line 91-109: The GitHub Actions step "Set worker secrets" exposes
inputs.telegram_bot_token directly and can leak it in logs; fix by reading the
token into an environment variable (e.g., TELEGRAM_BOT_TOKEN) instead of
embedding `${{ inputs.telegram_bot_token }}` directly, immediately add a mask
for that env value with the workflow mask command (use the ::add-mask:: action)
before any echo or wrangler calls, then use the env variable when calling npx
wrangler secret put for TELEGRAM_BOT_TOKEN; additionally prefer using a
repository Secret instead of workflow_dispatch input (move token to secrets and
reference via `${{ secrets.TELEGRAM_BOT_TOKEN }}`) for long-term safety.
In @.github/workflows/README.md:
- Around line 38-55: The README currently shows conflicting defaults/units for
SANDBOX_SLEEP_AFTER between the env table (default: `30m`) and the usage section
(default: `30` and "in minutes"); update the Usage / Create a new instance
section to match the workflow input which expects a minutes value with an `m`
suffix by: change the `sandbox_sleep_after` bullet to state the default as
`30m`, clarify the accepted format is a number with `m` (or `never`), and ensure
the `SANDBOX_SLEEP_AFTER` environment variable description and the
`sandbox_sleep_after` input description consistently use the `m` suffix and the
same default; reference the symbol SANDBOX_SLEEP_AFTER / `sandbox_sleep_after`
when editing.
In @.github/workflows/update-openclaw.yml:
- Around line 116-127: The "**Sleep After**" summary uses the expression that
blindly formats inputs.sandbox_sleep_after with format('{0}m',
inputs.sandbox_sleep_after), so when the input is the literal "never" it becomes
"neverm"; change the logic in the "Deployment summary" step to normalize the
value first (e.g., check if inputs.sandbox_sleep_after == 'never' and output
'never' or a friendly label, else if numeric format as '{0}m', else fall back to
'Unchanged') so the "**Sleep After**" line no longer concatenates "m" to the
literal "never".
- Around line 31-45: Add validation for the sandbox_sleep_after input by reading
it into a variable (e.g., SANDBOX_SLEEP_AFTER="${{ inputs.sandbox_sleep_after
}}") and checking it matches either "never" or a whole-number minute value using
a regex like '^(never|[0-9]+)$'; if it fails, emit an error with the invalid
value (e.g., "::error::Invalid sandbox_sleep_after '${SANDBOX_SLEEP_AFTER}'.
Must be 'never' or an integer number of minutes.") and exit 1, mirroring the
existing USERNAME and MODEL checks.
- Around line 88-92: The TELEGRAM_TOKEN is being expanded directly from `${{
inputs.telegram_bot_token }}` which can expose the secret in workflow logs;
instead, set TELEGRAM_TOKEN via the job's env: from the workflow input and
immediately mask it using the actions core mask mechanism (emit
`::add-mask::${TELEGRAM_TOKEN}`) before any echo/usage, then use that masked env
var in the conditional and the `npx wrangler secret put TELEGRAM_BOT_TOKEN
--name "$WORKER_NAME"` call (referencing TELEGRAM_TOKEN and WORKER_NAME) so the
token is never printed unmasked in logs.
| - name: Validate inputs | ||
| run: | | ||
| USERNAME="${{ inputs.username }}" | ||
| MODEL="${{ inputs.model }}" | ||
|
|
||
| if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then | ||
| echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens, cannot start/end with hyphen." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then | ||
| echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
sandbox_sleep_after 입력값 검증이 필요합니다.
현재는 숫자/never가 아닌 값도 통과해 foom 같은 잘못된 값이 저장될 수 있습니다.
✅ 제안 수정 (입력값 검증 추가)
- name: Validate inputs
run: |
USERNAME="${{ inputs.username }}"
MODEL="${{ inputs.model }}"
+ SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}"
if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then
echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens, cannot start/end with hyphen."
exit 1
fi
if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then
echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/"
exit 1
fi
+
+ if ! echo "$SLEEP_AFTER" | grep -qE '^(never|[0-9]+)$'; then
+ echo "::error::Invalid sandbox_sleep_after '${SLEEP_AFTER}'. Use minutes (e.g. 30) or 'never'."
+ exit 1
+ fi🤖 Prompt for AI Agents
In @.github/workflows/create-new-openclaw.yml around lines 32 - 45, Add
validation for the sandbox_sleep_after input similar to USERNAME and MODEL:
assign SANDBOX_SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}" and add an if
that tests it matches the allowed pattern (either "never" or a non-negative
integer) e.g. using grep -qE '^(never|[0-9]+)$'; if it fails, emit an ::error::
message referencing the invalid value and exit 1. Update the workflow block that
currently defines USERNAME and MODEL to include SANDBOX_SLEEP_AFTER and the new
validation check.
| - name: Set worker secrets | ||
| env: | ||
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} | ||
| run: | | ||
| WORKER_NAME="openclaw-${{ inputs.username }}" | ||
|
|
||
| # --- Cloudflare Account ID (shared: AI Gateway + R2 + wrangler) --- | ||
| echo "${{ secrets.CF_ACCOUNT_ID }}" | npx wrangler secret put CF_ACCOUNT_ID --name "$WORKER_NAME" | ||
| echo "${{ secrets.CF_ACCOUNT_ID }}" | npx wrangler secret put CF_AI_GATEWAY_ACCOUNT_ID --name "$WORKER_NAME" | ||
|
|
||
| # --- AI Gateway configuration --- | ||
| echo "${{ secrets.CLOUDFLARE_AI_GATEWAY_API_KEY }}" | npx wrangler secret put CLOUDFLARE_AI_GATEWAY_API_KEY --name "$WORKER_NAME" | ||
| echo "${{ secrets.CF_AI_GATEWAY_GATEWAY_ID }}" | npx wrangler secret put CF_AI_GATEWAY_GATEWAY_ID --name "$WORKER_NAME" | ||
| echo "${{ inputs.model }}" | npx wrangler secret put CF_AI_GATEWAY_MODEL --name "$WORKER_NAME" | ||
|
|
||
| # --- Telegram --- | ||
| echo "${{ inputs.telegram_bot_token }}" | npx wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME" | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
GitHub Actions workflow_dispatch inputs masking default behavior secrets logs
💡 Result:
workflow_dispatchinputs are not secrets by default. They’re exposed via theinputs/github.event.inputscontexts and should be treated like regular user-supplied data (i.e., they can be printed/logged unless you prevent it). [1]- Only values GitHub knows are secrets get auto-redacted in logs. If a secret (from the
secretscontext, includingGITHUB_TOKEN) is printed, GitHub “automatically redacts” it; GitHub also explicitly recommends masking any sensitive value that is not a GitHub secret with::add-mask::. [1][2] ::add-mask::masks future log occurrences, not what already got logged. GitHub’s docs warn you must register the mask before outputting the value. [3]- Gotcha with
workflow_dispatchinputs +add-mask: expression expansion can leak the value. If you doecho "::add-mask::${{ inputs.secret }}", the runner can print the expanded command (with the secret value) before masking takes effect; this is a known/longstanding issue report. [4] - Practical mitigation: don’t pass sensitive data via
workflow_dispatchinputs—use GitHub Secrets (or environment secrets/OIDC where applicable) instead. If you must accept sensitive-ish manual inputs, read them from the event payload ($GITHUB_EVENT_PATH) and thenadd-maskthe raw value before using it, or use a helper action that implements this safely. [2][3][5]
Sources
[1] GitHub Docs — Contexts reference (inputs, secrets, and notes on redaction) (docs.github.com)
[2] GitHub Docs — Using secrets in GitHub Actions (auto-redaction + guidance to add-mask) (docs.github.com)
[3] GitHub Docs — Workflow commands (::add-mask:: and warning to mask before output) (docs.github.com)
[4] actions/runner issue — add-mask with workflow_dispatch inputs leaking due to log printing (github.com)
[5] GitHub Marketplace — example “hide workflow_dispatch inputs” action and explanation of the leak pattern (github.com)
Citations:
- 1: https://docs.github.com/en/actions/learn-github-actions/contexts?utm_source=openai
- 2: https://docs.github.com/actions/security-guides/using-secrets-in-github-actions?utm_source=openai
- 3: https://docs.github.com/github/automating-your-workflow-with-github-actions/development-tools-for-github-actions?utm_source=openai
- 4: add-mask doesn't work with workflow_dispatch inputs actions/runner#643
- 5: https://github.com/marketplace/actions/hide-sensitive-inputs?utm_source=openai
🏁 Script executed:
# Find and examine the workflow file
find . -name "create-new-openclaw.yml" -type fRepository: team-michael/moltworker
Length of output: 110
🏁 Script executed:
# Check the workflow file around lines 91-109
fd "create-new-openclaw.yml" -x cat -n {} | sed -n '85,115p'Repository: team-michael/moltworker
Length of output: 49
🏁 Script executed:
# Read the workflow file to verify the context
cat -n .github/workflows/create-new-openclaw.yml | sed -n '85,115p'Repository: team-michael/moltworker
Length of output: 2139
🏁 Script executed:
# Also check the workflow dispatch inputs definition at the top of the file
head -50 .github/workflows/create-new-openclaw.yml | cat -nRepository: team-michael/moltworker
Length of output: 2018
Telegram 봇 토큰이 워크플로우 로그에 노출될 수 있습니다.
workflow_dispatch 입력값은 기본적으로 GitHub에 의해 마스킹되지 않으며, 직접 ${{ inputs.telegram_bot_token }}으로 사용할 경우 실행 로그에 평문으로 남습니다. 이는 보안 위험입니다.
제안된 해결책은 올바릅니다. 환경 변수로 전달한 후 ::add-mask::를 사용해 마스킹하세요:
제안 수정
- name: Set worker secrets
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
+ TELEGRAM_TOKEN: ${{ inputs.telegram_bot_token }}
run: |
+ echo "::add-mask::${TELEGRAM_TOKEN}"
WORKER_NAME="openclaw-${{ inputs.username }}"
# --- Cloudflare Account ID (shared: AI Gateway + R2 + wrangler) ---
echo "${{ secrets.CF_ACCOUNT_ID }}" | npx wrangler secret put CF_ACCOUNT_ID --name "$WORKER_NAME"
echo "${{ secrets.CF_ACCOUNT_ID }}" | npx wrangler secret put CF_AI_GATEWAY_ACCOUNT_ID --name "$WORKER_NAME"
# --- AI Gateway configuration ---
echo "${{ secrets.CLOUDFLARE_AI_GATEWAY_API_KEY }}" | npx wrangler secret put CLOUDFLARE_AI_GATEWAY_API_KEY --name "$WORKER_NAME"
echo "${{ secrets.CF_AI_GATEWAY_GATEWAY_ID }}" | npx wrangler secret put CF_AI_GATEWAY_GATEWAY_ID --name "$WORKER_NAME"
echo "${{ inputs.model }}" | npx wrangler secret put CF_AI_GATEWAY_MODEL --name "$WORKER_NAME"
# --- Telegram ---
- echo "${{ inputs.telegram_bot_token }}" | npx wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME"
+ echo "${TELEGRAM_TOKEN}" | npx wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME"장기적으로는 토큰을 GitHub Secrets에 저장하고 workflow_dispatch 입력으로 받지 않도록 변경하는 것이 더 안전합니다.
🤖 Prompt for AI Agents
In @.github/workflows/create-new-openclaw.yml around lines 91 - 109, The GitHub
Actions step "Set worker secrets" exposes inputs.telegram_bot_token directly and
can leak it in logs; fix by reading the token into an environment variable
(e.g., TELEGRAM_BOT_TOKEN) instead of embedding `${{ inputs.telegram_bot_token
}}` directly, immediately add a mask for that env value with the workflow mask
command (use the ::add-mask:: action) before any echo or wrangler calls, then
use the env variable when calling npx wrangler secret put for
TELEGRAM_BOT_TOKEN; additionally prefer using a repository Secret instead of
workflow_dispatch input (move token to secrets and reference via `${{
secrets.TELEGRAM_BOT_TOKEN }}`) for long-term safety.
| - name: Deployment summary | ||
| run: | | ||
| cat >> "$GITHUB_STEP_SUMMARY" << EOF | ||
| ## OpenClaw Created | ||
|
|
||
| | Item | Value | | ||
| |------|-------| | ||
| | **Worker** | \`openclaw-${{ inputs.username }}\` | | ||
| | **R2 Bucket** | \`openclaw-${{ inputs.username }}-data\` | | ||
| | **Model** | \`${{ inputs.model }}\` | | ||
| | **Telegram** | Configured | | ||
| | **Sleep After** | \`${{ inputs.sandbox_sleep_after }}m\` | | ||
| | **CDP** | Enabled (secret auto-generated) | |
There was a problem hiding this comment.
요약 출력이 never일 때 잘못 표시됩니다.
현재는 항상 m을 붙여 neverm이 됩니다.
🧾 제안 수정 (요약 값 정규화)
- name: Deployment summary
run: |
+ SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}"
+ if [ "$SLEEP_AFTER" = "never" ]; then
+ SLEEP_DISPLAY="never"
+ else
+ SLEEP_DISPLAY="${SLEEP_AFTER}m"
+ fi
cat >> "$GITHUB_STEP_SUMMARY" << EOF
## OpenClaw Created
@@
- | **Sleep After** | \`${{ inputs.sandbox_sleep_after }}m\` |
+ | **Sleep After** | \`${SLEEP_DISPLAY}\` |
| **CDP** | Enabled (secret auto-generated) |
| **Gateway Token** | Auto-generated (stored as worker secret) |
EOF🤖 Prompt for AI Agents
In @.github/workflows/create-new-openclaw.yml around lines 136 - 148, The
deployment summary step currently appends a literal "m" to the sandbox sleep
value, which produces "neverm" when inputs.sandbox_sleep_after is "never";
update the Deployment summary generation (the step that writes to
GITHUB_STEP_SUMMARY and uses inputs.sandbox_sleep_after) to normalize the value:
if inputs.sandbox_sleep_after equals "never" output "never" as-is, otherwise
append "m" to the numeric minute value (i.e., conditionalize the `m` suffix when
rendering inputs.sandbox_sleep_after).
| | `R2_BUCKET_NAME` | Auto-set to `openclaw-<username>-data` | | ||
| | `SANDBOX_SLEEP_AFTER` | Workflow input (default: `30m`) | | ||
| | `CDP_SECRET` | Auto-generated (32-byte hex) by create workflow | | ||
| | `WORKER_URL` | Auto-set to `https://openclaw-<username>.notifly.workers.dev` | | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Create a new instance | ||
|
|
||
| 1. Go to **Actions** > **Create New OpenClaw** > **Run workflow** | ||
| 2. Enter: | ||
| - `username`: lowercase alphanumeric, hyphens allowed (e.g. `alice`, `team-dev`) | ||
| - `model`: AI Gateway model (format: `<provider>/<model-id>`). Examples: | ||
| - `workers-ai/@cf/openai/gpt-oss-120b` — Workers AI GPT-OSS 120B | ||
| - `openai/gpt-5` — OpenAI GPT-5 | ||
| - `bedrock/anthropic.claude-opus-4-6-20250923-v1:0` — AWS Bedrock Claude Opus 4.6 | ||
| - `telegram_bot_token`: Telegram bot token ([how to create](#creating-a-telegram-bot-token)) | ||
| - `sandbox_sleep_after`: container sleep timeout in minutes, or `never` (default: `30`) |
There was a problem hiding this comment.
SANDBOX_SLEEP_AFTER 기본값/단위 표기가 상충합니다.
자동 관리 시크릿 표와 사용 방법 섹션이 기본값과 단위를 다르게 설명합니다. 워크플로우는 분 단위 숫자를 받아 m을 붙이므로, 문서도 그 기준으로 통일하는 편이 안전합니다.
📝 제안 수정 (문서 단위 통일)
-| `SANDBOX_SLEEP_AFTER` | Workflow input (default: `30m`) |
+| `SANDBOX_SLEEP_AFTER` | Workflow input (default: `30` minutes; stored as `<minutes>m` or `never`) |
...
- - `sandbox_sleep_after`: container sleep timeout in minutes, or `never` (default: `30`)
+ - `sandbox_sleep_after`: container sleep timeout in minutes, or `never` (default: `30`)
+ (value stored as `<minutes>m` in `SANDBOX_SLEEP_AFTER`)🤖 Prompt for AI Agents
In @.github/workflows/README.md around lines 38 - 55, The README currently shows
conflicting defaults/units for SANDBOX_SLEEP_AFTER between the env table
(default: `30m`) and the usage section (default: `30` and "in minutes"); update
the Usage / Create a new instance section to match the workflow input which
expects a minutes value with an `m` suffix by: change the `sandbox_sleep_after`
bullet to state the default as `30m`, clarify the accepted format is a number
with `m` (or `never`), and ensure the `SANDBOX_SLEEP_AFTER` environment variable
description and the `sandbox_sleep_after` input description consistently use the
`m` suffix and the same default; reference the symbol SANDBOX_SLEEP_AFTER /
`sandbox_sleep_after` when editing.
| - name: Validate inputs | ||
| run: | | ||
| USERNAME="${{ inputs.username }}" | ||
| MODEL="${{ inputs.model }}" | ||
|
|
||
| if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then | ||
| echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then | ||
| echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/" | ||
| exit 1 | ||
| fi | ||
|
|
There was a problem hiding this comment.
sandbox_sleep_after 유효성 검증이 빠져 있습니다.
never 또는 숫자 분 단위만 허용하도록 검사하면 잘못된 값 저장을 막을 수 있습니다.
✅ 제안 수정 (입력값 검증 추가)
- name: Validate inputs
run: |
USERNAME="${{ inputs.username }}"
MODEL="${{ inputs.model }}"
+ SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}"
if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then
echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens."
exit 1
fi
if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then
echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/"
exit 1
fi
+
+ if [ -n "$SLEEP_AFTER" ] && ! echo "$SLEEP_AFTER" | grep -qE '^(never|[0-9]+)$'; then
+ echo "::error::Invalid sandbox_sleep_after '${SLEEP_AFTER}'. Use minutes (e.g. 30) or 'never'."
+ exit 1
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Validate inputs | |
| run: | | |
| USERNAME="${{ inputs.username }}" | |
| MODEL="${{ inputs.model }}" | |
| if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then | |
| echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens." | |
| exit 1 | |
| fi | |
| if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then | |
| echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/" | |
| exit 1 | |
| fi | |
| - name: Validate inputs | |
| run: | | |
| USERNAME="${{ inputs.username }}" | |
| MODEL="${{ inputs.model }}" | |
| SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}" | |
| if ! echo "$USERNAME" | grep -qE '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$'; then | |
| echo "::error::Invalid username '${USERNAME}'. Must be lowercase alphanumeric with optional hyphens." | |
| exit 1 | |
| fi | |
| if ! echo "$MODEL" | grep -qE '^(workers-ai|openai|bedrock)/'; then | |
| echo "::error::Invalid model format '${MODEL}'. Must start with workers-ai/, openai/, or bedrock/" | |
| exit 1 | |
| fi | |
| if [ -n "$SLEEP_AFTER" ] && ! echo "$SLEEP_AFTER" | grep -qE '^(never|[0-9]+)$'; then | |
| echo "::error::Invalid sandbox_sleep_after '${SLEEP_AFTER}'. Use minutes (e.g. 30) or 'never'." | |
| exit 1 | |
| fi | |
🤖 Prompt for AI Agents
In @.github/workflows/update-openclaw.yml around lines 31 - 45, Add validation
for the sandbox_sleep_after input by reading it into a variable (e.g.,
SANDBOX_SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}") and checking it matches
either "never" or a whole-number minute value using a regex like
'^(never|[0-9]+)$'; if it fails, emit an error with the invalid value (e.g.,
"::error::Invalid sandbox_sleep_after '${SANDBOX_SLEEP_AFTER}'. Must be 'never'
or an integer number of minutes.") and exit 1, mirroring the existing USERNAME
and MODEL checks.
| # --- Telegram (only update if provided) --- | ||
| TELEGRAM_TOKEN="${{ inputs.telegram_bot_token }}" | ||
| if [ -n "$TELEGRAM_TOKEN" ]; then | ||
| echo "$TELEGRAM_TOKEN" | npx wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME" | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "update-openclaw.yml" -type fRepository: team-michael/moltworker
Length of output: 106
🏁 Script executed:
cat -n .github/workflows/update-openclaw.yml | sed -n '80,100p'Repository: team-michael/moltworker
Length of output: 1840
🌐 Web query:
GitHub Actions workflow_dispatch input masking behavior security
💡 Result:
What gets “masked” with workflow_dispatch inputs?
workflow_dispatchinputs are not treated as secrets. They’re plain text passed in the event payload (inputs/github.event.inputs) and can be exposed if you print them (or if tooling echoes commands). GitHub’s ownworkflow_dispatchexamples show inputs being echoed directly fromgithub.event.inputs. [6]- Anyone with read access to the repo can view/download Actions logs (and therefore any leaked input values) for that run. This is especially risky for public repos. [5]
Why ::add-mask:: can still leak workflow_dispatch inputs
- GitHub recommends masking sensitive non-secret values using
::add-mask::VALUE. [2] - However, with
workflow_dispatchinputs it’s easy to leak the value before masking takes effect because the runner logs the expanded command (e.g.,Run echo "::add-mask::password"). This behavior is documented in runner issues and reproductions. [3] - Similar “echoed before masking” behavior has been reported more generally with
add-mask. [4]
Security guidance / best practices
- Don’t use
workflow_dispatchinputs for secrets (tokens, passwords, private keys). Use GitHub Secrets (repo/org/environment) instead, which are designed for secure storage and automatic redaction behavior. [1][2] - If you must accept sensitive-ish data (e.g., PII like an email) via
workflow_dispatch, mask defensively and avoid expression interpolation in the masking command. A common mitigation is: read the value from the event JSON ($GITHUB_EVENT_PATH), thenadd-maskit, then only use the masked env var thereafter (so the raw value never appears in the “Run …” line). This approach is described by community tooling that exists specifically because inputs are otherwise visible/leaky. [7] - Treat
workflow_dispatchinputs as untrusted user input (potential for script/command injection if you splice them into shell). Prefer passing viaenv:and quoting, rather than inline string concatenation. [8]
Bottom line: workflow_dispatch inputs are for parameters, not secrets. If it must be sensitive, use Secrets; if it must be an input, assume it can leak unless you implement careful masking and safe handling. [1][3][5]
Telegram 토큰 마스킹 처리 필요합니다.
workflow_dispatch 입력은 기본적으로 마스킹되지 않아 워크플로우 로그에 노출될 위험이 있습니다. 현재 코드는 ${{ inputs.telegram_bot_token }}이 직접 expand되어 로그에 기록될 수 있습니다. 토큰을 env: 섹션으로 주입하고 초기에 ::add-mask::로 처리하여 로그 노출을 방지해야 합니다.
제안 수정
- name: Update worker secrets
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
+ TELEGRAM_TOKEN: ${{ inputs.telegram_bot_token }}
run: |
+ if [ -n "${TELEGRAM_TOKEN}" ]; then
+ echo "::add-mask::${TELEGRAM_TOKEN}"
+ fi
WORKER_NAME="openclaw-${{ inputs.username }}"
@@
# --- Telegram (only update if provided) ---
- TELEGRAM_TOKEN="${{ inputs.telegram_bot_token }}"
if [ -n "$TELEGRAM_TOKEN" ]; then
echo "$TELEGRAM_TOKEN" | npx wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME"
fi🤖 Prompt for AI Agents
In @.github/workflows/update-openclaw.yml around lines 88 - 92, The
TELEGRAM_TOKEN is being expanded directly from `${{ inputs.telegram_bot_token
}}` which can expose the secret in workflow logs; instead, set TELEGRAM_TOKEN
via the job's env: from the workflow input and immediately mask it using the
actions core mask mechanism (emit `::add-mask::${TELEGRAM_TOKEN}`) before any
echo/usage, then use that masked env var in the conditional and the `npx
wrangler secret put TELEGRAM_BOT_TOKEN --name "$WORKER_NAME"` call (referencing
TELEGRAM_TOKEN and WORKER_NAME) so the token is never printed unmasked in logs.
| - name: Deployment summary | ||
| run: | | ||
| cat >> "$GITHUB_STEP_SUMMARY" << EOF | ||
| ## OpenClaw Updated | ||
|
|
||
| | Item | Value | | ||
| |------|-------| | ||
| | **Worker** | \`openclaw-${{ inputs.username }}\` | | ||
| | **Model** | \`${{ inputs.model }}\` | | ||
| | **Telegram** | ${{ inputs.telegram_bot_token && 'Updated' || 'Unchanged (no token provided)' }} | | ||
| | **Sleep After** | ${{ inputs.sandbox_sleep_after && format('{0}m', inputs.sandbox_sleep_after) || 'Unchanged' }} | | ||
| | **Gateway Token** | Unchanged (not rotated) | |
There was a problem hiding this comment.
요약 출력이 never일 때 잘못 표시됩니다.
현재는 never 입력에도 neverm으로 표기됩니다.
🧾 제안 수정 (요약 값 정규화)
- name: Deployment summary
run: |
+ SLEEP_AFTER="${{ inputs.sandbox_sleep_after }}"
+ if [ -z "$SLEEP_AFTER" ]; then
+ SLEEP_DISPLAY="Unchanged"
+ elif [ "$SLEEP_AFTER" = "never" ]; then
+ SLEEP_DISPLAY="never"
+ else
+ SLEEP_DISPLAY="${SLEEP_AFTER}m"
+ fi
cat >> "$GITHUB_STEP_SUMMARY" << EOF
## OpenClaw Updated
@@
- | **Sleep After** | ${{ inputs.sandbox_sleep_after && format('{0}m', inputs.sandbox_sleep_after) || 'Unchanged' }} |
+ | **Sleep After** | ${SLEEP_DISPLAY} |
| **Gateway Token** | Unchanged (not rotated) |
EOF🤖 Prompt for AI Agents
In @.github/workflows/update-openclaw.yml around lines 116 - 127, The "**Sleep
After**" summary uses the expression that blindly formats
inputs.sandbox_sleep_after with format('{0}m', inputs.sandbox_sleep_after), so
when the input is the literal "never" it becomes "neverm"; change the logic in
the "Deployment summary" step to normalize the value first (e.g., check if
inputs.sandbox_sleep_after == 'never' and output 'never' or a friendly label,
else if numeric format as '{0}m', else fall back to 'Unchanged') so the "**Sleep
After**" line no longer concatenates "m" to the literal "never".




Summary
.dev.vars.exampleto match upstream to prevent merge conflictsWhat's included
Workflows
openclaw-<username>worker with dedicated R2 bucket, auto-generated gateway token, CDP secret, and all required secretsConfiguration
never)Upstream compatibility
.dev.vars.examplekept identical to upstream (diff = 0).github/workflows/git merge upstream/mainFiles changed
.github/workflows/create-new-openclaw.yml.github/workflows/update-openclaw.yml.github/workflows/delete-openclaw.yml.github/workflows/README.mdTest plan
git diff upstream/main -- .dev.vars.examplereturns emptySummary by CodeRabbit
문서
새 기능