Skip to content

fea: Add OpenClaw management workflows#1

Merged
pitzcarraldo merged 5 commits into
mainfrom
feat/workflow
Feb 13, 2026
Merged

fea: Add OpenClaw management workflows#1
pitzcarraldo merged 5 commits into
mainfrom
feat/workflow

Conversation

@pitzcarraldo
Copy link
Copy Markdown

@pitzcarraldo pitzcarraldo commented Feb 13, 2026

Summary

  • Add GitHub Actions workflows for managing multi-user OpenClaw instances (create, update, delete)
  • Add workflow README with required secrets, usage guide, and Telegram bot token setup instructions
  • Restore .dev.vars.example to match upstream to prevent merge conflicts
  • All new functionality lives in new files only — zero modifications to upstream code

What's included

Workflows

  • Create New OpenClaw — provisions openclaw-<username> worker with dedicated R2 bucket, auto-generated gateway token, CDP secret, and all required secrets
  • Update OpenClaw — re-deploys code and updates secrets (model, telegram token, sleep timeout)
  • Delete OpenClaw — tears down worker and optionally deletes R2 bucket

Configuration

  • 8 required GitHub repository secrets (Cloudflare API, AI Gateway, R2, CF Access)
  • Auto-managed secrets: gateway token, CDP secret, worker URL, R2 bucket name, sleep timeout
  • Configurable container sleep timeout (default 30m, supports never)
  • CDP browser automation enabled by default

Upstream compatibility

  • .dev.vars.example kept identical to upstream (diff = 0)
  • All changes are new files under .github/workflows/
  • No merge conflicts expected on git merge upstream/main

Files changed

File Status
.github/workflows/create-new-openclaw.yml New
.github/workflows/update-openclaw.yml New
.github/workflows/delete-openclaw.yml New
.github/workflows/README.md New

Test plan

  • Verify git diff upstream/main -- .dev.vars.example returns empty
  • Run Create New OpenClaw workflow with test username
  • Verify worker deploys and secrets are set
  • Run Update OpenClaw workflow to change model
  • Run Delete OpenClaw workflow to clean up

Summary by CodeRabbit

  • 문서

    • OpenClaw 관리 워크플로우 설명서 추가
  • 새 기능

    • 사용자별 OpenClaw 인스턴스를 자동으로 생성하는 워크플로우 추가
    • 기존 인스턴스의 모델 및 설정을 업데이트하는 워크플로우 추가
    • OpenClaw 인스턴스 및 관련 리소스를 삭제하는 워크플로우 추가

claude and others added 5 commits February 13, 2026 03:13
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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 13, 2026

Walkthrough

OpenClaw 인스턴스 관리를 위한 세 개의 새로운 GitHub Actions 워크플로우와 문서를 추가합니다. 워크플로우는 사용자명, 모델, Telegram 봇 토큰 등의 입력을 받아 검증한 후 Cloudflare Workers와 R2 버킷을 프로비저닝, 업데이트, 삭제합니다.

Changes

Cohort / File(s) Summary
워크플로우 문서
.github/workflows/README.md
OpenClaw 관리 워크플로우 세 가지(생성, 업데이트, 삭제)에 대한 문서. 필수 GitHub 시크릿 8개, 자동 관리 시크릿 8개, 사용 지침, Telegram 봇 토큰 생성 방법을 포함합니다.
인스턴스 생성 워크플로우
.github/workflows/create-new-openclaw.yml
사용자별 OpenClaw 인스턴스를 프로비저닝하는 워크플로우. 입력 검증, Node.js 설정, wrangler 구성, R2 버킷 생성, 토큰 생성, 워커 배포, 워커 시크릿 설정, 배포 요약 출력을 수행합니다.
인스턴스 삭제 워크플로우
.github/workflows/delete-openclaw.yml
사용자별 OpenClaw 인스턴스를 삭제하는 워크플로우. 입력 검증, wrangler를 통한 워커 삭제, 선택적 R2 버킷 삭제, 삭제 요약 보고를 포함합니다.
인스턴스 업데이트 워크플로우
.github/workflows/update-openclaw.yml
기존 OpenClaw 인스턴스의 설정을 업데이트하는 워크플로우. 입력 검증, wrangler 구성, 워커 배포, 시크릿 업데이트(모델, Telegram 토큰, 타임아웃 등)를 수행합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding GitHub Actions workflows for OpenClaw management (create, update, delete).
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/workflow

Comment @coderabbitai help to get the list of available commands and usage tips.

@pitzcarraldo pitzcarraldo changed the title Add OpenClaw management workflows fea: Add OpenClaw management workflows Feb 13, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@pitzcarraldo pitzcarraldo merged commit 0ef40c7 into main Feb 13, 2026
1 of 7 checks passed
@pitzcarraldo pitzcarraldo deleted the feat/workflow branch February 13, 2026 05:46
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +32 to +45
- 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +91 to +109
- 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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GitHub Actions workflow_dispatch inputs masking default behavior secrets logs

💡 Result:

  • workflow_dispatch inputs are not secrets by default. They’re exposed via the inputs / github.event.inputs contexts 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 secrets context, including GITHUB_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_dispatch inputs + add-mask: expression expansion can leak the value. If you do echo "::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_dispatch inputs—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 then add-mask the 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:


🏁 Script executed:

# Find and examine the workflow file
find . -name "create-new-openclaw.yml" -type f

Repository: 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 -n

Repository: 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.

Comment on lines +136 to +148
- 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) |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

요약 출력이 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).

Comment on lines +38 to +55
| `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`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +31 to +45
- 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
- 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.

Comment on lines +88 to +92
# --- 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "update-openclaw.yml" -type f

Repository: 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_dispatch inputs 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 own workflow_dispatch examples show inputs being echoed directly from github.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_dispatch inputs 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_dispatch inputs 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), then add-mask it, 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_dispatch inputs as untrusted user input (potential for script/command injection if you splice them into shell). Prefer passing via env: 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.

Comment on lines +116 to +127
- 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) |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

요약 출력이 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".

@github-actions
Copy link
Copy Markdown

E2E Test Recording (base)

❌ Tests failed

E2E Test Video

@github-actions
Copy link
Copy Markdown

E2E Test Recording (telegram)

❌ Tests failed

E2E Test Video

@github-actions
Copy link
Copy Markdown

E2E Test Recording (workers-ai)

❌ Tests failed

E2E Test Video

@github-actions
Copy link
Copy Markdown

E2E Test Recording (discord)

❌ Tests failed

E2E Test Video

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants