Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fe47bb2
fix: proxy and expose the MCP Apps sandbox port
stubbi Oct 3, 2026
e74a988
docs: document the MCP Apps sandbox port
stubbi Oct 3, 2026
6b27b4c
docs: list the MCP Apps sandbox port in the network policy overview
stubbi Oct 3, 2026
70630ee
test: add MCP Apps sandbox e2e test
stubbi Oct 3, 2026
9a3dca4
fix: skip init-data-owner under restricted PSS
stubbi Oct 4, 2026
a44d36a
fix: add restricted PSS namespace helper
stubbi Oct 4, 2026
0881cd1
fix: make storage.fixOwnership tri-state
stubbi Oct 4, 2026
c09fc61
chore: sync OLM bundle CRD and RBAC
stubbi Oct 4, 2026
4a04d44
chore: grant namespaces get in chart RBAC
stubbi Oct 4, 2026
78c3762
chore: sync Helm chart CRD
stubbi Oct 4, 2026
c1b0995
fix: wire the uncached API reader
stubbi Oct 4, 2026
e6b0b04
chore: regenerate CRD
stubbi Oct 4, 2026
3f34343
chore: regenerate manager role
stubbi Oct 4, 2026
e7d9f6e
docs: regenerate API reference
stubbi Oct 4, 2026
f4df94f
docs: describe fixOwnership under restricted PSS
stubbi Oct 4, 2026
2123dd2
Merge branch 'main' into stubbi-patch-1
stubbi Oct 4, 2026
ec4e9f3
test(e2e): dump pod diagnostics on readiness
stubbi Oct 4, 2026
7580f62
test(e2e): iterate statuses by index
stubbi Oct 4, 2026
eae70d0
fix: requeue instead of guessing when the namespace cannot be read
stubbi Oct 11, 2026
2b514fa
fix: give the main container a sticky /tmp
stubbi Oct 4, 2026
4cfd917
fix: reserve the init-tmp-dir container name
stubbi Oct 4, 2026
ff95716
test(e2e): cover the sticky /tmp mount
stubbi Oct 4, 2026
3bf8c00
docs: describe the sticky /tmp init container
stubbi Oct 4, 2026
a4bbc03
fix: authorize namespace reads in watched-namespace installs (#621)
jtatum Oct 11, 2026
a903cf7
fix: target the effective MCP Apps sandbox port and only expose it wh…
stubbi Oct 11, 2026
db2c419
fix: replace a dangling uv symlink instead of failing init-uv
stubbi Oct 11, 2026
1d7b480
test: use bytes.Equal in init-uv script test
stubbi Oct 11, 2026
cb9a1e8
Merge remote-tracking branch 'origin/main' into fix/data-owner-pss-aware
stubbi Oct 11, 2026
f08bccc
test: only flag unrewritten /data paths in the init-uv script test
stubbi Oct 11, 2026
4308c1b
fix: run init-tmp-dir as the gateway's effective UID
stubbi Oct 11, 2026
75c77a4
Merge remote-tracking branch 'origin/fix/sticky-main-tmp' into test/e…
stubbi Oct 11, 2026
0e0a9b1
Merge branch 'test/e2e-pod-diagnostics' into fix/data-owner-pss-aware
stubbi Oct 11, 2026
a229303
Merge branch 'fix/data-owner-pss-aware' into stubbi-patch-1
stubbi Oct 11, 2026
b52515f
Merge branch 'stubbi-patch-1' into fix/init-uv-dangling-symlink
stubbi Oct 11, 2026
0263840
ci: keep the release pipeline working after the repository transfer
stubbi Oct 11, 2026
0c8c0ad
Merge remote-tracking branch 'origin/main' into fix/sticky-main-tmp
stubbi Oct 11, 2026
3f8d0a4
Merge branch 'fix/sticky-main-tmp' into ci/release-after-transfer
stubbi Oct 11, 2026
ea37017
Merge branch 'ci/release-after-transfer' into test/e2e-pod-diagnostics
stubbi Oct 11, 2026
e9d0948
Merge branch 'test/e2e-pod-diagnostics' into fix/data-owner-pss-aware
stubbi Oct 11, 2026
143f110
Merge branch 'fix/data-owner-pss-aware' into stubbi-patch-1
stubbi Oct 11, 2026
2391dc5
Merge branch 'stubbi-patch-1' into fix/init-uv-dangling-symlink
stubbi Oct 11, 2026
360e461
fix: name the MCP Apps config helper results for gocritic
stubbi Oct 11, 2026
2e37f7f
Merge branch 'stubbi-patch-1' into fix/init-uv-dangling-symlink
stubbi Oct 11, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}

