workload — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited workload (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Tool availability: some MCP tools named here live in thefulltoolset profile — if one is not advertised on this connection, tell the user to reconnect the MCP server with?toolsets=full(or use thecplnCLI fallback). Reads and deletes work on every profile via the genericlist_resources/get_resource/delete_resourcetools.
A workload is Control Plane's unit of deployment: one or more containers plus how they scale, get exposed, store data, and stay healthy. This skill carries the must-know primary rules for safely creating, updating, and running a workload.
Need more detail on one subject? This skill covers the common case; for depth on a single topic, load the matching skill from the Deep-dive router at the end — you may load one or several, as the task spans. If this plugin is installed in your agent, the skill files are already available — open the relevant skill(s) directly. If you are using the Control Plane MCP server without the plugin, call get_cpln_skill with the skill name instead.
create_workload defaults the type to `standard` when you don't specify one and covers all four types — serverless / standard / stateful, and cron by setting `type: cron` (which makes schedule required). Type is chosen at creation and is immutable (see Immutability below). Pick from:
| standard (default) | serverless | stateful | cron | |
|---|---|---|---|---|
| Use for | long-running services, APIs, workers | request/event-driven HTTP that scales on demand | databases & anything needing stable disk or per-replica identity | scheduled jobs |
| Autoscaling metrics | cpu, memory, latency, rps, multi, keda, disabled | concurrency, cpu, memory, rps, disabled | cpu, memory, latency, rps, multi, keda, disabled | n/a — runs on a schedule |
| Capacity AI | on by default | on by default | not applied | not applied |
| Probes | define readiness + liveness | define readiness + liveness | define readiness + liveness | ignored |
ext4/xfs volumes | no | no | yes (only here) | no |
shared volumes | yes | yes | yes | yes |
| Scale to zero | KEDA only | yes | KEDA only | n/a |
Default minScale | 1 | 1 | 1 | n/a |
The intended scaling metric can decide the type: concurrency scaling exists only on serverless — if that's the intent, create the workload as serverless (type is immutable); on standard/stateful the closest equivalent is rps. Never pair a metric with a type that rejects it.
A workload has 1–8 containers.
There is ONE way to express each concept. Containers always go in the typed containers[] array (there are no flat image/cpu/port fields), scaling always goes in the single autoscaling block, and cron is `create_workload` / `update_workload` with `type: cron` — the schedule + job policy become available (and required), while autoscaling/capacityAI/timeoutSeconds/debug do not apply to cron and are rejected. The advanced blocks below were split into dedicated configure_workload_* tools to keep the common path lean.
| Spec block | What it controls | Set with |
|---|---|---|
containers[] — image, ports, cpu/memory, env, command/args, probes, metrics, volumes | the container(s) — the only way to define them | create_workload / update_workload (all types, cron included) |
autoscaling (→ spec.defaultOptions.autoscaling) + capacityAI / timeoutSeconds / suspend / debug scalars | scaling & resource optimization | create_workload / update_workload |
firewallConfig (or the public shortcut) | inbound/outbound/internal exposure | create_workload / update_workload (all types, cron included) |
schedule + cron policy (concurrencyPolicy, historyLimit, restartPolicy, activeDeadlineSeconds) | cron schedule & job policy | create_workload / update_workload with `type: cron` |
loadBalancer (direct / geo / replicaDirect) | custom ports, static IPs, geo headers | configure_workload_load_balancer |
sidecar.envoy | Envoy filter chain (e.g. JWT auth) | configure_workload_sidecar |
extras | BYOK-only affinity / tolerations / topology | configure_workload_extras |
localOptions (incl. spot, multiZone, capacityAIUpdateMinutes) | per-location overrides of defaultOptions | configure_workload_local_options |
rolloutOptions | graceful termination, surge/unavailable | configure_workload_rollout |
securityOptions | runAsUser, filesystemGroupId | configure_workload_security |
requestRetryPolicy | request retry attempts / conditions | configure_workload_retry |
update_workload merges containers[] by name — send only the container(s) you want to change; others are preserved (an unknown name adds a container). On a cron workload, update_workload patches the schedule / job policy / suspend / containers (and rejects autoscaling/capacityAI/timeoutSeconds/debug); schedule/job fields are rejected against a non-cron workload. Always call get_resource_schema for the workload kind before authoring a spec — never hand-write fields from memory.
Platform defaults are not a production design. For any real workload:
1; use 1 only with a named reason (single-writer DB, leader election, dev/staging). stateful is often correct at 1.serverless scales to zero directly; standard/stateful only with metric: keda; cron cannot.50m / 128Mi). Floors: CPU ≥ 25m, memory ≥ 32Mi. Keep memory(MiB) / cpu(millicore) ≤ 8 (raise to 32 with the tag cpln/relaxMemoryToCpuRatio).public: true or firewallConfig); creating closed and patching the firewall open afterward is a spec error, not a workflow.disabled / none / 1 / public — surface the conflict with realistic alternatives and a recommendation.//image/api:v1.0) — the preferred form.nginx:latest) — never add a docker.io/ prefix. ECR/GCR/etc. use their full host path.<your-org>.registry.cpln.io/NAME:TAG form also resolves, but for your own org prefer //image/NAME:TAG; the hostname form is mainly used by docker login / docker push.exec format error.spec.pullSecretLinks); only docker, ecr, and gcp secret types work as pull secrets. Same-org //image/... needs none.cpln image build --push); over MCP, images are list/get/delete only (mcp__cpln__list_resources / mcp__cpln__get_resource / mcp__cpln__delete_resource, kind="image").Run an actual container image — not an inline/base64/heredoc app on a generic base image. For databases, caches, queues, brokers, search, gateways, or other common infrastructure, install a Template Catalog entry first (mcp__cpln__browse_templates → mcp__cpln__install_template) rather than hand-building.
exec / grpc / tcpSocket / httpGet. Tune initialDelaySeconds to real cold-start time (readiness default 10s, liveness default 60s; periodSeconds default 10s).mcp__cpln__list_deployments until all locations report ready (it surfaces per-location errors and the workload's canonical public URL). Then give the user that canonical URL — never construct one or report a per-location deployment URL as the address. For a public workload, do not stop at "ready" — confirm it actually serves: make a real HTTP GET of the canonical endpoint (when you have that capability) and read the result — never claim reachability without a real response you received; if you cannot make a request, report readiness confirmed but external reachability not independently verified. A ready deployment can still be unreachable — firewall inbound unset, or TLS/DNS still propagating. Treat 2xx/3xx/401/403 as serving; a timeout/refused points first at firewall inbound, a TLS/DNS error at propagation (wait, don't redeploy). On failure, diagnose with mcp__cpln__get_workload_events (probe/scheduling reasons) then mcp__cpln__get_workload_logs (app error); pass the optional location to list_deployments (e.g. aws-us-east-1) to inspect ONE location's deployment in full detail. Never re-apply an unchanged failing spec, and don't poll in a tight loop.Set via spec.defaultOptions.autoscaling.metric; the system keeps the metric near but below target (default 95; capped at 100 for cpu/memory). If metric is omitted, serverless defaults to concurrency and standard/stateful default to cpu. Picker:
metricPercentile).cpu/memory/rps; mutually exclusive with metric/target).spec.keda.enabled: true on the GVC; target is rejected with keda.minScale.The metric must be valid for the workload type (the matrix above) or the spec is rejected — e.g. concurrency on a standard workload is rejected (it is serverless-only). Match the metric to the workload's traffic shape: rps/concurrency for HTTP, cpu/memory for compute-bound work, latency for SLO-driven APIs. For tuning targets/percentiles, multi-metric, KEDA, scale-to-zero, or Capacity AI, load the autoscaling-capacity skill.
Capacity AI auto-tunes CPU/memory between minCpu/minMemory and cpu/memory. On by default for standard and serverless; not applied to stateful or cron. It is rejected with the `cpu` metric (when explicitly enabled) and with GPUs.
inboundAllowType: none) are all blocked until configured. Blocked CIDRs beat allowed; CIDR rules beat hostname rules.outboundAllowPort replaces that set (re-list 80/443 if still needed). Private RFC1918/CGNAT ranges in outboundAllowCIDR are silently ignored on managed locations — reaching private networks takes a wormhole agent (native-networking).http://WORKLOAD.GVC.cpln.local:PORT (the sidecar adds mTLS — never https://). Same-GVC is free; cross-GVC needs inboundAllowType: same-org (or an explicit workload-list) and incurs egress.WORKLOAD.GVC.cpln.app serves a single port — the first container port. standard/stateful may expose more ports across containers (unique numbers), reachable at WORKLOAD.GVC.cpln.local:PORT or via a direct/dedicated load balancer; serverless is limited to one container / one port. WORKLOAD.GVC.cpln.app is the URL shape only — always report the actual canonical URL from list_deployments / the workload's status.canonicalEndpoint; never construct or guess it (custom domains, BYOK, and alias suffixes make the literal form wrong).ports: [{ number: 80, protocol: "http" }]; for a single port use a one-element array. The legacy scalar containers[].port field is deprecated — never use it, even if get_resource_schema still lists it (the platform keeps it for backward compatibility, but new specs must use ports[]).externalPort 22–32768, optional static IPs via an IP set, geo headers; set with configure_workload_load_balancer · dedicated — per-GVC custom domains and wildcard hosts; a GVC setting, enabled with update_gvc. firewallConfig stays on create_workload / update_workload. Toggling direct/dedicated needs the configureLoadBalancer permission — edit does not imply it (ipset-load-balancing skill).ext4 / xfs / shared) and performance class are immutable — set at creation.ext4/xfs mount on stateful only; shared mounts on any type. Up to 15 volumes per container, and no two mounts in a container may share a path or nest (one mount path cannot be a parent of another). Reserved mount paths (rejected): /dev, /dev/log, /tmp, /var, /var/log.ext4/xfs only.mcp__cpln__mount_volumeset_to_workload.cpln://secret/NAME (or cpln://secret/NAME.key for a keyed/dictionary secret) — or mounted as a volume with uri: cpln://secret/NAME. Either way the workload still needs all three pieces: an identity on the workload, a policy granting reveal, and the reference — or access fails silently. mcp__cpln__workload_reveal_secret sets the identity + policy but not the reference, and it requires the workload to already exist — for a new workload, create_workload first (its deployment pauses on the secret reference until access is granted, then resumes); never call workload_reveal_secret before the workload exists.CPLN_TOKEN, CPLN_ENDPOINT, CPLN_GLOBAL_ENDPOINT, CPLN_ORG, CPLN_GVC, CPLN_GVC_ALIAS, CPLN_LOCATION, CPLN_PROVIDER, CPLN_WORKLOAD, CPLN_WORKLOAD_VERSION, CPLN_IMAGE, CPLN_NAME (plus CPLN_MAIN on the first container, and PORT on standard when unset). K_SERVICE / K_CONFIGURATION / K_REVISION are also disallowed. Names match ^[-._a-zA-Z][-._a-zA-Z0-9]*$ (max 120 chars).curl -H "Authorization: Bearer $CPLN_TOKEN" $CPLN_ENDPOINT/org/$CPLN_ORG/... — CPLN_ENDPOINT is plain http (the sidecar secures and signs it in transit). Requests act as the attached spec.identityLink identity and succeed only where a policy grants that identity the permission — no identity attached or no policy means 403. The token works only from inside that workload, against `CPLN_ENDPOINT`: it does not authenticate to api.cpln.io, metrics.cpln.io, or logs.cpln.io (use a service-account key there).istio-proxy are reserved). Names are lowercase ^[a-z]([-a-z0-9])*[a-z0-9]$, max 64.-headless, and is immutable.preStop runs sh -c "sleep N". Minimal/distroless images often lack sleep — if it (or a custom preStop) fails in any container, all containers are SIGKILL'd immediately. Grace period is spec.rolloutOptions.terminationGracePeriodSeconds (0–900, default 90).8012, 8022, 9090, 9091, 15000, 15001, 15006, 15020, 15021, 15090, 41000. Valid container port range is 80–65535; port numbers must be unique across all containers. A serverless workload must expose exactly one port, on exactly one container. Declare every port in the containers[].ports array ([{ number, protocol }]) — the scalar containers[].port field is deprecated; do not use it.runAsUser: 1337 has its outbound traffic excluded from the Envoy sidecar redirect, so it bypasses the mesh — losing mTLS and firewall enforcement (it gets unfiltered egress, not "no networking").WORKLOAD.GVC.cpln.app, the internal DNS WORKLOAD.GVC.cpln.local, and policy targetLinks / identity bindings. Recreating with the same name preserves the URL/DNS; a different name silently breaks every external reference.spec.containers[].metrics with port (required) and path (default /metrics).mcp__cpln__list_metrics (discover real names/labels) → mcp__cpln__query_metrics (PromQL). Confirm a signal exists before changing scaling.mcp__cpln__list_workload_replicas → mcp__cpln__workload_exec runs ONE command in a replica. It is the highest-risk tool: audited, and it hits a replica serving live traffic.
ls, cat, env, df, curl localhost) are fine.cpln workload exec for those).pg_dump | psql restore — regardless of your approval, before the call reaches Control Plane), run the identical command via cpln workload exec from a trusted shell. The platform enforces the same authorization; only the client differs. This is an expected client limitation, not a Control Plane error — report it plainly and offer the CLI command rather than retrying the blocked tool.mcp__cpln__get_cpln_rules has the cross-cutting operating guide if you have not read it this session.mcp__cpln__get_resource_schema for the workload kind before authoring.mcp__cpln__list_resources (kind="workload") / mcp__cpln__get_resource (kind="workload").mcp__cpln__create_workload / mcp__cpln__update_workload (for a scheduled job, pass type: cron with a schedule; PATCH — only sent fields change, containers merged by name), plus configure_workload_* for load balancer / sidecar / extras / local options / rollout / security / retry.mcp__cpln__list_deployments until every location is ready; on failure diagnose with events → logs and fix.list_deployments or the workload's status.canonicalEndpoint; never construct/guess it or report a per-location URL as the address).| Tool | Purpose |
|---|---|
mcp__cpln__create_workload | Create any workload (typed containers[], single autoscaling block) — including a scheduled job with type: cron + a required schedule. |
mcp__cpln__update_workload | Update a workload (PATCH; containers merged by name) — on a cron workload, patches schedule / job policy / suspend. |
mcp__cpln__get_resource (kind="workload") / mcp__cpln__list_resources (kind="workload") | Read one / list in a GVC (capture state before changes). |
mcp__cpln__delete_resource (kind="workload") | Delete a workload (destructive — confirm blast radius first). |
mcp__cpln__configure_workload_load_balancer | Set/clear spec.loadBalancer (direct, geo headers, replicaDirect). |
mcp__cpln__configure_workload_sidecar | Set/clear spec.sidecar.envoy (Envoy filters, JWT auth). |
mcp__cpln__configure_workload_extras | Set/clear spec.extras (BYOK affinity/tolerations/topology). |
mcp__cpln__configure_workload_local_options | Set/clear spec.localOptions (per-location overrides). |
mcp__cpln__configure_workload_rollout | Set/clear spec.rolloutOptions (graceful termination, surge/unavailable). |
mcp__cpln__configure_workload_security | Set/clear spec.securityOptions (runAsUser, filesystemGroupId). |
mcp__cpln__configure_workload_retry | Set/clear spec.requestRetryPolicy (retry attempts/conditions). |
mcp__cpln__list_deployments | PRIMARY post-deploy readiness monitor (all locations); per-location errors and the canonical public URL to report. Pass the optional location (e.g. aws-us-east-1) for ONE deployment's full detail — version chain, per-container readiness, full JSON. |
mcp__cpln__get_workload_events | Probe/scheduling failures after a bad deploy. |
mcp__cpln__get_workload_logs | App-side logs (LogQL) for runtime/startup errors. |
mcp__cpln__list_workload_replicas → mcp__cpln__workload_exec | List replicas, then run one command in one. |
mcp__cpln__workload_start_cron | Trigger an out-of-band run of a cron workload. |
mcp__cpln__workload_reveal_secret | Grant an existing workload secret access (identity + reveal policy; you still add the reference — create the workload first). |
mcp__cpln__mount_volumeset_to_workload | Attach a volume set to a stateful workload. |
CLI fallback (read the cpln skill first): use when MCP is unavailable/unauthenticated, for interactive work (cpln workload exec, cpln workload connect, port-forward), image build/copy, or as the primary interface in CI/CD (CPLN_TOKEN + cpln apply --ready).
Raw API escape hatch: for a spec field no typed create_workload / update_workload / configure_workload_* tool exposes, use mcp__cpln__cpln_api_request (raw GET/POST/PATCH/DELETE; disabled by default — only when advertised) — call mcp__cpln__get_resource_schema first for the exact path and body, and prefer the typed tools whenever they cover the field. If it is not advertised, apply the full manifest with the cpln CLI instead.
Load the matching skill (one or several) when you need more than the primary rules above — open it directly if this plugin is installed, otherwise fetch it with get_cpln_skill:
| Need | Skill |
|---|---|
| Image refs, builds, buildpacks, registries, pull secrets, cross-org sharing | image |
| Autoscaling, Capacity AI, scale-to-zero, KEDA, custom-metric scaling | autoscaling-capacity |
| Probes in depth, JWT/Envoy auth, security options, graceful termination | workload-security |
| Firewall rules, inbound/outbound, header & geo filtering | firewall-networking |
| Static IPs, direct & dedicated load balancers, custom ports | ipset-load-balancing |
| CDN caching, request rate limiting, DDoS protection | cdn-rate-limiting |
| Volumes, volume sets, snapshots, persistence, expansion | stateful-storage |
| Metrics, PromQL, Grafana, Prometheus federation | metrics-observability |
| Logs, LogQL, events, per-execution cron logs | logql-observability |
| Private networking, agents, VPC, on-prem connectivity | native-networking |
| Databases, caches, queues, brokers, common infra | template-catalog |
| Secrets, identities, policies, RBAC, service accounts | access-control |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.