Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6533eea3f0 | |||
| 363c757bba | |||
| 3956518741 | |||
| 71bb10adce | |||
| 21baf112b9 | |||
| e6ebd23442 | |||
| d3d0c2d8e9 | |||
| d965413a9d | |||
| 29e4527745 | |||
| f2d5d296e2 | |||
| afc44ebbf8 | |||
| 1ef1c0e456 | |||
| f182211601 | |||
| 5d46d3ada0 | |||
| e98dd8b0a2 | |||
| 90a9aad15d | |||
| 4f4b4286dc | |||
| 1cc2ec2e92 | |||
| b3cc9b0975 | |||
| a6f19e23c6 | |||
| ec41528f1e | |||
| 23f16bc7a8 | |||
| 229d8e5389 | |||
| 69983fa321 | |||
| 9c2bc1223f | |||
| a31c259909 | |||
| 2230aae540 | |||
| 037d4d8994 | |||
| 55ee4b27be |
@@ -1,6 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: pieced-operator
|
||||
description: PieCed IT tenant lifecycle operator
|
||||
version: 0.1.23
|
||||
appVersion: "0.1.23"
|
||||
version: 0.1.52
|
||||
appVersion: "0.1.52"
|
||||
type: application
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# The package catalog is deployed as a ConfigMap in the operator namespace.
|
||||
# To update packages, edit the catalog data below and upgrade the Helm release.
|
||||
#
|
||||
# Categories:
|
||||
# - core — toggles platform-level OpenClaw behaviour (heartbeat, cron,
|
||||
# active-memory, voice) via config_patch. No channel bindings,
|
||||
# no skills.
|
||||
# - channel — adds a messaging channel (Telegram, Discord, …).
|
||||
# - skill — adds a ClawHub or pack: skill ref to the OpenClawInstance.
|
||||
#
|
||||
# Quiet hours are not exposed as a separate package — in OpenClaw they live
|
||||
# under the heartbeat config (via the active-window setting and via
|
||||
# HEARTBEAT.md content rules). When we expose a tenant-tunable time range
|
||||
# in the portal, it will become additional fields on core-heartbeat rather
|
||||
# than its own package.
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
@@ -10,6 +23,185 @@ metadata:
|
||||
data:
|
||||
catalog.yaml: |
|
||||
packages:
|
||||
|
||||
# =====================================================================
|
||||
# CORE — platform behaviour toggles. Patched into OCI config.raw via
|
||||
# config_patch (deep-merged on top of the operator's safe defaults).
|
||||
# =====================================================================
|
||||
|
||||
core-heartbeat:
|
||||
name: Heartbeat (Proactive Checks)
|
||||
category: core
|
||||
description: >
|
||||
Periodic agent run (default every 30 min) that lets the assistant
|
||||
check inbox, calendar, and other configured sources and message
|
||||
you proactively when something needs attention. Without this, the
|
||||
assistant only responds when you message it first.
|
||||
# OpenClaw 2026.x ships heartbeat ON by default (30m, or 1h on
|
||||
# Anthropic OAuth) when agents.defaults.heartbeat is absent —
|
||||
# see issues openclaw/openclaw#64293, #19445, #8123. The builder
|
||||
# accordingly writes `every: "0m"` (the known off-switch) into
|
||||
# the baseline configRaw. core-heartbeat's config_patch below
|
||||
# overlays the 30m cadence on top of that. The actual checklist
|
||||
# the heartbeat reads lives in workspace HEARTBEAT.md (seeded
|
||||
# separately via spec.workspaceFiles); without a HEARTBEAT.md
|
||||
# the heartbeat fires harmlessly and replies HEARTBEAT_OK.
|
||||
#
|
||||
# Quiet hours: OpenClaw supports both a config-level activeHours
|
||||
# window under agents.defaults.heartbeat (skipped outside the
|
||||
# window) and in-content rules inside HEARTBEAT.md. Neither is
|
||||
# exposed in the portal yet — when added, they become extra
|
||||
# fields on this package, not a separate core-quiet-hours
|
||||
# package.
|
||||
config_patch:
|
||||
agents:
|
||||
defaults:
|
||||
heartbeat:
|
||||
every: "30m"
|
||||
|
||||
core-cron:
|
||||
name: Scheduled Tasks (Cron)
|
||||
category: core
|
||||
description: >
|
||||
Allow the assistant to run scheduled tasks (daily briefings,
|
||||
recurring reminders, periodic reports). Off by default. When
|
||||
off, the agent's cron tool stays available but no scheduled
|
||||
job ever fires.
|
||||
# Flips the cron scheduler on. Default base configRaw sets
|
||||
# cron.enabled=false (see builder.go), so this overlay only
|
||||
# writes true when the package is enabled. Job storage lives on
|
||||
# the tenant PVC at ~/.openclaw/cron/jobs.json by default.
|
||||
config_patch:
|
||||
cron:
|
||||
enabled: true
|
||||
|
||||
core-active-memory:
|
||||
name: Active Memory
|
||||
category: core
|
||||
description: >
|
||||
Lets the assistant recall stable preferences, recurring habits,
|
||||
and long-term context from past conversations during a chat.
|
||||
Uses an extra sub-agent turn per inbound message to query the
|
||||
memory store. Direct-message sessions only — group and channel
|
||||
sessions stay deterministic. Trades a small amount of token
|
||||
cost for continuity and personalisation.
|
||||
# OpenClaw 2026.5.x ships Active Memory as a plugin under
|
||||
# plugins.entries.active-memory with a two-gate activation model:
|
||||
# (1) the plugin must be enabled, (2) the request must be an
|
||||
# eligible direct-chat session. Scoped to "main" agent and
|
||||
# "direct" chat types for safe-default behaviour. The recall
|
||||
# model inherits the session's chat model when available; the
|
||||
# modelFallback is used only when nothing else resolves and
|
||||
# should be present in LiteLLM. Adjust as needed for the
|
||||
# platform's default cheap model.
|
||||
config_patch:
|
||||
plugins:
|
||||
entries:
|
||||
active-memory:
|
||||
enabled: true
|
||||
config:
|
||||
enabled: true
|
||||
agents: ["main"]
|
||||
allowedChatTypes: ["direct"]
|
||||
modelFallback: "pieced-mini"
|
||||
queryMode: "recent"
|
||||
promptStyle: "balanced"
|
||||
timeoutMs: 15000
|
||||
maxSummaryChars: 220
|
||||
persistTranscripts: false
|
||||
logging: false
|
||||
|
||||
core-voice:
|
||||
name: Voice Interaction
|
||||
category: core
|
||||
description: >
|
||||
Speech-to-text on incoming voice notes, automatic text-to-speech
|
||||
on replies, and interactive Talk mode. Audio is routed through
|
||||
PieCed's LiteLLM gateway so STT/TTS spend is tracked per-tenant
|
||||
alongside chat usage. Inbound TTS uses kani-tts; Talk mode uses
|
||||
kokoro-fastapi; STT uses self-hosted Whisper (faster-whisper-
|
||||
large-v3). All three are private to the cluster.
|
||||
# PHASE B — wired in. Toggling this package now installs the full
|
||||
# voice surface into the tenant's OpenClawInstance config:
|
||||
#
|
||||
# - messages.tts: auto TTS on inbound replies, routed to
|
||||
# `pieced-tts-inbound` (kani-tts behind LiteLLM).
|
||||
# - talk: interactive Talk mode, routed to `pieced-tts-talk`
|
||||
# (kokoro-fastapi behind LiteLLM). `interruptOnSpeech: true`
|
||||
# so the agent stops talking when the user starts talking.
|
||||
# - tools.media.audio: STT for inbound voice notes, capped at
|
||||
# 20 MiB per message, routed to `pieced-stt` (whisper-openai
|
||||
# behind LiteLLM).
|
||||
#
|
||||
# Provider config notes
|
||||
# ---------------------
|
||||
# `models.providers.openai` is declared here with no chat models
|
||||
# (`models: []`) — its only role is to give the STT block under
|
||||
# `tools.media.audio` a place to resolve its apiKey/baseUrl from.
|
||||
# The agent's primary chat model still lives under
|
||||
# `models.providers.litellm` (set in builder.go base) and is
|
||||
# unaffected by this patch — deep-merge adds `openai` as a
|
||||
# sibling provider, not a replacement.
|
||||
#
|
||||
# `allowPrivateNetwork: true` on the openai provider is required
|
||||
# because the LiteLLM endpoint is a `http://*.svc` private-
|
||||
# network address. Without it OpenClaw refuses the outbound
|
||||
# call as a private-network destination.
|
||||
#
|
||||
# `${LITELLM_API_KEY}` is supplied via the tenant's envFrom
|
||||
# secretRef (see builder.go), populated by ESO from
|
||||
# secret/data/tenants/<ns>/litellm.
|
||||
#
|
||||
# Network policy
|
||||
# --------------
|
||||
# Audio traffic shares the existing LiteLLM egress hole in the
|
||||
# per-tenant CiliumNetworkPolicy (toEndpoints inference ns,
|
||||
# port 4000). No additional CNP rule needed — voice routes
|
||||
# through the same gateway as chat completions.
|
||||
config_patch:
|
||||
models:
|
||||
providers:
|
||||
openai:
|
||||
apiKey: "${LITELLM_API_KEY}"
|
||||
baseUrl: "http://litellm.inference.svc:4000/v1"
|
||||
models: []
|
||||
request:
|
||||
allowPrivateNetwork: true
|
||||
messages:
|
||||
tts:
|
||||
auto: "inbound"
|
||||
provider: "openai"
|
||||
providers:
|
||||
openai:
|
||||
apiKey: "${LITELLM_API_KEY}"
|
||||
baseUrl: "http://litellm.inference.svc:4000/v1"
|
||||
model: "pieced-tts-inbound"
|
||||
voice: "alloy"
|
||||
talk:
|
||||
provider: "openai"
|
||||
providers:
|
||||
openai:
|
||||
apiKey: "${LITELLM_API_KEY}"
|
||||
baseUrl: "http://litellm.inference.svc:4000/v1"
|
||||
model: "pieced-tts-talk"
|
||||
voice: "af_bella"
|
||||
interruptOnSpeech: true
|
||||
tools:
|
||||
media:
|
||||
audio:
|
||||
enabled: true
|
||||
maxBytes: 20971520
|
||||
models:
|
||||
- provider: "openai"
|
||||
model: "pieced-stt"
|
||||
baseUrl: "http://litellm.inference.svc:4000/v1"
|
||||
|
||||
# =====================================================================
|
||||
# CHANNELS — messaging integrations. Each ships a Channels map that
|
||||
# the builder copies into config.channels, env_vars for credentials,
|
||||
# and bindings so messages route to the default agent.
|
||||
# =====================================================================
|
||||
|
||||
telegram:
|
||||
name: Telegram
|
||||
category: channel
|
||||
@@ -62,51 +254,296 @@ data:
|
||||
2. Create app, add bot, copy token and app ID
|
||||
3. Invite bot to server with messages scope
|
||||
|
||||
email:
|
||||
name: Email (Gmail)
|
||||
# Threema via the central PieCed gateway (pieced-threema-gateway in
|
||||
# `threema-gateway` namespace). Differs from a typical channel
|
||||
# package in two important ways:
|
||||
#
|
||||
# 1. No customer-supplied secret. The token + HMAC secret used
|
||||
# by the openclaw-channel-threema-relay plugin are minted by
|
||||
# the relay's /admin/tokens endpoint when the portal enables
|
||||
# the package, then written to the same vault path suffix
|
||||
# below. So `secret_key` here lists the keys the plugin reads;
|
||||
# the WRITER is the portal (POST /api/tenants/:name/threema),
|
||||
# not a customer wizard step.
|
||||
#
|
||||
# 2. Cross-namespace egress to `threema-gateway:8080`. The new
|
||||
# `namespace` field on egress_rules emits a Cilium toEndpoints
|
||||
# rule scoped to that namespace; in-cluster traffic to a
|
||||
# sibling namespace would otherwise be blocked by the
|
||||
# cluster-wide tenant isolation policy.
|
||||
#
|
||||
# The matching cross-namespace INGRESS rule (relay → OpenClaw 18789)
|
||||
# is added by the builder when it sees `channels: { threema: ... }`
|
||||
# in any enabled package.
|
||||
threema:
|
||||
name: Threema
|
||||
category: channel
|
||||
description: Email integration via Gmail IMAP/SMTP
|
||||
description: Threema messaging via the PieCed central gateway
|
||||
channels:
|
||||
email:
|
||||
threema:
|
||||
enabled: true
|
||||
settings:
|
||||
provider: gmail
|
||||
env_vars:
|
||||
- name: EMAIL_ADDRESS
|
||||
secret_key: address
|
||||
vault_path_suffix: email
|
||||
- name: EMAIL_APP_PASSWORD
|
||||
secret_key: app-password
|
||||
vault_path_suffix: email
|
||||
- name: THREEMA_RELAY_URL
|
||||
default: "http://pieced-threema-gateway.threema-gateway.svc:8080"
|
||||
- name: THREEMA_RELAY_TOKEN
|
||||
secret_key: token
|
||||
vault_path_suffix: threema-relay
|
||||
- name: THREEMA_RELAY_HMAC_SECRET
|
||||
secret_key: hmac-secret
|
||||
vault_path_suffix: threema-relay
|
||||
bindings:
|
||||
- match:
|
||||
channel: email
|
||||
channel: threema
|
||||
egress_rules:
|
||||
- host: imap.gmail.com
|
||||
port: 993
|
||||
- host: smtp.gmail.com
|
||||
port: 465
|
||||
- namespace: threema-gateway
|
||||
port: 8080
|
||||
# OpenClaw 2026.5.x loads external plugins from
|
||||
# /data/extensions/<dir>/openclaw.plugin.json. Three gates must
|
||||
# be open for the runtime to register an external plugin:
|
||||
# 1. plugins.enabled: true — feature flag
|
||||
# 2. plugins.allow contains the id — security allowlist
|
||||
# 3. plugins.entries.<id>.enabled: true — per-plugin toggle
|
||||
# Cedric's personal instance.yaml hand-codes the same three gates
|
||||
# for his direct `openclaw-channel-threema` plugin; this patch
|
||||
# generates them automatically for every tenant that enables
|
||||
# threema. The init container that copies the plugin onto the
|
||||
# PVC is emitted by the operator (plugin_image below).
|
||||
config_patch:
|
||||
plugins:
|
||||
enabled: true
|
||||
allow:
|
||||
- "threema"
|
||||
entries:
|
||||
threema:
|
||||
enabled: true
|
||||
config: {}
|
||||
plugin_image:
|
||||
repository: registry.c5ai.ch/pieced/openclaw-channel-threema-relay
|
||||
tag: "0.1.1"
|
||||
target_dir: openclaw-channel-threema-relay
|
||||
customer_instructions: |
|
||||
1. Once enabled, register the Threema IDs you want to receive
|
||||
messages from under "Authorized Users → threema".
|
||||
2. PieCed will route messages between those Threema IDs and
|
||||
your assistant via the central gateway — no Gateway account
|
||||
of your own required.
|
||||
3. Each Threema ID can only belong to one PieCed tenant. If a
|
||||
registration fails, that ID is already claimed elsewhere.
|
||||
disclaimer: >
|
||||
Messages are end-to-end encrypted at the Threema boundary by
|
||||
the PieCed central gateway. Inbound and outbound message
|
||||
counts are logged per tenant for billing.
|
||||
|
||||
web-search:
|
||||
name: Web Search
|
||||
# =====================================================================
|
||||
# SKILLS — ClawHub skill installs. Operator passes each entry through
|
||||
# to spec.skills on the OpenClawInstance.
|
||||
# =====================================================================
|
||||
|
||||
git-cli:
|
||||
name: Git CLI
|
||||
category: skill
|
||||
description: Web search via internal SearXNG
|
||||
description: >
|
||||
Use git from the assistant's shell (clone, commit, push, pull,
|
||||
diff, log, status). For private repositories, configure
|
||||
credentials in your workspace.
|
||||
skills:
|
||||
- "pack:openclaw/skills/web-search@latest"
|
||||
- "openlang-cn/git-cli"
|
||||
egress_rules:
|
||||
- host: github.com
|
||||
port: 443
|
||||
- host: gitlab.com
|
||||
port: 443
|
||||
|
||||
github:
|
||||
name: GitHub (gh CLI)
|
||||
category: skill
|
||||
description: >
|
||||
Interact with GitHub repositories via the gh CLI — issues, PRs,
|
||||
CI runs, releases, gists. Requires a personal access token.
|
||||
skills:
|
||||
- "steipete/github"
|
||||
env_vars:
|
||||
- name: GH_TOKEN
|
||||
secret_key: token
|
||||
vault_path_suffix: github
|
||||
required: true
|
||||
egress_rules:
|
||||
- host: api.github.com
|
||||
port: 443
|
||||
- host: github.com
|
||||
port: 443
|
||||
- host: codeload.github.com
|
||||
port: 443
|
||||
customer_instructions: |
|
||||
1. Open https://github.com/settings/tokens
|
||||
2. Generate a fine-grained personal access token with the
|
||||
repo scopes you want the assistant to use.
|
||||
3. Copy the token (it is shown only once).
|
||||
|
||||
gitea:
|
||||
name: Gitea
|
||||
category: skill
|
||||
description: >
|
||||
Interact with a Gitea instance — repositories, issues, PRs,
|
||||
releases. Defaults to the PieCed-platform Gitea at
|
||||
git.c5ai.ch; supply your own GITEA_URL if you host elsewhere.
|
||||
skills:
|
||||
- "ericxliu1990/gitea"
|
||||
env_vars:
|
||||
- name: GITEA_URL
|
||||
default: "https://git.c5ai.ch"
|
||||
- name: GITEA_TOKEN
|
||||
secret_key: token
|
||||
vault_path_suffix: gitea
|
||||
required: true
|
||||
egress_rules:
|
||||
- host: git.c5ai.ch
|
||||
port: 443
|
||||
customer_instructions: |
|
||||
1. Log in to your Gitea instance (default https://git.c5ai.ch).
|
||||
2. Go to Settings → Applications → Generate New Token.
|
||||
3. Grant the scopes you want the assistant to use (repo, issue,
|
||||
user — minimum needed for most workflows).
|
||||
4. Copy the token.
|
||||
|
||||
whisper-self-hosted:
|
||||
name: Whisper (Self-Hosted Transcription)
|
||||
category: skill
|
||||
description: >
|
||||
Transcribe audio files via the platform's self-hosted Whisper
|
||||
ASR instance. Useful for ad-hoc transcription tasks initiated
|
||||
from chat; channel-level voice intake is handled separately by
|
||||
the Voice CORE feature.
|
||||
skills:
|
||||
- "xavjer/openclaw-self-hosted-whisper"
|
||||
env_vars:
|
||||
- name: WHISPER_URL
|
||||
default: "http://whisper-asr.whisper-asr.svc.cluster.local:9000"
|
||||
|
||||
searxng-local-search:
|
||||
name: Web Search (SearXNG)
|
||||
category: skill
|
||||
description: >
|
||||
Privacy-respecting web search via the platform's internal
|
||||
SearXNG instance. Search the web, images, news, and more
|
||||
without external API calls or trackers.
|
||||
skills:
|
||||
- "noblepayne/searxng-local-search"
|
||||
env_vars:
|
||||
- name: SEARXNG_URL
|
||||
default: "http://searxng.searxng.svc.cluster.local:8080"
|
||||
egress_rules: []
|
||||
|
||||
document-processing:
|
||||
name: Document Processing
|
||||
gog:
|
||||
name: Google Workspace (Gog)
|
||||
category: skill
|
||||
description: PDF, DOCX, spreadsheet processing
|
||||
description: >
|
||||
Bundled access to Gmail, Calendar, Drive, Docs, Sheets, and
|
||||
Contacts via Google OAuth. Setup requires a Google Cloud
|
||||
project and an OAuth client. NOTE: OAuth flow is not yet
|
||||
self-service in the portal — contact PieCed support for
|
||||
credentials onboarding.
|
||||
skills:
|
||||
- "pack:openclaw/skills/document-processing@latest"
|
||||
init_deps:
|
||||
apt:
|
||||
- pandoc
|
||||
- libreoffice-writer-nogui
|
||||
- ffmpeg
|
||||
egress_rules: []
|
||||
- "steipete/gog"
|
||||
env_vars:
|
||||
- name: GOG_CLIENT_ID
|
||||
secret_key: client-id
|
||||
vault_path_suffix: gog
|
||||
required: true
|
||||
- name: GOG_CLIENT_SECRET
|
||||
secret_key: client-secret
|
||||
vault_path_suffix: gog
|
||||
required: true
|
||||
- name: GOG_REFRESH_TOKEN
|
||||
secret_key: refresh-token
|
||||
vault_path_suffix: gog
|
||||
required: true
|
||||
egress_rules:
|
||||
- host: oauth2.googleapis.com
|
||||
port: 443
|
||||
- host: www.googleapis.com
|
||||
port: 443
|
||||
- host: gmail.googleapis.com
|
||||
port: 443
|
||||
- host: calendar.googleapis.com
|
||||
port: 443
|
||||
- host: drive.googleapis.com
|
||||
port: 443
|
||||
- host: docs.googleapis.com
|
||||
port: 443
|
||||
- host: sheets.googleapis.com
|
||||
port: 443
|
||||
- host: people.googleapis.com
|
||||
port: 443
|
||||
customer_instructions: |
|
||||
Google Workspace integration uses OAuth and requires manual
|
||||
onboarding for now. Please open a support ticket to start
|
||||
the setup process — we will exchange the client credentials
|
||||
and a refresh token offline, then enable this package on
|
||||
your tenant.
|
||||
disclaimer: >
|
||||
By enabling Google Workspace integration you authorize PieCed
|
||||
to access Gmail, Calendar, Drive, Docs, Sheets, and Contacts
|
||||
on your behalf. Data flows through Google's APIs subject to
|
||||
Google's terms.
|
||||
|
||||
mail:
|
||||
name: Email (IMAP / SMTP)
|
||||
category: skill
|
||||
description: >
|
||||
Read, search, and manage email via IMAP; send via SMTP. Works
|
||||
with Gmail (with an app password), Outlook, Fastmail, and any
|
||||
standard IMAP/SMTP host. Replaces the previous Gmail-only
|
||||
channel.
|
||||
skills:
|
||||
- "ivangdavila/mail"
|
||||
env_vars:
|
||||
- name: IMAP_HOST
|
||||
secret_key: imap-host
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
- name: IMAP_USER
|
||||
secret_key: imap-user
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
- name: IMAP_PASS
|
||||
secret_key: imap-pass
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
- name: SMTP_HOST
|
||||
secret_key: smtp-host
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
- name: SMTP_USER
|
||||
secret_key: smtp-user
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
- name: SMTP_PASS
|
||||
secret_key: smtp-pass
|
||||
vault_path_suffix: mail
|
||||
required: true
|
||||
# The mail skill connects to tenant-supplied IMAP/SMTP servers on
|
||||
# ports 993 / 465 / 587. The hostnames are not known at catalog
|
||||
# time, so we open these ports to "world" rather than declaring
|
||||
# FQDNs. Trade-off accepted for pilot — see catalog.EgressRule
|
||||
# for the rule shape and rationale.
|
||||
egress_rules:
|
||||
- port: 993
|
||||
world: true
|
||||
- port: 465
|
||||
world: true
|
||||
- port: 587
|
||||
world: true
|
||||
customer_instructions: |
|
||||
1. For Gmail: enable 2-Step Verification, then create an App
|
||||
Password at https://myaccount.google.com/apppasswords and
|
||||
use it as both IMAP and SMTP password.
|
||||
2. For Outlook/Microsoft 365 with MFA: generate an app
|
||||
password under your account's security settings.
|
||||
3. For other providers: refer to their IMAP/SMTP documentation
|
||||
for host names and ports.
|
||||
4. Typical IMAP_HOST values: imap.gmail.com, outlook.office365.com.
|
||||
5. Typical SMTP_HOST values: smtp.gmail.com, smtp.office365.com.
|
||||
disclaimer: >
|
||||
The assistant gains read/write access to the mailbox you
|
||||
configure. Use a dedicated address rather than a personal
|
||||
inbox if you want to limit scope.
|
||||
|
||||
@@ -87,6 +87,18 @@ spec:
|
||||
suspend:
|
||||
type: boolean
|
||||
description: Stops reconciliation without deleting resources.
|
||||
openClawImage:
|
||||
type: object
|
||||
description: >
|
||||
Per-tenant override for the OpenClaw container image
|
||||
tag. When unset, the operator uses the platform
|
||||
default from the pieced-openclaw-config ConfigMap.
|
||||
Set by platform admins via the portal; customer-
|
||||
facing onboarding does not expose this field.
|
||||
properties:
|
||||
tag:
|
||||
type: string
|
||||
description: Image tag (e.g. "2026.4.22").
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
@@ -123,6 +135,25 @@ spec:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
suspendedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
warnings:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- source
|
||||
properties:
|
||||
source:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
since:
|
||||
type: string
|
||||
format: date-time
|
||||
observedGeneration:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{{/*
|
||||
Platform-wide default OpenClaw image tag. Used by the operator when a
|
||||
PiecedTenant has no explicit `spec.openClawImage.tag` override.
|
||||
|
||||
Tag-only by design — see internal/openclawconfig/loader.go for
|
||||
rationale (single image-selector field avoids SSA field-ownership
|
||||
ambiguity). For reproducibility-critical deployments, pin by using
|
||||
an immutable release tag.
|
||||
|
||||
If `defaultTag` is empty (or this ConfigMap doesn't exist), the
|
||||
operator falls back to a hardcoded built-in version.
|
||||
|
||||
Tenants without an `openClawImage` override automatically follow
|
||||
changes to this ConfigMap on the next reconcile — the operator
|
||||
watches it and re-enqueues affected tenants.
|
||||
*/}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pieced-openclaw-config
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: pieced-operator
|
||||
data:
|
||||
defaultTag: {{ .Values.openClaw.defaultTag | quote }}
|
||||
@@ -8,9 +8,17 @@ metadata:
|
||||
app.kubernetes.io/name: pieced-operator
|
||||
rules:
|
||||
# --- PiecedTenant CRD ---
|
||||
# `delete` is required so the operator can self-initiate the post-
|
||||
# 60-day cleanup of suspended tenants (Bug 37b). Without it, the
|
||||
# `r.Delete(ctx, tenant)` call in the suspend block fails with a
|
||||
# 403 every reconcile cycle while the tenant sits past its
|
||||
# retention window. Until then this verb wasn't strictly needed —
|
||||
# the customer/portal initiated CR deletes, and the operator's
|
||||
# finalizer ran cleanup; only with operator-initiated deletion did
|
||||
# the missing verb become a problem.
|
||||
- apiGroups: ["pieced.ch"]
|
||||
resources: ["piecedtenants"]
|
||||
verbs: ["get", "list", "watch", "update", "patch"]
|
||||
verbs: ["get", "list", "watch", "update", "patch", "delete"]
|
||||
- apiGroups: ["pieced.ch"]
|
||||
resources: ["piecedtenants/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
@@ -34,29 +42,34 @@ rules:
|
||||
verbs: ["create", "patch"]
|
||||
|
||||
# --- Capsule Tenant ---
|
||||
# `patch` is required for server-side apply (SSA) — controller-runtime's
|
||||
# `client.Apply` uses HTTP PATCH with content-type application/apply-patch+yaml.
|
||||
# We keep `update` for backwards-compat in case any code path still does
|
||||
# replace-style writes (currently none). Same applies to all unstructured
|
||||
# resources below.
|
||||
- apiGroups: ["capsule.clastix.io"]
|
||||
resources: ["tenants"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "delete"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
# --- ESO SecretStore ---
|
||||
- apiGroups: ["external-secrets.io"]
|
||||
resources: ["secretstores"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "delete"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
# --- ESO ExternalSecret ---
|
||||
- apiGroups: ["external-secrets.io"]
|
||||
resources: ["externalsecrets"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "delete"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
# --- Cilium CiliumNetworkPolicy ---
|
||||
- apiGroups: ["cilium.io"]
|
||||
resources: ["ciliumnetworkpolicies"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "delete"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
# --- OpenClaw OpenClawInstance ---
|
||||
- apiGroups: ["openclaw.rocks"]
|
||||
resources: ["openclawinstances"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "delete"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
# --- Leader election (coordination) ---
|
||||
- apiGroups: ["coordination.k8s.io"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
image:
|
||||
repository: registry.c5ai.ch/pieced/pieced-operator
|
||||
tag: "0.1.23"
|
||||
tag: "0.1.52"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets:
|
||||
@@ -56,3 +56,23 @@ serviceAccount:
|
||||
# Network policy — restrict operator egress to only what it needs
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
|
||||
# OpenClaw image default (Feature: per-tenant version overrides).
|
||||
#
|
||||
# Materialised as the `pieced-openclaw-config` ConfigMap, which the
|
||||
# operator reads on every reconcile. Per-tenant overrides set via the
|
||||
# portal (PiecedTenant.spec.openClawImage.tag) take precedence over
|
||||
# this default for the affected tenants.
|
||||
#
|
||||
# We support tag-only (not digest) by design — a single image-selector
|
||||
# field avoids SSA field-ownership ambiguity when switching values,
|
||||
# and the downstream OpenClaw operator handles a tag-only image spec
|
||||
# unambiguously. For reproducibility-critical deployments, pin by
|
||||
# using an immutable release tag.
|
||||
#
|
||||
# Empty defaultTag falls back to the operator's built-in version.
|
||||
# Admins can edit this value at runtime via the portal admin UI;
|
||||
# the resulting ConfigMap edits trigger reconciles for every tenant
|
||||
# that doesn't have its own override.
|
||||
openClaw:
|
||||
defaultTag: "2026.4.22"
|
||||
|
||||
Reference in New Issue
Block a user