- uses: azure/setup-helm@v4

- uses: actions/setup-node@v4
with:
node-version: '22'
Expand Down
38 changes: 32 additions & 6 deletions .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,43 @@ permissions:
jobs:
release-please:
runs-on: ubuntu-latest
# Only needed by the GITHUB_TOKEN fallback below; the App token and the
# PAT carry their own permissions.
permissions:
contents: write
pull-requests: write
actions: write
env:
# Credential precedence: GitHub App token > RELEASE_PLEASE_TOKEN PAT >
# GITHUB_TOKEN. Events created with GITHUB_TOKEN do not trigger other
# workflows, so with the fallback the release PR gets no CI run and the
# tag does not start release.yaml on its own (dispatched explicitly below).
HAS_APP: ${{ vars.RELEASE_BOT_APP_ID != '' }}
HAS_PAT: ${{ secrets.RELEASE_PLEASE_TOKEN != '' }}
steps:
# Mint a short-lived token (~1h) from the paperclip-release-bot GitHub App.
# Avoids long-lived PATs that the paperclipinc org policy now blocks.
# Mint a short-lived token (~1h) from the release-bot GitHub App.
# Skipped when the App is not configured for this repository (e.g. right
# after a repository transfer, see #625) instead of failing the run.
- uses: actions/create-github-app-token@v1
id: app-token
if: vars.RELEASE_BOT_APP_ID != ''
with:
app-id: ${{ vars.RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}

- name: Warn about fallback credentials
if: env.HAS_APP != 'true' && env.HAS_PAT != 'true'
run: |
echo "::warning::Neither vars.RELEASE_BOT_APP_ID nor secrets.RELEASE_PLEASE_TOKEN is set; using GITHUB_TOKEN. The release PR will not get CI runs automatically (push an empty commit to it or close/reopen it), and release.yaml is dispatched explicitly after tagging."

- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
# App token makes the tag push appear as a user action,
# which triggers downstream release.yaml. GITHUB_TOKEN cannot.
token: ${{ steps.app-token.outputs.token }}
token: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || github.token }}

# skip-github-release is set in release-please-config.json so that
# release-please does NOT create a GitHub release (which would
Expand All @@ -36,7 +56,7 @@ jobs:
# tag manually below, and flip stale labels in a separate step.
- name: Create release tag
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || github.token }}
run: |
# Detect if the current push merged a release-please PR.
# Check both the commit message (squash merge) and the merged
Expand Down Expand Up @@ -67,7 +87,13 @@ jobs:
gh api "repos/${{ github.repository }}/git/refs" \
-f ref="refs/tags/${TAG}" \
-f sha="${{ github.sha }}"
echo "Tag ${TAG} created - release workflow will trigger"
echo "Tag ${TAG} created"
if [[ "${HAS_APP}" != "true" && "${HAS_PAT}" != "true" ]]; then
# A tag pushed with GITHUB_TOKEN does not fire release.yaml's
# push trigger, but a workflow_dispatch from GITHUB_TOKEN does.
echo "Dispatching release.yaml for ${TAG}"
gh workflow run release.yaml --repo "${{ github.repository }}" --ref "${TAG}"
fi
fi
else
echo "Not a release commit, skipping tag creation"
Expand All @@ -81,7 +107,7 @@ jobs:
# label gets fixed even if the tag step above was skipped or failed.
- name: Fix stale autorelease labels
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || github.token }}
run: |
for STATE in merged closed; do
STALE_PRS=$(gh pr list \
Expand Down
73 changes: 55 additions & 18 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,44 @@ on:
push:
tags:
- 'v*'
# Release Please dispatches this with --ref <tag> when it had to fall back to
# GITHUB_TOKEN, whose tag pushes do not fire the push trigger above. Run it
# manually the same way: gh workflow run release.yaml --ref vX.Y.Z
workflow_dispatch:

env:
REGISTRY: ghcr.io
# Single source of truth for the GHCR namespace the image and the chart are
# published to. It must match the chart's default image.repository (guarded
# by hack/check-chart-image-repository.sh), the bundle CSV and
# operatorhub.yaml, so it is pinned rather than derived from the repository
# owner (which changed when the repository moved to stubbi, see #625).
IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE || 'paperclipinc' }}

jobs:
release:
name: Release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
packages: write
id-token: write # For signing
actions: write # GITHUB_TOKEN fallback dispatches operatorhub.yaml
env:
HAS_APP: ${{ vars.RELEASE_BOT_APP_ID != '' }}
HAS_PAT: ${{ secrets.RELEASE_PLEASE_TOKEN != '' }}

steps:
# Mint a short-lived App installation token to use for actions that
# need to trigger downstream workflows (release.yaml's events from
# GITHUB_TOKEN don't fan-out). Replaces the long-lived
# RELEASE_PLEASE_TOKEN PAT that was previously stored as a secret.
# Skipped when the App is not configured (see #625); the publish step
# then falls back to RELEASE_PLEASE_TOKEN or GITHUB_TOKEN.
- uses: actions/create-github-app-token@v1
id: app-token
if: vars.RELEASE_BOT_APP_ID != ''
with:
app-id: ${{ vars.RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
Expand All @@ -45,25 +63,37 @@ jobs:
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# GITHUB_TOKEN can only push to packages this repository owns or
# has been granted access to. To publish into a namespace owned by
# another account (ghcr.io/paperclipinc after the transfer), set
# GHCR_PUSH_TOKEN to a PAT with write:packages from a member of that
# namespace and GHCR_PUSH_USER to its login.
username: ${{ vars.GHCR_PUSH_USER || github.actor }}
password: ${{ secrets.GHCR_PUSH_TOKEN || secrets.GITHUB_TOKEN }}

- name: Install Cosign
uses: sigstore/cosign-installer@v3

- name: Check image namespace matches the chart default
run: |
CHART_REPO=$(yq '.image.repository' charts/openclaw-operator/values.yaml)
if [ "${CHART_REPO}" != "ghcr.io/${IMAGE_NAMESPACE}/openclaw-operator" ]; then
echo "::error::IMAGE_NAMESPACE=${IMAGE_NAMESPACE} but the chart defaults to ${CHART_REPO}; publishing would ship a chart pointing at an image that was never pushed (#536)."
exit 1
fi

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# IMAGE_NAMESPACE (workflow env) is read by .goreleaser.yaml.

- name: Docker image name
id: docker-image
run: |
OWNER="${GITHUB_REPOSITORY_OWNER,,}"
echo "name=${{ env.REGISTRY }}/${OWNER}/openclaw-operator" >> $GITHUB_OUTPUT
run: echo "name=${{ env.REGISTRY }}/${IMAGE_NAMESPACE}/openclaw-operator" >> "$GITHUB_OUTPUT"

- name: Sign container images
run: |
Expand Down Expand Up @@ -107,11 +137,17 @@ jobs:

- name: Publish release
env:
# PAT required: events from GITHUB_TOKEN don't trigger other
# workflows. Using PAT ensures the release:published event
# fires the OperatorHub submission workflow.
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh release edit "${{ github.ref_name }}" --draft=false
# Events from GITHUB_TOKEN don't trigger other workflows. The App
# token (or PAT) makes release:published fire the OperatorHub
# submission workflow; with the GITHUB_TOKEN fallback it is
# dispatched explicitly instead.
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
run: |
gh release edit "${{ github.ref_name }}" --draft=false
if [[ "${HAS_APP}" != "true" && "${HAS_PAT}" != "true" ]]; then
echo "::warning::Published with GITHUB_TOKEN; dispatching operatorhub.yaml explicitly."
gh workflow run operatorhub.yaml --repo "${{ github.repository }}" -f tag="${{ github.ref_name }}"
fi

helm-release:
name: Helm Chart Release
Expand All @@ -126,10 +162,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Lowercase owner name
id: owner
run: echo "name=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT

- name: Install Helm
uses: azure/setup-helm@v4

Expand All @@ -138,19 +170,24 @@ jobs:

- name: Extract version
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# GITHUB_TOKEN can only push to packages this repository owns or
# has been granted access to. To publish into a namespace owned by
# another account (ghcr.io/paperclipinc after the transfer), set
# GHCR_PUSH_TOKEN to a PAT with write:packages from a member of that
# namespace and GHCR_PUSH_USER to its login.
username: ${{ vars.GHCR_PUSH_USER || github.actor }}
password: ${{ secrets.GHCR_PUSH_TOKEN || secrets.GITHUB_TOKEN }}

- name: Package, push, and sign Helm chart
run: |
VERSION="${{ steps.version.outputs.version }}"
OWNER="${{ steps.owner.outputs.name }}"
OWNER="${IMAGE_NAMESPACE}"
helm package charts/openclaw-operator --version "${VERSION}" --app-version "${VERSION}"
PUSH_OUT=$(helm push "openclaw-operator-${VERSION}.tgz" "oci://ghcr.io/${OWNER}/charts" 2>&1)
echo "${PUSH_OUT}"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/verify-signing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
id: verify
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE || 'paperclipinc' }}
run: |
set +e
make verify-signing
Expand Down
5 changes: 3 additions & 2 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ dockers_v2:
ids:
- manager
images:
- "ghcr.io/paperclipinc/openclaw-operator"
# Set by release.yaml (vars.IMAGE_NAMESPACE, default paperclipinc).
- 'ghcr.io/{{ envOrDefault "IMAGE_NAMESPACE" "paperclipinc" }}/openclaw-operator'
tags:
- "{{ .Tag }}"
- "{{ .Major }}.{{ .Minor }}"
Expand Down Expand Up @@ -70,5 +71,5 @@ release:
draft: true
replace_existing_artifacts: true
github:
owner: paperclipinc
owner: stubbi
name: openclaw-operator
9 changes: 7 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,13 @@ Automated via release-please + GoReleaser:
- `.release-please-manifest.json` — tracks current version
- `.goreleaser.yaml` — `release.draft: true` (immutable releases require draft→publish flow)

**Secrets required:**
- `RELEASE_PLEASE_TOKEN` — classic PAT with `repo` scope; used for tag creation, release publishing, and OperatorHub cross-repo PRs
**Credentials (repository settings):**
- `vars.RELEASE_BOT_APP_ID` + `secrets.RELEASE_BOT_PRIVATE_KEY` — GitHub App used by release-please and for publishing the release, so the tag push and `release: published` trigger `release.yaml` and `operatorhub.yaml`. Preferred.
- `secrets.RELEASE_PLEASE_TOKEN` — PAT fallback when the App is not configured.
- Neither set: both workflows fall back to `GITHUB_TOKEN` and dispatch `release.yaml` / `operatorhub.yaml` explicitly. The release PR then gets no automatic CI run (push an empty commit to it).
- `vars.IMAGE_NAMESPACE` (default `paperclipinc`) — GHCR namespace for the image and the chart. Must match the chart's default `image.repository` (`release.yaml` fails early otherwise).
- `secrets.GHCR_PUSH_TOKEN` + `vars.GHCR_PUSH_USER` — needed when `IMAGE_NAMESPACE` is owned by another account than the repository (the repository moved to `stubbi`; the images still live under `paperclipinc`).
- `secrets.OPERATORHUB_FORK_TOKEN` — PAT for the OperatorHub cross-repo PRs.

**Manual trigger:** `gh workflow run "OperatorHub Submission" -f tag=vX.Y.Z`

Expand Down
13 changes: 9 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,22 @@ conformance-gitops: ## Run the GitOps coexistence conformance category.
conformance-failure: ## Run the failure-modes conformance category.
cd test/conformance && go test -v -timeout 20m -ginkgo.v -ginkgo.focus="failure modes" ./...

# Releases signed before the repository moved from paperclipinc to stubbi carry
# the old workflow identity, later ones the new one; accept both.
SIGNING_IDENTITY_REGEXP ?= ^https://github.com/(paperclipinc|stubbi)/openclaw-operator/.github/workflows/.*
IMAGE_NAMESPACE ?= paperclipinc

.PHONY: verify-signing
verify-signing: ## Verify the latest published release is Cosign-signed and SBOM-attested.
@VERSION=$$(gh release view --repo paperclipinc/openclaw-operator --json tagName --jq .tagName); \
IMAGE="ghcr.io/paperclipinc/openclaw-operator:$${VERSION}"; \
@VERSION=$$(gh release view --repo stubbi/openclaw-operator --json tagName --jq .tagName); \
IMAGE="ghcr.io/$(IMAGE_NAMESPACE)/openclaw-operator:$${VERSION}"; \
echo "Verifying $${IMAGE}..."; \
cosign verify "$${IMAGE}" \
--certificate-identity-regexp 'https://github.com/paperclipinc/openclaw-operator/.github/workflows/.*' \
--certificate-identity-regexp '$(SIGNING_IDENTITY_REGEXP)' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com >/dev/null || { echo "::error::signature verification failed for $${IMAGE}"; exit 1; }; \
echo "Verifying SBOM attestation..."; \
cosign verify-attestation "$${IMAGE}" --type spdxjson \
--certificate-identity-regexp 'https://github.com/paperclipinc/openclaw-operator/.github/workflows/.*' \
--certificate-identity-regexp '$(SIGNING_IDENTITY_REGEXP)' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com >/dev/null || { echo "::error::SBOM attestation verification failed for $${IMAGE}"; exit 1; }; \
echo "OK: $${IMAGE} is signed and SBOM-attested."

Expand Down
Loading
Loading